ELF relocations base address always zero also when defining a memory region

2026-04-22

Stack Overflow: View Question

Tags: assembly, ld, elf, relocation

Score: 3 | Views: 68

The asker is building a bare-metal AArch64 kernel and wants to add runtime relocation support. Their linker script defines memory regions at non-zero addresses, yet the relocation entries in the resulting ELF all use a base address of zero. This is confusing because intuitively, if your MEMORY region starts at, say, 0x40000000, you'd expect relocations to be computed relative to that address.

This question touches on one of the trickiest aspects of ELF: the distinction between link-time addresses (the VMA assigned by the linker script) and relocation addends stored in the ELF's relocation tables. These are fundamentally different concepts, and conflating them is a common source of confusion in bare-metal work.

Why relocations show zero: On AArch64, the dominant relocation type for position-independent bare-metal code is R_AARCH64_RELATIVE. For this relocation type, the dynamic linker (or your runtime relocation code) computes the final address as base + addend, where base is the actual load address at runtime. The addend stored in the ELF is the link-time virtual address of the symbol. If the linker is producing a PIE or shared object with a base of zero in the ELF headers (p_vaddr of the first PT_LOAD segment), then all addends are effectively absolute VMAs from the linker script.

The key insight is this: the "base address" isn't stored in the relocation entries themselves. It's computed at runtime by your relocation code as:

runtime_base = actual_load_address - link_time_base

Then for each R_AARCH64_RELATIVE entry, you compute:

*(base + offset) = runtime_base + addend

If the linker emits the ELF with p_vaddr = 0 despite your MEMORY regions being non-zero, check these things:

A common gotcha in bare-metal relocation: if you're using -pie with a non-zero load address, you must account for the difference between the ELF's assumed base (zero) and your actual link address when writing the relocation loop. Many tutorials assume a zero-based link, which silently breaks when your kernel lives at 0x40000000.

The challenge: Understanding how ELF relocation entries encode addresses relative to an implicit base of zero, even when the linker script places code at non-zero memory regions, requires grasping the separation between link-time VMA and runtime relocation arithmetic.

All newsletters