3 链接脚本

每一个链接都有一个链接脚本控制。链接脚本通过链接器命令语言编写的。

链接脚本的主要目的是描述输入文件中的sections(段)应该如何映射到输出文件中,并且控制输出文件的内存布局。大部分链接脚本主要做这两件事。然而,必要时,链接脚本可以使用后面章节描述的命令指导链接器执行其他的操作。链接器必须使用一个链接脚本。如果没有提供自己的链接脚本,链接器将使用默认的链接脚本,一般情况下这个连接脚本被编译到了连接器的可执行文件中。可以使用"​--verbose​"命令行选项查看链接器的默认脚本。某些命令行选项如 ​‘-r’​ 或者 ​‘-N’​,将会影响默认的连接脚本。

可以通过 ​-T​ 命令行选项提供自己的链接脚本。如果这样做,链接将使用你提供的脚本作为默认的链接脚本。

还可以通过被链接的输入文件(需要被链接的对象文件)隐式的作为链接脚本。可以看3.11[隐式链接脚本](一般情况,链接器会把不能识别的对象文件(常说的静态库,.o文件等)当作链接脚本处理)

3.1 链接脚本的基本概念

为了描述链接脚本语言我们需要定义一些基本的概念和词汇。

链接脚本用于结合多个输入文件到单个输出文件。输出文件和每一个输入文件都是已知的​object文件格式​。每一个文件被称作​object文件​。输出文件经常被称作可执行文件,但是对于我们的目的仍然称作object文件。每一个对象文件包括​sections​列表和一些其他内容。我们又是提到的在输入文件中section被称作​输入section​。同样的,在输出文件中的section被称作​输出section​。

在object中的每个section都用有一个名字和大小。大部分section也包含一些数据块,被称作section的内容。有的section也会被标记为​loadable​,它的意思是当输出文件被运行时,section的内容会被加载到内存中。有的没有内容的section可以是​allocatable​,它的含义是应该在内存中保留一块位置,但是没有任何内容被加载到这里(有些时候这些内存必须被初始化,最常见的.bss段)。有的section既不是​loadable​也不是​allocatable​典型的是包含一些调试信息。

每个loadable和allocatable输出section有两个地址。低一个是 ​VMA​,或者虚拟内存地址。这个地址是当输出文件运行时section拥有的地址(常说的运行地址)。第二个是 ​LMA​*, 或者加载地址。这个地址表示这个section从哪里被加载。大多数情况这两个地址是相同的。有一个两个地址可能不相同的例子,data section(常说的数据段)加载地址在ROM,然后当程序运行时拷贝到RAM(这个技术经常被用于基于ROM的系统初始化全局变量。j解释最典型的就是单片机,全局变量或者静态变量的初始值存在FLASH中(LMA),然后上电的时候通过启动程序(crt.o即c运行时库)拷贝到RAM中(VMA),之后使用的都是VMA访问全局变量)。

可以使用​objdump​程序的’-h’选项查看在object文件中存在的section。

每个object文件都存在符号列表,被称作​symbols table​。一个符号可以是定义的,也可以是未定义的。每个符号都有名字,并且每个被定义的符号都有一个地址,除此之外还包括一些其他信息。如果编译的是c或者c++程序,每个定义的函数和全局变量或者静态变量都会得到一个定义的符号。每个在输入文件中被引用的函数或者全局变量都会变成一个未定义的符号。

你可以通过​nm​程序或者使用​objdump​函数加上-t选项来查看一个对象文件中的符号。

3.2 链接脚本格式

链接脚本是文本文件。

通过一些列的命令编写链接脚本。每一个命令是一个关键字,后面可能跟随着参数,或者是一个赋值符号。可以使用分号 **;**分割命令。空格一般是被忽略的。

文件名字符串或者格式名字符串可以被直接的输入。如果文件名中包含特殊的逗号字符,可以试用双引号把文件名括起来,否则将会被认为是几个文件。在文件名中包含双引号特殊字符是错误的方式。

链接脚本中也可以使用类似于c语言的注释,通过​​/*​​和​​*/​​作为界定符。和C语言类似注释被当作空格处理。