GNU的编译工具链

本书为韦东山老师《嵌入式Linux应用开发》中交叉工具篇章的总结

1.编译工具链

PC端:GCC ld objcopy objdump

交叉编译工具链: arm-linux-gcc arm-linux-ld arm-linux-objcopy arm-linux-objdump

2.编译流程

预处理: #开头的都是预处理命令,只做简单的转换。 由源文件到 .i 文件 (所用工具:cpp / arm-linux-cpp)

编译:进行语法检查, 将 .i 文件编译为 汇编代码 (所用工具:ccl)

汇编:将汇编代码汇编为.obj 目标文件 (elf 目标文件) (所用工具:as / arm-linux-as)

链接:生成可执行文件(将汇编生成的.o 和系统库中需要用到的.oy文件进行链接) (所用工具: ld/ arm-linux-ld)

3.编译过程常见的文件后缀名

.c : C语言

.C .cpp .cc .cxx : C++ 文件, 此处.cc常用于编写只有纯函数的文件

.i : 预处理后的C文件

.ii : 预处理后的C++文件

.s .S : 汇编文件

预处理是进行简单的替换 , 编译阶段只检查语法正确性,(所以只有声明也可以通过编译) ,但是链接阶段会查找程序中使用的所有定义symbol,如果找不到,也会报错。

4.GCC编译选项

(1)前提说明:除非使用 -c -S -E 选项,否则最后的步骤总是链接

示例: arm-linux-gcc -o hello hello.c

​ arm-linux-gcc -v -o hello hello.c -v选项用于查看编译详细过程

(2)总体选项:

-c : 预处理、编译和汇编, 不进行链接, 生成.o文件,默认用源文件名+.o后缀

-S :编译后停止, 生成的是.s后缀的汇编文件

-E : 预处理后即停止

-o file : 注意 -o 是与输出文件名一起出现的, 预处理、编译、汇编、链接阶段均可以使用这个选项

-v : 显示编译的详细信息

(3)警告选项: -Wall , 打开所有需要的警告信息

(4)调试选项: -g , 产生调试信息, GDB 工具可以使用这些信息

​ 此选项还有其它格式: -gstabs+ -gstabs -gxcoff+ -gxcoff -gdwarf+ -gdwarf (选项对应不同系统的格式)

(5)优化选项:

-O 或 -O1 : 进行优化

-O2, -O3

-O0 : 不进行优化

指定多个优化选项, 生效的总是最后一个-O选项

(6)链接器选项:

-llibrary :搜索真正名字为liblibrary.a的库文件并进行连接, 即使不指定-library, 一些默认的库也会被链接进去。连接器在标准搜索目录和 “-L”选项指定的路径中搜索这个 库文件

-nostartfiles :不连接系统标准启动文件 (ctl1.o ctli.o crtend.o crtn.o)。在连接内核、bootloader时会用到这个选项,而对于一般应用程序,这些启动文件是必须的。

-nostdlib :不连接的标准启动文件和标准库文件,只连接指定的文件。 在编译内核、bootloader时会用到这个选项。

-static : 在支持动态连接的系统上阻止连接共享库

-shared : 生成一个obj文件,只有部分系统支持这个选项 。

​ 用法: gcc -shared -o sub.a sub1.o sub2.o , 可以用来连接多个文件而生成一个库文件

​ gcc -shared -o sub.a sub.o —> gcc -o test main.o ./sub.a

-Xlinker operation : 用来传递系统特定的选项, 将GCC无法识别的这些选项传递给连接器。 若传递的选项带参数,则需要两次-Xlinker,一次传选项,一次传参数。

​ 示例 : -Xlinker -assert -Xlinker definitions

​ 完成的传递是 “ -assert definitions”

-Wl ,operation : 把选项operation传递给连接器。 如果有多个选项, 则operation间用逗号分隔成多个选项。

-u symbol : 使连接器认为取消了symbol定义, 从而连接库模块以取得定义。 可以使用多个 -u选项,各自跟不同的符号。

(7)目录选项

-Idir : 在头文件的搜索路径中添加dir目录

​ 以 “#include <>”包含的文件,只在标准库目录中搜索, 也包括 -Idir 指定的目录

​ 以 " #include “” " 包含的文件,先从用户工作目录中搜索 ,再搜索标准库目录

-I- : 在 -I- 选项前面用 -I选项指定的搜索路径只适用于 " #Include “file” " ,不能搜索 “#include <>”

​ 在 -I- 后面用 -I选项指定的目录可搜索所有 #include 文件 。 (一般来说 -I选项就是这么用的)

​ -I- 阻止当前目录成为搜索"#include “file”" 的第一选择 , 要克服这个选项, 要指定 -I. 搜索当前目录。

-Ldir : 在-I 选项的搜索路径列表中添加 dir 目录 例:gcc -L. -o test main.o -lsub

-Bprefix : 指出在何处寻找可执行文件, 库文件,以及编译器自己的数据文件。

​ 编译器执行子程序 (cpp ccl cclplus(for C ++) as ld)时把prefilx当作欲执行的程序的前缀。

​ 如果没有指定 -B 选项 ,或没有在该前缀中找到文件, 则 会试验两个标准前缀,

​ /usr/lib/gcc /usr/local/lib/gcc-lib/ 如果仍没有找到,就在‘PATH’ 环境变量指定的路径中寻找没有增加任何前缀的文件名。

​ 也可以指定 GCC_EXEC_PREFIX 变量来替代-B选项, 如果两个都有,优先使用 -B 选项。

5.ld / arm-linux-ld

-T 直接用它来指定代码段、数据段、bss段起始地址, 也可以用来指定一个链接脚本 ,在其中完成复杂设置。

​ 这个选项只用于连接Bootloader 内核等没有底层支持的软件。连接应用于操作系统上的程序,无需-T选项。

(1)直接指定:

-Ttext startaddr

-Tdata startaddr

-Tbss startaddr

eg: arm-linux-ld -Ttext 0x00000000 -g led_on.o -o led_on_elf

额外说明: link.s 中的b跳转指令说明

.text
.global  _start
_start:
    b   step1
step1:
    ldr pc,=step2
step2:
    b   step2

编译: arm-linux-ld -Ttext 0x00000000 link.o -o link_elf

b step1 是一条相对跳转指令 , b跳转指令的机器码格式如下 :

31:2827:2423:0
Cond101LOffset
条件码L 为0表示b跳转指令, 1表示BL跳转指令表示偏移地址

bl 跳转指令会保存当前PC寄存器的值到LR寄存器中

b/bl跳转目标地址计算:

将24位补码地址扩展为32位, 将此32位数左移两位,将得到的值加到PC寄存器中,得到跳转的目标地址。

(ARM结构中, PC中存的是当前指令下两条指令的地址)

0:  eaffffff          b   0x4
4:  e59ff000          ldr  pc, [pc,#0]
8:  eafffffe          b      0x8
c:  00000008          andeq  r0,r0,r8

ffffff —> ffffffff —> fffffffc (左移两位后的值), 其值为 -4

PC 中存放的值是当前指令下两条指令地址, 再-4, 即为当前指令下一条指令。

ldr pc , [pc,#0] , PC 中的值在编译时由-Ttext 指定为 0x00000000, 加偏移值 0 , 还是0x00000000

执行 b step2 , PC 中的值 为当前指令地址下两条, 变为 0x00000008 .

而 fffffe —> fffffffe --> fffffff8 , 其值为 -8, 则目标跳转地址为 0x00000008 - 8 = 0x00000000, 即为运行地址。

b bl 等指令为位置无关指令。

bootloader 内核等刚开始执行所处的地址通常不等于运行地址,所以程度开头先使用 b bl mov 等位置无关指令将代码从 flash等设备复制到内存的运行地址处,再跳转到内存的运行地址处执行。

(2)使用链接脚本设置地址

eg: arm-linux-ld -Ttimer.lds -o timer_elf $^

timer.lds

SECTIONS {
    . = 0x300000000;
    .text   : {*(.text)}
    .rodata  ALIGN(4) : {*(.rodata)}
    .data    ALIGN(4) : {*(.data)}
    .bss    ALIGN(4) : {*(.bss)  *(COMMON)}
}

SECTIONS 是基本命令,描述输出文件的“映射图”: 各段、各文件如何设置 。 可以含一个或多个段。 核心是段

格式:

secname start ALIGN(align) (NOLOAD) : AT(ldaddr) {contents} > region :phdr = fill

secname 和 contents 是必须的, secname确定段名, contents确定将什么内容放在这个段中。

start 是这个段的重定位地址,也称运行地址,如果代码中有位置相关的指令,则程序在运行时,这个段必须放在这个地址上。

ALIGN ,对齐指令, 对齐后的地址才是真正的运行地址。

(NOLOAD) 告诉加载器,运行时不用加载这个段,这选项只有在有操作系统时才有意义

AT : 指定这个段在映象文件中的地址。 也是加载地址。 不使用它,则加载地址等于运行地址。

6.objcopy / arm-linux-objcopy

用来复制一个目标文件的内容到另一个文件中, 可使用不同于源文件的格式来输出目标文件。

(例如,可用来将ELF格式的可执行文件转换为二进制文件。

​ arm-linux-objcopy -O binary -S elf_file bin_file), elf_file 是 input-file , bin_file 是outfile,可省略,若未指定,则会覆写输入文件

7.objdump/ arm-linux-objdump

显示二进制文件信息, 可用来查看汇编代码。

arm-linux-objdump -D elf_file > dis_file , 将ELF文件反汇编

arm-linux-ojbdump -D -bbinary -m arm bin_file > dis_file , 将二进制文件反汇编

-m arm , 用来指定反汇编时使用的架构。 ( 可用-i选项列出支持的架构)

机器码和汇编码的关系 , 最后程序中看到的都为机器码(机器码有各种指令格式 , 如arm 指令集的用户手册)。 汇编码也是为了阅读。

8. nm -C test.o

可用来查看目标文件

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值