x86平台gcc/ld编译32/16位混合引导程序相对调用地址偏移2字节问题
核心原因
2字节偏移问题本质是32位指令被误当作16位指令解析、链接起始地址不匹配两个问题共同导致的,和混合16/32位代码的编译链接配置错误直接相关:
- 你贴出的反汇编输出里
call 0x133是3字节的16位call指令,但使用-m32编译的C代码生成的是5字节长度的32位call指令(操作码E8后跟4字节相对偏移)。你反汇编纯二进制文件时没有指定32位模式,导致指令拆分错误,误以为call的目标地址偏移了2字节。 - 代码实际被MBR加载到
0x9000,但你没有在链接时显式指定代码段起始地址,LD默认按起始地址0x0做重定位,运行时相对寻址自然出错。 - 如果你是用
%include把包含BIOS_Interrupt的汇编文件导入到16位的bootloader.asm中,NASM汇编时会沿用当前的[bits 16]设置计算符号地址,也会导致符号地址偏移2字节。
解决方法
- 拆分汇编文件,不要把32位的
BIOS_Interrupt代码%include到16位的MBR代码里,单独汇编成32位ELF目标文件:
汇编命令保持; bios_int.asm 单独汇编,不要include到16位代码中 section .text [bits 32] align 4 ; 显式4字节对齐,避免段对齐问题 global BIOS_Interrupt BIOS_Interrupt: pushf ; 其余实现逻辑 retnasm -f elf32 bios_int.asm -o bios_int.o即可。 - 链接时显式指定代码段起始地址为你实际加载的地址
0x9000,修改LD参数为:ld -m elf_i386 -Ttext 0x9000 -nostdlib *.o -o bootloader.elf - 导出纯二进制文件后如果要反汇编校验,使用指定32位模式的参数:
objdump -D -b binary -m i386 -M x86-32 bootloader.bin - 如果确实需要在同一个汇编文件里混合16位和32位代码,每次切换模式时必须显式声明
[bits 16]/[bits 32],且每个模式的代码段单独对齐。
内容的提问来源于stack exchange,提问作者Mdd M
相关产品推荐
相关产品推荐

