The offsets between different sections of your executable are link-time constants, so RIP-relative addressing is usable for any section (including .data
where your non-const
globals are). Note the .data
in your asm output.
This applies even in a PIE executable or shared library, where the absolute addresses are not known until runtime (ASLR).
Runtime ASLR for position-independent executables (PIE) randomizes one base address for the entire program, not individual segment start addresses relative to each other.
All access to static variables uses RIP-relative addressing because that's most efficient, even in a position-dependent executable where absolute addressing is an option (because absolute addresses of static code/data are link-time constants, not relocated by dynamic linking).
Related and maybe duplicates:
In 32-bit x86, there are 2 redundant ways to encode an addressing mode with no registers and a disp32
absolute address.
(With and without a SIB byte). x86-64 repurposed the shorter one as RIP+rel32
, so mov foo, %eax
is 1 byte longer than mov foo(%rip), %eax
.
64-bit absolute addressing would take even more space, and is only available for mov
to/from RAX/EAX/AX/AL unless you use a separate instruction to get the address into a register first.
(In x86-64 Linux PIE/PIC, 64-bit absolute addressing is allowed, and handled via load-time fixups to put the right address into the code or jump table or statically-initialized function pointer. So code doesn't technically have to be position-independent, but normally it's more efficient to be. And 32-bit absolute addressing isn't allowed, because ASLR isn't limited to the low 31 bits of virtual address space.)
Note that in a non-PIE Linux executable, gcc will use 32-bit absolute addressing for putting the address of static data in a register. e.g. puts("hello");
will typically compile as
mov $.LC0, %edi # mov r32, imm32
call puts
In the default non-PIE memory model, static code and data get linked into the low 32 bits of virtual address space, so 32-bit absolute addresses work whether they're zero- or sign-extended to 64-bit. This is handy for indexing static arrays, too, like mov array(%rax), %edx
; add $4, %eax
for example.
See 32-bit absolute addresses no longer allowed in x86-64 Linux? for more about PIE executables, which use position-independent code for everything, including RIP-relative LEA like 7-byte lea .LC0(%rip), %rdi
instead of 5-byte mov $.LC0, %edi
. See How to load address of function or label into register
I mention Linux because it looks from the .cfi
directives like you're compiling for a non-Windows platform.