1

Während ich http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/#id1 Frage Lesen kam:Wie geteilte Bibliothek findet GOT Abschnitt?

Wie PIC Shared Library, nachdem sie in virtuellen Adressraum des Prozesses irgendwo geladen weiß, wie externe Variablen zu verweisen?

Hier ist der Code der gemeinsam genutzten Bibliothek in Frage:

#include <stdio.h> 

extern long var; 

void 
shara_func(void) 
{ 
     printf("%ld\n", var); 
} 

Produce Objektcode, die dann gemeinsam genutztes Objekt (Bibliothek):

gcc -fPIC -c lib1.c     # produce PIC lib1.o 
gcc -fPIC -shared lib1.o -o liblib1.so # produce PIC shared library 

Disassemble shara_func in Shared Library:

objdump -d liblib1.so 
... 
00000000000006d0 <shara_func>: 
6d0: 55      push %rbp 
6d1: 48 89 e5    mov %rsp,%rbp 
6d4: 48 8b 05 fd 08 20 00 mov 0x2008fd(%rip),%rax  # 200fd8 <_DYNAMIC+0x1c8> 
6db: 48 8b 00    mov (%rax),%rax 
6de: 48 89 c6    mov %rax,%rsi 
6e1: 48 8d 3d 19 00 00 00 lea 0x19(%rip),%rdi  # 701 <_fini+0x9> 
6e8: b8 00 00 00 00   mov $0x0,%eax 
6ed: e8 be fe ff ff   callq 5b0 <[email protected]> 
6f2: 90      nop 
6f3: 5d      pop %rbp 
6f4: c3      retq 
... 

Ich sehe diese Anweisung bei 0x6d4 Adresse bewegt sich etwas Adresse, den PC-rax relativ ist, nehme ich an, dass der Eintrag in GOT, verwies GOT relativ von PC Adresse von externen Variablen var zur Laufzeit zu erhalten (es wird in Abhängigkeit zur Laufzeit aufgelöst, wo var war geladen). Dann nach Anweisung an 0x6db Ausführung wir externe Variable tatsächlicher Inhalt in rax gestellt bekommen, dann Wert bewegen sich von rax-rsi - zweitem Funktionsparameter im Register übergeben.

Ich dachte, dass es nur einen GOT im Prozess Speicher gibt, jedoch sehen, dass Bibliothek verweist GOT? Wie weiß die shared library den Offset zu dem GOT des Prozesses, wenn sie (PIC-Bibliothek) nicht weiß, wo im Prozessspeicher sie geladen würde? Oder hat jede gemeinsame Bibliothek ihre eigene GOT, die mit ihr geladen ist? Ich wäre sehr froh, wenn Sie meine Verwirrung klären würden.

+0

Bulat, haben Sie überprüfen 'objdump -r' und' objdump -r' Suche nach RELOC (Verlagerungen) in Ihrem ELF - http://man7.org/linux/man- Seiten/man1/objdump.1.html? – osgx

+0

@osgx, ja, das ist 0x200fd8 R_X86_64_GLOB_DAT var + 0 relocation. PC-relativ: 0x2008fd + 0x6db = 0x200fd8. Ich verstehe nicht, wie Linker 0x2008fd Offset berechnet, wie es weiß, wo Prozess GOT in Runtime befindet. –

+0

