链接器负责的工作:
地址绑定、重定位、库查询、代码分段
加载器负责的工作:
重定位(先由于硬件重定位而省略)
地址绑定:
将更抽象的名字与更底层的名字绑定(如getline名字绑定到iosys模块内可执行代码的612字节处,或这个模块静态数据开始的第405字节处)。
重定位:
编译器和汇编器生成的目标代码地址从0开始。一个程序有多个子程序组成,子程序们的地址不能重叠。重定位就是为程序不同部分分配加载地址,调整程序中的数据和代码以反映所分配地址的过程。
重定位一般不止一次,链接时,各子程序通过重定位在大程序中确定位置。当程序加载时,系统会选择一个加载地址,而链接好的程序会作为整体被重定位到加载地址。
符号解析:
当多个子程序构建一个大程序时,子程序间互相作用是通过符号进行的,如主程序调用sqrt,sqrt定义在数学库中。链接器通过标明分配给sqrt的地址来解析这个符号,并修改目标代码使得call指令引用该地址。
程序加载:
将程序从辅助存储器拷贝到内存中,准备运行。有些情况下,还包括分配存储空间,设置保护位,或通过虚拟内存将虚拟地址映射到磁盘内存页上。
当允许一个程序的多个实例时,程序中的某些部分在所有的允许实例中都是相同的(尤其时可执行代码),而另一些部分是各实例独有的。
编译链接实例
m.c
m包含程序段16B,数据段16B(字符串)
符号表:_main(导出符号)、_a(导入符号)
程序先将字符串的地址压栈,用于传参。
在调用a函数时,由于a未知,所以用0x0代替地址,并做好标记。
a.c
a.c程序段0x1c字节
其中调用的库函数都是未知的所以用0x0代替入口地址
使用静态链接
链接后数据段变多,是由于使用静态链接库函数
程序增加了启动代码start-c,和调用的库函数例程
_c和_main符号都重新进行地址绑定。
可以看出静态链接会复制导入的符号相关的实例,这就造成了程序体积会很大