三种类型


可重定位文件(Relocatable File)

包含了代码和数据,可以和其他目标文件链接生成一个可执行文件或共享目标文件。

可执行文件 (Executable File)

包含了可以直接执行的文件。

共享目标文件 (Shared Object File)

包含了用于链接的代码和数据,共分两种情况:

  • 静态连接器将其他可重定位文件和共享目标文件链接起来,生成新的目标文件
  • 动态链接器将多个共享目标文件与可执行文件结合,作为进程映像的一部分(当静态链接加载的文件不完善时)

加载和动态链接

静态链接

在程序运行时无需装入函数库,而是把需要用到的库函数的目标代码(二进制)从程序库中抽取出来,嵌入到程序的代码中动态链接

是指库函数的代码并不进入程序的代码中,程序在编译/链接阶段并不完成跟库函数的链接,而是把函数库的代码也交给用户,到启动程序 目标代码运行时才把程序库的代码也装入用户空间(并加以定位),再完成程序与库函数的连接。

特殊节区

名称 类型 属性 含义
.bss SHT_NOBITS SHF_ALLOC+SHF_WRITE 包含出现在程序的内存映像中的未初始化数据。程序开始执行,系统将把这些数据初始化为0
.comment SHT_PROGBITS 包含版本控制信息
.data SHT_PROGBITS SHF_ALLOC+SHF_WRITE 包含了初始化了的数据,将出现在程序的内存映像中
.dynamic SHT_DYNAMIC SHF_ALLOC 包含了用于动态连接信息,节区的属性将包含SHF_ALLOC位,是否SHF_WRITE位被设置取决于处理器
.dynstr SHT_STRTAB SHF_ALLOC 包含了用于动态连接的字符串,大多数情况下这些字符串代表了于符号表项相关的名称
.dynsym SHT_DYNSYM SHF_ALLOC 包含了动态连接符号表
.got SHT_PROGBITS 包含了全局偏移表
.hash SHT_HASH SHF_ALLOC 包含了一个符号哈希表
.init SHT_PROGBITS SHF_ALLOC+SHF_EXECINSTR 包含了可执行指令,是进程初始化代码的一部分。在主程序入口(main函数)之前执行这些代码。
.plt SHT_PROGBITS 包含了过程链接表
.text SHT_PROGBITS SHF_ALLOC+SHF_EXECINSTR 包含程序的可执行指令

.rel.dyn.rel.plt

.rel.dyn

用于变量重定位

.rel.plt

用于函数重定位,包含了链接C库的函数

例如:wirte函数

  • r_offset=0x401c
  • r_info=0x807

image-20200222003705944

.dynsym

包含了动态链接符号表

例如:.dynsym[8]指向的就是write函数

image-20200222003329958

.dynstr

包含了动态链接的字符串

该节以\x00为结尾和开始,中间间隔的每个字符串也是以\x00分隔

Elf32_Sym[6]指向的便是这里的内容。

Elf32_Sym[6]->st_name=0x4c(.dynsym + Elf32_Sym_size * num)

Symbol Table 符号表

符号表保存了程序实现或使用的所有(全局)变量函数

它的主要任务是将一个字符串和一个值关联起来。例如printf符号表示printf函数在虚拟地址空间中的地址,该函数的绝对地址(程序运行时的地址)就存在于该地址上。

当然该符号也可能是一个未定义的符号,也就是在程序的源代码中没有声明而直接使用的某个函数。这类的引用必须在静态链接期间用其他目标模块或者库解决,或者是在加载期间通过加载动态链接来解决

image-20200220210821923

ExecuTable 文件布局图

GOT 全局偏移表

全局偏移表用来将位置独立的地址计算重定向到绝对地址。

当在程序中引用某个共享库中的符号(函数)时,编译链接阶段并不知道这个符号(函数)的具体位置,只有等到动态链接器将所需要的共享库加载时进内存后,也就是在运行阶段,符号的地址才会最终确定。因此,需要有一个数据结构来保存符号的绝对地址,这就是GOT表的作用,GOT表中每项保存程序中引用其它符号的绝对地址。这样,程序就可以通过引用GOT表来获得某个符号的地址。

如图片中的offset printf只是在程序中的虚拟地址,当程序在加载相应的函数库时只用将虚拟地址+基地址便等于绝对地址,也就是真实可用地址。

PLT 过程链接表

过程链接表用于把位置无关的函数调用重定向到绝对地址。

然后通过引用GOT表中的函数的绝对地址,来把控制转移到实际的函数。

ELF文件结构