uvuv
uvuv

Reputation: 388

Who performs runtime relocations?

I'm trying to better understand runtime relocations in Linux, specifically who performs them in different situations. Below is my current understanding, is it accurate?

Thanks

Upvotes: 7

Views: 711

Answers (1)

Employed Russian
Employed Russian

Reputation: 213877

Your answers are all (mostly) correct.

You can observe where the relocation is happening using a debugger, and confirm your understanding.

Example:

#include <stdio.h>

int main()
{
  printf("%d\n", 123);
  return 0;
}

Let's start with position-dependent, dynamically linked binary.

gcc -g -fno-pie -no-pie t.c
gdb -q ./a.out
Reading symbols from ./a.out...
(gdb) starti
Starting program: /tmp/a.out

Program stopped.
0x00007ffff7fd2090 in _start () from /lib64/ld-linux-x86-64.so.2
(gdb) disas main
Dump of assembler code for function main:
   0x0000000000401126 <+0>:     push   %rbp
   0x0000000000401127 <+1>:     mov    %rsp,%rbp
   0x000000000040112a <+4>:     mov    $0x7b,%esi
   0x000000000040112f <+9>:     mov    $0x402010,%edi
   0x0000000000401134 <+14>:    mov    $0x0,%eax
   0x0000000000401139 <+19>:    call   0x401030 <printf@plt>
   0x000000000040113e <+24>:    mov    $0x0,%eax
   0x0000000000401143 <+29>:    pop    %rbp
   0x0000000000401144 <+30>:    ret
End of assembler dump.

(gdb) disas 0x401030
Dump of assembler code for function printf@plt:
   0x0000000000401030 <+0>:     jmp    *0x2fe2(%rip)        # 0x404018 <[email protected]>
   0x0000000000401036 <+6>:     push   $0x0
   0x000000000040103b <+11>:    jmp    0x401020
End of assembler dump.

Here we can see that the address to be relocated is 0x404018. Let's see where that address gets updated:

(gdb) watch *(void**)0x404018
Hardware watchpoint 1: *(void**)0x404018
(gdb) c
Continuing.

Hardware watchpoint 1: *(void**)0x404018

Old value = (void *) 0x401036 <printf@plt+6>
New value = (void *) 0x7ffff7e54270 <printf>
0x00007ffff7fe0f10 in _dl_fixup (l=<optimized out>, reloc_arg=<optimized out>) at dl-runtime.c:146
146     dl-runtime.c: No such file or directory.
(gdb) bt
#0  0x00007ffff7fe0f10 in _dl_fixup (l=<optimized out>, reloc_arg=<optimized out>) at dl-runtime.c:146
#1  0x00007ffff7fe84fe in _dl_runtime_resolve_xsavec () at ../sysdeps/x86_64/dl-trampoline.h:126
#2  0x000000000040113e in main () at t.c:5

(gdb) info symbol $pc
_dl_fixup + 288 in section .text of /lib64/ld-linux-x86-64.so.2

So in the dynamically linked non-pie case, it is indeed the dynamic loader performs relocations.

Note: this is a lazy relocation, and is happening on-demand, after main is already running.

If we make it non-lazy, it will be performed by ld.so before invoking main:

gcc -g -fno-pie -no-pie t.c -Wl,-z,now

Repeat above steps and observe the relocation happening before main:

(gdb) run
Starting program: /tmp/a.out

Hardware watchpoint 1: *(void**)0x403fe8

Old value = (void *) 0x401036 <printf@plt+6>
New value = (void *) 0x7ffff7e54270
elf_machine_rela (skip_ifunc=<optimized out>, reloc_addr_arg=<optimized out>, version=<optimized out>, sym=<optimized out>, reloc=<optimized out>, map=<optimized out>)
    at ../sysdeps/x86_64/dl-machine.h:464
464     ../sysdeps/x86_64/dl-machine.h: No such file or directory.
(gdb) bt
#0  elf_machine_rela (skip_ifunc=<optimized out>, reloc_addr_arg=<optimized out>, version=<optimized out>, sym=<optimized out>, reloc=<optimized out>, map=<optimized out>)
    at ../sysdeps/x86_64/dl-machine.h:464
#1  elf_dynamic_do_Rela (skip_ifunc=<optimized out>, lazy=<optimized out>, nrelative=<optimized out>, relsize=<optimized out>, reladdr=<optimized out>, map=0x7ffff7ffe1a0)
    at do-rel.h:137
#2  _dl_relocate_object (l=l@entry=0x7ffff7ffe1a0, scope=<optimized out>, reloc_mode=<optimized out>, consider_profiling=<optimized out>, consider_profiling@entry=0)
    at dl-reloc.c:274
#3  0x00007ffff7fd555b in dl_main (phdr=<optimized out>, phnum=<optimized out>, user_entry=<optimized out>, auxv=<optimized out>) at rtld.c:2341
#4  0x00007ffff7fec1a2 in _dl_sysdep_start (start_argptr=start_argptr@entry=0x7fffffffe380, dl_main=dl_main@entry=0x7ffff7fd34c0 <dl_main>) at ../elf/dl-sysdep.c:252
#5  0x00007ffff7fd3021 in _dl_start_final (arg=0x7fffffffe380) at rtld.c:504
#6  _dl_start (arg=0x7fffffffe380) at rtld.c:597
#7  0x00007ffff7fd2098 in _start () from /lib64/ld-linux-x86-64.so.2

Repeat for other build combinations.

Upvotes: 6

Related Questions