diff --git a/VKernel/Kernel/intr/isr.s b/VKernel/Kernel/intr/isr.s index 3b0b984..c4b5420 100644 --- a/VKernel/Kernel/intr/isr.s +++ b/VKernel/Kernel/intr/isr.s @@ -11,9 +11,6 @@ .comm _fault_rsp, 8, 8 .comm _fault_rbp, 8, 8 .comm _fault_rax, 8, 8 -# syscall 入口来源标记(1 = 内核态发起):出口据此决定 sysretq(用户)还是 jmp rcx(内核)。 -# 刻意不用 gs:[…]:引导早期内核可能还没 swapgs 过(GS ≠ &_per_cpu),gs 相对写会打偏地址。 -.comm _syscall_from_kernel, 8, 8 # ISR 入口暂存 caller-saved 寄存器(rax/rcx/r8-r11):本宏会用它们当临时寄存器 # (读 RIP/CS/RFLAGS、算 is_user),若不保存就会破坏被中断代码在这些寄存器里的 # 活跃值 → 概率性内存破坏/异常。出口(iretq 前)恢复。 @@ -440,10 +437,10 @@ syscall_from_user: swapgs mov gs:[0x0], rsp mov rsp, gs:[0x8] - mov qword ptr [_syscall_from_kernel], 0 # 标记:用户态发起(出口据此选 sysretq) + push 0 # 来源标记(用户态发起):出口据此选 sysretq jmp syscall_common syscall_from_kernel: - mov qword ptr [_syscall_from_kernel], 1 # 标记:内核态发起(出口据此 jmp rcx 返回,不 swapgs) + push 1 # 来源标记(内核态发起):出口据此 jmp rcx 返回,且不 swapgs syscall_common: push rcx push r11 @@ -461,9 +458,11 @@ syscall_common: push r15 mov rbp, rsp and rsp, ~15 - # 对齐补齐:被调者入口必须满足 SysV(rsp ≡ 8 (mod 16),栈上 1 个参数时要求调用前 rsp ≡ 8)。 - # 只 `and rsp,~15`(≡0) 再 push arg6 会让 handler 入口 ≡ 0 → 一旦其调用树里出现 - # rsp 相对的 16 字节对齐 SSE(内核里 printf 就有 movaps %xmm0,0x60(%rsp))即 #GP。 + # 对齐补齐:被调者入口必须满足 SysV(rsp ≡ 8 (mod 16);栈上 1 个参数时要求调用前 rsp ≡ 8)。 + # 注意 `and rsp,~15` 已把 rsp 归零,所以只有**它之后**的深度才影响对齐: + # 补齐(8) + arg6(8) + call(8) = 24 ≡ 8 ✓。标记槽与 14 个寄存器都在 and 之前,不参与。 + # (去掉这里的 `sub rsp,8` 会让 handler 入口 ≡ 0 → 其调用树里 rsp 相对的 16 字节对齐 SSE + # 例如内核 printf 的 movaps %xmm0,0x60(%rsp) 就 #GP。) sub rsp, 8 push r9 mov r9, r8 @@ -489,18 +488,21 @@ syscall_common: pop rbx pop r11 pop rcx - # 返回去向改用**入口记下的 _syscall_from_kernel 标记**。原来用 `mov rax,rcx; shr rax,47` - # 判:① 会把 rax 里的返回值覆盖掉(app 的 trampoline 就靠它取结果);② 用户 rip 在 0x40xxxx、 - # 内核 rip 在 0x10xxxx,两者都 < 2^47,根本无法区分,内核态发起的 syscall 会误走 sysretq。 - cmp qword ptr [_syscall_from_kernel], 0 + # 返回去向用**栈上的来源标记**([rsp],入口 push 0/1)。必须是栈上而不是全局:syscall 会 + # 嵌套(app 的 SO_CALL1 → 内核调 .so → .so 内部又 syscall 打日志),全局标记会被内层覆盖, + # 外层(用户态)就会误走内核返回分支 → jmp rcx 回用户 rip 却不换回用户栈,app 用内核栈继续跑。 + # 原来用 `mov rax,rcx; shr rax,47` 判还有第二个错:覆盖 rax 里的返回值,且用户 rip(0x40xxxx) + # 与内核 rip(0x10xxxx) 都 < 2^47 无法区分。 + cmp qword ptr [rsp], 0 jne syscall_ret_kernel syscall_ret_user: - mov rsp, gs:[0x0] + mov rsp, gs:[0x0] # 用户路径直接切回用户栈(本层标记槽留在内核栈,下次入口由 TSS.RSP0 重置) swapgs push r11 popfq sysretq syscall_ret_kernel: + add rsp, 8 # 内核路径:弹掉本层标记槽,恢复到入口 rsp push r11 popfq jmp rcx