2015-07-13 11 views
7

Ho notato che mentre giocava con GDB di rbreak ., e poi ha fatto un esempio minimo:Perché _init da csu/init-first.c di glibc viene chiamato prima di _start anche se _start è il punto di ingresso ELF?

(gdb) file hello_world.out 
Reading symbols from hello_world.out...done. 
(gdb) b _init 
Breakpoint 1 at 0x4003e0 
(gdb) b _start 
Breakpoint 2 at 0x400440 
(gdb) run 
Starting program: /home/ciro/bak/git/cpp/cheat/gdb/hello_world.out 

Breakpoint 1, _init (argc=1, argv=0x7fffffffd698, envp=0x7fffffffd6a8) at ../csu/init-first.c:52 
52 ../csu/init-first.c: No such file or directory. 
(gdb) continue 
Continuing. 

Breakpoint 2, 0x0000000000400440 in _start() 
(gdb) continue 
Continuing. 

Breakpoint 1, 0x00000000004003e0 in _init() 
(gdb) info breakpoints 
Num  Type   Disp Enb Address   What 
1  breakpoint  keep y <MULTIPLE> 
    breakpoint already hit 2 times 
1.1       y  0x00000000004003e0 <_init> 
1.2       y  0x00007ffff7a36c20 in _init at ../csu/init-first.c:52 
2  breakpoint  keep y 0x0000000000400440 <_start> 
    breakpoint already hit 1 time 

noti che ci sono 2 _init: uno in csu/init-first.c, e l'altro sembra provenire da sysdeps/x86_64/crti.S. Sto parlando dello csu.

Non è _start il punto di ingresso impostato dal linker e memorizzato nell'intestazione ELF? Quale meccanismo esegue prima _init? Qual è il suo scopo?

Testato su GCC 4.8, glibc 2.19, GDB 7.7.1 e Ubuntu 14.04.

+0

davvero. C'è una sezione init definita in ELF. Dai un'occhiata a [QUESTO] (http://l4u-00.jinr.ru/usoft/WWW/www_debian.org/Documentation/elf/node3.html) Potresti anche provare 'readelf -d selfelf' per controllare INIT e Sezioni FINI. Ad ogni modo se si compila con -nostdlib quelle parti vengono evitate. – LPs

risposta

8

Se il debugger si arresta per primo nell'esempio, non è il vero inizio del processo.

Nell'intestazione ELF è presente una voce per l'interprete del programma (linker dinamico). Su Linux 64 bit il suo valore è /lib64/ld-linux-x86-64.so.2. Il kernel imposta il puntatore di istruzioni iniziale al punto di ingresso di questo interprete di programma . Il nome del simbolo è _start, come i programmi _start.

Dopo che il linker dinamico ha fatto il suo lavoro, chiamando anche le funzioni nel programma, come _init in glibc, chiama il punto di ingresso del programma.

Il punto di interruzione su _start non funziona per il linker dinamico perché prende solo l'indirizzo del programma _start.

È possibile trovare l'indirizzo del punto di ingresso con readelf -h /lib64/ld-linux-x86-64.so.2.

È anche possibile impostare un punto di interruzione su _dl_start e stampare un backtrace per verificare che questa funzione venga richiamata dal linker dinamico _start.

Se si scarica il codice sorgente di corrente di glibc si può trovare il punto di ingresso del caricatore dinamica a partire glibc-2.21/sysdeps/x86_64/dl-machine.h on line 121.

Non
+0

Grazie! glibc è così grande, contiene anche il caricatore. –

Problemi correlati