Linux Ubuntu可执行文件中GOT是“指针的指针”吗?是否由加载器处理?
关于Linux(Ubuntu)中GOT与PIE的寻址疑问解答
先明确GOT的核心作用
在Linux(Ubuntu)的位置无关代码(PIC/PIE)可执行文件中,文本段是只读的,静态链接阶段无法确定全局数据/函数的实际加载地址——因为PIE程序每次启动都会被加载到随机内存地址。这时候GOT(全局偏移表)就作为一个可修改的间接层存在:加载时,操作系统的动态链接器会自动修改GOT中的条目,把它们指向对应数据/函数的真实内存地址。
解答你的核心疑问
结论先行:不需要手动做两次解引用,加载器会自动处理GOT条目,也不会让PIE中的指针变成“指针的指针”。具体拆解:
无GOT的场景(非PIE静态链接)
此时全局数据的地址在编译时就已经确定,指令mov fun_data(%rip), %rax的逻辑是:
- 通过RIP相对偏移计算出
fun_data的直接内存地址 - 读取该地址上存储的数据值,放到
rax中
等价于代码层面的rax = *fun_data
有GOT的场景(PIE/动态链接)
编译器会自动调整对fun_data的访问逻辑,把它重定向到GOT条目,而非直接访问数据地址:
- 指令会变成类似
lea fun_data@GOTPCREL(%rip), %rax—— 通过RIP相对偏移找到GOT条目的内存地址,存入rax - 接着执行
mov (%rax), %rax—— 读取GOT条目中存储的内容(也就是fun_data的真实加载地址),更新rax - 如果要获取数据值,再执行
mov (%rax), %rbx—— 此时rbx就是fun_data的值
但这并不是你担心的“两次解引用fun_data”,因为在PIE编译模式下,原fun_data符号已经不再对应数据的直接地址,而是编译器生成的指向GOT条目的偏移。动态链接器会在加载时自动把GOT条目填充为数据的真实地址,所以代码层面不需要你手动处理指针的指针,编译器和动态链接器已经帮你完成了所有间接层的处理。
简单说:GOT是编译器和动态链接器共同实现PIE的底层机制,对开发者透明——你写代码访问fun_data时,不需要关心GOT的存在,最终的执行效果和非PIE下直接访问数据是一致的。
内容的提问来源于stack exchange,提问作者Shay
相关产品推荐
相关产品推荐

