it-swarm.com.de

Option GCC -fPIC

Ich habe über GCCs Optionen für Codegenerierungskonventionen gelesen, konnte aber nicht verstehen, was "Positionsunabhängigen Code (PIC) generieren" bewirkt. Bitte geben Sie ein Beispiel, um mir zu erklären, was es bedeutet.

387
Narek

Positionsunabhängiger Code bedeutet, dass der generierte Maschinencode nicht davon abhängig ist, dass er sich an einer bestimmten Adresse befindet, um zu arbeiten.

Z.B. Sprünge würden eher relativ als absolut erzeugt.

Pseudo-Assembly:

PIC: Dies würde funktionieren, egal ob der Code an der Adresse 100 oder 1000 war

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL CURRENT+10
...
111: NOP

Non-PIC: Dies funktioniert nur, wenn sich der Code unter der Adresse 100 befindet

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL 111
...
111: NOP

BEARBEITEN: Als Antwort auf Kommentar.

Wenn Ihr Code mit -fPIC kompiliert wurde, kann er in eine Bibliothek aufgenommen werden. Die Bibliothek muss in der Lage sein, von ihrem bevorzugten Speicherort an eine andere Adresse verschoben zu werden. Möglicherweise ist bereits eine andere Bibliothek an der von Ihrer Bibliothek bevorzugten Adresse geladen.

466
Erik

Ich werde versuchen, auf einfachere Weise zu erklären, was bereits gesagt wurde.

Wann immer eine gemeinsam genutzte Bibliothek geladen wird, ändert der Loader (der Code auf dem Betriebssystem, der ein von Ihnen ausgeführtes Programm lädt) einige Adressen im Code, je nachdem, wohin das Objekt geladen wurde.

Im obigen Beispiel wird "111" im Nicht-PIC-Code beim ersten Laden vom Loader geschrieben.

Für nicht gemeinsam genutzte Objekte möchten Sie möglicherweise, dass dies so ist, da der Compiler einige Optimierungen an diesem Code vornehmen kann.

Wenn ein anderer Prozess ein gemeinsames Objekt mit diesem Code "verknüpfen" möchte, muss er ihn unter denselben virtuellen Adressen lesen, da sonst "111" keinen Sinn ergibt. Dieser virtuelle Speicherplatz wird möglicherweise bereits im zweiten Prozess verwendet.

54
Roee Gavirel

Code, der in gemeinsam genutzte Bibliotheken integriert ist, sollte normalerweise positionsunabhängiger Code sein, damit die gemeinsam genutzte Bibliothek problemlos an (mehr oder weniger) jeder Adresse im Speicher geladen werden kann. Das -fPIC Option stellt sicher, dass GCC solchen Code erzeugt.

37

Die Verknüpfung zu einer Funktion in einer dynamischen Bibliothek wird aufgelöst, wenn die Bibliothek geladen wird oder zur Laufzeit. Daher werden sowohl die ausführbare Datei als auch die dynamische Bibliothek beim Ausführen des Programms in den Speicher geladen. Die Speicheradresse, unter der eine dynamische Bibliothek geladen wird, kann nicht im Voraus bestimmt werden, da eine feste Adresse möglicherweise mit einer anderen dynamischen Bibliothek kollidiert, die dieselbe Adresse benötigt.


Es gibt zwei häufig verwendete Methoden zur Behebung dieses Problems:

1. Umzug. Alle Zeiger und Adressen im Code werden bei Bedarf geändert, um der tatsächlichen Ladeadresse zu entsprechen. Die Verlagerung erfolgt durch den Linker und den Loader.

2.Positionsunabhängiger Code. Alle Adressen im Code beziehen sich auf die aktuelle Position. Gemeinsame Objekte in Unix-ähnlichen Systemen verwenden standardmäßig positionsunabhängigen Code. Dies ist weniger effizient als eine Verlagerung, wenn das Programm längere Zeit ausgeführt wird, insbesondere im 32-Bit-Modus.


