Deciphering the indirection of runtime dynamic linking can be a bit tricky.

I always seem to forget how the PLT and GOT interact.

Consider this simple example of calling the puts function in the dynamically linked libc:

#include <stdio.h>

int main(int argc, char *argv[])
	puts("foo bar baz qux");
	return 0;
$ gcc test.c -o test 

# Show the dynamically linked libraries for the test binary.
$ ldd test =>  (0xb77d4000) => /lib/i386-linux-gnu/ (0xb7615000)
	/lib/ (0xb77d5000)

Disassembling test, you’ll find a call to puts, which is located in the dynamically linked library

0804842d:  call   80482f0 <puts@plt>

So, the PLT (procedure linkage table) is responsible for jumping to the puts function if the dynamic linker has already found the function in libc, or otherwise jumping to ld to find and link the function:

080482f0 <puts@plt>:
 80482f0:   jmp    DWORD PTR ds:0x804a00c  ; jump to address in 0x804a00c
 80482f6:   push   0x0                     ; push index of puts in GOT
 80482fb:   jmp    80482e0 <_init+0x30>    ; jump to ld trampoline

First, notice that the jump at 0x8048df0 is actually a jump to a PTR, which means it does not jump to the address 0x804a00c but rather the address stored at that address:

(gdb) info file
0x0804a000 - 0x0804a018 is .got.plt

(gdb) x/wx 0x804a00c
0x804a00c <puts@got.plt>:	0x080482f6

So, we can see that the address 0x804a00c is actually in the .got.plt section, and is where the address of puts will be stored when ld links the function.

However, it is initialized to the value 0x080482f6, which is actually the address of push 0x0, directly below the jump.

After pushing the value 0x0 to the stack, we jump again to the code that will actually take us into ld to perform the runtime lookup and linking:

080482e0 <puts@plt-0x10>:
 80482e0:  push   DWORD PTR ds:0x804a004   ; push address of link_map
 80482e6:  jmp    DWORD PTR ds:0x804a008   ; jump to _dl_fixup() in ld
 80482ec:  add    BYTE PTR [eax],al

Inspecting the two addresses, which you should note are stored immediately before the puts address in the .got.plt, we see that both addresses are in pages mapped to ld:

(gdb) x/wx 0x804a004
0x804a004:	0xb7fff938

0xb7fff000 0xb8000000 rw-p	/lib/i386-linux-gnu/

(gdb) x/wx 0x804a008
0x804a008:	0xb7ff24f0

0xb7fde000 0xb7ffe000 r-xp	/lib/i386-linux-gnu/

So, you can see the first address is in writable space and the second executable. In the next post, I’ll discuss what happens in ld.