printf() Marke Eigenbau

Und Schuld hat libsupc++

… dass ich jetzt auch noch printf() und seine Freunde nachbauen darf.

Und so beginnt ein weiterer Tag meiner Reise ins EFI-Land.


Mit dem GCC werden C++ Exceptions in EFI Apps kein Problem mehr sein.

dachte ich. Tja, falsch gedacht.

Das alte Problem, dass C++ Exceptionhandler und RTTI jede Menge im Hintergrund ausführen, der in der C-Runtime verankert ist, verfolgt mich nun nicht nur am MSVC sondern auch im GCC Build.

Grundsätzlich gibt es dort zwar eine Trennung von Kernfunktionen und Runtime-Zusätzen. Und zwar bietet die libsupc++ einige Basis Unwinding-Routinen an, doch diese greifen auf jede Menge C Funktionen zu, die es in nativen EFI Apps nicht gibt.

In Foren ließt man dann, man könnte die Bibliothek und Teile der glibc anpassen und sich neu zusammenkompilieren, doch dann schaffe ich wieder Code, der im MSVC explizit ausgeblendet werden muss.

Unabhängige printf(), sprintf() und fprintf() Funktionen

Das Problem sind tiefe Verschachtelungen zwischen String-Funktionen und den Betriebssystem-Primitiven.

Wenn eine printf() Implementierung direkt ein write auf STDOUT beinhaltet, ist das zwar effizient, aber nur POSIX oder Linux tauglich. Das gleiche gilt dann auch für die WinAPI am MSVC.
Und schlimmer wird es, wenn im Code dann noch Compiler-spezifische Erweiterungen benutzt werden.

Ich brauche also ein printf(), das Zeichen für Zeichen an einen Callback schickt. Und dieser Callback kann dann einfach an EFI oder etwas anderes angepasst werden. sprintf() und fprintf() funktionieren genau so, nur dass noch ein Parameter angefügt wird, der eine Referenz zum String oder zum FILE Objekt mitführt.

Und eben mit dieser Aufgabe werde ich wohl etwas Zeit verbringen müssen, denn erst wenn man seine Implementierung startet, merkt man, dass printf gar nicht so “unkomplex” ist, wenn die unterschiedlichen Format-Spezifizierer kombiniert werden müssen.

Und wozu das Ganze?

Der GCC produziert interne Funktionen für z.B. purecall oder cast Fehler und diese nutzen offenbar printf() und fprintf() und sprintf() um Debugging Nachrichten auf die Konsole zu schreiben.

Und dann wird beim Linken sichtbar, dass das unresolvable symbol printf nicht in cxx_purecall_xyz aufgerufen werden kann.

Während es mir (mehr oder weniger) gelungen ist, andere Bibliotheken von zlib bis zur libjpeg so abzuspecken, dass sie nur noch ihre Algorithmen umsetzen und keine OS-Funktionen aufrufen, zwingt mich jetzt ausgerechnet der C++ Compiler selbst, diese C-Schnittstelle auch in EFI nachzubauen.

Die Bibliothek GNU-EFI hätte zwar eine eigene Print() Implementierung, doch diese arbeitet nur mit wchar_t und unterstützt nicht alle Refinessen von printf().

Mich enttäuscht daher, dass sich dieses Verhalten nicht “leichter” abschalten lässt.

Fazit

Aber etwas Gutes hat es am Ende:

Früher oder später habe ich die Sourcen für einen kleinen Betriebssystemkern beieinander.

Denn wenn man alle STD-C Funktionen selbst implementiert hat, kann man viele Fremdprogramme damit neu kompilieren und sie über die “eigene Schnittstelle” laufen lassen.