Consider the following program hello.c
:
#include <stdio.h>
int main(int argc, char** argv)
{
printf("hello");
return 0;
}
The file is compiled with gcc -o hello -Og -g hello.c
and then loaded with gdb hello
.
Inspecting the GOT for the call to printf
with p 'printf@got.plt'
gives
$1 = (<text from jump slot in .got.plt, no debug info>) 0x1036 <printf@plt+6>
which is the offset of the second instruction in the corresponding PLT entry relative to the start of the section.
After starting and linking the program with starti
, p 'printf@got.plt'
now gives
$2 = (<text from jump slot in .got.plt, no debug info>) 0x555555555036 <printf@plt+6>
which is the absolute address of the second instruction in the corresponding PLT entry.
I understand what is going on and why. My question is how does the dynamic linker/loader know to update the section offset (0x1036) to the absolute address (0x555555555036)?
A p &'printf@got.plt'
before linking gives
$1 = (<text from jump slot in .got.plt, no debug info> *) 0x4018 <printf@got.plt>
and readelf -r simple
shows a relocation entry for this address
Relocation section '.rela.plt' at offset 0x550 contains 1 entry:
Offset Info Type Sym. Value Sym. Name + Addend
000000004018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
But my reading of the System V Application Binary Interface AMD64 Architecture Processor Supplement, p.76, is that these relocation entries are only used when LD_BIND_NOW
is non-null. Are there other relocation entries that I missed? What is the mechanism for rebasing offsets relative to the GOT's ultimate address?
According to Drepper's How To Write Shared Libraries, the dynamic linker relocates two kinds of dependencies:
For the PLT's GOT, Drepper states (§1.5.5) At startup time the dynamic linker fills the GOT slot with the address pointing to the second instruction of the appropriate PLT entry. A reading of the glibc source code suggests that the linker indeed loops through the R_X86_64_JUMP_SLOT
relocations (elf/do-rel.h:elf_dynamic_do_Rel
) and increments the offsets they contain (sysdeps/x86_64/dl-machine.h:elf_machine_lazy_rel
):
if (__glibc_likely (r_type == R_X86_64_JUMP_SLOT))
{
/* Prelink has been deprecated. */
if (__glibc_likely (map->l_mach.plt == 0))
*reloc_addr += l_addr;
else
...
when lazy PLT binding is used (the default case).