You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

启用ASLR的系统中,非PIE标识可执行文件可运行的困惑

问题解析与解答

嘿,其实你忽略了一个关键点:ASLR的作用范围不止PIE程序,它同样会随机化共享库、栈、堆以及内核提供的VDSO的加载地址——你通过ldd看到的地址变化,正是这些共享组件的地址,而非你编译的程序本身的代码段地址。

咱们一步步拆解:

1. 先搞清楚ASLR到底在随机什么

你的机器上/proc/sys/kernel/randomize_va_space值为2,这是Linux的全ASLR模式,它会处理这些内容:

  • 系统共享库(比如libc.so.6)的加载地址
  • 程序栈、堆的起始位置
  • VDSO(linux-vdso.so.1,内核提供的虚拟共享库,用来加速系统调用)的地址
  • 只有PIE(位置无关可执行文件),才会让程序自身的代码段、数据段基地址也被随机化

你两次运行ldd看到的地址差异,完全是共享库和VDSO的随机化效果,这和你的程序是不是PIE没有关系。

2. 非PIE程序的地址是固定的(自身代码段)

你用默认gcc编译的程序是普通ELF可执行文件(ELF 64-bit LSB executable),这类非PIE程序的自身代码和数据段的基地址是固定不变的。你可以用这个命令验证:

readelf -l prog.out | grep "LOAD" | head -2

非PIE程序的输出会显示固定的加载基址(比如常见的0x400000),而如果是PIE程序,这里会显示0x0(因为加载时会随机分配基址)。

或者用gdb实际跑两次看看:

gdb -q prog.out
starti
info symbol $pc

重复运行的话,非PIE程序的入口地址会完全一致,而PIE程序每次启动的入口地址都不一样。

3. 关于gcc默认PIE的疑问

Ubuntu从16.04开始,gcc 5及以上版本默认开启PIE编译,但你的gcc版本是4.9.3,这个版本默认并没有启用PIE。你可以用这个命令确认默认参数:

gcc -dumpspecs | grep -E "(pie|PIE)"

如果没有相关输出,就说明默认不开启PIE。如果要编译PIE程序,需要手动加参数:

gcc -fPIE -pie -o prog_pie.out some_source.c

这时候再用file查看,就会显示ELF 64-bit LSB shared object,而且程序自身的地址也会被ASLR随机化。

总结一下

你看到的ldd地址变化是共享库的ASLR效果,和程序是否为PIE无关。非PIE程序自身的代码段地址是固定的,只有PIE程序才会让自身整个地址空间随机化。你的gcc 4.9.3默认确实没开PIE,这和file命令的输出完全一致。

内容的提问来源于stack exchange,提问作者izac89

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:53:44