交叉编译工具链

在这里插入图片描述

笔者最近在工作中接触到交叉编译工具链,由于刚接触,一切都比较陌生,所以整理了相关资料,和大家一起学习。

1. 简介

1.1 嵌入式系统

了解嵌入式系统的朋友,对交叉编译应该不会陌生。

嵌入式系统由硬件和软件组成.是能够独立进行运作的器件。其软件内容只包括软件运行环境及其操作系统。硬件内容包括信号处理器、存储器、通信模块等在内的多方面的内容。相比于一般的计算机处理系统而言,嵌入式系统存在较大的差异性, 它不能实现大容量的存储功能,因为没有与之相匹配的大容量介质,大部分采用的存储介质有E-PROM、EEPROM 等, 软件部分以API编程接口作为开发平台的核心。

在这里插入图片描述

嵌入式系统的特点

  • 系统内核小

      由于嵌入式系统一般是应用于小型电子装置的,系统资源相对有限,所以内核较之传统的操作系统要小得多。
      比如Enea公司的OSE分布式系统,内核只有5K。
    
  • 专用性强

      嵌入式系统的个性化很强,其中的软件系统和硬件的结合非常紧密,一般要针对硬件进行系统的移植,即使在同一品牌、同一系列的产品中也需要根据系统
      硬件的变化和增减不断进行修改。同时针对不同的任务,往往需要对系统进行较大更改,程序的编译下载要和系统相结合,
      这种修改和通用软件的“升级”是完全两个概念。
    
  • 系统精简

      嵌入式系统一般没有系统软件和应用软件的明显区分,不要求其功能设计及实现上过于复杂,这样一方面利于控制系统成本,同时也利于实现系统安全。
    
  • 高实时性

      高实时性的系统软件(OS)是嵌入式软件的基本要求。而且软件要求固态存储,以提高速度;软件代码要求高质量和高可靠性。
    
  • 多任务的操作系统

      嵌入式软件开发要想走向标准化,就必须使用多任务的操作系统。嵌入式系统的应用程序可以没有操作系统直接在芯片上运行;但是为了合理地
      调度多任务、利用系统资源、系统函数以及和专用库函数接口,用户必须自行选配RTOS(Real-Time Operating System)开发平台,这样才
      能保证程序执行的实时性、可靠性,并减少开发时间,保障软件质量。
    
  • 需要开发工具和环境

      嵌入式系统开发需要开发工具和环境。由于其本身不具备自主开发能力,即使设计完成以后用户通常也是不能对其中的程序功能进行修改的,
      必须有一套开发工具和环境才能进行开发,这些工具和环境一般是基于通用计算机上的软硬件设备以及各种逻辑分析仪、混合信号示波器等。
      开发时往往有主机和目标机的概念,主机用于程序的开发,目标机作为最后的执行机,开发时需要交替结合进行。
    

1.2 交叉编译

由于开发时需要在主机和目标机之间交替结合进行,所以就有了交叉编译的概念

嵌入式软件开发所采用的编译为交叉编译。所谓交叉编译就是在一个平台上生成可以在另一个平台上执行的代码。因此,不同的CPU需要有相应的编译器,而交叉编译就如同翻译一样,把相同的程序代码翻译称不同的CPU对应语言。要注意的是,编译器本身也是程序,也要在与之对应的某一个CPU平台上运行

主机,目标机,交叉编译器
编译器是将源代码转换为可执行代码的程序。像所有程序一样,编译器运行在特定类型的计算机上,输出的新程序也运行在特定类型的计算机上。

运行编译器的计算机称为主机,运行新程序的计算机称为目标机。当主机和目标是同一类型的机器时,编译器是本机编译器。当主机和目标机不同时,编译器是 交叉编译器

1.3 为什么要交叉编译?

某些设备构建程序的PC,用户可以获得适当的目标硬件(或模拟器),启动 Linux Release版,在该环境中进行本地编译。这是一种有效的方法(在处理 Mac Mini时甚至可能是一个好主意),但对于 linksys 路由器,或 iPod,有一些突出的缺点:

  • 速度- 目标平台通常比主机慢一个数量级或更多。大多数专用嵌入式硬件是为低成本和低功耗而设计的,而不是高性能。由于在高性能桌面硬件上运行,现代模拟器(如 qemu)实际上比模拟的许多现实世界的硬件要快。
  • 性能- 编译非常耗费资源。目标平台通常没有台式机GB 内存和数百 GB 磁盘空间;甚至可能没有资源来构建“hello world”,更不用说大而复杂的包了。
  • 可用性-未运行过的硬件平台上运行 Linux,需要交叉编译器。即使在 Arm 或 Mips 等历史悠久的平台上,给定目标找到最新的全功能预构建本机环境很困难。如果平台通常不用作开发工作站,可能没有现成的最新预构建Release版,如果有,则可能已经过时。如果必须先为目标构建Release版,才能在目标上进行构建,无论如何都将返回交叉编译。
  • 灵活性- 功能齐全的 Linux Release版,由数百个软件包组成,但交叉编译环境可以从大多数方面依赖于主机的现有Release版。交叉编译的重点是构建要部署的目标包,不是花时间获取在目标系统上运行的仅构建先决条件。
  • 方便-用户界面不友好,debug构建中断不方便。从 CD 安装到没有 CD-ROM 驱动器的机器上,在测试环境和开发环境之间来回重新启动。

