WinCE Patches

Mit jeder Plattform gibt es Probleme in Sachen Kompatibilität, aber mit keiner gibt es so viel wie mit Windows CE.

Und vermutlich beschäftigte ich mich genau deshalb immer wieder mit dieser eigentlich schon ausgestorbenen Umgebung, denn:

Ich liebe die Herausforderung.


Ich selbst habe mit WinCE eigentlich gar keine Probleme, doch leider haben eine Vielzahl anderer Bibliotheken ihre Probleme mit dem limitierten Funktionsset.

Und das liegt in erster Linie daran, dass der C-Standard nicht vollständig abgebildet ist. Das manifestiert sich folgendermaßen:

  1. Ein Header fehlt (z.B.: stdbool.h oder io.h)
  2. Funktionen sind im Header, aber nicht in der C-Runtime implementiert (z.B.: time() oder clock())
  3. Funktionen fehlen im Header (z.B.: strcoll())

Ein fehlender Header lässt sich ganz leicht ersetzen. Man fügt ihn dem eigenen Projekt hinzu inklusive einer Implementierung, die z.B.: direkt auf WinAPI Aufrufe umleitet.

Wenn nur die Implementierung einer C-Funktion in der C-Runtime fehlt, verfährt man ähnlich: Man fügt einfach eine neue Implementierung im eigenen Projekt an und schon kann das “nicht-aufgelöste-Symbol” gefunden werden.

Am schlechtesten ist die dritte Variante, nämlich das Fehlen einer Funktion im Header und der CRT.

Viele Header in WinCE sind nur Umleitungen auf stdlib.h in der vieles notwendige enthalten ist. Doch leider fehlen die meisten IO-Funktionen wie freopen(), remove() oder rename().

Nutzt man nun eine Bibliothek, die selbst nur stdio.h einbindet und sich eine Funktion dahinter erwartet, treten seltsame Seiteneffekte auf.
Denn C nutzt den Funktionsaufruf als Deklaration mit eine undefinierten Prototyp wie:
int c_function(...);

Fügt man nun seine eigene Implementierung mit der richtigen Deklaration parallel hinzu, gilt die One-Definition-Rule als verletzt, weil das gleiche Symbol zwei mal unterschiedlich definiert wurde.
z.B.:

1int c_function(...);          //< implicit declaration
2void c_function(char const*); //< correct declaration

Platform Support Layer

Im Platform-Layer des GATE Projektes nutzte ich bisher ein wince Unterverzeichnis, in dem einige Header mit Inline Funktionen enthalten waren. Diese Header wurden dann von CMake aus adressiert, um unter Windows CE meine manipulierten Header zu laden und nicht jene C-Standard-Bibliothek.

Doch leider nahm die Menge der notwendigen Funktionen zu und daher brauchte ich einen neuen Ansatz.

stdcpatches Bibliothek

Die Hilfsbibliothek stdcpatches ist eine statische Bibliothek im libs Ordner des GATE Projektes und soll eine Vielzahl von std-c Header und Implementierungen nachbilden.

Für jede Plattform und jeden Typ von C-Header gibt es Unterverzeichnisse, die alle notwendigen Dateien beinhalten.
Am Ende entscheidet dann CMake, welche Ordner als Include-Directories eingebunden oder übersprungen werden sollen.

Auf diese Weise können Patches für Plattformen wie Windows CE oder Compiler wie MSVC eingebaut werden.

Fordert eine Bibliothek wie libpng z.B.: errno.h an, existiert dafür in stdcpatches ein eigener Header errno.h, der fehlende Makros und Definitionen nachliefert. Parallel dazu veröffentlicht eine eigene wince_errno.c Einheit die Implementierung innerhalb der statischen Bibliothek.

Für den MSVC allgemein existieren Header wie sys/stat.h oder stdint.h, die in älteren Versionen nicht enthalten waren. Auch hier bindet CMake die Dateien nur bei älteren Compilern ein und verzichtet darauf, wenn sie in neuen Versionen schon enthalten sind.

Fazit

Die Umstellung von “kleinen Patches” in gate/platform/wince auf eine eigene Bibliothek stdcpatches war eine größere und auch recht mühsam.
Dennoch glaube ich, dass es mir langfristig mehr bringt, solche Codeverrenkungen kontrolliert an genau einer Stelle durchzuführen.

Schließlich hat die bisherige Implementierung den Abhängigkeitsbaum korrumpiert, denn externe Bibliotheken sollten vor dem GATE Framework Code kompiliert werden. Doch nur für WinCE wurden einige wenige Include-Directories schon vorher aus dem GATE-Projekt geladen.

Jetzt sind externe Bibliotheken in Sonderfällen von der Bibliothek stdcpatches abhängig, und am Ende wird alles zusammen vom GATE Projekt konsumiert.

graph LR GATE[[GATE
Framework]] EXT[[External
Libraries]] PATCH[[stdcpatches]] STDC[[STD-C
Libraries]] OS[[OS native API]] GATE --include--> EXT EXT --include--> PATCH EXT --include --> STDC PATCH --include--> STDC PATCH --include--> OS

Ein Vorteil ergibt sich daraus außerdem: stdcpatches kann jetzt auch ohne das GATE Projekt mit anderen Libs für Fremdprojekte benutzt werden.