一、一些需要知道的概念

在正式谈论交叉编译工具ARM-LINUX-GCC前,我想有必要明确两个非常基本的概念。

1、什么是交叉编译,什么是交叉编译工具链:https://www.crifan.com/files/doc/docbook/cross_compile/release/html/cross_compile.html#what_is_crosscompile,只需要关注此文章的第一章与第二章。2、GCC与ARM-LINUX-GCC的关系:GCC是一套编译工具链,一般来说,其用于将代码编译成在X86架构电脑上运行的可执行文件,而ARM-LINUX-GCC可以看成经过特殊配置的GCC,其编译出的程序并不是跑在X86架构电脑上的,而是跑在ARM架构上。

二、当我们谈编译时,我们在谈什么

静态语言写成的代码,需要经过编译才能在机器上跑起来,那么当我们说编译一个程序时,这个“编译”到底是指什么?我是想说,从代码到可以被机器识别执行的0和1之间,到底发生了什么?要搞懂这个问题,我想很有必要找本厚厚的《编译原理》来看一遍,但是这篇文章的主题是阐述ARM-LINUX-GCC编译工具链的基本用法,所以这里大概说一下(要我详细说我也说不明白,我自己也没好好读过《编译原理》,毕竟我是电子信息工程专业的,又不是软件工程专业和计算机科学专业的)。

一般来说,当使用GCC编译一份程序时(后面会大量混用GCC和ARM-LINUX-GCC,毕竟它们其实是一个东西,使用方式和工作原理基本是一致的),需要经过如下四个步骤:预处理(preprocessing)、编译(compilation)、汇编(assembly)、连接(linking)。其中前三个步骤每一份源代码文件(一般在C语言里,它们是.c文件)都要进行一遍,最后一步的连接,则只需要进行一次。当我们说“编译”时,其实是统称以上四个步骤中的前三个。

空说无益,举个栗子吧,现在我有一份程序,包含test.c与main.c两个源代码,在linux的bash中输入如下指令开始整个编译过程:

gcc -o main test.c main.c #编译test.c main.c 生成名为mian的可执行文件

1、预处理

首先来到整个流程的第一步:预处理。在这一步,gcc会调用名为cpp的工具处理所有输入的源文件中的预处理语句,也就是写代码时输入的#打头的那些语句,什么#include、#define之类的。处理好后,会生成xxx.i(对于C++的源文件,是xxx.ii)。main.c与test.c经过预处理后,默认生成main.i和test.i。这两个文件其实还是源文件,只是不包含任何预处理语句了,有兴趣可以使用nano、vim等文本编辑工具打开一探究竟。

2、编译

生成好的main.i与test.i之后,gcc会调用ccl工具将这两个文件进行编译,生成.s结尾的汇编文件,也就是说,main.i变为了main.s、test.i变为了test.s。经过这一步,所有的代码被转换成了更贴近底层的汇编代码,此时的main.s与test.s依旧可以被文本编辑器打开,不过里面的内容已经变成了一条条汇编代码。

3、汇编

将汇编代码翻译成一定格式的机器码,这一流程被称之为汇编。gcc通过调用as工具来完成汇编工作,汇编完成的汇编码被转换为.o结尾的文件,也就是说,在这一步,main.s变为了main.o、test.s变为了test.o。我们称.o结尾的文件为目标文件(OBJ文件),其为ELF(Executable and Linkable Format)文件的一种。以下摘录了维基百科中的一段,其阐述了什么是ELF文件。

In computing, the Executable and Linkable Format (ELF, formerly named Extensible Linking Format), is a common standard file format for executable files, object code, shared libraries, and core dumps. First published in the specification for the application binary interface (ABI) of the Unix operating system version named System V Release 4 (SVR4),[2] and later in the Tool Interface Standard,[1] it was quickly accepted among different vendors of Unix systems. In 1999, it was chosen as the standard binary file format for Unix and Unix-like systems on x86 processors by the 86open project.

https://en.wikipedia.org/wiki/Executable_and_Linkable_Format

4、连接

将汇编后生成的目标文件与系统自己的一些目标文件以及库文件(在linux中,库文件可以看成多个目标文件的集合,库文件分为两种:.a结尾的静态库文件、.so结尾的动态库文件)连接起来,最终生成可在特定平台执行的可执行文件,就是所谓的连接了。gcc通过调用ld工具来完成此步,这也是整个编译流程的最后一步。

对于上一段提到的库文件、静态库与动态库的进一步说明,可以参考此摘录:

什么是库
在windows平台和linux平台下都存在着大量的库。
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。
由于windows和linux的本质不同,因此二者库的二进制是不兼容的。

库的种类
linux下的库有两种:静态库和共享库(动态库)。
二者的不同点在于代码被载入的时刻不同。
静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。对于静态库和动态库的选择,需要结合二者的优缺点折中考虑。一般来说,比较通用的库,应该做成共享库。

库存在的意义
库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议。
现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。共享库的好处是,不同的应用程序如果调用相同的库,那么 在内存里只需要有一 份该共享库的实例。

https://www.jb51.net/article/125091.htm

5、以上四个步骤的一图流总结

编译步骤图示

三、常用的arm-linux-gcc/gcc选项

现在我们来到了这篇文章最最重要的部分,此部分将会列出常用的ARM-LINUX-GCC/GCC选项,对于每个选项,我争取解释到位并尽可能给出合适的例子。这里再重申一遍,ARM-LINUX-GCC和GCC它们的使用方法是几乎相同的,只是生成的可执行文件面向的执行平台不同。所以出于方便举例的原因,后面选项对应的例子都是基于GCC的,但是对于ARM-LINUX-GCC来说,这些选项是完全通用的。

1、基本选项

-o

gcc里用的最多的选项了吧,指定文件输出的位置,一般配合下面介绍的其他选项一起使用,后面到处都能看到它的身影。

gcc -o test test.c #还是举一个,处理test.c文件,生成名为test的可执行文件gcc -o test test1.c test2.c #一般当然不会只有一份源代码文件

-c

对文件进行预处理、编译和汇编,但是不进行连接,输出生成的目标文件,文件默认.o结尾。

gcc -c -o test.o test.c #对test.c文件进行预处理、编译和汇编,生成test.o文件gcc -c test.c #也可以这样,当不指定文件名,生成与输入文件同名的.o文件

-S

对文件进行预处理与编译,但不进行汇编与连接,输出生成的汇编文件,文件默认为.s结尾。

gcc -S -o test.s test.c #对文件test.c进行预处理与编译,生成 test.sgcc -S test.c #当然也可以不用-o参数指定文件名,此时默认生成与输入文件同名的.s文件

-E

这个命令做得更少了,只对输入文件进行预处理,值得注意的是,这个选项默认将预处理后的代码打印到标准输入输出流(说人话就是linux的终端界面),所以一般配合重定向符“>”或者“-o”选项一起使用。

gcc -E -o test.i test.c #只对test.c进行预处理,然后输出处理后的代码到test.igcc -E test.c > test.i #与上面的功能一样,linux就是这样,很多指令可以灵活替代,条条大路通罗马gcc -E test.c #这样就是直接输出到终端了,如果是很多行代码的文件,体验刷屏的快感吧

-v

单独使用时,显示gcc的版本信息,配合其他选项使用时,可以打印一些处理流程相关的信息。

gcc -v #打印当前gcc的版本信息gcc -v -o test test.c #处理test.c文件,生成名为test的可执行文件,同时打印生成过程中的信息

一些tips

1、 当同时使用-S、-c选项处理多个文件时,不能带上-o选项,切记,但使用-E选项时可以,此时会将多个文件的预处理后代码输出到一个文件。
2、可以将-dM选项与-E选项一起使用,配合grep指令,可以快速查找所处理文件中是否包含由#define定义的特定的宏,例子如下:

gcc -dM -E test.c #打印test.c文件中所包含的所有宏定义,同时也包括由#include预处理指令包含的其他文件中的宏gcc -dM -E test.c | grep _DEBUG #查找是否存在包含字符串“_DEBUG”的宏,有就打印出来

