elf-i686环境C调用汇编例程出现栈错误引发QEMU重启问题求助
核心问题原因
你的故障和栈清理无关,本质是入口函数指向错误:
- 你的链接命令未指定入口函数,同时你将汇编目标文件
kernelasm.o放在链接顺序的第一位,因此链接器默认将汇编代码的第一个标签_printcool作为整个内核的入口点,偏移为0x1000 - 内核被加载运行时,CPU直接从
_printcool开始执行,而非你预期的先运行kmain再调用_printcool。此时没有call指令往栈上压返回地址,执行ret时会把栈上的未知值当做返回地址跳转,直接触发异常导致重启 - 把
ret替换为jmp $后不会触发跳转,因此不会崩溃,只是临时规避了问题
其他附加错误:
- C代码开头的
kmain();是全局作用域的无效语句,属于冗余错误代码 - 汇编中
add esp,4完全多余:你的_printcool无参数,且C默认使用cdecl调用约定,参数栈由调用方清理,被调方不需要修改esp,这行代码反而会破坏栈结构
修复步骤
- 修改链接命令,显式指定入口函数
给ld命令添加-e kmain参数,指定内核入口为kmain:i686-elf-ld --oformat binary -Ttext 0x1000 -e kmain kernelasm.o kernelc.o -o kernel.bin - 删除冗余错误代码
- 删掉汇编中
add esp,4一行 - 删掉C代码开头的
kmain();一行
- 删掉汇编中
- 补充栈初始化(推荐)
裸机环境下没有默认可用栈,建议在跳转到内核入口之前,在你的启动代码中初始化栈指针到安全内存区域,例如:
如果你没有额外的启动loader,直接加载内核运行,可以在汇编代码开头添加入口逻辑:mov esp, 0x90000
此时链接命令调整为指定[bits 32] global _start global _printcool global _wait _start: mov esp, 0x90000 ; 初始化栈 call kmain jmp $ _printcool: mov byte [0xB8000], 'R' mov byte [0xB8001], 0x52 ret _wait: jmp _wait_start为入口:i686-elf-ld --oformat binary -Ttext 0x1000 -e _start kernelasm.o kernelc.o -o kernel.bin
内容的提问来源于stack exchange,提问作者Mark Dyks
相关产品推荐
相关产品推荐

