GAS汇编ELF内核时出现栈指针对齐失效问题
操作系统开发栈指针对齐失效问题解答
问题描述
我正在编写一个玩具操作系统(仅出于学习目的),大致参考了OSDev bare bones教程以及第一版《Writing an OS in Rust》,目前遇到的问题是:用GRUB生成的启动镜像可以在QEMU中成功启动,但之后会出现随机Bug。
排查后发现是栈指针没有正确对齐,页表符号、stack_bottom和stack_top最终都落在同一个奇数地址上,导致所有ret指令都会跳转到错误位置。
相关代码如下:
汇编代码
.intel_syntax noprefix .code32 .set ALIGN, 1<<0 /* align loaded modules on page boundaries */ .set MEMINFO, 1<<1 /* provide memory map */ .set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */ .set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */ .set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */ .section .multiboot .align 4 .long MAGIC .long FLAGS .long CHECKSUM .section .bss .align 16 p4_table: .skip 4096 p3_table: .skip 4096 p2_table: .skip 4096 stack_bottom: .skip 4*4096 stack_top: .section .text .global _start .type _start, @function _start: mov esp, stack_top call check_multiboot [...]
链接脚本
ENTRY(_start) SECTIONS { . = 1M; .text : ALIGN(4K) { *(.multiboot) *(.text) } .rodata : ALIGN(4K) { *(.rodata*) } .data : ALIGN(4K) { *(.data) } .bss : ALIGN(4K) { *(.bss) *(COMMON) } }
已经验证栈指针对齐错误是Bug诱因:在gdb中手动将esp调整为16的倍数后,call check_multiboot就可以正常返回。
原因分析
问题出在GNU汇编器(GAS)的Intel语法取值逻辑上:
在.intel_syntax noprefix语法规则下,mov esp, stack_top的语义是读取stack_top标签指向的内存地址中存储的数值,再赋值给esp寄存器,而非直接把stack_top本身的地址值赋值给esp。
由于.bss段在初始化前内存中是随机值,相当于把随机数赋值给了栈指针,自然会出现栈不对齐、ret指令跳转错误的随机问题。你的链接脚本和段对齐设置本身没有问题,.bss段的align 16以及链接脚本的4K对齐规则都可以保证stack_top的地址是对齐的。
解决方案
把汇编代码中_start入口处的栈指针赋值语句修改为:
mov esp, OFFSET stack_top
OFFSET关键字会告诉汇编器,你要取的是stack_top这个标签本身的地址值,而不是该地址指向的内存内容,这样就能按照预期把栈顶的正确地址赋值给esp,符合对齐规则。
内容的提问来源于stack exchange,提问作者Christian Kuhl
相关产品推荐
相关产品推荐

