Reputation: 894
I'm writing an OS. I use asm!
macro to change the code segment. (Details on 2020/06/08 changes to asm!
here and in 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);
}
This works. However, if I use push 1f
, the address of label 1:
will not be pushed. Instead, it will be a memory source operand, loading from [1:]
So the following code
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);
}
will not work. Disassembled (by ndisasm
) code is this:
11103 │ 0000B9EC 57 push rdi
11104 │ 0000B9ED FF3425F6B90080 push qword [0xffffffff8000b9f6]
11105 │ 0000B9F4 48CB retfq
The desired code written in nasm syntax is this:
[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
Linked with the kernel (and extern "C" { pub fn set_code_segment(offset_of_cs: u16) -> () }
), the code works. The address of change_code_segment
will successfully be pushed.
So my question is: why push 1f
of asm!
pushes the content of address 1:
, not the address of 1:
?
Upvotes: 3
Views: 761
Reputation: 217
The rust asm!
macro is built upon llvm
.
And there is a specific bug in llvm that interpret labels only composed of 0
and 1
digits, such as 0
, 11
or 101010
, as binary values. That's what's happening here, this binary value is read as an address in memory.
Also, the rust asm!
documentation had been updated and now include a labels section.
Upvotes: 3