C语言指针地址定位疑惑:PIE程序内存地址解析
关于禁用ASLR后C指针与ELF段地址的疑问解答
首先明确几个核心点:
在x86_64 Linux系统中,C程序里的指针直接对应进程虚拟地址空间的线性地址(也就是常说的虚拟地址),不存在逻辑地址到线性地址的分段转换——长模式下用户态已废弃传统分段机制,逻辑地址等价于线性地址。
你看到的ELF段信息里的
Virtual Address(p_vaddr),和运行时全局变量地址的差异,根源在于程序是否为位置无关可执行文件(PIE):- 现代GCC默认编译生成PIE程序,这类程序的ELF段p_vaddr是相对于加载基址的偏移量,而非最终虚拟地址。禁用ASLR后,PIE程序的加载基址固定为
0x555555554000(默认值),运行时实际地址是「加载基址 + 段偏移(p_vaddr) + 变量在段内的偏移」。比如你看到的全局变量地址0x555555558018,就是基址0x555555554000加上.data段的偏移(如0x4018)得到的,这就是为什么你在ELF段信息里找不到0x555555558018——ELF里只存了偏移,没存最终加载后的完整地址。 - 如果用
gcc -no-pie编译非PIE程序,ELF段的p_vaddr就是程序运行时的实际虚拟地址,此时你能在段信息里找到和全局变量地址匹配的范围。
- 现代GCC默认编译生成PIE程序,这类程序的ELF段p_vaddr是相对于加载基址的偏移量,而非最终虚拟地址。禁用ASLR后,PIE程序的加载基址固定为
关于48位指针:x86_64架构仅实现48位虚拟地址空间(硬件限制),高16位必须是第47位的符号扩展(即第47位为0时高16位全0,为1时高16位全为0xffff),所以实际使用的指针有效位是48位,但存储时仍按64位处理,高16位无意义。
再针对你的两个假设做修正:
- 「C指针是段的偏移量」:错误。C指针是完整的虚拟地址,对于PIE程序,地址可拆分为「加载基址 + 段内偏移」,但指针本身不是偏移量。
- 「C指针为逻辑地址,而段虚拟地址属于线性地址空间」:错误。x86_64用户态下逻辑地址就是线性地址,ELF的p_vaddr对于PIE是偏移,对于非PIE就是线性地址本身。
验证方法:
- 用
readelf -l <你的程序>查看LOAD类型的段,观察Virtual Address和Offset,对比运行时基址(可通过cat /proc/<pid>/maps查看进程内存映射,找到程序加载基址)。 - 用
objdump -t <你的程序>查看全局变量a的符号地址,该地址即为相对于PIE基址的偏移,加上基址后就是运行时实际地址。
内容的提问来源于stack exchange,提问作者linux
相关产品推荐
相关产品推荐

