Beim Kompilieren von gemeinsam genutzten Bibliotheken in gcc kompiliert die Option -fPIC den Code als positionsunabhängig. Gibt es einen Grund (Leistung oder anderweitig), warum Sie nicht alle Codepositionen unabhängig kompilieren würden?
Beim Kompilieren von gemeinsam genutzten Bibliotheken in gcc kompiliert die Option -fPIC den Code als positionsunabhängig. Gibt es einen Grund (Leistung oder anderweitig), warum Sie nicht alle Codepositionen unabhängig kompilieren würden?
Antworten:
Dieser Artikel erklärt die Funktionsweise von PIC und vergleicht es mit der Alternative - Verlagerung der Ladezeit . Ich denke, es ist relevant für Ihre Frage.
Ja, es gibt Leistungsgründe. Einige Zugriffe befinden sich effektiv unter einer anderen Indirektionsebene, um die absolute Position im Speicher zu erhalten.
Es gibt auch die GOT (Global Offset Table), in der Offsets globaler Variablen gespeichert sind. Für mich sieht dies wie eine IAT-Fixup-Tabelle aus, die von Wikipedia und einigen anderen Quellen als positionsabhängig klassifiziert wird.
Neben der akzeptierten Antwort. Eine Sache, die die Leistung des PIC-Codes stark beeinträchtigt, ist das Fehlen einer "relativen IP-Adressierung" auf x86. Mit "IP relative Adressierung" können Sie Daten anfordern, die X Bytes vom aktuellen Befehlszeiger entfernt sind. Dies würde den PIC-Code viel einfacher machen.
Sprünge und Anrufe sind normalerweise EIP-relativ, sodass diese kein wirkliches Problem darstellen. Der Zugriff auf Daten erfordert jedoch einige zusätzliche Tricks. Manchmal wird ein Register vorübergehend als "Basiszeiger" für Daten reserviert, die der Code benötigt. Eine übliche Technik besteht beispielsweise darin, die Funktionsweise von Aufrufen auf x86 zu missbrauchen:
call label_1
.dd 0xdeadbeef
.dd 0xfeedf00d
.dd 0x11223344
label_1:
pop ebp ; now ebp holds the address of the first dataword
; this works because the call pushes the **next**
; instructions address
; real code follows
mov eax, [ebp + 4] ; for example i'm accessing the '0xfeedf00d' in a PIC way
Diese und andere Techniken fügen den Datenzugriffen eine Indirektionsebene hinzu. Zum Beispiel die von gcc-Compilern verwendete GOT (Global Offset Table).
x86-64 hat einen "RIP relative" -Modus hinzugefügt, der die Dinge viel einfacher macht.
Durch die Implementierung eines vollständig positionsunabhängigen Codes wird dem Codegenerator eine Einschränkung hinzugefügt, die die Verwendung schnellerer Operationen verhindern kann, oder es werden zusätzliche Schritte hinzugefügt, um diese Einschränkung beizubehalten.
Dies kann ein akzeptabler Kompromiss sein, um eine Mehrfachverarbeitung ohne ein virtuelles Speichersystem zu erhalten, bei dem Sie darauf vertrauen, dass Prozesse nicht in den Speicher des anderen eindringen, und möglicherweise eine bestimmte Anwendung an einer beliebigen Basisadresse laden müssen.
In vielen modernen Systemen sind die Leistungskompromisse unterschiedlich, und ein verlagernder Lader ist häufig günstiger (es kostet jedes Mal, wenn der Code zum ersten Mal geladen wird) als das Beste, was ein Optimierer tun kann, wenn er freie Hand hat. Auch die Verfügbarkeit virtueller Adressräume verbirgt in erster Linie den größten Teil der Motivation zur Positionsunabhängigkeit.
Außerdem bedeutet virtuelle Speicherhardware in den meisten modernen Prozessoren (die von den meisten modernen Betriebssystemen verwendet werden), dass viel Code (alle User-Space-Apps, außer bei der skurrilen Verwendung von mmap oder dergleichen) nicht positionsunabhängig sein muss. Jedes Programm erhält einen eigenen Adressraum, der seiner Meinung nach bei Null beginnt.
Heutzutage machen Betriebssystem und Compiler standardmäßig den gesamten Code als positionsunabhängigen Code. Versuchen Sie, ohne das Flag -fPIC zu kompilieren. Der Code wird gut kompiliert, aber Sie erhalten nur eine Warnung. OS-ähnliche Fenster verwenden eine Technik, die als Speicherzuordnung bezeichnet wird, um dies zu erreichen.
Die Frage stammt aus dem Jahr 2009. Zehn Jahre sind vergangen, und jetzt ist der gesamte Code tatsächlich positionsunabhängig. Dies wird jetzt von Betriebssystemen und Compilern erzwungen. Es gibt keine Möglichkeit, sich abzumelden. Der gesamte Code wird mit PIE erzwungen kompiliert, und das Flag -no-pic / -no-pie wird als Teil dieser ASLR-Entschuldigung ignoriert. Der Grund dafür ist, früher schnelle Apps zu verlangsamen und neuere Hardware unter dem Deckmantel erhöhter Sicherheit zu verkaufen. Das ist völlig irrational, denn jetzt können wir mit großen Speichergrößen die Hölle der dynamischen Verknüpfung überhaupt loswerden und alle Apps statisch kompilieren.
Dasselbe geschah zuvor, als die Leute stillschweigend den realen Modus akzeptierten und andere Freiheiten wegnahmen. Und ich denke, MMU wird aufgrund von Kontextwechseln und Latenz bei der Adressübersetzung stark verlangsamt. Sie werden MMU nicht in leistungskritischen Systemen finden, wie sie von Wissenschaftlern verwendet werden, um physikalische Experimente zu testen.
Sie beschweren sich nicht, weil Sie nicht einmal wissen, dass Ihr Code durch all diese Stützräder behindert wird. Was kann ich sagen? Genießen Sie jetzt 2-mal langsamere Software mit ihrem PIC! Darüber hinaus wird mit dem Aufkommen von LLVM bald JIT (Managed Code) ohne Zugriff auf die x86-Inline-Assembly erzwungen, wodurch jeder C / C ++ - Code weiter verlangsamt wird. "Diejenigen, die die Freiheit für die Sicherheit opfern, verdienen beides nicht."