Warum ist nicht der gesamte Code positionsunabhängig kompiliert?

Lesezeit: 4 Minuten

Beim Kompilieren gemeinsam genutzter Bibliotheken in gcc kompiliert die Option -fPIC den Code positionsunabhängig. Gibt es einen Grund (Leistung oder andere), warum Sie nicht alle Codepositionen unabhängig kompilieren würden?

  • Aber wowest ist nicht ganz richtig. Viele Funktionsaufrufe und Sprünge verwenden relative Sprünge, sodass sie nicht einmal eine Sprungtabelle benötigen, nachdem sie verschoben wurden.

    – Unbekannt

    2. Mai 2009 um 2:47 Uhr

  • Wenn man sich den generierten Assembler-Code ansieht, scheint es, dass die Adresse der Funktion geladen wird, während es sich bei Nicht-FPIC-Code nur um einen Sprung handelt. Verstehe ich deine Aussage falsch?

    – ojblass

    2. Mai 2009 um 5:21 Uhr

  • @ojblass was ich meine ist, dass einige Sprünge wie “50 Anweisungen vor hier springen” oder “5 Anweisungen rückwärts springen” statt “zu 0x400000 springen” sind. Zu sagen, dass Sie jedes Mal eine Adresse mit -fPIC laden müssen, ist nicht ganz richtig.

    – Unbekannt

    2. Mai 2009 um 5:47 Uhr

  • Die Wikipedia Artikel liefert 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 Archcs teurer. Weitere Informationen finden Sie in der Antwort von @EvanTeran.

    – Alexej Scholik

    25. August 2013 um 15:05 Uhr


Es fügt eine Indirektion hinzu. Bei 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.

Ja, es gibt Performance-Gründe. Einige Zugriffe befinden sich effektiv unter einer anderen Indirektionsschicht, um die absolute Position im Speicher zu erhalten.

Es gibt auch die GOT (Global Offset Table), die Offsets von globalen Variablen speichert. Für mich sieht das nur wie eine IAT-Fixup-Tabelle aus, die von Wikipedia und einigen anderen Quellen als positionsabhängig eingestuft wird.

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

Zusätzlich zur akzeptierten Antwort. Eine Sache, die die Leistung des PIC-Codes stark beeinträchtigt, ist das Fehlen von “IP-relativer Adressierung” auf x86. Mit “IP-relativer Adressierung” könnten Sie nach Daten fragen, die X Bytes vom aktuellen Befehlszeiger entfernt sind. Dies würde den PIC-Code viel einfacher machen.

Sprünge und Anrufe sind normalerweise EIP-bezogen, also stellen diese kein wirkliches Problem dar. Der Zugriff auf Daten erfordert jedoch ein wenig zusätzliche Trickserei. Manchmal wird ein Register vorübergehend als “Basiszeiger” für Daten reserviert, die der Code benötigt. Eine gängige Technik besteht beispielsweise darin, die Funktionsweise von Anrufen 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 indirekte Ebene hinzu. Zum Beispiel die GOT (Global Offset Table), die von gcc-Compilern verwendet wird.

x86-64 hat einen “RIP Relative”-Modus hinzugefügt, der die Dinge a viel einfacher.

  • IIRC MIPS hat auch keine PC-relative Adressierung, außer bei relativen Sprüngen

    – phuklv

    8. August 2014 um 8:17 Uhr

  • Dies ist eine gängige Technik, die in Shellcode verwendet wird, um die Adresse zu erhalten, von der es ausgeführt wird. Ich habe dies in einigen CTF-Lösungen verwendet.

    – sherrelbc

    24. März 2018 um 0:53 Uhr


Weil die Implementierung von vollständig positionsunabhängigem Code dem Codegenerator eine Einschränkung hinzufügt, die die Verwendung schnellerer Operationen verhindern kann, oder zusätzliche Schritte hinzufügt, um diese Einschränkung beizubehalten.

Dies könnte ein akzeptabler Kompromiss sein, um Multiprocessing 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 Kompromisse bei der Leistung anders, und ein verlagernder Lader ist oft weniger teuer (es kostet jedes Mal, wenn Code zum ersten Mal geladen wird) als das Beste, was ein Optimierer leisten kann, wenn er freie Hand hat. Außerdem verdeckt die Verfügbarkeit virtueller Adressräume den größten Teil der Motivation für Positionsunabhängigkeit von vornherein.

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

Dies dient also der Leistung.

Benutzeravatar von smcameron
smcameron

Außerdem bedeutet virtuelle Speicherhardware in den meisten modernen Prozessoren (die von den meisten modernen Betriebssystemen verwendet werden), dass viele Codes (alle User Space-Apps, abgesehen von der skurrilen Verwendung von mmap oder dergleichen) nicht positionsunabhängig sein müssen. Jedes Programm bekommt seinen eigenen Adressraum, der seiner Meinung nach bei Null beginnt.

Benutzeravatar von Govardhan Murali
Govardhan Murali

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. Betriebssysteme wie Windows verwenden eine Technik, die als Speicherzuordnung bezeichnet wird, um dies zu erreichen.

  • Wo würde ich eine solche Warnung sehen? Welches Betriebssystem und welchen Compiler hatten Sie im Sinn?

    – Anton Menschow

    29. April 2021 um 4:41 Uhr

1420610cookie-checkWarum ist nicht der gesamte Code positionsunabhängig kompiliert?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy