【发布时间】:2018-03-06 07:06:39
【问题描述】:
作为 Rust 操作系统的一部分,我有以下系统调用入口点:
#[no_mangle]
#[naked]
#[inline(never)]
unsafe extern "C" fn syscall_handler() {
// switch to the kernel stack dedicated for syscall handling, and save the user task's details
asm!("swapgs; \
mov gs:[0x8], rsp; \
mov gs:[0x10], rcx; \
mov gs:[0x18], r11; \
mov rsp, gs:[0x0];"
: : : "memory" : "intel", "volatile");
let (rax, rdi, rsi, rdx, r10, r8, r9): (u64, u64, u64, u64, u64, u64, u64);
asm!("" : "={rax}"(rax), "={rdi}"(rdi), "={rsi}"(rsi), "={rdx}"(rdx), "={r10}"(r10), "={r8}"(r8), "={r9}"(r9) : : "memory" : "intel", "volatile");
// do stuff with rax, rdi, rsi...
这在调试模式和发布模式(启用调试信息)下工作正常,因为它生成的汇编代码在基指针 @987654324 的负偏移处存储局部堆栈变量,如 rdi、rsi 等@。
例如,这是生成的代码:
<syscall_handler>:
swapgs
mov %rsp,%gs:0x8
mov %rcx,%gs:0x10
mov %r11,%gs:0x18
mov %gs:0x0,%rsp
mov %rax,-0x1f0(%rbp)
mov %rdi,-0x1e8(%rbp)
mov %rsi,-0x1e0(%rbp)
mov %rdx,-0x1d8(%rbp)
mov %r10,-0x1d0(%rbp)
mov %r8,-0x1c8(%rbp)
mov %r9,-0x1c0(%rbp)
movb $0x4,-0x1b1(%rbp)
该代码工作正常,因为我的系统调用处理程序使用指向当前内核堆栈顶部的堆栈指针运行(像往常一样),这意味着可以使用堆栈指针/基指针(基指针)的负偏移量rbp 在此之前根据堆栈指针值设置)。
当我在没有调试信息的情况下以发布模式构建时,它生成的代码使用堆栈指针本身的正偏移量(rsp,而不是基指针)作为本地堆栈变量的位置。这真的很奇怪,并且会导致问题,因为当前堆栈指针rsp 上方的内存超出了界限。
这是在没有调试信息的纯发布模式下生成的代码:
<syscall_handler>:
swapgs
mov %rsp,%gs:0x8
mov %rcx,%gs:0x10
mov %r11,%gs:0x18
mov %gs:0x0,%rsp
mov %rax,0x1c0(%rsp)
mov %rdi,0x1c8(%rsp)
mov %rsi,0x1d0(%rsp)
mov %rdx,0x1d8(%rsp)
mov %r10,0x1e0(%rsp)
mov %r8,0x1e8(%rsp)
mov %r9,0x1f0(%rsp)
为什么会生成这段代码,使用堆栈指针的正偏移量的代码?这让我觉得很奇怪。
有什么方法可以避免这种情况或以某种方式更改代码生成?
【问题讨论】:
-
谢谢。我的目标 json 文件中已禁用红色区域,所以这不是问题。需要明确的是,来自 RSP 的负偏移量很好,正偏移量是问题,因为 RSP 上面的地址没有被映射。
-
只有当您确实有一个红色区域时,RSP 的负偏移才可以。但你不这样做,所以除非中断被禁用,否则它们是不安全的。
-
嗯,也许我误会了什么。假设我有一个内核堆栈占用地址 0x4000 到 0x8000,顶部 (
rsp) 是 0x8000。如果我从堆栈顶部有一个负偏移量,比如 -8,足够的空间用于 u64,那么内存访问将在地址 0x7FF8 处有效。如果我有一个正偏移量,如我的代码示例中所示,访问将是无效的,不是吗? -
是的,访问你没有用
sub rsp, whatever保留的堆栈内存也是不安全的,或者更一般地说,你不拥有由于某种原因(例如堆栈上的红色区域或函数参数) )。在普通函数中,您会在调用者的堆栈空间上乱涂乱画。在您的情况下,您将引用未分配或非堆栈内存,使所有堆栈内存低于rsp(直至0x4000)未使用。 -
它奇怪/令人困惑的原因是您要求一个
naked函数(因此没有函数序言来保留堆栈空间),但无论如何您在其中使用了局部变量。我认为在一些支持裸函数的 C 编译器中,这是不受支持的;只允许内联汇编作为整个函数体。但是,就内联汇编和编译器之间的奇怪探戈而言,Rust 所说的 IDK 得到了官方支持。
标签: assembly x86 rust code-generation osdev