RISC-V Global Pointer

When programming bare-metal (and probably with OS) RISC-V, correctly managing the global pointer (gp) is crucial. The gp is primarily used to access small data variables with an offset relative to its address, a technique known as linker relaxation.

The gp should point to the .data section of your code to efficiently access variables within a ±2048-byte range. Use the following in your linker script to set the global pointer:

__global_pointer$ = MIN(_sdata + 0x800, MAX(_data + 0x800, _end - 0x800));

If you prefer not to use the gp, link your program with the --no-relax flag.

RISC-V programs assume that the gp register is correctly set, but this may not be the case at boot time. Therefore, you must initialize the gp immediately at program start. Use the following assembly code to do so:

.globl _start

.section .text.init
_start
    /* Initialize gp */
    .option push
    .option norelax
    la  gp,__global_pointer$
    .option pop
    /* Initialize other variables */
    la  sp,__stack_top

The norelax directive temporarily disables linker relaxation when loading the gp. Without this, the linker might incorrectly relax the initialization of gp with mv gp, gp.

Sources: