Dune enthält eine Bibliothek zum Abfragen von betriebssystemspezifischen Informationen, den sogenannten Konfigurator. Es ist in der Lage, C-Expressions auszuwerten und in Ocaml-Werte umzuwandeln. Überraschenderweise funktioniert es sogar bei der Kompilierung für eine andere Architektur. Wie kann es das tun?
Ein CD-ROM-Problem.
Nehmen wir ein altes Beispiel: Angenommen, wir wollen ein CD-ROM-Laufwerk auswerfen. Unter Linux besteht der Weg darin, die Gerätedatei wie /dev/cdrom zu öffnen und ioctl (fd, CDROMEJECT, 0) darauf aufzurufen. Der CDROMEJECT-Teil ist eine Konstante, die in <linux/cdrom.h> definiert ist.
Um das gleiche in Ocaml zu tun, ist es möglich, eine C-Funktion zu definieren, die ioctl direkt aufruft.
Oder dies kann direkt mit ctypes geschehen, aber wir müssen den Wert der Konstanten CDROMEJECT kennen. Dazu kann der Konfigurator verwendet werden.
Konfigurator eingeben.
Die Erklärung zu der Frage „Wie man den Konfigurator in einem Dune-Projekt verwendet?“, würde den Rahmen dieses Artikels sprengen, aber im Kern ist eine Funktion C_define.import, die den Wert einige C-Expressions, einschließlich Makros, lesen kann.
Das folgende Programm verwendet den Konfigurator, um den Wert der Konstanten CDROMEJECT zu holen und anzuzeigen.
Beachten Sie, dass das Abrufen der Konstanten durch das Parsen der Header-Dateien selbst erfolgen kann. Dies unterstützt aber auch konstante C-Expressions (z.B. 1 << 8) und einige C-Features wie sizeof(int).
Also, wie funktioniert es?
Es ist sicherlich notwendig, etwas C zu generieren und zu kompilieren, um dies zu tun. Eine erste Version ist die Generierung eines kurzes C-Programms wie das Folgende.
Durch das Ausführen dieses Programms und das Parsen der Ausgabe kann der Konfigurator den richtigen Wert erhalten.
Abgesehen davon, dass Dune Cross-Compilation unterstützt: Beim Kompilieren eines Unikernels für eine ESP32-CPU könnte es praktisch sein, den Wert von Konstanten wie ESP_ERR_WIFI_PASSWORD zu haben, die nur über eine fremde Toolchain verfügbar sind. Es ist jedoch nicht möglich, ESP32-Binärdateien auf dem Hostsystem auszuführen.
Eine bessere Lösung.
Da es notwendig ist, einen C-Compiler zu verwenden, aber kein Programm auszuführen, betrachtet die Lösung den kompilierten Code:
Das ist es, was der Konfigurator macht. Da das Parsen von kompiliertem Code schwierig ist (und nicht alle Ziele das gleiche Binärformat verwenden), werden die Werte in konstanten Zeichenketten zwischen bekannten Markern gespeichert.
Hier ist die generierte C-Datei. Beachten Sie, dass dies im Gegensatz zum vorherigen Versuch keine vollständige ausführbare Datei ist, sondern nur eine Datei, die mit -c erstellt werden muss.
Die Dn(x)-Makros erscheinen zunächst ermutigend, aber denken Sie daran, dass wir eine Zeichenkettenkonstante benötigen, also ist es notwendig, den ganzzahligen Wert in eine Liste von Zeichen zu konvertieren. Der Komma-Operator stellt sichern, dass das Ergebnis wie „1“, „2“, „3“, „4“ aussieht, dass in den Array-Initialisierer eingefügt wird.
Nach dem Kompilieren dieser Datei ist die Zeichenkette direkt in der Binärdatei sichtbar:
Es ist sogar möglich, sie mit einfachen Unix-Tools zu analysieren.
Die eigentliche Konfiguratorbibliothek analysiert sie mit einem sehr einfachen Lexikon. Es verwendet die Zahl direkt nach BEGIN (-0- oben), um zwischen den verschiedenen Konstanten zu unterscheiden, die angefordert wurden.
Es unterstützt auch mehr Arten von Bindings, wie z.B. Zeichenketten. In diesem Fall wird die Zeichenkette direkt zwischen BEGIN-0- und -END eingefügt.
Fazit.
Binäre Dateiformate können schwierig zu analysieren sein, aber in einigen Fällen ist dies die richtige Lösung. Im Zusammenhang mit Dune, wenn es nicht immer möglich ist, die Ausgabebinärdateien auszuführen, ist dies die richtige Lösung, um Informationen aus dem Zielsystem zu extrahieren.
Vielen Dank für Ihren Besuch.