Nios® II Processor Reference Guide

ID 683836
Date 8/28/2023
Public
Document Table of Contents

7.9.5. Linux Position-Independent Code

Every position-independent code (PIC) function which uses global data or global functions must load the value of the GOT pointer into a register. Any available register may be used. If a caller-saved register is used the function must save and restore it around calls. If a callee-saved register is used it must be saved and restored around the current function. Examples in this document use r22 for the GOT pointer.

The GOT pointer is loaded using a PC-relative offset to the _gp_got symbol, as shown below.

Loading the GOT Pointer

nextpc r22
1:
   orhi r1, %hiadj(_gp_got - 1b) # R_NIOS2_PCREL_HA _gp_got
   addi r1, r1, %lo(_gp_got - 1b) # R_NIOS2_PCREL_LO _gp_got - 4  
   add r22, r22, r1
   # GOT pointer in r22

Data may be accessed by loading its location from the GOT. A single word GOT entry is generated for each referenced symbol.

Small GOT Model Entry for Global Symbols

addi   r3, r22, %got(x)      # R_NIOS2_GOT16

GOT[n]                         R_NIOS2_GLOB_DAT x

Large GOT Model Entry for Global Symbols

movhi r3,     %got_hiadj(x)    # R_NIOS2_GOT_HA
addi  r3, r3, %got_lo(x)       # R_NIOS2_GOT_LO
add   r3, r3, r22

GOT[n]                           R_NIOS2_GLOB_DAT x

For local symbols, the symbolic reference to x is replaced by a relative relocation against symbol zero, with the link time address of x as an addend, as shown in the example below.

Local Symbols for small GOT Model

addi   r3, r22, %got(x)         # R_NIOS2_GOT16

GOT[n]                            R_NIOS2_RELATIVE +x

Local Symbols for large GOT Model

movhi r3,     %got_hiadj(x)     # R_NIOS2_GOT_HA
addi  r3, r3, %got_lo(x)        # R_NIOS2_GOT_LO
add   r3, r3, r22

GOT[n]                            R_NIOS2_RELATIVE +x

The call and jmpi instructions are not available in position-independent code. Instead, all calls are made through the GOT. Function addresses may be loaded with %call, which allows lazy binding. To initialize a function pointer, load the address of the function with %got instead. If no input object requires the address of the function its GOT entry is placed in the PLT GOT for lazy binding, as shown in the example below.

For information about the PLT, refer to the "Procedure Linkage Table" section.

Small GOT Model entry in PLT GOT

ldw      r3, %call(fun)(r22)      # R_NIOS2_CALL16 fun
callr    r3

PLTGOT[n]                           R_NIOS_JUMP_SLOT fun

Large GOT Model entry in PLT GOT

movhi r3,     %call_hiadj(x)     # R_NIOS2_CALL_HA
addi  r3, r3, %call_lo(x)        # R_NIOS2_CALL_LO
add   r3, r3, r22
ldw   r3, 0(r3)
callr r3

PLTGOT[n]                           R_NIOS_JUMP_SLOT fun

When a function or variable resides in the current shared object at compile time, it can be accessed via a PC-relative or GOT-relative offset, as shown below.

Accessing Function or Variable in Current Shared Object

orhi      r3, %gotoff_hiadj(x)       # R_NIOS2_GOTOFF_HA x
addi      r3, r3, %gotoff_lo(x)      # R_NIOS2_GOTOFF_LO x
add       r3, r22, r3
# Address of x in r3

Multiway branches such as switch statements can be implemented with a table of GOT-relative offsets, as shown below.

Switch Statement Implemented with Table

# Scaled table offset in r4
   orhi      r3, %gotoff_hiadj(Ltable)       # R_NIOS2_GOTOFF_HA Ltable
   addi      r3, r3, %gotoff_lo(Ltable)      # R_NIOS2_GOTOFF_LO Ltable
   add       r3, r22, r3                     # r3 == &Ltable
   add       r3, r3, r4
   ldw       r4, 0(r3)                       # r3 == Ltable[index]
   add       r4, r4, r22                     # Convert offset into destination   
   jmp       r4
   ...
Ltable:
   .word %gotoff(Label1)
   .word %gotoff(Label2)
   .word %gotoff(Label3)