Skip to content

023_syscall FPU/SSE 与用户态编译排查笔记

问题 1:用户态程序 #GP — 编译器生成 SSE 指令

现象

用户态 C++ 程序在 Ring 3 执行时触发 #GP:

RIP   = 0x0000000000400019
RSP   = 0x00000007FFFFEFD8
movaps XMMWORD PTR [rsp],xmm0  ← #GP

根因

GCC 将 const char msg[] = "..." 的初始化优化为 SSE movdqa/movaps 指令。movaps 要求目标地址 16 字节对齐,但 RSP 未对齐。

反汇编确认:

asm
400000: sub    rsp, 0x28
...
400019: movaps XMMWORD PTR [rsp], xmm0   ← 崩溃点

解决路径

有两个方案:

方案 A:禁用 SSE(加 -mgeneral-regs-only)—— 临时方案 方案 B:在内核启用 FPU/SSE 支持 —— 正确方案

选择方案 B,因为用户态程序理应能使用 SSE。


问题 2:FPU/SSE 启用后仍然 #GP — 栈对齐

现象

FPU 已在 boot.S 中通过 CR0/CR4 启用,但 movaps 仍然触发 #GP。

根因

不是 FPU 未启用,而是栈未满足 x86_64 SysV ABI 对齐要求:

  • x86_64 ABI:函数入口 RSP ≡ 8 mod 16(模拟 call 指令压入 8 字节返回地址)
  • 我们的 USER_STACK_TOP = 0x7FFFFF000,是 0 mod 16
  • 编译器假设 RSP ≡ 8 mod 16,生成 sub rsp, 0x28 后期望 RSP 对齐
  • 0x7FFFFF000 - 0x28 = 0x7FFFFEFD8,8 mod 16 ≠ 0movaps 对齐失败

修复

跳转前 RSP 减 8,并用 static_assert 编译期检查:

cpp
constexpr uint64_t USER_ABI_RSP_OFFSET = 8;
static_assert((USER_STACK_TOP - USER_ABI_RSP_OFFSET) % 16 == 8,
              "User entry RSP must satisfy x86_64 ABI alignment");

调用处:

cpp
jump_to_usermode(USER_ENTRY_BASE, USER_STACK_TOP - USER_ABI_RSP_OFFSET, 0);

问题 3:boot.S 指令顺序导致大内核测试无法启动

现象

make run-kernel-test 输出 "Loaded ELF is not a real kernel, exiting",169 个大内核测试全部跳过。

根因

mini kernel 通过检查大内核入口前 3 字节验证是否为真实内核:

cpp
// kernel/mini/test/main_test.cpp
bool is_real_kernel = (code[0] == 0xFA) &&       // cli
                      (code[1] == 0x48) &&        // REX.W
                      (code[2] == 0xC7 || code[2] == 0xBC);  // mov rsp, imm

原始 boot.S 顺序:climovq $__kernel_stack_top, %rsp(字节 FA 48 BC ✓)

加入 FPU 初始化后变为:climovq %cr4, %rax(字节 FA 0F 20 ✗)

修复

把 FPU 初始化移到栈设置之后,保持 cli + mov rsp, imm 为前两条指令:

asm
_start:
    cli
    movq  $__kernel_stack_top, %rsp   # FA 48 BC — mini kernel 验证点
    xorq  %rbp, %rbp
    # FPU init follows...
    movq  %cr4, %rax
    orq   $((1 << 9) | (1 << 10)), %rax
    ...

FPU 支持改动总览

文件改动
kernel/arch/x86_64/boot.SCR0/CR4 初始化(OSFXSR + OSXMMEXCPT + 清 EM/TS)
kernel/proc/process.hppTask 结构体增加 alignas(16) uint8_t fpu_state[512]
kernel/proc/process.cppTaskBuilder::build() 中 fninit + fxsave 初始化 FPU 状态
kernel/proc/scheduler.cppschedule()/exit_current()/run_first() 中 fxsave/fxrstor
kernel/arch/x86_64/usermode.hppUSER_ABI_RSP_OFFSET + static_assert
kernel/arch/x86_64/usermode.cppjump_to_usermode 传 ABI 对齐的 RSP

用户态编译基础设施

文件作用
user/CMakeLists.txt编译用户态程序 → objcopy flat binary → ld -r -b binary 嵌入
user/linker.ld用户态链接脚本,VMA=0x400000
user/programs/hello.cppC++ 写的 Hello 程序,替代手写字节码

关键编译标志:-mcmodel=small(用户态在低 2GB),-fno-pie -nostdlib -static

035_multi_terminal-40-g5d72b8b · 5d72b8b · 2026-06-26