LOGBOOK

HELP

Quiz Entry - updated: 2026.07.10

Why does PC-relative relocation use an addend of -4 on x86_64?

Because by the time the CPU reads the 4-byte offset, the PC has already advanced past it; the −4 addend cancels that 4-byte head start.

Byte layout of a 5-byte call: opcode e8 at 0x9, the 4-byte offset field at 0xa-0xd, next instruction at 0xe; PC (0xe) minus position (0xa) equals 4

* The relocation patches the offset field at 0xa, but the CPU adds the offset to the PC at 0xe — four bytes further on, which the −4 addend cancels. *

When the CPU executes a call:

  1. The opcode byte e8 sits at 0x9; the 4-byte offset field follows at 0xa
  2. A call rel32 is 5 bytes, so once it's fetched the PC already points to the next instruction at 0xe
  3. The CPU computes the target as PC + offset = 0xe + offset

Example disassembly:

Address   Bytes          Instruction
0:        55             push %rbp
1:        48 89 e5       mov %rsp,%rbp
4:        b8 00 00 00 00 mov $0x0,%eax
9:        e8 00 00 00 00 callq e <main+0xe>   ← offset field at 0xa
e:        5d             pop %rbp             ← PC points here after the call is fetched

Relocation calculation:

encoded_offset = target - PC
               = (symbol + addend) - PC
               = (symbol + addend) - (position + 4)

The relocation's position is the offset field at 0xa; the PC is 0xe, so PC - position = 0xe - 0xa = 4. The linker folds that constant into the addend as -4.

That's why R_X86_64_PC32 and R_X86_64_PLT32 use addend = -4: to compensate for the PC being 4 bytes ahead of the offset field by the time the call is decoded.

Go deeper:

From Quiz: REVE1 / Program Execution | Updated: Jul 10, 2026