Der Name " positionsunabhängiger Code " impliziert tatsächlich Folgendes:

  • Der Codeabschnitt enthält keine absoluten Adressen, die verschoben werden müssen, sondern nur selbst relative Adressen. Daher kann der Codeabschnitt an einer beliebigen Speicheradresse geladen und von mehreren Prozessen gemeinsam genutzt werden.

  • Der Datenabschnitt wird nicht von mehreren Prozessen gemeinsam genutzt, da er häufig beschreibbare Daten enthält. Daher kann der Datenabschnitt Zeiger oder Adressen enthalten, die verschoben werden müssen.

  • Alle öffentlichen Funktionen und öffentlichen Daten können unter Linux überschrieben werden. Wenn eine Funktion in der ausführbaren Hauptdatei denselben Namen wie eine Funktion in einem gemeinsam genutzten Objekt hat, hat die Version in main Vorrang, nicht nur beim Aufruf von main, sondern auch beim Aufruf vom gemeinsam genutzten Objekt. Wenn eine globale Variable in main denselben Namen wie eine globale Variable im gemeinsam genutzten Objekt hat, wird die Instanz in main ebenfalls verwendet, auch wenn vom gemeinsam genutzten Objekt aus darauf zugegriffen wird.


Diese sogenannte Symbolinterposition soll das Verhalten statischer Bibliotheken nachahmen.

Ein gemeinsam genutztes Objekt verfügt über eine Zeigertabelle zu seinen Funktionen, die als Prozedurverknüpfungstabelle (PLT) bezeichnet wird, und eine Zeigertabelle zu seinen Variablen, die als globale Versatztabelle (GOT) bezeichnet wird, um diese "Überschreibungs" -Funktion zu implementieren. Alle Zugriffe auf Funktionen und öffentliche Variablen durchlaufen diese Tabellen.

p.s. Wenn dynamische Verknüpfungen nicht vermieden werden können, gibt es verschiedene Möglichkeiten, die zeitaufwendigen Funktionen des positionsunabhängigen Codes zu umgehen.

Sie können mehr aus diesem Artikel lesen: http://www.agner.org/optimize/optimizing_cpp.pdf

13
bruziuz

Ein kleiner Zusatz zu den bereits geposteten Antworten: Objektdateien, die nicht positionsunabhängig kompiliert wurden, können verschoben werden. Sie enthalten Umzugstabelleneinträge.

Mit diesen Einträgen kann der Loader (das Codebit, das ein Programm in den Speicher lädt) die absoluten Adressen neu schreiben, um sie an die tatsächliche Ladeadresse im virtuellen Adressraum anzupassen.

Ein Betriebssystem versucht, eine einzelne Kopie einer "gemeinsam genutzten Objektbibliothek", die in den Speicher geladen wurde, für alle Programme freizugeben, die mit derselben gemeinsam genutzten Objektbibliothek verknüpft sind.

Da der Codeadressraum (im Gegensatz zu Abschnitten des Datenraums) nicht zusammenhängend sein muss und die meisten Programme, die mit einer bestimmten Bibliothek verknüpft sind, einen relativ festen Abhängigkeitsbaum für die Bibliothek haben, gelingt dies meistens. In den seltenen Fällen, in denen eine Diskrepanz besteht, ist es möglicherweise erforderlich, zwei oder mehr Kopien einer gemeinsam genutzten Objektbibliothek im Speicher zu haben.

Offensichtlich wird jeder Versuch, die Ladeadresse einer Bibliothek zwischen Programmen und/oder Programminstanzen zufällig zu ordnen (um die Möglichkeit der Erstellung eines ausnutzbaren Musters zu verringern), solche Fälle häufig machen, nicht selten. Man sollte jeden Versuch unternehmen, alle gemeinsam genutzten Objektbibliotheken so zu kompilieren, dass sie positionsunabhängig sind.

Da auch Aufrufe aus dem Hauptprogramm in diese Bibliotheken verlagerbar gemacht werden, ist es weniger wahrscheinlich, dass eine gemeinsam genutzte Bibliothek kopiert werden muss.

6
user1016759