Passing the address of a variable to asm!()

I entry code for some MIPS core. Basically what I do is to set the stackpointer to a address I set via the linker script and jump to my main. This works:

#[naked]
#[no_mangle]
#[link_section = ".init"]
unsafe fn _start() -> ! {
    extern "C" {
        static _STACK_START: usize;
    }
    // Set stackpointer and call main()
    asm!("
       move $$sp, $0
        j $1"
        :: "r"(&_STACK_START), "i"(main as extern "C" fn() -> !)
        : "$$sp");
    ::core::intrinsics::unreachable();
}

However, I am confused why I cannot load the stack pointer directly using an immediate value (the address should be known statically):

asm!("
        li $$sp, $0
        j $1"
        :: "i"(&_STACK_START), "i"(main as extern "C" fn() -> !)
        : "$$sp");

Whenever I do it this way I get code wich loads 0 to the stackpointer:

a0000000:	00 00 1d 24 	addiu	$sp, $zero, 0
a0000004:	04 00 00 08 	j	16 <mipswififw.6kl3m8ym-cgu.0+0x10>

Just as a reference the code that is generated by the working example. It does an unnecessary addiu even though optimization is on:

a0000000:	04 a0 01 3c 	lui	$1, 40964
a0000004:	00 00 22 24 	addiu	$2, $1, 0
a0000008:	25 e8 40 00 	move	$sp, $2
a000000c:	06 00 00 08 	j	24 <mipswififw.6kl3m8ym-cgu.0+0x18>

Any idea how to load the address of a static variable as an immediate? It is no problem to load the value of a static as immediate this way.

I think the issue is that the addres of _STACK_START is not known at compile time. It will not be known until link time. Therefore I think you can’t use it as an immediate.

I’m not familiar with MIPS, but on x86 I’m just able to use the main and _STACK_START symbols directly in my assembly code:

    asm!("
        mov _STACK_START, %rsp
        jmp main"
        ::: "rsp");
1 Like

Yes you might be right that for this to work _STACK_START must be known by the compiler. Directly referencing the label in the assembly was the right call. MIPS assembler has its own pseudo instruction la to load the value of an label:

 asm!("
        la $$sp, _STACK_START
        j $0"
        :: "i"(main as extern "C" fn() -> !)
        : "$$sp");

Regarding the main symbol I am passing it in instead of hardcoding it in the asm for two reasons:

  • I can leave name mangling enabled for main
  • The compiler sees that I am referencing main and does not remove it even though its a private symbol (as it should be)