Ubuntu 22.04 x86_64下函数编译与运行地址不一致的底层问询
Ubuntu 22.04.3 x86_64下虚拟内存、ASLR与地址映射机制解答
场景说明
使用Ubuntu 22.04.3 x86_64系统,编译test.c生成可执行文件后,通过objdump查看得到main函数的静态偏移地址为0x1149;但每次运行程序时,printf输出的main函数地址都不同,例如0x55b032770149。
针对该场景,以下是问题解答:
1. 从0x1149转换为0x55b032770149的完整底层过程
0x1149是ELF可执行文件中main函数的相对虚拟地址(RVA),是链接阶段确定的、相对于.text段基址的静态偏移量。转换为运行时虚拟地址的过程如下:
- 进程启动时,内核为其分配独立的虚拟地址空间,并启用ASLR(地址空间布局随机化):内核会随机生成一个加载基址(示例中的
0x55b032770000),作为整个ELF文件映射到虚拟空间的起始地址。 - 用户空间的动态加载器
ld.so负责将ELF的各个段(如.text、.data)映射到虚拟地址空间,其中.text段会被映射到加载基址 + .text段的文件偏移位置。 - main函数的运行时虚拟地址 = 加载基址 + 静态偏移地址,即
0x55b032770000 + 0x1149 = 0x55b032770149。 - 内核通过MMU(内存管理单元)和分页机制,将该虚拟地址映射到物理内存,但这个映射对进程完全透明,进程仅与虚拟地址交互。
2. 程序中打印的地址是否为程序的真实确定虚拟地址?
是。程序打印的地址是当前进程虚拟地址空间中固定且确定的虚拟地址:
- ASLR仅在进程启动阶段随机选择加载基址,一旦进程启动完成,其虚拟地址空间的布局就不再变化,main函数的虚拟地址在进程生命周期内保持固定。
- 对进程而言,虚拟地址就是它能直接感知和使用的“真实”地址,所有内存访问、指令执行都基于这个地址空间。
3. RIP寄存器是否存储这类虚拟地址作为下一条执行指令的地址?
是的。x86_64架构下,RIP(指令指针寄存器)存储的始终是下一条要执行指令的虚拟地址:
- CPU执行指令时,通过RIP指向的虚拟地址,结合MMU的分页转换逻辑,将虚拟地址转换为物理地址以获取指令。
- 物理地址的转换由硬件(MMU)和内核页表共同完成,对用户进程完全透明,RIP全程使用虚拟地址工作。
内容的提问来源于stack exchange,提问作者alessio solari
相关产品推荐
相关产品推荐