2、警告选项

警告,也就是我们常说的warning了,用来在编译过程中提示一些没有语法错误,但是可能会对程序运行造成影响的潜在问题,保险起见,还是检查所有警告选项比较好,这节只有一个选项。

-Wall

打开所有的警告选项,就不举例子了,其实GCC还有很多类似于-Wxxx的选项,有兴趣了解可以使用man指令查询GCC手册。

3、调试选项

-g

使用GCC时添加这个选项可以在生成的可执行文件中嵌入GDB的调试信息,方便使用GDB进行断点调试,一般在嵌入式开发时用的不多吧,了解即可。对GDB的使用感兴趣的,可以参考这篇文章:https://www.yanbinghu.com/2019/04/20/41283.html。

4、优化选项

-Olevel

设定GCC的优化等级,level可以用0~3代替,数字越大,优化等级越高,一般调试时为了方便跟踪每一条语句设置为0,防止部分程序语句被自动优化掉。程序正式发布时,一般设置为2。

5、连接选项

当进行编译四个步骤的最后一步连接时,gcc通过调用ld(对于arm-linux-gcc,调用的是arm-linux-ld)来对需要连接的文件进行连接,可以通过本节的选项来改变连接时ld的行为方式。

-Wl,-option1,-option2,…,optionn

这是一个非常重要的选项,当使用gcc/arm-linux-gcc编译文件时,如果希望将一些设置选项传递给被调用的ld/arm-linux-ld,即可以通过-Wl选项将一个或者多个参数传送给ld,多个选项之间用”,”分隔。

arm-linux-gcc -Wl,-Ttext=0x80000000,-Tdata=0x90000000 -o test test.c #通过-Wl传送选项-Ttext=0x80000000,-Tdata=0x90000000给调用的arm-linux-ld,这两个选项会在后文提及

-llibrary

在连接时,指定同时连接名为library的目标文件(.o文件)或者静态库文件(.a文件),默认在系统指定的标准搜索目录中查找这两个文件。以下附上GCC手册中对这个选项的解释,解释得更加详细。

-l library
Search the library named library when linking. (The second alternative with the library as a separate argument is only for POSIX compliance and is not recommended.)
It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified. Thus, foo.o -lz bar.o searches library z after file foo.o but before bar.o. If bar.o refers to functions in z, those functions may not be loaded. The linker searches a standard list of directories for the library, which is actually a file named liblibrary.a. The linker then uses this file as if it had been specified precisely by name. The directories searched include several standard system directories plus any that you specify with -L. Normally the files found this way are library files—archive files whose members are object files. The linker handles an archive file by scanning through it for members which define symbols that have so far been referenced but not defined. But if the file that is found is an ordinary object file, it is linked in the usual fashion. The only difference between using an -l option and specifying a file name is that -l surrounds library with lib and .a and searches several directories.

-L dir

这个选项一般与前一个选项配合使用,当指定需要连接的.o或者.a文件在非系统标准搜寻目录时,用这个选项可以指定查找目录。

gcc -L ./lib/ -lz -o test test.c #编译test.c时,同时连接z.o或者z.a文件,除了搜寻系统标准目录外,还在当前目录下的lib目录查找要连接的文件是否存在

-nostartfiles -nostdlib

这两个选项一般在编译bootloader、linux内核等需要脱离linux直接运行于裸机的代码时使用,-nostartfiles选项指定不连接系统标准的启动文件,-nostdlib选项除了有-nostartfiles的功能,同时还指定不连接标准库文件(即stdio、stdint等),但是如果存在libgcc.a文件,还是依然会连接它。

-static

以静态方式连接本来默认动态链接的共享库文件,这里就牵扯到静态连接与动态连接的问题了。一般来说,在linux系统下库文件分为两种:.a结尾的静态库与.so结尾的动态库。对于静态库,一般使用静态连接的方式进行连接,即库文件中需要使用的函数被直接装入可执行文件,可执行文件可以脱离库文件独立执行;对于动态库,一般使用动态连接的方式进行连接,在连接时只装入需要的库文件中函数的名字,运行时再根据名字在系统指定目录中查找库文件中的函数本体,可执行文件无法脱离库文件独立执行,不过动态连接有利于减小可执行文件的体积。

-shared

此选项用于连接多个目标文件,生成一个.a结尾的共享库文件,下次需要使用多个目标文件中包含的函数功能时,直接连接生成的.a文件即可。

gcc -shared -o test.a test1.o test2.o test3.o #连接test1.o、test2.o、test3.o,生成一个名为 test.a的共享库文件

6、目录选项

-Idir

添加名为dir的目录到头文件搜寻目录,当搜索某个头文件时,会优先在-I选项指定的目录中搜索,然后再到系统标准搜索目录中搜索,所以当指定目录中存在与系统标准目录中同名的头文件时,优先使用指定目录中的。

gcc -I./dir1 -I./dir2 -o test test.c #将当前目录下的dir1与dir2目录加入头文件搜索目录

-iquotedir

添加名为dir的目录到头文件搜寻目录 ,-iquote选项的作用与-I选项类似,唯一不同的是,使用此选项添加的头文件搜寻目录只会在搜寻由#include”file”指定的文件时使用,在搜寻由#include<file>包含的头文件时,这些包含目录并不会被搜寻。

-Bprefix

指定gcc运行时调用的ld、cpp、ccl等程序的路径与前缀名称,如果没有使用此选项明确指定,会在 /usr/lib/gcc与/usr/local/lib/gcc目录中搜寻,如果还没找到,再到用户的环境变量PATH指定的路径中寻找。

gcc -B/home/lib/gcc/ test.c #在/home/lib/gcc/目录下查找需要调用的程序gcc -B/home/lib/gcc/prefix- test.c #与上一句差不多,但是注意,这里指定了前缀prefix-,所以在查找时会加上前缀,比如在/home/lib/gcc/目录查找ld变为了查找prefix-ld

四、常用的arm-linux-ld/ld选项

其实当我们直接使用arm-linux-ld/ld是,前面介绍的大多数arm-linux-gcc/gcc选项均可以直接用于arm-linux-ld/ld ,尤其是连接选项一节介绍的选项,本章介绍一个arm-linux-ld/ld专属的选项。

-T

此选项用于在连接时指定程序各段的起始地址,在详细阐述这个选项之前,有必要先了解下一个可执行程序在执行时再内存中各部分分布。下图为程序执行时典型的内存分布:

典型程序内存分布

在拥有操作系统时,程序运行在哪个物理地址上,是由操作系统自动管理的(操作系统通过称为虚拟内存地址映射的技术使得每个程序在运行时,认为自己分配到了从0地址开始的物理内存,然后将自己的各个部分装载到对应的虚拟地址开始执行。在程序看来,其所在的虚拟地址就是实际物理地址,可惜事实并不是这样)。

-T选项就是用来指定程序运行时,各个程序段(bss段、data段、text段)应该装载到的地址的(这个地址称为运行时地址)。举几个例子说明吧:

arm-linux-ld -Ttext 0x08000000 -o test test1.o test2.o #指定text段从0x08000000地址开始,其他未指定的段依次向高地址排列
arm-linux-gcc -Wl,-Ttext=0x08000000 -o test test1.c test2.c #还记得之前说过的 -Wl 选项吗,一样的效果哦

除了-Ttext,其他可用的还有-Tdata、-Tbss。除此之外,还可以通过-T选项配合连接脚本(.lds文件)来更加灵活的设置各段地址:

arm-linux-ld -Tlink.lds test1.o test2.o -o test #通过link.lds连接脚本设置各段地址

关于连接脚本的基本编写方法,可以参考:https://www.jianshu.com/p/42823b3b7c8e。

五、常用的arm-linux-objcopy/objcopy选项

