Does the linker need to understand the machine instructions it relocates?
No — the linker treats .text as opaque bytes and only patches the exact positions named by relocation entries; it never decodes or disassembles the code.
This is a key mental model for reverse engineers: linking is a byte-patching step, not a code-analysis step. Each .o carries a relocation table that says, in effect, "at this offset, write symbol + addend (PC-relative or absolute)." The linker walks that table, computes each value once final addresses are known, and overwrites those 4- or 8-byte slots — leaving every other byte of the instruction stream untouched.
Concretely, for one call the linker only sees:
Relocations in .text:
7: R_X86_64_PC32 buf-0x4 ← patch the 4 bytes at .text offset 0x7
a: R_X86_64_PLT32 swap-0x4 ← patch the 4 bytes at .text offset 0xa
It does not know e8 means "call" or that 48 8d 05 is a lea. It just knows "these particular offsets hold address fields that still need filling in."
Why this matters:
- The linker is language- and instruction-set-agnostic about the body of the code — all the semantics live in the relocation entries the compiler/assembler emitted.
- It's also why a corrupt or missing relocation entry produces a binary that links cleanly but jumps to the wrong address — the linker had no way to notice the instruction was wrong.
Tip: Compare before and after with objdump -dr file.o (relocations inline) vs objdump -d prog (final addresses filled in) to watch the zeroed offset fields get patched.
Go deeper:
Ian Lance Taylor — Linkers, part 1 — a linker author on why linking is address-patching, not code analysis.