1.4 交叉编译流程

根据编写语言的不同,又分为以下几套流程:

  • C/C++程序编译过程
    整个过程分为四个阶段:预处理(Pre-Processing)、编译(Compilation)、汇编(Assembling)、链接(Linking)
  • 纯C语言的编译链接过程
    把编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。

    编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。
    链接是把目标文件、操作系统的启动代码和用到的库文件进行组织,形成最终生成可执行代码的过程。

在这里插入图片描述

使用gcc编译

1. 预编译,将程序中的宏定义等预编译
2. 编译,将`*.h,*.c`等文件编译成为`*.o`文件
3. 汇编
4. 链接,将*.o文件连接库,生成可执行文件。

例如,设有a.c,b.c,c.c
gcc -c a.c
gcc -c b.c
gcc -c c.c
(这样生成a.o b.o c.o)
gcc -o main a.o b.o c.o
(这样就生成名为:main的执行档了)
./main
就可以执行

2. GCC

GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了

2.1 GCC、gcc、g++三者关系

gcc(小写,GNU C Compiler)是GCC中的c编译器,而g++(GNU C++ Compiler)是GCC中的c++编译器。gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++

2.2 gcc编译过程

用gcc编译*.c文件并非直接生成可执行文件,中间还经历了预处理、编译、汇编和链接几个过程
在这里插入图片描述

  • 在预处理阶段,gcc会把需要调用的头文件包含进来,替换宏常量和宏代码段
  • 在编译阶段,gcc会检查代码的规范性、是否有语法错误等,在检查无误后,gcc会把文件翻译成 .s 后缀的汇编文件
  • 在汇编阶段,gcc会把 .s 后缀的汇编文件 翻译成 .o后缀的目标文件(机器可识别的二进制文件)
  • 在链接阶段,gcc会把目标文件链接到库中,生成可执行文件

2.3 生成文件类型

在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。而gcc则通过后缀来区别输入文件的类别,下面介绍gcc所遵循的部分约定规则

后缀名说明
.cC语言源代码文件;
.a是由目标文件构成的库文件;
.C、.cc或.cxx 是C++源代码文件;
.h是程序所包含的头文件;
.i 是已经预处理过的C源代码文件;
.m是Objective-C源代码文件;
.o是编译后的目标文件;
.s是汇编语言源代码文件;
.S是经过预编译的汇编语言源代码文件

2.4 gcc编译选项

选项作用
E激活预处理;头文件、宏等展开(.i文件)
S激活预处理、编译;生成汇编代码(.s文件)
c激活预处理、编译、汇编;生成目标文件(.o文件)
激活预处理、编译、汇编、链接;生成可执行文件(.out文件)
o生成目标
Wall打开编译告警(所有)
g嵌入调试信息,方便gdb调试
llib链接 lib 库 (这里是小写 L ) 相当于 C++ #pragma comment(lib, “xxx.lib”)
Idir增加 include 目录 (这里是大写 i ) 头文件路径
LDir增加 lib 目录 (编译静态库和动态库)

2.5 示例

  1. 编写程序
[root@node-252 yurq]# cat hello.c
#include<stdio.h>

int main()
{
    printf("hello world!\n");
    return 0;
}
  1. 预处理
[root@node-252 yurq]# gcc -E hello.c -o hello.i

预编主要前面处理带“#”的指令,如#include、#define等,还要删除注释,添加行号和文件名标识

  1. 编译
[root@node-252 yurq]# gcc -S hello.i -o hello.s

编译过程是把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件

  1. 汇编
[root@node-252 yurq]# gcc -c hello.s -o hello.o

将代码转换成机器可以执行的指令

  1. 连接
    连接是一个复杂的过程,使用ld指令(ld :GNU链接器)
ld -static crt1.o crti.o crtbeginT.o hello.o -start -group -lgcc -lgcc_eh -lc -end-group crtend.o crtn.o

连接的过程包括按序叠加、相似段合并、符号地址的确定、符号解析与重定位、指令修正、全局构造与解析等等

gcc通过-o*.c预处理、汇编、编译并链接形成可执行文件。

[root@node-252 yurq]# gcc -o hello hello.o
[root@node-252 yurq]# ll
total 44
-rwxr-xr-x 1 root root  8360 Jun 25 18:35 hello
-rw-r--r-- 1 root root    78 Jun 25 18:32 hello.c
-rw-r--r-- 1 root root 16880 Jun 25 18:32 hello.i
-rw-r--r-- 1 root root  1496 Jun 25 18:32 hello.o
-rw-r--r-- 1 root root   449 Jun 25 18:32 hello.s
[root@node-252 yurq]# ./hello
hello world!

