In this lesson, you will learn how Position Independent Code (PIC) enables software to execute correctly regardless of the specific memory address where it is loaded.
The Challenge of Absolute Addressing
In standard compilation, code often contains absolute memory addresses. For example, if your code calls a function at 0x08000100, the compiler encodes that specific address directly into the machine instruction. If you move your binary to a different region of flash or RAM, that instruction will point to the wrong location, causing a crash. In embedded systems—such as firmware that needs to be updated in-place or code running from shared memory—we cannot always guarantee the execution address at compile time.
The PIC Solution: Relative Addressing
To solve this, we generate Position Independent Code. Instead of using hardcoded absolute addresses, the compiler uses PC-relative addressing. The instruction tells the CPU: "The target function is located 128 bytes ahead of where I am currently standing (the Program Counter)."
Consider a simple function call in assembly:
- Absolute:
BL 0x08000100(Always goes to 0x08000100) - PIC (Relative):
BL PC + 0x80(Goes to the current address plus an offset)
Because the distance between the calling instruction and the target function remains constant regardless of where the entire block of code is loaded, the relative offset remains valid.
Relocation: Fixing What Cannot Be Relative
Sometimes, PIC isn't enough. For example, if your code needs to access a global variable located in a fixed data section, you might use a Global Offset Table (GOT). The code contains an index into this table rather than an address. When the binary is loaded into memory, the dynamic loader or startup code performs Relocation. This is the process of patching the binary by calculating the actual target addresses and writing them into the GOT entries.
By combining relative addressing for branching and a relocation table for data access, we ensure our firmware remains flexible and robust, capable of functioning even when memory maps shift during development or field updates.
Key Takeaway: Position Independent Code uses relative offsets to remain functional at any address, while relocation provides a mechanism for the loader to patch absolute references at runtime.