Position Indipendent Code (-fPIC) für Assembler

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!