链接器把一些独立的 object files 和库文件链接起来,形成可执行文件。在这个过程中,链接器需要解决一些符号的引用以及指令的重定位。
除此之外,还有一个动态链接的过程。比如有些符号是定义在某个 so 文件中的,需要由动态链接器在装载的过程中进行一些符号查找和地址重定位的工作。要完成此工作,动态链接器需要一些信息,它们存储在一些特殊的 section 中,比如 .dynamic。
section 和 segment 是分别针对链接视图和执行视图来说的,各种资料对他们的翻译比较混乱,慢慢就会理解了。
还有一些用于支持调试功能的 section,比如 .debug、.line。下面是一张描述特殊 section 的表格:
.bss:保存未初始化的数据,比如那些未初始化的全局变量。因为是“未初始化”,所以也没必要在文件中占用任何空间去记录其初始值(所以类型为 SHT_NOBITS)。在程序开始运行时,系统会将 .bss 映射的内存区域清零。
.comment:保存版本控制信息。
.data / .data1:保存已初始化的数据。它们会在文件中占用存储空间,这与 .bss 不同。
.debug:保存调试相关的信息。
.dynamic:保存动态链接信息。
.dynstr:保存动态链接所需的字符串。比如符号表中的每个符号都有一个 st_name(符号名),他是指向字符串表的索引,这个字符串表可能就保存在 .dynstr。
.dynsym:保存需要动态连接的符号表。
.fini / .init:分别保存进程退出和初始化时要执行的指令。.init 指令会在程序入口点(main)之前被执行。
.got:保存全局偏移量表 (global offset table)。
在 Android 中,GOT 分为两部分:.got 和 .got.plt。其中 .got 表用来保存全局变量引用的地址,而 .got.plt 用来保存函数引用的地址。
.hash:保存符号哈希表,用于快速查找与其对应的符号表中的符号。
.interp:保存 ELF 程序解释器(比如 Android 下的动态链接器)的路径名。
.line:保存用于调试的行号信息。
.note:保存一些注释信息。
.plt:保存过程链接表 (procedure linkage table)。每个外部定义的函数都会在 PLT 中有对应的一项,用于定位外部函数的地址。
.relname / .relaname:保存重定位表。比如:.rel.dyn、.rel.plt。
.rodata / .rodata1:保存程序中的只读数据。
.shstrtab:保存一个字符串表,这些字符串都是 section 的名字。
.strtab:保存字符串表,类似于 .dynstr,但 .dynstr 中保存的都是那些需要动态链接的符号的名字。
.symtab:保存符号表(非动态链接)。
.text:保存可执行的指令代码。
这些以“.”为前缀的 section 名字为系统保留。应用程序可以构造自己的段,但最好不要与系统已定义 section 节重名,也不要以“.”开头,以避免潜在的冲突。