toku-sa-n
toku-sa-n

Reputation: 894

`push label` pushs [label], not the address of label (Rust asm!)

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

Answers (1)

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

Related Questions