程序启动时加载器是否修改重定位信息?地址解析认知困惑咨询
嘿,这个问题问得特别精准,刚好戳中了链接器和加载器分工的模糊地带!我来给你把这事捋得明明白白~
答案是:得看程序的链接方式和是否采用地址无关代码(PIC),不是绝对的“全是链接器的活”或者“加载器完全不插手”。
1. 静态链接的非PIC程序:加载器可能被迫修正重定位
你之前的认知没毛病——链接器确实会把所有目标文件打包成一个可执行文件,并且把所有绝对地址绑定到它预设的加载基地址(比如Linux下默认是0x400000)。但问题来了:如果系统开了ASLR(地址空间布局随机化,现在主流系统都默认开启),或者因为某些原因加载器没法把程序放到链接器指定的地址时,加载器就必须重新计算所有绝对地址的偏移量,然后修改可执行文件里的对应位置——这其实就是加载器在处理重定位信息。
举个具体的例子:链接器预设程序要加载到0x400000,printMe()的地址被设为0x400123。但加载器实际把程序放到了0x500000,那加载器就会把main里调用printMe()的地址改成0x500123,这一步就是加载器在处理重定位。
2. 动态链接的程序:重定位分两种情况
(1)非PIC的动态库/程序
这种情况现在比较少见了。链接器没法提前把绝对地址绑定好,因为动态库最终加载到哪个地址完全不确定。所以加载器在加载动态库和依赖它的程序时,会扫描重定位表,把所有绝对引用修正成实际加载的地址,这一步完全是加载器的工作。
(2)PIC(地址无关代码)的程序/动态库
这是现在的主流操作(比如Linux下的.so动态库基本都是PIC的)。PIC代码在编译时就用相对地址来引用符号,链接器会生成一个全局偏移表(GOT)和过程链接表(PLT)。加载器只需要填充GOT表中的实际地址,不需要修改代码段里的内容——这其实也算处理重定位信息,但和直接修改代码本身不一样,是修改数据段的表项。
3. 解决你的核心困惑
你觉得“解析绝对地址完全是链接器的工作”,这个在没有ASLR、没有动态链接的老场景下是对的。但现代系统为了安全(ASLR防攻击)和资源复用(动态库只加载一次),加载器必须参与到地址修正的过程中,因为链接器根本没法预知程序最终会被加载到哪个地址。
拿你给出的示例代码来说:
- 如果是静态链接的非PIC程序,且系统开了ASLR,加载器会修改main里调用
printMe()的地址; - 如果是动态链接的PIC程序,加载器会填充GOT表,让PLT能找到
printMe()的实际加载地址。
内容的提问来源于stack exchange,提问作者GamefanA