arm-linux-objcopy/objcopy,常用于拷贝或者转换不同格式的目标文件,基本用法是这样:arm-linux-objcopy [-option] infile [outfile] ,其中[]中为可选部分,-option为程序选项,infile为输入文件 outfile为输出文件,当未指定输出文件时,默认将生成的输出文件替换输入文件,即输入文件会被取代。

在嵌入式开发中,这个程序一般用于将生成的elf格式文件转换成可以烧录的bin文件,常用用法如下:

arm-linux-objcopy -O binary -S elf_file bin_file #将输入的elf_file转换成二进制格式的bin_file

下面介绍常用选项。

-I bfdname或者–input-target=bfdname

使用bfdname指定输入的源文件的格式,其中bfdname为BFD库中的标准名(BFD库,全称 Binary File Descriptor Library,其希望通过一种统一的接口来处理不同的目标文件格式,其广泛用于gcc编译器中)。如果没有指定输入的源文件的格式,objcopy在处理时会尝试猜测输入的文件的格式。

-O bfdname或者–target=bfdname

使用bfdname指定输出的文件的格式 。

-R sectionname或者–remove-section=sectionname

在输出文件时,从输出文件中移除名为sectionname的段。此选项可以在一条指令中多次使用,用于移除多个段。

-S或者–strip-all

在复制转换时,不从源文件中复制重定位(relocation)和符号信息(symbol information)。

-g或者–strip-debug

在复制转换时,不从源文件中复制调试符号和调试段(debugging symbol or sections)。

六、常用的arm-linux-objdump/objdump选项

在嵌入式开发中,此程序常用于对二进制文件进行反汇编,默认显示到标准输出,所以一般配合重定向符”>”一起使用。

-D或者–disassemble-all

此选项用于反汇编二进制文件的所有段。

arm-linux-objdump -D elf_file &gt; dis_file #反汇编elf_file的所有段,并输出到dis_file文件

-b bfdname或者–target=bfdname

与objcopy的-I选项相似,指定转换源文件的格式,未指定时,objdump会尝试去猜测文件的格式。

arm-linux-objdump -D -b binary -m arm bin_binfile &gt; dis_file #指定源文件bin_file为二进制文件格式,-m选项指定架构为ARM

-d或者–disassemble

与-D功能类似,但是只反汇编.text段(即指令段)。

-m machine

前面提到了,指定反汇编时采用的架构。

-i或者–info

很有用的选项,可以用于查询 -b与-m选项支持的参数列表。

