浅回忆链接与装载
0x00 链接
编译的前三步(预处理编译汇编)生成的代码时可重定位目标文件,并非可执行文件。经过链接后才能生成可执行文件。
链接做了两件事,①解析重定位目标文件里定义和引用的符号 ②为这些符号重新分配地址,修改使用这些符号的地址。
由此,我们可以将多个目标文件链接在一起且能正确工作。
这两件事情,实现了编译时的静态链接,和装载时的动态链接。前者将引用的外部文件一起打包进入可执行文件,在编译时就完成了上述两件事。后者则在装载时调用链接器完成上述两件事。
装载时的动态链接,仍然是解析符号,修改使用符号的地址。动态链接的好处是避免了库的重复储存和装载。但仍然不够灵活,需要在装载时确定全部符号位置
(位置无关代码)运行时的动态链接,则实现了运行时动态调用库。其原理是延迟绑定(c++虚函数是动态绑定)。代码中对动态链接的符号的访问,都是用的相对偏移地址,偏移到数据节(下面装载讲)里的一个表里的表项,这个表项里的值只有在第一次访问时才会被置为对应符号的地址。 这样无论库到底被映射到虚拟内存的任何地方,代码的访问都没有影响。
0x01 装载
首先我们得知到可执目标文件格式(linux上的elf):
①elf头:总体描述文件格式,及程序入口点
②段头部表:描述有多少个段,每个段在文件中的起始位置,长度,及映射到内存的地址
③init节 //实际的入口代码,运行时调用动态库init
④.txt节
⑤.rodata节 //③-⑤为代码段
⑥.data
⑦.dss //⑥⑦为数据段
⑧.symbol //声明的 引用的 全局和静态符号 静态为loacl
⑨.debug //-g指令生成的符号表
⑩.line // -g行与指令的对应关系
11.strtab
12.节头部表 8-12是符号表和调试信息
静态链接和装载时动态链接解析的符号就是.symbol里的,并对引用这些符号的地方修改地址。
程序装载进内存后:
①内核空间
②用户栈
③共享内存映射
④堆
⑤读写数据段 .bss .data //全局/静态变量
⑥只读代码段 .init .text .rodata