GCC中-pie选项如何影响文件作用域变量的地址?
GCC中-pie选项对文件作用域变量地址的影响机制
问题示例代码
#include <stdio.h> int gprs[32]; int main(void) { printf("%p\n", (void*)&gprs); }
不同编译选项的输出
- 使用
-pie编译(GCC当前默认):0x55c183951040 - 使用
-no-pie编译:0x404060
注:Clang默认使用-no-pie选项。
核心机制解释
1. PIE的本质
PIE(Position-Independent Executable)是位置无关可执行文件,核心设计目标是配合操作系统的地址空间随机化(ASLR),让程序每次运行时都被加载到内存的随机地址,以此降低内存攻击的成功率。
2. -no-pie:固定地址的传统可执行文件
用-no-pie编译出的是普通ELF可执行文件(类型标记为EXEC):
- 链接阶段会直接给全局/静态变量分配固定虚拟地址(通常落在
0x400000起始的地址段,比如示例中的0x404060属于数据段)。 - 程序运行时,内核会严格按照链接器指定的固定地址加载程序,因此全局变量的运行时地址始终固定。
3. -pie:随机基址的位置无关可执行文件
用-pie编译出的是类共享库的可执行文件(ELF类型标记为DYN,和动态库一致):
- 链接阶段仅计算全局变量相对于程序加载基址的偏移量,不会分配固定绝对地址。
- 程序启动时,内核会随机选择一个基址(通常落在
0x550000000000附近,比如示例中的0x55c183951040)加载整个程序。全局变量的实际运行时地址 = 随机加载基址 + 编译链接时确定的偏移量。 - 由于每次加载的基址是随机的,全局变量的实际地址每次运行都会变化。
4. 底层访问方式差异
- 非PIE程序:直接使用编译时确定的绝对地址访问全局变量。
- PIE程序:在64位系统下通常用RIP相对寻址获取变量地址,32位系统则依赖**GOT(全局偏移表)**间接寻址,确保无论程序加载到哪个基址,都能正确定位全局变量。
内容的提问来源于stack exchange,提问作者pmor
相关产品推荐
相关产品推荐

