How to properly initialize $gp on mips?

I am developing some bare metal application for a mipsel core. I supply -C relocation-model=static to the compiler in order to generate absolutely positioned code. This is my LLVM spec:

{
  "llvm-target": "mipsel-unknown-none",
  "arch": "mips",
  "cpu": "mips32r2",
  "data-layout": "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64",
  "executables": true,
  "features": "+mips32r2,+soft-float",
  "linker-flavor": "ld.lld",
  "linker": "rust-lld",
  "panic-strategy": "abort",
  "max-atomic-width": 32,
  "os": "none",
  "target-c-int-width": "32",
  "target-endian": "little",
  "target-pointer-width": "32"
}

However, when I use optimization levels < 2 rustc uses the global pointer $gp some times to reference data. I do not set the value of $gp at all in my startup code because it is my understanding that it should not be used in statically positioned code. Therefore the code does crash when it tries to use $gp.

Is there some way to tell the compiler to stop using $gp?

In my desperation I also tried to get it somehow working by setting $gp to the correct value.

First I added _gp to my linker script in order to tell the linker where $gp will be positioned. It is my understanding that the linker should now relocate the offsets to $gp properly in my code:

START_ADDR = 0xA0000000;
MEMORY_SIZE = 0x40000;
ASSERT(DEFINED(_start), "Cannot find entry point: fn _start() -> !");
ENTRY(_start);

SECTIONS
{
    . = START_ADDR;

    .text :
    {
        *(.init)
        *(.text*)
    }

    .rodata :
    {
        *(.rodata*)
    }

    _gp = ALIGN(16);

    .data :
    {
        *(.data*)
    }

    _BSS_START = .;
    .bss :
    {
        *(.bss*)
    }
    _BSS_END = .;

    .vectors ALIGN(0x1000) :
    {
        _VECTOR_START = .;
        KEEP(*(.tlb_exception))
        . = _VECTOR_START + 0x180;
        KEEP(*(.exception))
    }

    _STACK_END = .;
    _STACK_START =  START_ADDR + MEMORY_SIZE;
}

Then I setup$gp in my startup asm!() code:

 lui $$gp, %hi(_gp)
 ori $$gp, %lo(_gp)

This yields:

a000002c:       00 a0 1c 3c     lui     $gp, 40960
a0000030:       90 7a 9c 37     ori     $gp, $gp, 31376

However, this also crashed because the instructions that load/store relatively to $gp use wrong offsets. Shouldn't the linker fixed that for me?.

How to properly setup $gp?

After a lot of debugging I found my mistakes. I answer my own questions now:

Is there some way to tell the compiler to stop using $gp ?

Setting the relocation model to static seems to be the correct to way to do that. I did set it in .cargo/config:

 [target.mipsel-unknown-none]
 rustflags = [
        "-C", "link-arg=-Tlink.ld",
        "-C", "relocation-model=static",
        "-C", "inline-threshold=275"
 ]

This however seems to only apply to my own code but not to the core crate that is built with xbuild. It did notice that none of my own functions did use $gp. Setting the value in my llvm target spec did fix this issue:

"relocation-model": "static",

How to properly setup $gp?

The asm to load the label was wrong. I not know why but it seems that this %hi, $lo syntax does not work and was loading a slightly off value. The proper way to load the address of an label is the la pseudo instruction:

la $$gp, _gp

After fixing the asm the code worked fine as the $gp had the proper value and all functions could be found.

1 Like