Bulat, die Wahrheit ist: Es gibt zwei Linker: Einer ist 'ld' Befehl und andere ist Runtime Linker, RTLD,' ld-linux.so.2'-Programm in Linux (es ist als Interpreter von dynamischen ELFs registriert - http://stackoverflow.com/questions/5130654). R_X86_64_GLOB_DAT, erstellt von ld-Linker, wird also vom RTLD-Laufzeit-Linker aufgelöst: http://osxr.org:8080/glibc/source/sysdeps/x86_64/dl-machine.h#0304. Es ist derjenige, der die lib-Datei öffnet; Mmaps es an den richtigen Ort, erstellen Sie die echte GOT und löst Umlagerungen auf echte GOT zeigen. – osgx

Antwort

3

Ich dachte, dass es nur einen GOT im Prozessspeicher gibt, aber diese Bibliothek verweist auf GOT?

Wir sehen deutlich .got Abschnitt als Teil der Bibliothek. Mit readelf können wir das finden, was die Abschnitte der Bibliothek sind und wie sie geladen werden:

readelf -e liblib1.so 
... 
Section Headers: 
    [21] .got    PROGBITS   0000000000200fd0 00000fd0 
     0000000000000030 0000000000000008 WA  0  0  8 
... 

Program Headers: 
    Type   Offset    VirtAddr   PhysAddr 
       FileSiz   MemSiz    Flags Align 
    LOAD   0x0000000000000000 0x0000000000000000 0x0000000000000000 
       0x000000000000078c 0x000000000000078c R E 200000 
    LOAD   0x0000000000000df8 0x0000000000200df8 0x0000000000200df8 
       0x0000000000000230 0x0000000000000238 RW  200000 
... 
Section to Segment mapping: 
    Segment Sections... 
    00  ... .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame 
    01  .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 
    02  .dynamic 

So gibt Abschnitt ist .got, aber runtime linker ld-linux.so.2 (registriert als Dolmetscher für die dynamische ELFs) Abschnitte nicht geladen werden; es lädt Segmente wie vom Programmkopf mit dem Typ LOAD beschrieben. .got ist Teil von Segment 01 LOAD mit RW-Flags. Andere Bibliotheken haben eigene GOT (denken Sie daran, dass Sie liblib2.so aus der gleichen Quelle kompilieren, es wird nichts über liblib1.so wissen und wird GOT haben); so ist es "global" nur für die Bibliothek; aber nicht auf das gesamte Programmbild im Speicher nach dem Laden.

Wie shared library weiß, Offset zu Prozess GOT, wenn es (PIC-Bibliothek) nicht weiß, wo im Prozessspeicher würde es geladen werden?

Es wird von statischen Linker getan, wenn es mehrere ELF-Objekte benötigt und sie alle in einer Bibliothek kombinieren.Linker erzeugt .got Abschnitt und legte es an einer Stelle mit bekannten Offset aus dem Bibliothekscode (pc-relative, rip-relative). Es schreibt Anweisungen in den Programmheader, so dass die relative Adresse bekannt ist und es die einzige benötigte Adresse ist, um auf den eigenen GOT zuzugreifen.

Wenn objdump mit -r/-R Flags verwendet wird, wird es Informationen über Umlagerungen (statisch/dynamisch) drucken, die in der ELF-Datei oder -Bibliothek aufgezeichnet sind; es kann mit dem Flag -d kombiniert werden. lib1.o Objekt hatte hier Umzug; nicht zu GOT bekannten Versatz, mov hat alle Null:

$ objdump -dr lib1.o 
lib1.o:  file format elf64-x86-64 

Disassembly of section .text: 

0000000000000000 <shara_func>: 
    0: 55      push %rbp 
    1: 48 89 e5    mov %rsp,%rbp 
    4: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax  # b <shara_func+0xb> 
      7: R_X86_64_REX_GOTPCRELX var-0x4 
    b: 48 8b 00    mov (%rax),%rax 
    e: 48 89 c6    mov %rax,%rsi 

In-Bibliotheksdatei dies durch gcc -shared auf die relative Adresse umgewandelt wurde (es nennt ld Variante collect2 innen):

$ objdump -d liblib1.so 

liblib1.so:  file format elf64-x86-64 

00000000000006d0 <shara_func>: 
6d0: 55      push %rbp 
6d1: 48 89 e5    mov %rsp,%rbp 
6d4: 48 8b 05 fd 08 20 00 mov 0x2008fd(%rip),%rax  # 200fd8 <_DYNAMIC+0x1c8> 

Und schließlich gibt es dynamische Verlagerung in GOT hier aktuelle Adresse von var setzen (done von rtld - ld-linux.so.2):

$ objdump -R liblib1.so 

liblib1.so:  file format elf64-x86-64 

DYNAMIC RELOCATION RECORDS 
OFFSET   TYPE    VALUE 
... 
0000000000200fd8 R_X86_64_GLOB_DAT var 

Lassen Sie uns Ihre lib verwenden, fügen ing ausführbare Datei mit Definition, es läuft mit rtld Debuggen aktiviert Kompilieren:

$ cat main.c 
long var; 
int main(){ 
    shara_func(); 
    return 0; 
} 
$ gcc main.c -llib1 -L. -o main -Wl,-rpath=`pwd` 
$ LD_DEBUG=all ./main 2>&1 |less 
... 
    311:  symbol=var; lookup in file=./main [0] 
    311:  binding file /test3/liblib1.so [0] to ./main [0]: normal symbol `var' 

So Linker konnte Umzug binden, für var an die "main" ELF-Datei, wo es definiert:

$ gdb -q ./main 
Reading symbols from ./main...(no debugging symbols found)...done. 
(gdb) b main 
Breakpoint 1 at 0x4006da 
(gdb) r 
Starting program: /test3/main 

Breakpoint 1, 0x00000000004006da in main() 
(gdb) disassemble shara_func 
Dump of assembler code for function shara_func: 
    0x00007ffff7bd56d0 <+0>: push %rbp 
    0x00007ffff7bd56d1 <+1>: mov %rsp,%rbp 
    0x00007ffff7bd56d4 <+4>: mov 0x2008fd(%rip),%rax  # 0x7ffff7dd5fd8 
    0x00007ffff7bd56db <+11>: mov (%rax),%rax 
    0x00007ffff7bd56de <+14>: mov %rax,%rsi 

Keine Änderungen in mov in Ihrem func. rax nach Func + 4 0x601040 ist, ist es dritte Abbildung von ./main nach/proc/pid $/maps:

00601000-00602000 rw-p 00001000 08:07 6691394       /test3/main 

Und es wurde aus dem Haupt nach diesem Programmkopf geladen (readelf-./main

)
LOAD   0x0000000000000df0 0x0000000000600df0 0x0000000000600df0 
       0x0000000000000248 0x0000000000000258 RW  200000 

Es ist Teil .bss Abschnitt:

[26] .bss    NOBITS   0000000000601038 00001038 
     0000000000000010 0000000000000000 WA  0  0  8 

nach dem func Schritt + 11, können wir Wert in GOT überprüfen:

(gdb) b shara_func 
(gdb) r 
(gdb) si 
0x00007ffff7bd56db in shara_func() from /test3/liblib1.so 
1: x/i $pc 
=> 0x7ffff7bd56db <shara_func+11>: mov (%rax),%rax 
(gdb) p $rip+0x2008fd 
$6 = (void (*)()) 0x7ffff7dd5fd8 
(gdb) x/2x 0x7ffff7dd5fd8 
0x7ffff7dd5fd8: 0x00601040 0x00000000 

Wer hat den richtigen Wert in diesen GOT-Eintrag geschrieben?

(gdb) watch *0x7ffff7dd5fd8 
Hardware watchpoint 2: *0x7ffff7dd5fd8 
(gdb) r 
The program being debugged has been started already. 
Start it from the beginning? (y or n) y 
Starting program: /test3/main 

Hardware watchpoint 2: *0x7ffff7dd5fd8 

Old value = <unreadable> 
New value = 6295616 
0x00007ffff7de36bf in elf_machine_rela (..) at ../sysdeps/x86_64/dl-machine.h:435 
(gdb) bt 
#0 0x00007ffff7de36bf in elf_machine_rela (...) at ../sysdeps/x86_64/dl-machine.h:435 
#1 elf_dynamic_do_Rela (...) at do-rel.h:137 
#2 _dl_relocate_object (...) at dl-reloc.c:258 
#3 0x00007ffff7ddaf5b in dl_main (...)  at rtld.c:2072 
#4 0x00007ffff7df0462 in _dl_sysdep_start ([email protected]=0x7fffffffde20, 
    [email protected]=0x7ffff7dd89a0 <dl_main>) at ../elf/dl-sysdep.c:249 
#5 0x00007ffff7ddbe7a in _dl_start_final (arg=0x7fffffffde20) at rtld.c:307 
#6 _dl_start (arg=0x7fffffffde20) at rtld.c:413 
#7 0x00007ffff7dd7cc8 in _start() from /lib64/ld-linux-x86-64.so.2 

(gdb) x/2x 0x7ffff7dd5fd8 
0x7ffff7dd5fd8: 0x00601040 0x00000000 

Runtime-Linker von glibc tat (rtld.c), kurz vor main Aufruf - hier ist die Quelle (etwas andere Version) - http://code.metager.de/source/xref/gnu/glibc/sysdeps/x86_64/dl-machine.h

329 case R_X86_64_GLOB_DAT: 
330 case R_X86_64_JUMP_SLOT: 
331 *reloc_addr = value + reloc->r_addend; 
332 break; 

Mit Reverse-Stepping wir die Geschichte von Code bekommen und alter Wert = 0:

(gdb) b _dl_relocate_object 
(gdb) r 
(gdb) dis 3 
(gdb) target record-full 
(gdb) c 
(gdb) disp/i $pc 
(gdb) rsi 
(gdb) rsi 
(gdb) rsi 
(gdb) x/2x 0x7ffff7dd5fd8 
0x7ffff7dd5fd8: 0x00000000 0x00000000 


=> 0x7ffff7de36b8 <_dl_relocate_object+1560>: add 0x10(%rbx),%rax 
=> 0x7ffff7de36bc <_dl_relocate_object+1564>: mov %rax,(%r10) 
=> 0x7ffff7de36bf <_dl_relocate_object+1567>: nop 
+0

In der Mitte lesen, wie könnte man PID des Prozesses finden, den man in gdb debuggt? –

+0

Man kann andere Konsole (Terminal) öffnen und 'ps' Ausgabe für den Prozess suchen. Auch 'pidof main' wenn das Testprogramm' main' heißt und es nur einen Prozess des Programms gibt. – osgx

+0

Bulat, können Sie bitte Ihren Kommentar über Randomize Va Raum von http://StackOverflow.com/Questions/5130654/5130690#comment66925036_5130690 entfernen und die Frage hier verschieben? Sie kann in Ihrem Kernel zur Build-Zeit deaktiviert werden (für MMU-lose Kernel .. oder zu alte Kernel .. oder durch einen Drittanbieter-Patch);/proc kann nicht gemountet sein; Wert kann mit sysctl erreicht werden - 'sysctl -a | grep randomize'; Sie versuchen auch, 'setarch x86_64 -R./program' zu deaktivieren (http://askubuntu.com/questions/318315). – osgx