linker文件

​ 链接脚本的主要目的是描述如何将输入文件中的各个section(节)映射到输出文件中,并控制输出文件的内存布局。链接器总是使用链接脚本,如果不主动修改,链接器将使用一个默认的链接脚本,这个脚本被编译进了链接器可执行文件中。

​ 链接器将输入文件组合成一个输出文件。输出文件和每个输入文件都采用一种特殊的数据格式,称为目标文件格式。每个文件称为一个目标文件。输出文件通常称为可执行文件,有时也仍然会称为目标文件。每个目标文件都有一个段(section)列表。有时把输入文件的段称作输入段,类似的,输出文件的段称作输出段。目标文件中的每个段都有名称和大小。大多数段还具有关联的数据块,称为段内容。一个段可能被标记为可加载(loadable),这意味着在运行输出文件时,段内容需要先加载到内存中。一个没有内容的段是可分配的,这意味着应该在内存中预留一个区域,但是这里不需要加载任何东西(在某些情况下,该内存必须清零)。既不可装载也不可分配的部分通常包含某种调试信息。

使用下面的命令查看文件中的section信息:

objdump -h x.out

对于loongArch64使用loongarch64-unknown-linux-gnu-objdump

​ 每个目标文件还具有一个符号列表,称为符号表。符号可以是定义的也可以是未定义的。每个符号都有一个名称,每个定义的符号都有一个地址,以及其他信息。如果将C或C ++程序编译到目标文件中,则将会将所有定义过的函数和全局变量以及静态变量作为已定义符号。输入文件中引用的每个未定义函数或全局变量都将成为未定义符号。

使用linux命令nm查看符号信息。

链接脚本是一种特殊的文本文件。文本描述是一系列的命令,每个命令都是一个关键字,可能后面还跟有一个参数,或者一个符号的赋值。使用分号分割命令,空格通常被忽略。

一个最简单的链接脚本文件如下:

SECTIONS
{
    . = 0x10000;
    .text : { *(.text) }
    . = 0x8000000;
    .data : { *(.data) }
    .bss : { *(.bss) }
}

最简单的链接脚本只有一个命令:SECTIONS,这个命令用来描述输出文件的内存布局。

.符号是location counter,用于指定section的地址,SECTIONS命令开始时,其值为0,可以显式设置,如果没有设置,则按照section大小自动增长。.text定义一个ouput section。后面跟一个冒号,现在可以省略。大括号用来指定input sections,*是通配符,匹配任何文件名。表达式'*(.text)'表示输入文件的所有.text section。链接器保证output section满足对齐要求。

程序执行的第一条指令叫做entry point。ENTRY命令用于设置入口点。链接器按照下面的顺序寻找入口点。

  1. -e 命令行选项。
  2. ENTRY(symbol)链接脚本命令。
  3. 目标指定的符号,通常是start。
  4. .text的第一个字节。
  5. 0

在操作系统实验中一般使用第二种方法指定。

PHDRS 命令用于创建程序头,链接器缺省或创建合适的程序头。

链接脚本也可以定义变量,这时只会生成一个 symbol 项,并不会分配内存。 在目标文件内定义的符号也可以在链接脚本内被赋值。符号定义是全局的。每个符号都对应了一个地址。使用nm命令时可以看到定义的符号,在内核代码中需要查看各个段的内存范围需要在链接脚本提供相应的符号。

PROVIDE 关键字于定义这类符号:在目标文件内被引用,但没有在任何目标文件内被定义的 符号

SECTIONS
{
  .text :
  {
    *(.text)
    _etext = .;
    PROVIDE(etext = .);
  }
}

当目标文件内引用了 etext 符号,却没有定义它时,etext 符号对应的地址被定义 为 .text section 之后的第一个字节的地址。

ALIGN(align)命令常用来指定对齐地址。