Position Indipendent Code (-fPIC) für Assembler
« | 23 Apr 2022 | »Mit dem Fehler
/usr/bin/ld: (…S.o): relocation R_X86_64_PC32 against symbol ‘…’ can not be used when making a shared object; recompile with -fPIC
konnte ich genau gar nichts anfangen, schließlich hatte ich alle Sourcen
mit -fpic
kompiliert.
Was unterscheidet also meine Assembler-Funktion von meinen anderen C-Funktionen?
Weil mich der MSVC in Sachen C++ bei UEFI im Stich lässt, wollte ich das Ganze dem GCC anvertrauen.
Doch das erforderte einige Updates, denn beim GCC kann man nicht so einfach
EFI-Applications erstellen.
Man baut zuerst spezielle Shared-Libraries und wandelt diese dann in EFI
Anwendungen um.
Doch hier scheiterte ich an meinen handgeschriebenen Assembler-Implementierungen für den Stack-Context-Switch bei Koroutinen.
Konkret ging es um einen Aufruf der Assembler-Funktion B
innerhalb der
Funktion A
.
Wenn A
also B
aufrufen will, gibt es ein Relocation Problem.
Lösung: @plt
Anstatt
1call B
nutzt man einfach
1call B@plt
und schon lässt sich wieder alles kompilieren.
Die Hintergründe
Shared-Libraries und EFI schreiben ihre Funktionen nicht an fixe Adressen,
sondern arbeiten mit Einträgen in einer “Global Offset Table” (GOT
).
Wird eine Lib geladen, landet der Code an einer beliebigen Speicheradresse
und um Funktionen anspringen zu können, muss diese Adresse errechnet werden
können.
Und genau dafür generiert der Assemblercompiler Hilfsfunktionen die mit @PLT
(Procedure Linkage Table) erreicht werden können um zur korrekten Adresse
der Funktion gelangen zu können.
Hinter dieser Magie dürfte offenbar relative Adressierung stecken, wo der
Aufruf der @plt
Funktion relativ zur aufrufenden Funktion fix ist und die
@plt
Funktion löst dann die echte aufzurufende Adresse auf und springt hin.
Eine tolle Aufschlüsselung der Vorgänge findet man unter maskray.me/blog
Fazit
Meine Erwartungen aus der MSVC Welt basieren auf der Vorstellung, dass solche
Details immer im Hintergrund aufgelöst werden müssen.
Doch wie sich zeigt, muss man beim GCC sehr wohl zwischen Aufrufen
zu öffentlichen und privaten Funktionen unterscheiden.
Tja, bisher bin ich von solchen Details verschont geblieben, denn damals,
als ich mit Assembler noch in der DOS
Welt unterwegs war, gab es keine Shared-Libraries, die man hätte beachten
müssen.
Und heutige Mikrocontroller
Spielereien kennen auch keine Relocations.
Eines kann ich also jede Woche über die Programmierung sagen:
Hier lernt man nie aus!