3. GNU 连接器(ld)

  • 连接器的功能:是将一个可执行程序所需的目标文件和库最终整合为一体。一个程序或库通常都包含三个段:.text.data.bss段。除了将程序和库的各段合并在一起的功能以外,还要完成重定位的工作。

  • 重定位:当一个源文件被编译成目标文件时,此时目标文件的各段没有具体地址,在目标文件中只记录了程序中的符号和各符号在段中的相对位置。当连接器链接生成可执行程序时,需要安排每个符号在内存中的真实地址,从而实现真正的函数调用和变量引用功能,这一动作就是重定位。而连接器如何知道一个目标文件中的各个段应当放在哪一地址的呢?这就是链接脚本的作用。

  • 链接脚本:为了了解链接脚本到底长什么样子,我们可以查看linux-kernel/arch/arm/vmlinux.lds.S编译之后的vmlinux.lds。链接脚本的功能就是告诉连接器,如何将各个不同的目标文件(包括库)中的段合在一起并最终生成一个可执行程序。它一方面需要描述输出,即最终输出到可执行程序文件中段;另一方面又要描述输入,即来自各个目标文件中的段。

ld命令是GNU的连接器,将目标文件连接为可执行程序。很少单独使用ld命令对目标进行连接操作,通常都是使用gcc命令在编译后自动进行连接。

语法格式:

NAME
       ld - The GNU linker

SYNOPSIS
       ld [options] objfile ...

常用参数:

-o 指定输出文件名
-e 指定程序的入口符号

查看版本

[root@node-252 yurq]# ld -version
GNU ld version 2.27-44.base.el7_9.1
Copyright (C) 2016 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.

参考实例:
将文件“/lib/linuxcool_1.o”与“ linuxcool_2 .o”和库“linuxcool_3.a”链接起来,生成一个名为 linuxcool_4的文件,该文件将来自标准搜索目录:

[root@lmlphp.com ~]# ld -o linuxcool_4 /lib/linuxcool_1.o linuxcool_2 .o -lc

4. 详述编译和链接

一个完整的 C 语言项目可能包含多个 .c 源文件,项目的运行需要经过“编译”和“链接”两个过程:

  • 编译由编译器逐个对 所有源文件 做词法分析、语法分析、语义分析等操作,最终生成 多个目标文件。每个目标文件都是二进制文件,但由于它们会相互调用对方的函数或变量,还可能会调用某些链接库文件中的函数或变量,编译器无法跨文件找到它们确切的存储地址,所以这些目标文件无法单独执行。
  • 链接:对于各个目标文件中缺失的函数和变量的存储地址(后续简称“缺失的地址”),由链接器负责修复,并最终将所有的目标文件和链接库组织成一个可执行文件

4.1 库和库的链接

  • 静态函数库(static libraries):在编译期间(compile-time)静态链接库会全部拷贝进编译对象中。
  • 动态函数库(shared libraries):在程序启动的时候加载到程序中,它可以被不同的程序共享。

预编译的时候要处理头文件,因此gcc -E的时候需要通过 -I(大写的i )来指定头文件搜索路径。
查找路径链接的时候需要各个库,因此gcc -o的时候需要通过-L指定库文件搜索路径,以及-l(小写的L)来指定具体的库文件名称。

编译时,加上“-L <库文件目录>”这样的选项,用来指定库目录;
编译时,加上“-l abc”这样的选项,用来指定库文件libabc.so。

所以说自己写的头文件和主函数放在一起,然后编译、链接是不会出错的,这是因为""的头文件就默认寻找范围就是当前文件夹,所以找得到;
如果你把头文件放在另外一个文件夹中,就需要通过-L dir去指定一下库文件位置。总之,就是想办法让程序找到库文件。

4.2 路径相关

头文件

符号说明
<>默认在指定目录查找,由工具链指定的目录
<>-l dir自行指定的路径,如果默认的找不到就回去指定路径找
“”默认当前路径

系统目录:交叉编译工具链中的某个include目录

库文件

