Why can I assemble and link get_got.asm
as position-independent code when it contains a reference to the absolute address of its GOT?
get_got.asm
extern _GLOBAL_OFFSET_TABLE_
section .text
global get_got
get_got:
mov rax, _GLOBAL_OFFSET_TABLE_
ret
main.c
#include <stdio.h>
void* get_got(void);
int main(int argc, char* argv[]) {
printf("%p\n", get_got());
}
assembling, compiling, linking, running:
nasm -felf64 -o get_got.o get_got.asm
gcc -fPIC -shared -o get_got.so get_got.o
gcc -Wl,-rpath=\$ORIGIN -o main main.c get_got.so
./main
0x148ba1cba000
What's going on here? It looks to me like get_got.so somehow has a baked-in absolute address for a GOT that won't have a known address until runtime. Disassembling get_got.so shows that the mov does in fact contain an immediate (0x201000). Obviously I have a major misunderstanding of something. I expected this to cause nasm to generate a relocation that the linker would choke on.
I built your code and a modified version using
lea rax, [rel _GLOBAL_OFFSET_TABLE_]
.I diffed the
readelf -a
output. There's a lot of noise from different addresses, though.readelf -a get_got.so | diff -u - <(readelf -a get_got_rel.so) | less
The most interesting difference is:
So the absolute version has a text relocation. I didn't know Linux / ELF dynamic linking could apply fixups after mapping shared libraries. But apparently it can. (It's better not to, because it dirties the memory page so it's no longer just backed by the file on disk.)
But I checked with GDB, and that's what's going on: set a breakpoint in
get_got
and run it:objdump -dRC -Mintel get_got.so
: (note line wrapping without-w
):Thanks @Jester for the
-R
tip; I normally useobjdump -dr ...
, not-R
, and lower-case r doesn't print any relocations for the.so
.On
get_got.o
,-r
showsmovabs rax,0x0 2: R_X86_64_64 _GLOBAL_OFFSET_TABLE_
.gcc -nostdlib -pie
will link 64-bit absolute relocations into PIE executables as well. (A PIE executable is an ELF shared object).What isn't allowed in PIC / PIE are 32-bit absolute relocations: 32-bit absolute addresses no longer allowed in x86-64 Linux?. You get a linker error. Addressing modes like
array[rcx*4]
aren't usable in PIC/PIE code, you need a separate instruction to get the address into a register.lea rdi, [rel array]
is a much better choice than a 64-bit immediate absolute, because it's smaller and more friendly to the uop cache, and doesn't require a fixup when loading.