动态链接相关结构
在动态链接情况下,操作系统在映射完可执行文件之后,会先启动一个动态链接器(dynamic linker),操作系统同样以映射的方式将他加载到进程的地址空间中,然后将控制权交给动态链接器的入口地址,当链接工作完成后,动态链接器将控制权转交给可执行文件的入口地址。
.interp段
.interp段中保存的就是一个字符串,这个字符串就是可执行文件所需要动态链接器的路径。
查看.interp段
Code: Select all
Contents of section .interp:
0318 2f6c6962 36342f6c 642d6c69 6e75782d /lib64/ld-linux-
0328 7838362d 36342e73 6f2e3200 x86-64.so.2.
查看使用动态链接器类型
Code: Select all
readelf -l a.out | grep interpreter
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
.dynamic段
保存了动态链接器所需要的基本信息,比如依赖于哪些共享对象,动态符号表的位置,动态链接重定位表的位置等等。
Code: Select all
/* Dynamic section entry. */
typedef struct
{
Elf32_Sword d_tag; /* Dynamic entry type */
union
{
Elf32_Word d_val; /* Integer value */
Elf32_Addr d_ptr; /* Address value */
} d_un;
} Elf32_Dyn;
查看.dynamic段信息
Code: Select all
$ readelf -d file
Dynamic section at offset 0x2dc0 contains 27 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000c (INIT) 0x1000
0x000000000000000d (FINI) 0x1208
0x0000000000000019 (INIT_ARRAY) 0x3db0
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x3db8
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x3a0
0x0000000000000005 (STRTAB) 0x488
0x0000000000000006 (SYMTAB) 0x3c8
0x000000000000000a (STRSZ) 136 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x3fb0
0x0000000000000002 (PLTRELSZ) 48 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x600
0x0000000000000007 (RELA) 0x540
0x0000000000000008 (RELASZ) 192 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) Flags: NOW PIE
0x000000006ffffffe (VERNEED) 0x520
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x510
0x000000006ffffff9 (RELACOUNT) 3
0x0000000000000000 (NULL) 0x0
查看一个程序依赖于哪些模块或共享库
Code: Select all
$ ldd file
# 系统调用库
linux-vdso.so.1 (0x00007ffd11337000)
# GNU C库
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcc4edc3000)
# 动态链接库
/lib64/ld-linux-x86-64.so.2 (0x00007fcc4efc4000)
动态符号表
动态符号表,保存了与动态链接相关的符号,对于模块内部的符号,比如模块私有变量则不保存,这些一般保存在.symtab中。所以一般情况下都有这两个表。
动态符号也需要一些辅助表,比如保存符号名的字符串表,如动态符号字符串表.dynstr;由于动态链接下,我们需要在程序运行时查找符号,为了加快符号查找的过程,往往还是辅助的符号哈希表.hash
查看动态符号表和哈希表
Code: Select all
Symbol table '.symtab' contains 66 entries:
Num: Value Size Type Bind Vis Ndx Name
# puts函数,是一个UND(undefined)状态
49: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
61: 0000000000001169 37 FUNC GLOBAL DEFAULT 16 main
64: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sleep@@GLIBC_2.2.5
Symbol table of '.gnu.hash' for image:
Num Buc: Value Size Type Bind Vis Ndx Name
7 0: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize
动态链接重定位表
对于PIC技术的可执行文件或共享对象来说,虽然他们的代码不需要重定位(因为地址无关),但是数据段还包含了绝对地址的引用,因为代码段中绝对地址相关的部分被分离了出来(PIC地址无关技术),变成了GOT,而GOT实际上是数据段的一部分。
主要有两个段,一个是.rel.dyn是对数据引用的修正,他所修正的位置位于.got,一个是.rel.plt是对函数引用的修正,他所修正的位置位于.got.plt
查看重定位表
Code: Select all
$ readelf -r file
Relocation section '.rela.dyn' at offset 0x540 contains 8 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000003db0 000000000008 R_X86_64_RELATIVE 1160
000000003db8 000000000008 R_X86_64_RELATIVE 1120
000000004008 000000000008 R_X86_64_RELATIVE 4008
000000003fd8 000100000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0
000000003fe0 000300000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000003fe8 000400000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000003ff0 000500000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTa + 0
000000003ff8 000700000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize@GLIBC_2.2.5 + 0
Relocation section '.rela.plt' at offset 0x600 contains 2 entries:
Offset Info Type Sym. Value Sym. Name + Addend
# 两个共享对象函数引用
000000003fc8 000200000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0
000000003fd0 000600000007 R_X86_64_JUMP_SLO 0000000000000000 sleep@GLIBC_2.2.5 + 0
当动态链接器需要进行重定位时,他首先查找printf的位置,printf位于libc-2.6.1.so,假设链接器在全局符号表里面找到printf的地址为0x08801234,那么链接器就会将这个地址填入到.got.plt中偏移为000000003fc8的位置中去,从而实现了地址的重定位,例如在前面调用puts@plt中反汇编代码为
Code: Select all
0000000000001060 <puts@plt>:
1064: f2 ff 25 5d 2f 00 00 bnd jmpq *0x2f5d(%rip) # 3fc8 <puts@GLIBC_2.2.5>
106b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
动态链接时进程堆栈初始化信息
进程在初始化的时候,堆栈里面保存了动态链接器所需要的一些辅助信息数组(auxiliary vector)
Code: Select all
typedef struct
{
uint32_t a_type; /* Entry type */
union
{
uint32_t a_val; /* Integer value */
/* We use to have pointer elements added here. We cannot do that,
though, since it does not work when using 32-bit definitions
on 64-bit platforms and vice versa. */
} a_un;
} Elf32_auxv_t;