Ich habe die Optionen von GCC für Konventionen zur Codegenerierung gelesen , konnte jedoch nicht verstehen, was "Generieren von positionsunabhängigem Code (PIC)" bewirkt. Bitte geben Sie ein Beispiel, um mir zu erklären, was es bedeutet.
436
Antworten:
Positionsunabhängiger Code bedeutet, dass der generierte Maschinencode nicht davon abhängt, dass er sich an einer bestimmten Adresse befindet, um arbeiten zu können.
ZB würden Sprünge eher relativ als absolut erzeugt.
Pseudo-Assemblierung:
PIC: Dies würde funktionieren, unabhängig davon, ob sich der Code an der Adresse 100 oder 1000 befindet
Nicht-PIC: Dies funktioniert nur, wenn sich der Code an der Adresse 100 befindet
EDIT: Als Antwort auf einen Kommentar.
Wenn Ihr Code mit -fPIC kompiliert wurde, ist er für die Aufnahme in eine Bibliothek geeignet. Die Bibliothek muss von ihrem bevorzugten Speicherort an eine andere Adresse verschoben werden können. Möglicherweise befindet sich eine andere bereits geladene Bibliothek an der von Ihrer Bibliothek bevorzugten Adresse.
quelle
-fPIC
beim Kompilieren eines Programms oder einer statischen Bibliothek darauf verzichten, da in einem Prozess nur ein Hauptprogramm vorhanden ist und daher keine Laufzeitverschiebung erforderlich ist. Auf einigen Systemen werden Programme zur Erhöhung der Sicherheit immer noch positionsunabhängig gemacht.Ich werde versuchen, das, was bereits gesagt wurde, auf einfachere Weise zu erklären.
Immer wenn 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 die "111" im Nicht-PIC-Code beim ersten Laden vom Loader geschrieben.
Für nicht freigegebene Objekte möchten Sie möglicherweise, dass dies so ist, da der Compiler einige Optimierungen an diesem Code vornehmen kann.
Wenn ein gemeinsam genutztes Objekt bei einem gemeinsam genutzten Objekt mit diesem Code "verknüpfen" möchte, muss er ihn an denselben virtuellen Adressen lesen. Andernfalls macht "111" keinen Sinn. Dieser virtuelle Raum wird jedoch möglicherweise bereits im zweiten Prozess verwendet.
quelle
Whenever a shared lib is loaded, the loader changes some addresses in the code depending on where the object was loaded to.
Ich denke, dies ist nicht korrekt, wenn es mit -fpic kompiliert wurde und der Grund, warum -fpic existiert, z. B. aus Leistungsgründen oder weil Sie einen Loader haben, der nicht verschoben werden kann, oder weil Sie mehrere Kopien an verschiedenen Orten oder aus vielen weiteren Gründen benötigen.Code, der in gemeinsam genutzte Bibliotheken integriert ist, sollte normalerweise positionsunabhängiger Code sein, damit die gemeinsam genutzte Bibliothek problemlos an (mehr oder weniger) jede Adresse im Speicher geladen werden kann. Die
-fPIC
Option stellt sicher, dass GCC einen solchen Code erzeugt.quelle
-fPIC
Flag aktiviert ist? ist es nicht mit dem Programm verbunden? Wenn das Programm ausgeführt wird, lädt das Betriebssystem es in den Speicher hoch. Vermisse ich etwas-fPIC
Flag verwendet, um sicherzustellen, dass diese Bibliothek in eine beliebige virtuelle Adresse in dem Prozess geladen werden kann, der sie verknüpft? Entschuldigung für doppelte Kommentare 5 Minuten verstrichen können vorherige nicht bearbeiten.libwotnot.so
) und dem Verknüpfen mit dieser (-lwotnot
). Während des Verlinkens müssen Sie sich keine Sorgen machen-fPIC
. Früher mussten Sie beim Erstellen der gemeinsam genutzten Bibliothek sicherstellen-fPIC
, dass alle Objektdateien in die gemeinsam genutzte Bibliothek integriert wurden. Die Regeln haben sich möglicherweise geändert, da Compiler heutzutage standardmäßig mit PIC-Code erstellt werden. Was vor 20 Jahren kritisch war und vor 7 Jahren wichtig gewesen sein könnte, ist heutzutage meiner Meinung nach weniger wichtig. Adressen außerhalb des O / S-Kernels sind "immer" virtuelle Adressen.-fPIC
. Ohne dieses Flag zu übergeben, muss der beim Erstellen der .so generierte Code auf bestimmte virtuelle Adressen geladen werden, die möglicherweise verwendet werden.Weitere ...
Jeder Prozess hat denselben virtuellen Adressraum (Wenn die Randomisierung der virtuellen Adresse mithilfe eines Flags in Linux OS gestoppt wird) (Weitere Informationen Deaktivieren und erneutes Aktivieren der Randomisierung des Adressraumlayouts nur für mich selbst )
Wenn es sich also um eine Exe ohne gemeinsame Verknüpfung handelt (hypothetisches Szenario), können wir derselben asm-Anweisung immer dieselbe virtuelle Adresse zuweisen, ohne Schaden zu nehmen.
Wenn wir jedoch ein freigegebenes Objekt mit der exe verknüpfen möchten, sind wir uns nicht sicher, welche Startadresse dem freigegebenen Objekt zugewiesen ist, da dies von der Reihenfolge abhängt, in der die freigegebenen Objekte verknüpft wurden. Allerdings hat die Anweisung asm in .so immer eine Unterschiedliche virtuelle Adresse je nach Prozess, mit dem die Verknüpfung hergestellt wird.
Ein Prozess kann also .so als 0x45678910 in seinem eigenen virtuellen Raum eine Startadresse geben, und ein anderer Prozess kann gleichzeitig die Startadresse 0x12131415 geben. Wenn sie keine relative Adressierung verwenden, funktioniert .so überhaupt nicht.
Sie müssen also immer den relativen Adressierungsmodus und damit die Option fpic verwenden.
quelle
Die Verknüpfung zu einer Funktion in einer dynamischen Bibliothek wird beim Laden der Bibliothek oder zur Laufzeit aufgelöst. 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, um dieses Problem zu lösen:
1. Umzug. Alle Zeiger und Adressen im Code werden bei Bedarf an die tatsächliche Ladeadresse angepasst. Der Umzug erfolgt durch den Linker und den Loader.
2.Positionsunabhängiger Code. Alle Adressen im Code beziehen sich auf die aktuelle Position. Freigegebene 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, selbst 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 für seine Funktionen, die als Prozedurverknüpfungstabelle (PLT) bezeichnet wird, und eine Zeigertabelle für seine Variablen, die als globale Offset-Tabelle (GOT) bezeichnet wird, um diese Funktion zum Überschreiben zu implementieren. Alle Zugriffe auf Funktionen und öffentliche Variablen werden in diesen Tabellen durchgeführt.
ps Wenn eine dynamische Verknüpfung nicht vermieden werden kann, gibt es verschiedene Möglichkeiten, um die zeitaufwendigen Funktionen des positionsunabhängigen Codes zu vermeiden.
Sie können mehr aus diesem Artikel lesen: http://www.agner.org/optimize/optimizing_cpp.pdf
quelle
Eine kleine Ergänzung zu den bereits veröffentlichten Antworten: Objektdateien, die nicht positionsunabhängig kompiliert wurden, können verschoben werden. Sie enthalten Einträge in der Verschiebungstabelle.
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 in den Speicher geladenen "gemeinsam genutzten Objektbibliothek" für alle Programme freizugeben, die mit derselben gemeinsam genutzten Objektbibliothek verknüpft sind.
Da der Code-Adressraum (im Gegensatz zu Abschnitten des Datenraums) nicht zusammenhängend sein muss und die meisten Programme, die mit einer bestimmten Bibliothek verknüpft sind, einen ziemlich festen Bibliotheksabhängigkeitsbaum haben, ist dies meistens erfolgreich. In den seltenen Fällen, in denen eine Diskrepanz besteht, kann es erforderlich sein, 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 sortieren (um die Möglichkeit der Erstellung eines ausnutzbaren Musters zu verringern), solche Fälle häufig und nicht selten machen, wenn ein System diese Funktion aktiviert hat. Man sollte jeden Versuch unternehmen, alle gemeinsam genutzten Objektbibliotheken positionsunabhängig zu kompilieren.
Da Aufrufe dieser Bibliotheken aus dem Hauptteil des Hauptprogramms ebenfalls verschoben werden können, ist es weniger wahrscheinlich, dass eine gemeinsam genutzte Bibliothek kopiert werden muss.
quelle