分类说明
动态库.so运行中默认路径/lib、/usr/lib,由工具链指定的路径
动态库.so自行指定的路径,如`export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:路径
静态库.a将多个.o文件压缩在一起,可以避免修改一个.c文件而全部重新编译,默认为当前路径,也可指定

链接时库文件在哪?系统目录:交叉编译工具链的某个lib目录;
运行时库文件在哪?就是arm设备上/lib、/usr/lib目录,运行程序用环境变量LD_LIBRARY_PATH指定。

在编程过程中,程序代码往往被拆成很多部分,每部分放在一个独立的源文件中,而不是将所有的代码放在一个源文件中。

考虑一个简单的小例子:程序中有两个函数main()和abc()。main()函数位于main.cpp,abc()函数位于abc.cpp,main()函数中调用abc()函数。在编译阶段,由于编译是对单个文件进行编译,所以编译main.cpp时,编译器不知道是否存在abc()函数以及abc()调用是否正确,因此需要头文件辅助。

库文件中包含一系列的子程序。

例如abc.cpp 源文件中实现了abc()函数,我们假设abc()函数是包含重要算法的函数,我们需要将abc()函数提供给客户使用,但是不希望客户看到算法源代码。为了达到这一目的,我们可以将abc.cpp编译成库文件,库文件是二进制的,在库文件中是看不到原始的源代码的。库和可执行文件的区别是,库不是独立程序,他们是向其他程序提供服务的代码。当然使用库文件的好处不仅仅是对源代码进行保密,使用库文件还可以减少重复编译的时间,增强程序的模块化。

将库文件连接到程序中,有两种方式,一种是静态连接库,另一种是动态连接库。

简单来说abc.h是头文件,在编译的时候他是啥编译器也不知道,一般只是包含函数的声明;abc.c主要为abc()的定义,在编译后形成二进制的库文件;在链接的时候将主程序中的abc()用二进制库文件代替执行。

在linux下头文件有头文件的目录,如果缺少头文件,就把头文件加到这个目录,主程序编译时会自动搜索。库文件也有自己的目录,里面全是.so文件;储存函数的.c文件编译后的二进制文件;可以把.so文件放在这个目录下,主程序链接时会自动去搜索。

5. 交叉编译工具链

和程序编译链接等相关的gcc,binutils等工具按照先编译后链接等相关的编译程序的内在逻辑串起来,就成了我们所说的:交叉编译工具链。工具链主要包括Binutils(汇编工具)、GCC(编译器)和Glibc(标准C函数库),主要用于把源代码编译连接生成可执行程序。

我们也常把交叉工具链,简称为交叉编译器。严格意义上,交叉编译器只是指的是交叉编译版本的gcc。但是为了方便,我们常说的交叉编译器都是指的交叉编译版本的gcc,比如arm-linux-gcc,包含一系列交叉编译版本的交叉工具链:arm-linux-gcc,arm-linux-ld,arm-linux-as等。

关于更多工具链知识,可以参考 http://www.crifan.com/files/doc/docbook/cross_compile/release/html/cross_compile.html#what_is_toolchain
讲解的非常详细

5.1 Binutils基本作用

Binutils 是GNU工具之一,它包括连接器、汇编器和其他用于目标文件和档案的工具,它是二进制代码的处理维护工具。安装Binutils工具包含的程序有 addr2line、ar、as、c++filt、gprof、ld、nm、objcopy、objdump、ranlib、readelf、size、 strings、strip、libiberty、libbfd和libopcodes。

工具说明
addr2line把程序地址转换为文件名和行号。在命令行中给它一个地址和一个可执行文件名,它就会使用这个可执行文件的调试信息指出在给出的地址上是哪个文件以及行号。
ar建立、修改、提取归档文件。归档文件是包含多个文件内容的一个大文件,其结构保证了可以恢复原始文件内容。
as主要用来编译GNU
c++filt连接器使用它来过滤C++和Java符号,防止重载函数冲突。
gprof显示程序调用段的各种数据。
ld是连接器,它把一些目标和归档文件结合在一起,重定位数据,并连接符号引用。通常,建立一个新编译程序的最后一步就是调用ld。
nm列出目标文件中的符号。
objcopy把一种目标文件中的内容复制到另一种类型的目标文件中。
objdump显示一个或者更多目标文件的信息。使用选项来控制其显示的信息,它所显示的信息通常只有编写编译工具的人才感兴趣。
ranlib产生归档文件索引,并将其保存到这个归档文件中。在索引中列出了归档文件各成员所定义的可重分配目标文件。
readelf显示elf格式可执行文件的信息。
size列出目标文件每一段的大小以及总体的大小。默认情况下,对于每个目标文件或者一个归档文件中的每个模块只产生一行输出。
strings打印某个文件的可打印字符串,这些字符串最少4个字符长,也可以使用选项-n设置字符串的最小长度。默认情况下,它只打印目标文件初始化和可加载段中的可打印字符;对于其他类型的文件它打印整个文件的可打印字符。这个程序对于了解非文本文件的内容很有帮助。
strip丢弃目标文件中的全部或者特定符号。
libiberty包含许多GNU程序都会用到的函数,这些程序有getopt、obstack、strerror、strtol和strtoul。
libbfd二进制文件描述库。
libopcode用来处理opcodes的库,在生成一些应用程序的时候也会用到它。

5.2 Glibc基本作用

在执行辅助命令make命令时,会调用工具链里的编译器GCC进行编译,使用汇编器Binutils链接到C函数库Glibc,将源代码转换成可执行程序。

Glibc是C函数库是内核与应用程序的中间部分,主要提供C函数库文件。安装Glibc就是在/lib安装一系列的库文件,直接在链接阶段就将库文件链接到可执行文件中,叫静态库。静态库文件是/lib目录下的.a文件;在程序运行时才被载入叫动态库,动态库文件是/lib下的.so文件。

Glibc是C函数库,Linux的命令执行过程中都要调用Glibc,其他的函数库也会调用Glibc,Glibc再去调用系统中的内核,内核再进行资源分配。Gcc和Binutils是应用程序,会引用里面的C函数库的库文件,同时使用工具链编译出来的程序软件也会调用Glibc函数库里的库文件。

5.3 制作交叉编译工具链

编写可以在ARM上运行的程序需要使用arm-linux-gcc交叉编译工具。制作arm-linux交叉编译工具链一般通过crosstool工具或者crosstool-ng,前者使用方便,但是制作会受到一些限制,使用crosstool最多只能编译gcc4.1.1、glibc2.x的版本,无法编译版本高于2.6.29的linux内核。crosstool-ng有更好的定制性,并且一直保持着更新,对新版本的编译工具链的支持比较好,当然也带来了一些麻烦,它并不是下载下来就可以使用的,必须先配置安装。

crosstool-ng,全称是crosstool Next Generation,即下一代crosstool。crosstool是个交叉编译器的制作工具,但是做的不够好,于是有人(Yann E. MORIN)弄出了个更好的——crosstool-ng。crosstool-ng的特点:

  • 支持menuconfig(类似于Linux内核配置)
  • 支持众多的架构
  • 可选多种不同的C库等模块
  • 提供示例配置
  • 支持多种主机编译环境:各种Linux发行版,Cygwin等

crosstool-ng官网地址:https://crosstool-ng.github.io/

1. 安装工具
#yum安装需要的包
yum install -y gperf bison flex texinfo help2man gcc-c++ expat-devel patch ncurses-devel automake libstdc++-static
 
 
#下载crosstool-ng并源码编译 设置软连接
wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.23.0.tar.xz
tar -Jvxf crosstool-ng-1.23.0.tar.xz
cd crosstool-ng-1.23.0
./configure --prefix=/usr/local/crosstool-ng
make
make install
ln -s  /usr/local/crosstool-ng/bin/ct-ng /usr/local/bin/ct-ng
[root@node-252 crosstool-ng-1.23.0]# ct-ng -v
GNU Make 3.82
Built for x86_64-redhat-linux-gnu
Copyright (C) 2010  Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
2. 制作交叉编译工具链
  1. 使用ct-ng list-samples命令查看具有哪些默认配置。具体如下:
[root@node-252 crosstool-ng-1.23.0]# ct-ng list-samples
Status  Sample name
  MKDIR config.gen
  IN    config.gen/arch.in
  IN    config.gen/kernel.in
  IN    config.gen/cc.in
  IN    config.gen/binutils.in
  IN    config.gen/libc.in
  IN    config.gen/debug.in
  IN    config.gen/companion_tools.in
[L..]   aarch64-rpi3-linux-gnueabi
[L..]   aarch64-unknown-linux-gnueabi
[L..]   aarch64-unknown-linux-uclibcgnueabi
[L..]   alphaev56-unknown-linux-gnu
[L..]   alphaev67-unknown-linux-gnu
[L..]   arm-bare_newlib_cortex_m3_nommu-eabi
[L..]   arm-cortex_a15-linux-gnueabihf
[L.X]   arm-cortexa5-linux-uclibcgnueabihf
[L..]   arm-cortex_a8-linux-gnueabi
[L.X]   arm-cortexa9_neon-linux-gnueabihf
[L.X]   x86_64-w64-mingw32,arm-cortexa9_neon-linux-gnueabihf
[L..]   armeb-unknown-eabi
[L..]   armeb-unknown-linux-gnueabi
[L..]   armeb-unknown-linux-uclibcgnueabi
[L..]   arm-multilib-linux-uclibcgnueabi
[L..]   arm-nano-eabi
[L..]   arm-unknown-eabi
[L..]   arm-unknown-linux-gnueabi
[L.X]   arm-unknown-linux-musleabi
[L..]   arm-unknown-linux-uclibcgnueabi
[L.X]   arm-unknown-linux-uclibcgnueabihf
[L..]   armv6-nommu-linux-uclibcgnueabi
[L..]   armv6-rpi-linux-gnueabi
[L..]   armv7-rpi2-linux-gnueabihf
[L..]   armv8-rpi3-linux-gnueabihf
[L..]   avr
[L..]   i586-geode-linux-uclibc
[L..]   i686-centos6-linux-gnu
[L..]   i686-centos7-linux-gnu
[L..]   i686-nptl-linux-gnu
[L..]   i686-ubuntu12.04-linux-gnu
[L..]   i686-ubuntu14.04-linux-gnu
[L..]   i686-ubuntu16.04-linux-gnu
[L.X]   i686-w64-mingw32
[L..]   m68k-unknown-elf
[L..]   m68k-unknown-uclinux-uclibc
[L..]   powerpc-unknown-linux-uclibc,m68k-unknown-uclinux-uclibc
[L..]   mips64el-multilib-linux-uclibc
[L..]   mips-ar2315-linux-gnu
[L..]   mipsel-multilib-linux-gnu
[L..]   mipsel-sde-elf
[L..]   mipsel-unknown-linux-gnu
[L..]   mips-malta-linux-gnu
[L..]   mips-unknown-elf
[L..]   mips-unknown-linux-uclibc
[L.X]   i686-w64-mingw32,nios2-spico-elf
[L..]   powerpc-405-linux-gnu
[L..]   powerpc64le-unknown-linux-gnu
[L..]   powerpc64-multilib-linux-gnu
[L..]   powerpc64-unknown-linux-gnu
[L..]   powerpc-860-linux-gnu
[L..]   powerpc-e300c3-linux-gnu
[L..]   powerpc-e500v2-linux-gnuspe
[L..]   x86_64-multilib-linux-uclibc,powerpc-unknown-elf
[L..]   powerpc-unknown-linux-gnu
[L..]   powerpc-unknown-linux-uclibc
[L..]   powerpc-unknown_nofpu-linux-gnu
[L.X]   s390-ibm-linux-gnu
[L..]   s390x-ibm-linux-gnu
[L..]   sh4-multilib-linux-gnu
[L..]   sh4-multilib-linux-uclibc
[L..]   sh4-unknown-linux-gnu
[L..]   sparc64-multilib-linux-gnu
[L..]   sparc-leon-linux-uclibc
[L..]   sparc-unknown-linux-gnu
[L..]   x86_64-centos6-linux-gnu
[L..]   x86_64-centos7-linux-gnu
[L..]   x86_64-multilib-linux-gnu
[L.X]   x86_64-multilib-linux-musl
[L..]   x86_64-multilib-linux-uclibc
[L.X]   x86_64-w64-mingw32,x86_64-pc-linux-gnu
[L..]   x86_64-ubuntu12.04-linux-gnu
[L..]   x86_64-ubuntu14.04-linux-gnu
[L..]   x86_64-ubuntu16.04-linux-gnu
[L..]   x86_64-unknown-linux-gnu
[L..]   x86_64-unknown-linux-uclibc
[L.X]   x86_64-w64-mingw32
[L..]   xtensa-fsf-linux-uclibc
 L (Local)       : sample was found in current directory
 G (Global)      : sample was installed with crosstool-NG
 X (EXPERIMENTAL): sample may use EXPERIMENTAL features
 B (BROKEN)      : sample is currently broken

列表中名词解析参考:crosstool-ng 交叉工具链默认配置 名词解释

  1. 以其中的 arm-unknown-linux-gnueabi 配置为基础,进行一些自定义配置:必须是非root用户 这里以test用户为示例
cd /home/test
mkdir -p crosstool-ng_build/src crosstool-ng_build/arm-unknown-linux-gnueabi
cp /home/test/crosstool-ng/samples/arm-unknown-linux-gnueabi/* /home/test/crosstool-ng_build/src
cd crosstool-ng_build/arm-unknown-linux-gnueabi/
ct-ng arm-unknown-linux-gnueabi
ct-ng menuconfig

弹出配置对话框

  • 修改 Paths and misc options —>(${HOME}/src) Local tarballs directory :指定下载的源码保存的位置,根据自己情况修改为:/home/test/crosstool-ng_build/src

  • 选中​ Forbid downloads:不从网络下载需要的源码包 因为自动下载速度太慢 自己在windows用迅雷下载更快些

  • Target options —>Emit assembly for CPU 设置CPU内核

例如 armv4可设置为arm920t armv5可设置为arm1022e armv6可设置为arm1136j-s

  • 选择GCC版本:C compiler —> gcc version (6.3.0)
  • Operating System —>Linux kernel version (3.19.8 (EOL)) —>

这个项是选择内核版本的 当然选择后在构建工具链时 需要提供相应的内核源码包

  • C-library —> Minimum supported kernel version (Same as kernel headers (default)) —>

使用gcc编译后的程序 能够运行的最低的内核版本

假如gcc内核版本选择的3.19.8 系统环境是3.10.0 此时运行该gcc编译的程序会报错:FATAL: kernel too old
就是因为这个选项造成的 这里修改为 Let ./configure decide 就不会有这个问题了

在这里插入图片描述

3. 使用"ct-ng build"命令开始制作交叉工具编译链

这时使用root权限会提示错误[ERROR] You must NOT be root to run crosstool-NG
用普通用户运行 ct-ng build ,这里用test用户执行,也会报错

[test@node-252 arm-unknown-linux-gnueabi]$ ct-ng build
[INFO ]  Performing some trivial sanity checks
[INFO ]  Build started 20230626.161110
[INFO ]  Building environment variables
[EXTRA]  Preparing working directories
[EXTRA]  Installing user-supplied crosstool-NG configuration
[EXTRA]  =================================================================
[EXTRA]  Dumping internal crosstool-NG configuration
[EXTRA]    Building a toolchain for:
[EXTRA]      build  = x86_64-pc-linux-gnu
[EXTRA]      host   = x86_64-pc-linux-gnu
[EXTRA]      target = arm-unknown-linux-gnueabi
[EXTRA]  Dumping internal crosstool-NG configuration: done in 0.07s (at 00:02)
[INFO ]  =================================================================
[INFO ]  Retrieving needed toolchain components' tarballs
[EXTRA]    Retrieving 'automake-1.15'
[EXTRA]    Saving 'automake-1.15.tar.xz' to local storage
[EXTRA]    Retrieving 'libtool-2.4.6'
[EXTRA]    Saving 'libtool-2.4.6.tar.xz' to local storage
[EXTRA]    Retrieving 'linux-4.10.8'
[ERROR]
[ERROR]  >>
[ERROR]  >>  Build failed in step 'Retrieving needed toolchain components' tarballs'
[ERROR]  >>        called in step '(top-level)'
[ERROR]  >>
[ERROR]  >>  Error happened in: do_kernel_get[scripts/build/kernel/linux.sh@869]
[ERROR]  >>        called from: main[scripts/crosstool-NG.sh@603]
[ERROR]  >>
[ERROR]  >>  For more info on this error, look at the file: 'build.log'
[ERROR]  >>  There is a list of known issues, some with workarounds, in:
[ERROR]  >>      '/usr/local/crosstool-ng/share/doc/crosstool-ng/crosstool-ng-1.23.0/B - Known issues.txt'
[ERROR]  >>
[ERROR]  >>  If you feel this is a bug in crosstool-NG, report it at:
[ERROR]  >>      https://github.com/crosstool-ng/crosstool-ng/issues/
[ERROR]  >>
[ERROR]  >>  Make sure your report includes all the information pertinent to this issue.
[ERROR]  >>  Read the bug reporting guidelines here:
[ERROR]  >>      http://crosstool-ng.github.io/support/
[ERROR]
[ERROR]  (elapsed: 7:07.64)
[07:09] / gmake: *** [build] Error 1

查看build.log

[ALL  ]    HTTP request sent, awaiting response... 301 Moved Permanently
[ALL  ]    Location: https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/longterm/linux-4.10.8.tar.xz [following]
[ALL  ]    --2023-06-26 16:21:50--  https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/longterm/linux-4.10.8.tar.xz
[ALL  ]    Resolving mirrors.edge.kernel.org (mirrors.edge.kernel.org)... 147.75.199.223, 2604:1380:45d1:ec00::1
[ALL  ]    Connecting to mirrors.edge.kernel.org (mirrors.edge.kernel.org)|147.75.199.223|:443... failed: Connection timed out.
[ALL  ]    Connecting to mirrors.edge.kernel.org (mirrors.edge.kernel.org)|2604:1380:45d1:ec00::1|:443... failed: Network is unreachable.

可以看到无法下载linux-4.10.8.tar.xz,这里我们手动下载,然后配置到${HOME}/crosstool-ng_build/src目录下

然后,我这里需要重新执行下面命令,因为找不到.config

ct-ng arm-unknown-linux-gnueabi
ct-ng menuconfig

然后执行

ct-ng build

然后需要漫长的等待,等待的时间长度取决于网速,如果在某个步骤失败,查看build.lg,手动下载文件

完成后,交叉工具编译链的位置在/home/tools/${CT_TARGET}目录下

[root@bogon bin]# pwd
/home/test/x-tools/arm-unknown-linux-gnueabi/bin
[root@bogon bin]# ll
total 30256
-r-xr-xr-x. 1 test test  841072 Feb 11 01:56 arm-unknown-linux-gnueabi-addr2line
-r-xr-xr-x. 2 test test  869656 Feb 11 01:56 arm-unknown-linux-gnueabi-ar
-r-xr-xr-x. 2 test test 1451520 Feb 11 01:56 arm-unknown-linux-gnueabi-as
-r-xr-xr-x. 2 test test  817280 Feb 11 01:56 arm-unknown-linux-gnueabi-c++
lrwxrwxrwx. 1 test test      29 Feb 11 01:46 arm-unknown-linux-gnueabi-cc -> arm-unknown-linux-gnueabi-gcc
-r-xr-xr-x. 1 test test  836528 Feb 11 01:56 arm-unknown-linux-gnueabi-c++filt
-r-xr-xr-x. 1 test test  817280 Feb 11 01:56 arm-unknown-linux-gnueabi-cpp
-r-xr-xr-x. 1 test test    3177 Feb 11 00:58 arm-unknown-linux-gnueabi-ct-ng.config
-r-xr-xr-x. 1 test test 2457632 Feb 11 01:56 arm-unknown-linux-gnueabi-dwp
-r-xr-xr-x. 1 test test   31552 Feb 11 01:56 arm-unknown-linux-gnueabi-elfedit
-r-xr-xr-x. 2 test test  817280 Feb 11 01:56 arm-unknown-linux-gnueabi-g++
-r-xr-xr-x. 2 test test  813184 Feb 11 01:56 arm-unknown-linux-gnueabi-gcc
-r-xr-xr-x. 2 test test  813184 Feb 11 01:56 arm-unknown-linux-gnueabi-gcc-6.3.0
-r-xr-xr-x. 1 test test   27152 Feb 11 01:56 arm-unknown-linux-gnueabi-gcc-ar
-r-xr-xr-x. 1 test test   27152 Feb 11 01:56 arm-unknown-linux-gnueabi-gcc-nm
-r-xr-xr-x. 1 test test   27152 Feb 11 01:56 arm-unknown-linux-gnueabi-gcc-ranlib
-r-xr-xr-x. 1 test test  429344 Feb 11 01:56 arm-unknown-linux-gnueabi-gcov
-r-xr-xr-x. 1 test test  388408 Feb 11 01:56 arm-unknown-linux-gnueabi-gcov-tool
-r-xr-xr-x. 1 test test 5295208 Feb 11 01:56 arm-unknown-linux-gnueabi-gdb
-r-xr-xr-x. 1 test test  903760 Feb 11 01:56 arm-unknown-linux-gnueabi-gprof
-r-xr-xr-x. 1 test test     143 Feb 11 01:07 arm-unknown-linux-gnueabi-ld
-r-xr-xr-x. 2 test test 1350528 Feb 11 01:56 arm-unknown-linux-gnueabi-ld.bfd
-r-xr-xr-x. 1 test test   15078 Feb 11 01:56 arm-unknown-linux-gnueabi-ldd
-r-xr-xr-x. 2 test test 4584576 Feb 11 01:56 arm-unknown-linux-gnueabi-ld.gold
-r-xr-xr-x. 2 test test  854064 Feb 11 01:56 arm-unknown-linux-gnueabi-nm
-r-xr-xr-x. 2 test test 1033232 Feb 11 01:56 arm-unknown-linux-gnueabi-objcopy
-r-xr-xr-x. 2 test test 1308880 Feb 11 01:56 arm-unknown-linux-gnueabi-objdump
-r-xr-xr-x. 1 test test   10448 Feb 11 01:56 arm-unknown-linux-gnueabi-populate
-r-xr-xr-x. 2 test test  869656 Feb 11 01:56 arm-unknown-linux-gnueabi-ranlib
-r-xr-xr-x. 2 test test  505760 Feb 11 01:56 arm-unknown-linux-gnueabi-readelf
-r-xr-xr-x. 1 test test  840912 Feb 11 01:56 arm-unknown-linux-gnueabi-size
-r-xr-xr-x. 1 test test  841072 Feb 11 01:56 arm-unknown-linux-gnueabi-strings
-r-xr-xr-x. 2 test test 1033232 Feb 11 01:56 arm-unknown-linux-gnueabi-strip

出现以下信息说明交叉编译工具链有效

[root@bogon bin]# ./arm-unknown-linux-gnueabi-gcc -v
Using built-in specs.
COLLECT_GCC=./arm-unknown-linux-gnueabi-gcc
COLLECT_LTO_WRAPPER=/home/test/x-tools/arm-unknown-linux-gnueabi/libexec/gcc/arm-unknown-linux-gnueabi/6.3.0/lto-wrapper
Target: arm-unknown-linux-gnueabi
Configured with: /home/test/Desktop/crosstool-ng_build/arm-unknown-linux-gnueabi/.build/src/gcc-6.3.0/configure --build=x86_64-build_pc-linux-gnu --host=x86_64-build_pc-linux-gnu --target=arm-unknown-linux-gnueabi --prefix=/home/test/x-tools/arm-unknown-linux-gnueabi --with-sysroot=/home/test/x-tools/arm-unknown-linux-gnueabi/arm-unknown-linux-gnueabi/sysroot --enable-languages=c,c++ --with-float=soft --with-pkgversion='crosstool-NG crosstool-ng-1.23.0' --disable-sjlj-exceptions --enable-__cxa_atexit --disable-libmudflap --disable-libgomp --disable-libssp --disable-libquadmath --disable-libquadmath-support --disable-libsanitizer --disable-libmpx --with-gmp=/home/test/Desktop/crosstool-ng_build/arm-unknown-linux-gnueabi/.build/arm-unknown-linux-gnueabi/buildtools --with-mpfr=/home/test/Desktop/crosstool-ng_build/arm-unknown-linux-gnueabi/.build/arm-unknown-linux-gnueabi/buildtools --with-mpc=/home/test/Desktop/crosstool-ng_build/arm-unknown-linux-gnueabi/.build/arm-unknown-linux-gnueabi/buildtools --with-isl=/home/test/Desktop/crosstool-ng_build/arm-unknown-linux-gnueabi/.build/arm-unknown-linux-gnueabi/buildtools --enable-lto --enable-threads=posix --enable-target-optspace --enable-plugin --enable-gold --disable-nls --disable-multilib --with-local-prefix=/home/test/x-tools/arm-unknown-linux-gnueabi/arm-unknown-linux-gnueabi/sysroot --enable-long-long
Thread model: posix
gcc version 6.3.0 (crosstool-NG crosstool-ng-1.23.0) 
4. 标准化安装交叉编译器

由于标准Makefile需要实用标准的交叉编译器的名称,一般这个名称是arm-linux-gcc这样的

cat link.sh
#!/bin/sh
PREFIX=arm-unknown-linux-gnueabi- 
AFTFIX=arm-linux-
ln -s ${PREFIX}gcc ${AFTFIX}gcc
ln -s ${PREFIX}addr2line ${AFTFIX}addr2line
ln -s  ${PREFIX}gdbtui ${AFTFIX}gdbtui
ln -s  ${PREFIX}ar ${AFTFIX}ar
ln -s  ${PREFIX}as ${AFTFIX}as
ln -s  ${PREFIX}c++ ${AFTFIX}c++
ln -s  ${PREFIX}c++filt ${AFTFIX}c++filt
ln -s  ${PREFIX}cpp ${AFTFIX}cpp
ln -s  ${PREFIX}g++ ${AFTFIX}g++
ln -s  ${PREFIX}gccbug ${AFTFIX}gccbug
ln -s  ${PREFIX}gcj ${AFTFIX}gcj
ln -s  ${PREFIX}gcov ${AFTFIX}gcov
ln -s  ${PREFIX}gdb ${AFTFIX}gdb
ln -s  ${PREFIX}gfortran ${AFTFIX}gfortran
ln -s  ${PREFIX}gprof ${AFTFIX}gprof
ln -s  ${PREFIX}jcf-dump ${AFTFIX}jcf-dump
ln -s  ${PREFIX}ld ${AFTFIX}ld
ln -s  ${PREFIX}ldd ${AFTFIX}ldd
ln -s  ${PREFIX}nm ${AFTFIX}nm
ln -s  ${PREFIX}objcopy ${AFTFIX}objcopy
ln -s  ${PREFIX}objdump ${AFTFIX}objdump
ln -s  ${PREFIX}populate ${AFTFIX}populate
ln -s  ${PREFIX}ranlib ${AFTFIX}ranlib
ln -s  ${PREFIX}readelf ${AFTFIX}readelf
ln -s  ${PREFIX}run ${AFTFIX}run
ln -s  ${PREFIX}size ${AFTFIX}size
ln -s  ${PREFIX}strings ${AFTFIX}strings
ln -s  ${PREFIX}strip ${AFTFIX}strip

保存并执行,这样我们就得到了整个使用标准名称的交叉编译工具链

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值