You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确设置Bootloader、Linker与GDT间的栈工作流?

保护模式下Bootloader、链接脚本与GDT的栈配置工作流

一、GDT栈段描述符的正确配置

  • 栈段属于向下扩展的数据段,描述符Type字段需设为0x92(二进制10010010:段存在、特权级0、向下扩展、可读写)
  • 基址(Base)必须完全覆盖栈的物理内存区域,限长(Limit)按栈大小计算:
    • 128KB栈大小为0x20000,开启粒度位(G位=1,单位4KB)时,限长需设为0x1FFFF(公式:(限长+1)*4KB = 栈大小)
    • 示例配置(对应基址0x80000的128KB栈):
      set_gdt_gate_access(5, 0x80000, 0x1FFFF, 0x92, 0xCF);
      // 第5个GDT项(索引从0开始),基址0x80000,限长0x1FFFF,Type=0x92,G/D/B位等=0xCF
      
  • 栈段DPL(特权级)必须与当前运行特权级一致(Bootloader/内核通常为0),否则会触发保护异常

二、链接脚本中栈段的地址分配

  • 链接脚本需明确.stack段的物理地址,必须落在GDT栈段描述符的基址+限长范围内:
    SECTIONS {
        . = 0x100000; /* 内核加载基址 */
        .text : { *(.text) }
        .data : { *(.data) }
        .bss : { *(.bss) }
        /* 将栈放在内核之后,分配128KB空间 */
        .stack : ALIGN(4) {
            stack_start = .;
            . += 0x20000;
            stack_end = .;
        }
    }
    
  • 若栈放在独立内存区域(如0x80000~0xA0000),需修改.stack起始地址为0x80000,确保与GDT基址完全匹配

三、Bootloader(osload.asm)中的栈初始化流程

进入保护模式后,按以下顺序执行:

  1. 加载GDT(lgdt指令)并切换到保护模式;
  2. 加载栈段选择器到SS:选择器值为GDT中栈段项的索引×8(如第5项为5*8=0x28);
  3. 设置ESP为栈顶地址(即链接脚本导出的stack_end);
    示例代码:
    ; 加载栈段选择器
    mov ax, 0x28
    mov ss, ax
    ; 设置栈顶地址
    mov esp, stack_end
    
  • 注意:mov ss, ax和mov esp, stack_end需连续执行,CPU会自动屏蔽这两条指令间的中断,避免栈未初始化完成就触发异常

四、内核端的注意事项

  • 不要在内核中重复修改GDT栈段描述符:Bootloader已完成GDT和栈的初始化,内核直接复用即可;
  • 若内核必须重新加载GDT,需保证新GDT的栈段描述符与原配置完全一致(基址、限长、特权级),且栈地址仍在有效范围内

五、常见错误排查

  • SS bound violation:ESP超出栈段有效范围。检查:
    • ESP初始值是否为基址 + 栈大小(向下扩展栈的栈顶是段最高地址,push操作会先减ESP再写入数据);
    • 链接脚本的.stack段是否落在GDT栈段的基址+限长范围内;
  • mov ss, ax执行错误:GDT栈段描述符的DPL与当前特权级不匹配,或Type字段未设为向下扩展数据段;
  • 栈地址与GDT基址不匹配:链接脚本分配的栈物理地址不在GDT栈段描述符覆盖区域内,需同步修改两者配置

内容的提问来源于stack exchange,提问作者raven_lee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 10:35:56