Exception Drama

Einst liebte ich sie … doch da wusste ich noch nicht, woraus sie bestehen.

So könnte man meine aktuelle Beziehung zu C++ Exception beschreiben. Denn dieses wirklich gute “Sprachmittel” ist so etwas von unterschiedlich implementiert, dass man sie nur durch das Neuschreiben eines vollständigen Compilers in ihren Abläufen nachvollziehen kann.


Hintergrund EFI

Ich bin wieder mal im EFI Land unterwegs bzw. würde ich gerne mit einem C++ Compiler dort hinreisen, doch das erweist sich mit jedem weiteren Versuch als unmachbarer.

Ich werde zwar noch nicht aufgeben, aber mit so krassen Widersprüchen zwischen den Implementierungen beim gleichen Hersteller hätte ich nie gerechnet.

Hier mal ein Lagebericht von der Front:

STD-Lib und Runtime

Man sollte glauben, es gäbe einen “Standard” der Standard-Exception Objekte festlegt. Und den gibt es auch, nur der befasst sich lediglich mit öffentlichen Schnittstellen.

std::exception wird aber von einigen anderen Klassen geerbt und diese bedienen sich dann an unterschiedlichen Methoden aus der Basisklasse, die nicht spezifiziert sind.

Microsofts MSVC ändert diese Schnittstellen mit jedem Release leicht ab und fabriziert so neue Formen std::exception.

Wenn ich nun für EFI einen Build ohne die C++-Runtime erstelle, erhalte ich zahlreiche “undefinierte” Symbole in Zusammenhang mit std::exception. Implementiere ich diese nach, gelten diese nur für eine MSVC Version, z.B. MSVC-2015. MSVC-2017/19/22 heißen dann die Member anders oder Methoden werden durch andere ersetzt.

Eine von mir angestrebte “generische” Implementierung existiert nicht.

Spezielle CPU Register und Adressen

Viele Exception-Jobs laufen über “interne” Funktionen, die ebenfalls als “undefinierte” Symbole sichtbar werden, wenn man “Freestanding” zu kompilieren versucht. Und das wäre gut, denn hier kann man manuell eingreifen.

Doch leider passiert das nicht überall, und so finde ich im X64-Assembler-Dump einige Segmentregister-Zugriffe auf FS zum Thread-Local-Storage (TLS). Manchmal werden dafür zwar Funktionen aufgerufen, die man abfangen könnte, aber FS:addr Aufrufe, die der Compiler einfügt, gegen die bin ich leider machtlos.

Ich kann nur hoffen, dass es sich dabei um Dead-Codes oder um falsche Instruktionsinterpretationen handelt.

Unwinding-Tabellen

Das Finden von Aufräumcode funktioniert heute vermehrt über Metadaten, als über IP-(Instruction-Pointer) Tabellen. Wie effizient das letztendlich ist, steht noch zur Diskussion, Ein throw stellt fest, von welcher Codeadresse der Aufruf kam, und sucht dann in einer Tabelle zwischen Anfang- und Endpunkten, was mit dem Ursprung assoziiert ist.
Und von dort erhält man wieder weitere Codezeiger, die man anspringen kann um den Stack-Cleanup einleiten zu können.

Im GCC wird hierfür die Funktion dl_iterate_phdr() aufgerufen, die durch die geladenen ELF-Handler durchgehen soll und Info-Objekte dazu an einen Callback übergeben soll (z.B.: _Unwind_IteratePhdrCallback()).

Leider ist der “Dynamic-Linker” im Freestanding-Betrieb ebenfalls nicht vorhanden, somit muss ich die dl_iterate_phdr() offenbar nachbauen. Ein Problem könnte dann sein, dass mein EFI-Binary ein von ELF/Dwarf nach COFF/PE konvertiertes Trägerformat ist.

Ich hoffe also, dass durch diese Konvertierung (siehe objcopy) die Codeadressen nicht negativ beeinflusst.

Fazit

Es ist ein Alptraum.

Weder mit dem MSVC, noch mit dem GCC unter Linux oder mit dem MinGW ist die Nutzung von Exceptions außerhalb der Betriebssystemumgebung machbar.

Ich hätte mir ein vernünftiges API erwartet, das Exceptions und Typinformationen allokieren kann und eine Registrierungsfunktion für Cleanup-Routinen, sowie standardisierte Einsprungpunkte für Catch-Handler. Wäre dann die Exception-Behandlung und das Stack-Unwinding ein statischer vom Compiler generierter Code, dann könnte all das mit den generischen Metadaten betriebssystemunabhängig arbeiten.

Doch so bleibt die Windows-Implementierung an Win32-SEH angeknüpft und der GCC verbeißt sich in ELF und DL-Strukturen.

Und das ist leider alles andere als “generisch”.

Mal sehen wie das weitergehen wird.