您好,欢迎来到99网。
搜索
您的当前位置:首页linker & 链接脚本

linker & 链接脚本

来源:99网

linker

链接器主要有两个作用:

  • 一是将若干输入文件(.o文件)根据一定规则合并为一个输出文件(例如ELF格式的可执行文件);
  • 一是将符号与地址绑定(当然加载器也要完成这一部分工作)。

关于链接器的工作机制可以参考 《Linker and Loader》 一书,本文只关心它的第一个功能,即如何根据一定规则将一个或多个输入文件合并成输出文件。这里的 “一定规则” 是通过链接脚本描述的。

在进行链接时,linker 会根据链接脚本从输入的 .o 文件中挑选出感兴趣的 section,把它们合并生成新的 section,这些新产生的 section 归属于目标文件的某个 segment(段),并出现在目标文件中。这里提到了 segment 的概念。Segment可以看作一组具有相同属性(或部分相同属性)的 section 的集合,属性是指“读、写、执行”。例如 .text 通常存放的是代码编译后的二进制指令,它具有 r-x 权限;.rodata 存放是的只读数据,如常量字符串,它通常具有 r-- 权限(实际上也可以具有x权限,例如用一个全局 const 数组存放可执行的机器码);那么在生成目标文件时,.text.rodata 就可以通过一个具有 r-x 属性的 text segment 来包含它们,这就是我们通常说的 “文本段”。

a.out:     file format elf-x86-

Program Header:
    PHDR off    0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**3
         filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r-x
  INTERP off    0x0000000000000238 vaddr 0x0000000000400238 paddr 0x0000000000400238 align 2**0
         filesz 0x000000000000001c memsz 0x000000000000001c flags r--
    LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21
         filesz 0x00000000000006fc memsz 0x00000000000006fc flags r-x
    LOAD off    0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**21
         filesz 0x0000000000000228 memsz 0x0000000000000230 flags rw-
 DYNAMIC off    0x0000000000000e28 vaddr 0x0000000000600e28 paddr 0x0000000000600e28 align 2**3
         filesz 0x00000000000001d0 memsz 0x00000000000001d0 flags rw-
    NOTE off    0x0000000000000254 vaddr 0x0000000000400254 paddr 0x0000000000400254 align 2**2
         filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
EH_FRAME off    0x00000000000005d0 vaddr 0x00000000004005d0 paddr 0x00000000004005d0 align 2**2
         filesz 0x0000000000000034 memsz 0x0000000000000034 flags r--
   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
   RELRO off    0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**0
         filesz 0x00000000000001f0 memsz 0x00000000000001f0 flags r--
...

elf 文件

object file

.text,代码段,就是CPU要运行的指令代码;
.data,数据段,程序中包含的一些数据,放在这个段里;
.bss,未初始化段,记录了程序里有哪些未初始化的变量,段表中只记录对应的大小,留着程序运行前去初始化为0,所以,此处并不占用elf 文件的空间。

symbol table 符号表

objdump -t 或者 nm 命令

defined symbol:通俗说就是全局变量、静态变量、本文件的函数
undefined symbol:通俗说就是未初始化的全局变量、本文件引用的外部变量(extern)、以及引用的外部函数

section 分类

section 地址

linker script

前面说到 linker 的作用,那么链接器是依据什么规则来生成最终的可执行文件的呢?没错,就是链接脚本。
如果没有指定链接脚本的话,链接器有一个默认的供使用,可以用 ld --verbose 查看。可以通过 ld -T 选项指定自己程序的链接脚本。

linker script 语法

// 什么时候指定 LMA 和 VMA

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

INCLUDE filename 在看到这个命令的时候才去载入filename这个linker script。可以被放在不同的命令如SETCTION, MEMORY等。
INPUT(file1 file2 ...) 指定加载的输入object档案,如abc.o这样的档案。
GROUP(file1 file2 ...) 指定加载的输入archieve档案,如libabc.a这样的档案。
AS_NEEDED(file1 file2 ...) 在INPUT和GROUP使用的命令,用来告诉linker说如果object里面的数据有被reference到才link进来,
猜测应该可以减少储存空间。范例(未测试请自行斟酌):INPUT(file1.o file2.o AS_NEEDED(file3.o file4.o))
OUTPUT(filename) 和gcc -o filename 一样
SEARCH_DIR(path) 和-L path一样
STARTUP(filename) 和INPUT相同,唯一差别是ld保证这个档案一定是第一个被link

OUTPUT_FORMAT(bfdname) 指定输出object档案的binary 文件格式,可以使用objdump -i列出支持的binary 文件格式
OUTPUT_FORMAT(default, big, little) 指定输出 object 档案预设的 binary 文件格式
TARGET(bfdname) 指定使用哪种 binary 文件格式读取输入 object 档案
可以使用 objdump -i 列出支持的 binary 文件格式。

HIDDEN(symbol name) 隐藏某个全局符号?
PROVIDE(symbol = expression) 如果程序没有这个符号就使用这里提供的符号和值
PROVIDE_HIDDEN(symbol = expression) 结合了上述两者

有意思的地方

每次读取一个目标文件,匹配里面的 section,输出到输出目标文件相应的 section 中。

*(.sec1 .sec2):

File1.sec1
File1.sec2
File2.sec1
File2.sec2

*(.sec1) *(.sec2):

File1.sec1
File2.sec1
File1.sec2
File2.sec2

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- 99spj.com 版权所有 湘ICP备2022005869号-5

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务