Sie sind nicht mit symbol-interposition Bau freigegeben (eine Nebenwirkung von -fPIC
), so dass die call
Zieladresse kann potenziell zur Verknüpfungszeit an eine Adresse in einer anderen Objektdatei aufgelöst werden, die statisch in die gleiche ausführbaren Datei verknüpft wird. (z.B. gcc foo.o bar.o
).
Wenn jedoch das Symbol nur in einer Bibliothek gefunden ist, dass Sie dynamisch zu verknüpfen (gcc foo.o -lbar
), die call
hat durch die PLT werden indirected zu unterstützen.
Nun ist diese der schwierige Teil ist: without -fPIC
or -fPIE
, gcc emittiert noch asm, dass die Funktion direkt aufruft:
int puts(const char*); // puts exists in libc, so we can link this example
void call_puts(void) { puts("foo"); }
# gcc 5.3 -O3 (without -fPIC)
movl $.LC0, %edi # absolute 32bit addressing: slightly smaller code, because static data is known to be in the low 2GB, in the default "small" code model
jmp puts # tail-call optimization. Same as call puts/ret, except for stack alignment
Aber wenn man sich die verknüpften binären aussehen: (auf this Godbolt compiler explorer link, klicken Sie auf die „binäre“ Taste zwischen gcc -S
ASM Ausgang und objdump -dr
Demontage)
# disassembled linker output
mov $0x400654,%edi
jmpq 400490 <[email protected]>
während eines Bindens war der Anruf zu puts
„magische“ mit Indirektion ersetzt, um durch [email protected]
und eine [email protected]
Definition ist in der verknüpften ausführbaren Datei vorhanden.
Ich weiß nicht die Details, wie dies funktioniert, aber es ist bei Link-Zeit beim Link zu einer gemeinsam genutzten Bibliothek getan. Entscheidend ist, dass in den Header-Dateien nichts erforderlich ist, um den Funktionsprototyp als gemeinsam genutzte Bibliothek zu kennzeichnen. Sie erhalten die gleichen Ergebnisse von einschließlich <stdio.h>
wie Sie von puts
selbst zu deklarieren. (Dies ist sehr nicht empfohlen;.. Es ist wahrscheinlich legal für eine C-Implementierung nur zu arbeiten richtig mit den Erklärungen in Header kommt es vor, auf Linux zu arbeiten, obwohl)
Wenn ein positionsunabhängige ausführbare Kompilieren (mit -fPIE
), springt die verknüpfte Binärdatei über den PLT zu puts
, identisch wie ohne -fPIC
. Allerdings Ausgabe der Compiler asm ist anders (versuchen Sie es selbst auf dem Godbolt Link oben):
call_puts: # compiled with -fPIE
leaq .LC0(%rip), %rdi # RIP-relative addressing for static data
jmp [email protected]
Der Compiler Kräfte indirection durch die PLT für alle Anrufe auf Funktionen, die es nicht die Definition sehen können. Ich verstehe nicht warum. Im PIE-Modus kompilieren wir Code für eine ausführbare Datei und nicht für eine gemeinsam genutzte Bibliothek.Der Linker sollte in der Lage sein, mehrere Objektdateien in eine positionsunabhängige ausführbare Datei mit direkten Aufrufen zwischen in der ausführbaren Datei definierten Funktionen zu verknüpfen. Ich teste auf Linux (mein Desktop und godbolt), nicht OS X, wobei ich davon ausgehe, dass der Standard ist. Es könnte anders konfiguriert werden, IDK.
Mit -fPIC
statt -fPIE
, sind die Dinge noch schlimmer: sogar Anrufe auf globale Funktionen innerhalb derselben Übersetzungseinheit muss durch die PLT gehen definiert, symbol interposition zu unterstützen. (Z LD_PRELOAD=intercept_some_functions.so ./a.out
)
Die Unterschiede zwischen -fPIC
und -fPIE
sind in erster Linie, dass PIE kein Symbol Zwischenschaltung für Funktionen in derselben Übersetzungseinheit annehmen kann, aber PIC kann es nicht. OS X erfordert positionsunabhängige ausführbare Dateien sowie gemeinsam genutzte Bibliotheken. Es gibt jedoch eine andere Möglichkeit, was der Compiler tun kann, wenn er Code für eine Bibliothek erstellt oder Code für eine ausführbare Datei erstellt.
Diese Godbolt example hat einige weitere Funktionen, die Dinge über PIC-und PIE-Modus, z. Das call_puts()
kann nicht in eine andere Funktion im PIC-Modus, nur PIE.
Siehe auch: Shared object in Linux without symbol interposition, -fno-semantic-interposition error.
verwirrt durch mov $0, %edi
bei Demontage Ausgabe aus dem .o
Sie suchen, wo Adressen werden nur 0s Platzhalter, der durch den Linker zur Verknüpfungszeit ersetzt werden, die Verlagerung basierend auf Informationen in der ELF-Objektdatei. Deshalb schlug @Leandros objdump -r
vor.
In ähnlicher Weise ist die relative Verschiebung im call
Maschinencode nur Nullen, weil der Linker es noch nicht ausgefüllt hat.
Verwenden Sie 'objdump -dr', um Verschiebungseinträge anzuzeigen. – Jester
FYI, 'clang-S' gibt dies anstelle Ihres mov 0x0/callq-Paares aus:' leaq L_.str (% rip),% rdi; callq _give_me_a_ptr'. –
Wie für die PLT, die nur verwendet wird, wenn Sie PIC kompilieren, verwenden Sie das Flag "-fPIC". – Jester