Warum ist nicht jeder Code kompilierungspositionsunabhängig?

85

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?

ojblass
quelle
2
Aber wowest ist nicht ganz richtig. Viele Funktionsaufrufe und Sprünge verwenden relative Sprünge, sodass sie nach dem Verschieben nicht einmal eine Sprungtabelle benötigen.
Unbekannt
Betrachtet man den generierten Assembler-Code, so scheint es, dass die Adresse der Funktion geladen ist, während es sich bei dem nicht-fpic-Code lediglich um einen Sprung handelt. Verstehe ich Ihre Aussage falsch?
Ojblass
@ojblass was ich meine ist, dass einige Sprünge wie "Sprung 50 Anweisungen vor hier" oder "Sprung 5 Anweisungen rückwärts" anstelle von "Sprung zu 0x400000" sind. Zu sagen, dass Sie jedes Mal eine Adresse mit -fPIC laden müssen, ist nicht ganz richtig.
Unbekannt
Der Wikipedia- Artikel bietet eine gute Beschreibung. Grundsätzlich gibt es auf einigen Architekturen keine direkte Möglichkeit, zu einer relativen Adresse zu springen. Daher ist die Verwendung von PIC auf diesen Arhcs teurer. Weitere Informationen finden Sie in der Antwort von @ EvanTeran.
Alexei Sholik

Antworten:

67

Es wird eine Indirektion hinzugefügt. Mit positionsunabhängigem Code müssen Sie die Adresse Ihrer Funktion laden und dann dorthin springen. Normalerweise ist die Adresse der Funktion bereits im Befehlsstrom vorhanden.

wowest
quelle
33

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.

Eli Bendersky
quelle
16
@ Nick: Ich bin anderer Meinung. Wenn es dem Fragesteller hilft, ist es eine Antwort. Das Verweisen auf einen oder zwei relevante Artikel kann eine Fülle von Informationen liefern.
Eli Bendersky
5
In diesem Beitrag gibt es keine Schlussfolgerung, nur einen Link zu einem Artikel. Nicht einmal ein Hinweis darauf, dass PIC aufgrund von Leistungsproblemen nicht standardmäßig verwendet wird.
Nick
10
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
Rob
4
@Rob: Das Produktive wäre, eine Bearbeitung vorzuschlagen und keine Kommentare zum Jammern zu verwenden. Diese Antwort ist 4 Jahre alt. Damals hatte SO weniger strenge Regeln, wie eine Antwort aussehen sollte
Eli Bendersky
6
Dieser Beitrag wurde unter "Überprüfung" angezeigt und hat mich dazu aufgefordert. Jemand anderes hat es markiert. Der "jammernde Kommentar" wird automatisch von SO produziert, nicht von mir.
Rob
27

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.

http://en.wikipedia.org/wiki/Position_independent_code

Unbekannt
quelle
23

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:

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.

Evan Teran
quelle
1
IIRC MIPS hat auch keine PC-relative Adressierung, außer für relative Sprünge
phuclv
1
Dies ist eine übliche Technik, die im Shellcode verwendet wird, um die Adresse abzurufen, von der sie ausgeführt wird. Ich habe dies in einigen CTF-Lösungen verwendet.
Sherellbc
2

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.

RBerteig
quelle
1

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.

smcameron
quelle
4
Aber auch mit einer VM-MMU wird PIC-Code benötigt, um sicherzustellen, dass dieselbe .so-Bibliothek nur einmal in den Speicher geladen wird, wenn sie von verschiedenen ausführbaren Dateien verwendet wird.
mmmmmmmm
1

position-independent code hat bei den meisten Architekturen einen Leistungsaufwand, da ein zusätzliches Register erforderlich ist.

Dies ist also zu Leistungszwecken.

Eric Wang
quelle
0

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.

Govardhan Murali
quelle
-5

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."

SmugLispWeenie
quelle
Das ist nur eine Tatsachenfeststellung: Vor 10 Jahren war PIC optional, heute ist es Standard und obligatorisch. Ich bezweifle, dass Nicht-PIE-Code in weiteren Betriebssystemversionen unterstützt wird. Genau wie die Unterstützung im Real-Modus nach Windows 9x eingestellt wurde. Die Frage, ob PIC verwendet oder nicht verwendet werden soll, wird eher zu einem theoretischen Informatik-Thema, es sei denn, Sie entsperren Ihr Betriebssystem und aktivieren die Unterstützung dafür erneut. Das Wichtigste, was die Leute über PIC wissen müssen, ist, dass es langsam genug ist, dass Compiler bisher die statische Kompilierung unterstützten, und es gab statische Versionen der meisten DLLs.
SmugLispWeenie
1
Ihre ersten paar Sätze sind nur eine Tatsachenfeststellung. Der Rest ist eine Meinung, die an Verschwörung grenzt.
Mitch Lindgren
Sprechen Sie einfach mit Leuten und fragen Sie sie nach ihrer Meinung. Ich persönlich fand, dass PIC gegen Nicht-PIC auch eine Frage der Ideologie wurde. PIC ist das Programmieräquivalent des Kommunismus, bei dem Code in Massenproduktion hergestellt wird und jeder die gleiche Kopie erhält. Non-PIC ist ein Programmieräquivalent zum Kapitalismus, bei dem es viele konkurrierende Versionen desselben Codes gibt. Menschen mit einer eher linken Denkweise unterstützen PIC unbewusst, um zu beweisen, dass ihre Lieblingsideologie zumindest im Computerbereich funktionieren könnte. Dieselben Leute würden Ihnen davon abraten, persönlich modifizierte libpng zu verwenden.
SmugLispWeenie
2
Können wir nicht politische Beschimpfungen auf einer Programmwebsite haben, bitte danke
Ryan McCampbell