为何每次在GDB中反汇编函数都会得到相同的指令及常量地址?
为什么GDB反汇编的地址每次都一模一样?
这事儿其实很好理解,核心原因和可执行文件的编译链接方式、系统的地址随机化机制直接相关,咱们一步步说:
1. 你编译出的是「固定地址」的可执行文件
当你执行gcc -o hello hello.c -ggdb时,在大多数Linux发行版的默认配置下,编译器会生成一个非位置无关可执行文件(non-PIE)。这类文件在链接阶段,链接器会直接给代码段(.text,存汇编指令)、只读数据段(.rodata,存常量字符串这类内容)分配固定的虚拟内存地址——比如x86_64架构下通常从0x400000开始。
这些固定地址会被明确写入最终生成的ELF可执行文件里的程序头表和节头表中。GDB加载这个文件时,会严格按照文件里记录的地址来映射内存区域,所以不管你重启多少次GDB,反汇编出来的指令、常量地址都不会变。
2. 地址空间布局随机化(ASLR)没起作用?
现在大部分Linux系统默认开启了ASLR(地址空间布局随机化),但它只对**位置无关可执行文件(PIE)**和共享库生效。对于non-PIE的可执行文件,ASLR不会去随机化它的代码段、数据段地址——因为这类程序的代码是基于固定地址编写的,随便改地址会直接导致程序崩溃。
如果你想验证这一点,可以试试用gcc -o hello hello.c -ggdb -fPIE -pie编译生成PIE可执行文件,之后每次启动GDB反汇编main,你会发现地址完全不一样了。
可执行文件里真的包含地址信息吗?
没错,你的hello文件确实存着指令和常量的地址相关信息:
- ELF文件的节头表会记录每个段(比如
.text、.rodata)的起始虚拟地址,以及段内每个内容的偏移量 - 每条汇编指令在
.text段里的偏移是固定的,段起始地址加上这个偏移,就是指令最终加载到内存里的地址 - 常量(比如你代码里的字符串)存在
.rodata段,同样通过「段起始地址+偏移」来确定最终内存地址
GDB就是靠读取这些文件里的信息,再结合内存映射的实际情况,才能准确显示出指令和常量的地址。
内容的提问来源于stack exchange,提问作者VeryLazyBoy
相关产品推荐
相关产品推荐

