我正在尝试手动设置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!();
}
这是除 rust 剂:
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资源管理器)
我不了解两者之间的区别,也不了解所生成代码的区别。
最佳答案
jmp [$1]
是您在AT&T版本中没有的附加间接级别。
AT&T jmp *%1
等效于Intel jmp %1
。
注意%[entry]
只是写%1
的一种象征性方式;方括号是操作数名称语法的一部分,并且不会以寻址模式语法结尾在最终的asm输出中。
(另外,您的问题是一团糟,因为在编写GNU C内联汇编以使用默认-masm=intel
构建时,您在Godbolt上使用了-masm=att
。)
其他主要区别在于,当您在启用优化的情况下构建Rust代码时,GCC默认为-O0
(反优化 Debug模式)。
您可能应该在C __builtin_unreachable()
语句之后使用asm("")
,以确保编译器知道执行确实不会出现在asm语句的另一端。我担心标记包装函数noreturn
可能不足以防止编译器假定它可以将存储推迟到内联之后的asm语句之后。 (跳出asm
语句通常需要使用asm goto
作为已知标签,否则建议使用__builtin_unreachable()
。)
关于assembly - 从Rust更改x86中的执行堆栈,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60251776/