ELF 可执行文件
自己动手实现动态加载
动态链接一直都没能讲得很简单
例子:libc.so
问题:真的整个操作系统里只有一个 libc 的副本吗?
for i in $(seq 1 1000); do; ./a.out &; done
操作系统内核:不难实现
printf("Hello, World\n");
链接时
printf
符号的地址加载时
printf
代码执行库内的代码和数据:只需生成位置无关代码即可
liba_foo:
movl $1, %eax
ret
liba_bar:
incl x(%rip)
movl x(%rip), %eax
ret
x:
.int 0
如果另一个库需要调用 foo/bar?
libb_baz:
call liba_bar // 编译时: 00 00 00 00
ret
两个问题
把需要地址解析的部分集中在表里。
call liba_bar
.section .data
liba_bar_dyn:
.quad 0 // 加载时重填
.section .text
call *liba_bar_dyn(%rip)
如果要给这张表一个名字,就叫它 GOT (Global Offset Table) 吧
struct symbol {
int64_t offset;
char name[REC_SZ - sizeof(int64_t)];
};
动态库是由 “symbol” 列表、代码 + 数据组成
hello();
编译时无法决定是以下哪种情况
call hello
call *hello_dyn(%rip)
解决办法
liba_bar@plt:
jmp *liba_bar_dyn(%rip) // 这里可以实现 “延迟绑定”
puts_bar@plt:
jmp *puts_dyn(%rip)
call liba_bar@plt
call puts@plt
Program header 中有 PT_INTERP
PT_INTERP
: The array element specifies the location and size of a null-terminated pathname to invoke as an interpreter. This segment type is meaningful only for executable files (though it may occur for shared objects). However it may not occur more than once in a file. If it is present, it must precede any loadable segment entry.ldd
LD_PRELOAD
(允许我们预先加载 “覆盖” 一些符号)本次课内容与目标
Take-away messages