【发布时间】:2020-11-09 08:51:33
【问题描述】:
我正在编写一个操作系统。我使用asm! 宏来更改代码段。 (2020/06/08 的详细信息更改为 asm! here 和 Rust RFC 2873)
pub unsafe fn set_code_segment(offset_of_cs: u16) {
asm!("push {0:r} // 64-bit version of the register
lea rax, 1f // or more efficiently, [rip + 1f]
push rax
retfq
1:", in(reg) offset_of_cs);
}
这行得通。但是,如果我使用push 1f,标签1:的地址将不会被推送。相反,它将是一个内存源操作数,从[1:]加载
所以下面的代码
pub unsafe fn set_code_segment(offset_of_cs: u16) {
asm!("push {0:r}
push 1f // loads from 1f, how to push the address instead?
retfq
1:", in(reg) offset_of_cs);
}
不会工作。反汇编(ndisasm)代码是这样的:
11103 │ 0000B9EC 57 push rdi
11104 │ 0000B9ED FF3425F6B90080 push qword [0xffffffff8000b9f6]
11105 │ 0000B9F4 48CB retfq
用 nasm 语法编写的所需代码是这样的:
[bits 64]
extern set_code_segment
set_code_segment:
push rdi
push change_code_segment ; absolute address as a 32-bit immediate
retfq
change_code_segment:
ret
与内核(和extern "C" { pub fn set_code_segment(offset_of_cs: u16) -> () })链接,代码可以工作。 change_code_segment的地址将被成功推送。
所以我的问题是:为什么asm!的push 1f推送地址1:的内容,而不是1:的地址?
【问题讨论】:
-
AFAIK,Rust 的内联汇编从 GCC 中汲取了一些灵感。因此,也许您需要使用
$前缀来指定您的意思是立即操作数:push $1f -
@Michael 感谢您的评论,但
push $1f并没有改变这种情况...反汇编的代码是一样的。 -
@PeterCordes
rof{0:r}将寄存器的大小指定为 64 位。令我惊讶的是,使用 16 位寄存器会导致一般保护错误。 -
我发现的文档明确说它应该使用 GAS / LLVM 样式
.intel_syntax noprefix,所以我认为这是一个错误push OFFSET 1f不起作用。我不确定报告的最佳地点。 -
@PeterCordes 感谢许多有用的 cmets。我发送了a bug report。
标签: rust x86-64 inline-assembly