为什么objdump -d反汇编结果中未显示.data段内容?
为什么objdump -d的反汇编结果里看不到定义的Hello World字符串
objdump -d的默认逻辑是仅反汇编ELF文件中被标记为可执行权限的段,也就是你代码里存放指令的.text段。你定义的Hello, World!字符串存放在.data段,这个段的权限是可读可写、不可执行,不在-d参数的默认处理范围内,自然不会出现在反汇编输出里。
如果要查看字符串内容,可以换用以下两个命令:
- 执行
objdump -s a.out,打印所有段的原始十六进制值和对应ASCII内容,就能在.data段的对应偏移位置找到完整字符串 - 执行
objdump -D a.out,强制反汇编文件内的所有段,也能在.data段的对应地址看到字符串数据
你贴的反汇编结果里,movabs $0x402000,%rsi指令中的立即数0x402000,就是msg字符串加载到内存后的虚拟起始地址。另外你用equ定义的len是汇编阶段计算的常量,不会占用实际的段存储空间,值14(也就是0xe)被直接硬编码到了mov $0xe,%edx的指令里,所以也不会在数据段里找到它的存储位置。
程序执行时字符串如何加载到内存
ELF可执行文件的头部会存储程序头表,记录所有需要加载到内存的段的元信息:包括段在文件内的偏移、要映射到的目标虚拟地址、段长度、内存访问权限(读/写/执行)。
运行程序时,操作系统的程序加载器会解析程序头表,完成以下映射操作:
- 将
.text段映射到预先约定的虚拟地址范围,赋予可读、可执行权限 - 将存放字符串的
.data段映射到预先计算好的虚拟地址范围(你这个程序里就是0x402000开头的地址),赋予可读、可写权限 - 按默认规则分配栈、堆等运行时需要的内存区域
整个映射采用按需调页机制:加载初期只建立虚拟地址和文件对应页的映射关系,等程序真正访问到对应地址时,才会把磁盘上存储的段内容读到物理内存中,不需要程序手动编写加载字符串的逻辑。
为什么程序执行前就能确定msg的地址,地址分配是否由链接器完成
这个地址确实是链接阶段确定的。
你在汇编代码里用msg引用字符串时,汇编器生成的目标文件会先把这个地址位置留成待重定位的占位符。等链接器处理时,会按照64位非PIE(位置无关可执行文件)ELF的默认虚拟内存布局,给各个段分配固定的虚拟起始地址:
- 通常
.text段默认从0x401000地址开始,和你反汇编结果里_start的地址完全吻合 - 后续的
.data段会按内存页对齐规则排列到下一个合适的地址位置,你这个程序里就分配到了0x402000
因为非PIE的可执行文件采用固定加载基址,加上每个程序都拥有独立的虚拟内存空间,链接器不需要担心地址和其他程序冲突,在生成最终可执行文件时就能把所有符号(包括msg)的虚拟地址完全计算好,直接填充到对应指令的立即数位置,不需要程序运行时再动态计算地址。
内容的提问来源于stack exchange,提问作者chikako Ubukata

