解析Linux内核入口点x86汇编代码相关疑问
Linux内核启动汇编代码疑问解答
首先贴出对应的AT&T语法汇编代码:
00: leal (BP_scratch+4)(%esi), %esp 01: call 1f 02: 1: popl %edx 03: addl $_GLOBAL_OFFSET_TABLE_+(.-1b), %edx 04: 05: /* Load new GDT */ 06: leal gdt@GOTOFF(%edx), %eax 07: movl %eax, 2(%eax) 08: lgdt (%eax) 09: 0A: /* Load segment registers with our descriptors */ 0B: movl $__BOOT_DS, %eax 0C: movl %eax, %ds 0D: movl %eax, %es 0E: movl %eax, %fs 0F: movl %eax, %gs 10: movl %eax, %ss
问题解答
第一行指令中%esi寄存器存储的内容是什么?
%esi存储的是boot_params结构体的物理地址。BP_scratch是该结构体中的字段,这段代码用它来设置栈指针%esp,将栈起始位置设为BP_scratch+4相对于boot_params的偏移处。被弹出并存储到%edx中的栈内容是什么?
call 1f指令会把标签1:对应指令的物理地址压入栈中,随后popl %edx将这个地址弹出到%edx。这是x86汇编里获取当前指令地址的经典技巧,用于计算全局偏移表(GOT)的地址。第7行movl指令的操作逻辑是什么?是否是将%eax的值设为%eax+2?
完全不是。AT&T语法遵循源, 目的的顺序,这条指令的意思是:把%eax中的值,写入到内存地址为%eax+2的位置。
这里%eax指向GDT描述符的起始地址,GDT描述符是6字节结构:前2字节是GDT大小,后4字节是GDT基地址。这条指令是把GDT的实际物理地址(存在%eax中)写入描述符的基地址字段(从偏移2字节开始的位置),让lgdt指令能正确加载GDT的基地址和大小。为何所有段寄存器都要加载__BOOT_DS常量?
__BOOT_DS是GDT中定义的平坦数据段选择子,对应的段描述符基地址为0、限长4GB。在保护模式下,段寄存器存储段选择子,加载这个值后:
- 所有数据段(ds、es、fs、gs)和栈段(ss)共用同一个平坦段,内存访问无需计算段偏移,直接使用线性地址(此时分页未开启,线性地址等于物理地址)。
- 统一段寻址规则,避免因段配置不一致导致内存访问错误,为后续内核初始化流程铺路。
内容的提问来源于stack exchange,提问作者Jeremy
相关产品推荐
相关产品推荐

