In this lesson, you will learn how to configure the linker to produce binaries that remain functional even when loaded at varying physical memory addresses.
Why Position Independence Matters
In standard embedded systems, we typically link code to run at a specific "Virtual Address." However, scenarios like dynamically loading modules or implementing bootloaders often require "Position Independent Code" (PIC). If your code contains absolute memory addresses (like ldr r0, =0x20000000), it will crash if moved. To solve this, we must ensure the binary uses relative addressing—calculating the location of data based on the current program counter (PC) rather than an absolute address.
Configuring the Linker for PIC
To achieve this, your toolchain must be instructed to generate position-independent instructions (e.g., using -fPIC or -fpic in GCC) and the linker must process these correctly.
- Compiler Level: The compiler generates code that avoids direct absolute addressing. Instead of jumping to an absolute label, it uses a Global Offset Table (GOT). The GOT is a table of absolute addresses that is updated at runtime by the loader based on the actual load address.
- Linker Level: You must ensure your linker script does not force absolute placement for sections like
.textor.data. You can use the.(dot) symbol to imply relative offsets.
Consider this snippet in your linker script:
SECTIONS {
. = 0; /* Base is relative to load point */
.text : { *(.text) }
.got : { *(.got) }
.data : { *(.data) }
}
By setting the base to 0 or using relative positioning, the code accesses the .got section via a PC-relative offset. The code says, "I don't know where my base is, but I know the GOT is exactly 1KB away from here." Once the binary is loaded, the startup code calculates the delta between the current address and the linked address, effectively "fixing up" the base pointer for the GOT.
By offloading the hard-coded absolute addresses to a dynamic table (the GOT) and using PC-relative instructions, your software becomes portable across any memory region.
Key Takeaway: Position Independence is achieved by replacing hard-coded absolute addresses with PC-relative lookups via a Global Offset Table, allowing the executable to function regardless of its physical load address.