Blog
Blog copied to clipboard
aarch64体系结构与编程3--GNU LD链接器
链接器Linker
- 链接器(Linker)是一个程序,将一个或多个由编译器或汇编器生成的目标文件外加 库链接为一个可执行文件。
- GNU Linker采用AT&T链接脚本语言
- 官方文档:最新版本v2.34
- 链接脚本最终会把一大堆编译好的二进制文件(.o文件)综合生成最终二进制可执行文件,也就是把每一个二进制文件整合到一个大文件中。这个大文件有一个总的text/data/bss段。
ld命令
- aarch64-linux-gnu-ld:arm64版本的链接器命令
- 命令参数查看: aarch64-linux-gnu-ld --help 常用参数: -T: 指定链接脚本 -Map:输出一个 符号表 文件 -o: 输出最终可执行二进制文件
基本概念
- 输入段(input section),输出段(output section)
- 每个段包括 name和大小
- 段的属性
- loadable:运行时会加载这些段的内容到内存中
- allocatable:运行时不会加载段的内容,
- 段的地址:
- VMA(virtual memory address):虚拟地址,运行地址
- LMA (load memory address):加载地址
- 通常 ROM的地址为加载地址,而RAM的地址为VMA
链接脚本命令
- ENTRY(symbol):设置程序的入口函数。
- 链接程序有如下几种方式来确定入口点:
- 使用-e参数
- 使用ENTRY(symbol)
- 在.text的最开始的地方
- 0地址
- INCLUDE filename:引入filename的链接脚本
- OUTPUT filename:输出二进制文件,类似在命令行里使用“-o filename”
- OUTPUT_FORMAT(bfd): 输出BFD格式
- OUTPUT_ARCH(bfdarch):输出处理器体系结构格式
符号赋值
- 符号也可以像 C语言一样赋值
- “.” 表示 location counter,表示当前位置
符号的引用
- 高级语言(C语言) 常常需要引用 链接脚本定义的符号
- 在C语言里,定义一个变量并初始化变量。 例如
- 编译器会在符号表中定义了一个符号foo -编译器会在内存中为符号foo存储100.
- 在链接脚本中定义一个变量 链接器仅仅在符号表里定义这个符号,没有分配内存来存储变量的值
- 访问链接脚本定义的变量:访问的是变量的地址,不能访问变量的值
- 我们可以在每个段中设置一些符号,以方便C语言访问每个段的起始地址和结束地址
section命令
- SECTIONS命令:告诉链接器如何把输入段(input sections)映射到输出段(output sections),以及如何在内存中摆放这些 输出段。
- 输出section的描述符:
LMA加载地址
- 每个段有VMA (虚拟地址,运行地址)以及 LMA(加载地址)
- 在输出段描述符中使用 “AT” 来指定 LMA
- 如果没有通过”AT”来指定 LMA,通常 LMA = VMA
- 构建一个基于ROM的映像文件常常会设置输出段的虚拟地址和加载地址不一致
例子
下面的链接文件会创建3个段,其中
- text段的虚拟地址和加载地址为0x1000
- mdata段的虚拟地址设置为0x2000,但是通过AT符号指定了加载地址是在text段的结束地址,_data指定了data段的虚拟地址为0x2000
- bss段的虚拟地址是在0x3000
- data段的加载地址和链接地址(虚拟地址)不一样,因此程序的初始化代码需要把data段从ROM的加载地址复制到SDRAM中的虚拟地址中
- 数据段加载地址在_etext起始的地方,数据段的运行地址是在_data起始的地方,数据段的大小为“_edata - _data”,下面这段代码把数据段从_etext起始的地方复制到_data起始的地方。
常见的内建函数(builtin functions)
-
ADDR(section) : 返回前面已经定义过的段的VMA地址
-
ALIGN(n) : 返回 下一个与n字节对齐的地址,它是基于当前的位置(location counter)来计算对齐地址的。
-
注意这里是n个字节,而不是2^n个字节。
-
SIZEOF(section):返回一个段的大小
-
MAX(exp1, exp2)/ MIN(exp1,exp2):返回两个表达式的最大值或者最小值