首页 > 解决方案 > 从 Rust 更改 x86 中的执行堆栈

问题描述

我正在尝试手动设置 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, $0
         xor rdx, rdx
         jmp [$1]"
         :
         :"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)

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

标签: assemblyrustx86inline-assembly

解决方案


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

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

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

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


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


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


推荐阅读