链接i686-elf目标文件时ld报R_386_16截断错误求助
问题
我使用nasm编译以下汇编代码,并将其与其他i686-elf输出文件进行链接:
global x86_Video_GetVbeInfo x86_Video_GetVbeInfo: ; make new call frame push ebp ; save old call frame mov ebp, esp ; initialize new call frame x86_EnterRealMode ; save modified regs push edi push es push ebp ; bochs vbe changes ebp ; call interrupt mov ax, 0x4f00 LinearToSegOffset [bp + 8], es, edi, di int 10h ; check return cmp al, 4fh jne .error ; put status in eax mov al, ah and eax, 0xFF jmp .cont .error: mov eax, -1 .cont: ; restore regs pop ebp ; bochs vbe changes ebp pop es pop ebx push eax x86_EnterProtectedMode pop eax ; restore old call frame mov esp, ebp pop ebp ret
链接命令:
i686-elf-ld -T ../src/linker.ld -o ../binary/isolation/rcmiracle.bin -ffreestanding -O2 -nostdlib ../binary/compiler_out/x86.o ../binary/compiler_out/boot.o ../binary/compiler_out/kernel.o -lgcc
nasm编译无错误,但ld出现如下错误:
../binary/compiler_out/x86.o: in function `x86_Video_GetVbeInfo': ../src/x86.asm:(.text+0x44): relocation truncated to fit: R_386_16 against `.text' ../binary/compiler_out/x86.o: in function `x86_Video_GetVbeInfo.pmode16': ../src/x86.asm:(.text+0x51): relocation truncated to fit: R_386_16 against `.text'
修改链接脚本后问题仍未解决,求解决思路。
解决思路
这个错误的核心原因是:16位重定位无法适配32位段地址。你的代码位于32位保护模式段中,但x86_EnterRealMode、x86_EnterProtectedMode这类模式切换宏的实现里,大概率使用了16位的跳转或地址引用操作,而链接器检测到.text段的地址超出了16位能容纳的范围(即高于0xFFFF),因此触发截断错误。
具体修复步骤:
- 排查模式切换宏的实现:打开定义
x86_EnterRealMode和x86_EnterProtectedMode的文件,检查是否存在jmp .pmode16这类16位段内跳转,或是直接引用了32位地址的低16位部分。这类操作在段地址高于0xFFFF时必然失效。 - 将模式切换代码隔离到16位兼容段:在汇编代码中单独声明一个16位代码段,比如用nasm的
section .text16 align=16指令,然后把模式切换的核心逻辑移到这个段中。同时在链接脚本里将该段映射到0~0xFFFF范围内的地址空间。 - 调整链接脚本的段布局:确保包含模式切换代码的段被放置在低16位地址空间(虚拟地址小于0x10000)。如果你的链接脚本中
.text段的起始地址过高(比如0xC0000000这类内核高地址),16位重定位肯定无法正常工作。 - 使用正确的指令集前缀与跳转方式:如果必须在32位段内执行16位代码,需要用
bits 16前缀临时切换指令集,跳转时使用远跳转(far jump)明确指定16位段选择子,避免链接器生成错误的16位重定位项。 - 修正寄存器栈平衡错误:注意到代码中
pop ebx对应的是之前的push edi,这会导致寄存器栈不平衡,引发后续未知问题,需先将其修正为pop edi。
内容的提问来源于stack exchange,提问作者Lâm Bảo
相关产品推荐
相关产品推荐