ARM-LINUX-GCC交叉编译工具链必知必会相关推荐

  1. arm hisiv100 linux,hisiv100交叉编译工具链安装

    hisi交叉编译工具链安装 一.         摘要: 交叉编译简单的说,就是A机器上编译生成,运行在B机器上.那么在A机器上的编译工具安装,就是本文所要描述的内容. 工欲善其事必先利其器,所以交叉 ...

  2. Linux下gcc交叉编译工具链制作实例详细总结(附下载地址)

    一.简介 本文主要讲解gcc交叉编译工具链搭建的全过程,以本人实际的操作去讲述整个工具链的搭建,希望对大家有所帮助. 二.工具链的搭建流程 1.下载相关源码:包括binutils.kernel.gcc ...

  3. 构建gcc交叉编译工具链

    如何构建一个GCC交叉编译工具链 GCC不仅是一个编译器,它是一个开源工程,可以让你建立各种编译器.一些编译器支持多线程,一些支持共享库,一些支持Multilib(典型的应用是在64位机上运行32位应 ...

  4. 【linux】安装Linux的交叉编译工具链

    1.linux中装软件的特点 linux中安装软件比windows中复杂.linux中安装软件一般有以下几种方法:     第一种:在线安装.譬如ubuntu中使用apt-get install vi ...

  5. arm linux gcc交叉编译,arm-linux-gcc交叉编译器安装

    在网上下载arm-linux-gcc-4.4.3.tar.gz. 1.解压#tar -zxvf arm-linux-gcc-4.4.3.tar.gz 2.打开解压目录,将FriendlyARM文件夹拷 ...

  6. Linaro GCC 交叉编译工具链 国内源下载列表 (持续更新)

    gcc-linaro-4.9-2016.02-x86_64_arm-linux-gnueabihf.tar.xz 链接:https://pan.baidu.com/s/1-DCIVVs6QTGv5tY ...

  7. hisi linux nptl.tar,hisiv100交叉编译工具链安装

    hisi交叉编译工具链安装 一.         摘要: 交叉编译简单的说,就是A机器上编译生成,运行在B机器上.那么在A机器上的编译工具安装,就是本文所要描述的内容. 工欲善其事必先利其器,所以交叉 ...

  8. linux使用交叉工具链产生的程序怎么下载到mini2440里,Ubuntu安装ARM架构GCC工具链(ubuntu install ARM toolchain)最简单办法...

    一.安装ARM-Linux-GCC工具链 只需要一句命令: sudo apt-get install gcc-arm-linux-gnueabi 前提是你的Ubuntu系统版本是官网支持的最新的版本, ...

  9. linux centos7 使用 crosstool-ng 构建 交叉编译 工具链 即构建各cpu架构平台的gcc编译器

    简介 crosstool-ng,全称是crosstool Next Generation,即下一代crosstool.crosstool是个交叉编译器的制作工具,但是做的不够好,于是有人(Yann E ...

  10. Arm Linux 交叉编译(交叉编译是什么?CROSS_COMPILE)(交叉编译工具链【待更】)

    文章目录 交叉编译 1.编译 2.本地编译 3.交叉编译 交叉编译工具链 交叉编译 在嵌入式系统开发中,经常会听到一个词:交叉编译.到底什么是"交叉编译"呢?为什么要使用" ...

最新文章

  1. maven-assembly-plugin和maven-shade-plugin打包区别及弊端
  2. 艰难就业季,2020 AI算法岗春招汇总 面经大全来了!!!
  3. hive向mysql导数据_导入Hive数据导MySQL
  4. Java SE 6 新特性: JMX 与系统管理(转载)-asp.net关注
  5. python bootstrap 4_Python3.4+Django1.9+Bootstrap3
  6. 【openjudge 1.11 07】和为给定数
  7. 谈谈MySQL InnoDB存储引擎事务的ACID特性
  8. JavaScript强化教程——Bootstrap
  9. APSINx010HC系列射频模拟信号发生器—输出高达6.1GHz
  10. 调手表(蓝桥杯2018年B组真题)
  11. 科技公司逐鹿手机配件赛道,“降维打击”会出现吗?
  12. 矩阵分析_1 线性方程组解与列向量组的关系
  13. 点云3D目标检测之——尝试SFD代码跑通(超详细!!)
  14. Windows 上的网络通信编程
  15. WARNING: The directory ‘/home/xt/.cache/pip‘ or its parent directory is not owned or is not writable
  16. Unity NavMesh导航报错“SetDestination“ can only be called on an active agent that has been placed on a Na
  17. RapidIO协议概述(一)
  18. 资源 | 吴恩达斯坦福CS230深度学习课程全套资料放出(附下载)
  19. 求知讲堂Java视频-基础整理-数据类型
  20. Non-dairynbsp;creamernbsp;植脂末

热门文章

  1. CSDN周赛52期及53期浅析
  2. mysql指令sum_MySQL Sum()函数
  3. 计算机管理硕士,2020年伦敦国王学院计算机系统工程与管理硕士-暂不开设专业硕士申请条件-学费-世界排名...
  4. 计算机毕业设计php的毕业设计选题管理系统
  5. 使用NGUI模仿制作“切水果”
  6. 男士不得不看的21种经典拍照姿势
  7. Mysql之账号管理、建库以及四大引擎【入门篇】
  8. 四川省工程技术研究中心申报解读(条件认定流程各市奖励政策)
  9. 计算机更新后启动不了,win10系统更新显卡之后开不了机如何解决
  10. for根据ID去重_汽车ECU参数标定之配置Overlay RAM实现Qorivva MPC57xx系列MCU参数在线标定和代码重映射原理和方法详解...