Exception Drama
« | 24 Sep 2022 | »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.