从 Rust 更改 x86 中的执行堆栈

Changing execution stack in x86 from Rust

我正在尝试手动设置 RSP 并使用内联 x86_64 程序集在 Rust 中的自定义地址开始执行。

我有这个有效的 C 代码:

#include <stddef.h>

void __attribute ((noreturn)) jump_with_stack(size_t jump_addr, size_t *jump_stack) {
            __asm__ volatile ( \
                        "movq %[stack], %%rsp\n" \
                        "xor %%rdx, %%rdx\n" \
                        "jmp *%[entry]" \
                        : /* None  */ \
                        : [stack] "r" (jump_stack), [entry] "r" (jump_addr) \
                        : "rdx", "memory" \
                        );
}

这是反汇编:

jump_with_stack:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     QWORD PTR [rbp-16], rsi
        mov     rax, QWORD PTR [rbp-16]
        mov     rcx, QWORD PTR [rbp-8]
        movq rax, %rsp
        xor %rdx, %rdx
        jmp *rcx
        nop
        pop     rbp
        ret

而这个 Rust 代码没有:

#![feature(asm)]

pub unsafe extern fn rust_jump_with_stack(target: usize, targ_stack: *mut usize) -> ! {

    asm!("mov rsp, [=12=]
         xor rdx, rdx
         jmp []"
         :
         :"r"(targ_stack), "r"(target)
         : "rdx", "memory"
         : "intel");
    unreachable!();
}

这里是铁锈拆解:

example::rust_jump_with_stack:
    push    rax

    mov     rsp, rsi
    xor     rdx, rdx
    jmp     qword ptr [rdi]

    lea     rdi, [rip + .L__unnamed_3]
    lea     rdx, [rip + .L__unnamed_4]
    mov     rax, qword ptr [rip + std::panicking::begin_panic@GOTPCREL]
    mov     esi, 40
    call    rax
    ud2

(两个反汇编输出均来自 Godbolt explorer)

我不明白两者之间的区别或生成的代码中的区别意味着什么。

jmp [] 是您的 AT&T 版本中没有的额外间接级别。

AT&T jmp *%1 相当于英特尔 jmp %1.

注意%[entry]只是%1的符号写法;方括号是操作数名称语法的一部分,不会作为寻址模式语法出现在最终的 asm 输出中。

(你的问题也是一团糟,因为你在 Godbolt 上使用 -masm=intel,而你的 GNU C 内联 asm 被编写为使用默认 -masm=att 构建。)


其他主要区别是 GCC 默认为 -O0(反优化调试模式),而您在启用优化的情况下构建 Rust 代码。


您可能应该在 C asm("") 语句之后使用 __builtin_unreachable() 以确保编译器知道执行确实不会出现在 asm 语句的另一侧。我担心标记包装函数 noreturn 可能不足以阻止编译器假设它可以将存储推迟到 asm 语句之后,在内联之后。 (跳出 asm 语句通常需要 asm goto 已知标签,否则建议 __builtin_unreachable()。)