Cross-Compilation mit Lyx
lyxc ist ein self-contained Cross-Compiler: kein externer Linker, keine separate Toolchain-Konfiguration. Eine einzige Binary erzeugt nativen Code für alle unterstützten Zielplattformen.
Alle Zielplattformen
| Target-String | Alias | Architektur | Format | ABI | Status |
|---|---|---|---|---|---|
linux | x86_64 | x86_64 | ELF64 | SysV AMD64 | Stabil |
arm64 | linux-arm64 | ARM64 | ELF64 | AAPCS64 | Stabil |
linux-riscv64 | – | RV64GC | ELF64 | LP64D | Stabil |
riscv | riscv64 | RV64GC | ELF64 | LP64D | Stabil |
arm-cm4 | arm-cm33 | ARM Cortex-M4/M33 | ELF32 | ARM EABI5 | Stabil |
arm_cm | – | ARM Cortex-M (generisch) | ELF32 | ARM EABI5 | Stabil |
win64 | windows-x86_64 | x86_64 | PE32+ | Windows x64 | Stabil |
windows-arm64 | – | ARM64 | PE32+ | Windows ARM64 | Stabil |
macosx64 | macos-x86_64 | x86_64 | Mach-O | SysV AMD64 | Stabil |
macos-arm64 | – | ARM64 (Apple Silicon) | Mach-O | AAPCS64 | Stabil |
android | android-arm64 | ARM64 | ELF64 (.so) | Android Bionic | Stabil |
android-x86_64 | – | x86_64 | ELF64 (.so) | Android Bionic | Stabil |
esp32 | esp32s3 | Xtensa LX6 | ELF32 | Vereinfacht SysV | Experimentell |
Standard: Kein –target → Host-Plattform (in der Regel linux/x86_64).
Typische Workflows
ARM64 Linux (Raspberry Pi, Embedded-Server)
lyxc main.lyx --target=arm64 -o firmware.elf
# Mit Energy-Optimierung für Batteriebetrieb:
lyxc main.lyx --target=arm64 --target-energy=1 -o sensor.elf
# Safety-Build (DO-178C):
lyxc flight.lyx --target=arm64 --lint --static-analysis -o flight.elf
Die erzeugte ELF verwendet /lib/ld-linux-aarch64.so.1 als Dynamic Linker — das muss auf dem Zielsystem vorhanden sein. Für Systeme ohne Standard-Loader:
--staticgibt es in lyxc 1.0.16F nicht — der Aufruf bricht mitunbekannter Schalterund Exit 2 ab. Der Schalter sollte den Dynamic-Linker-Eintrag entfernen.
--no-libc-initwird angenommen, taucht aber inlyxc --helpnicht auf: Der Aufruf läuft durch (Exit 0) und erzeugt ein lauffähiges Binary. Ob dabei der vorgesehene Bare-Metal-Stub statt des gewöhnlichen_startentsteht, ist von außen nicht abzulesen — für die Beispiele unten zum Cortex-M ist er weiterhin die dokumentierte Form.
Beides ist derzeit gegenstandslos: Der Compiler erzeugt ohnehin statische ELF-Dateien ohne Dynamic Linker — das meldet auch das LCBS-Audit jedes Builds (RELRO (kein GOT/PLT-Angriffsziel — statisches ELF)).
Windows x64 (Cross von Linux)
lyxc main.lyx --target=win64 -o app.exe
lyxc main.lyx --target=windows-arm64 -o app_arm64.exe
Erzeugt eine vollständige PE32+-Datei. Keine Wine- oder MSVC-Installation nötig.
Seit lyxc 1.1.3E sind aus einer solchen Binary auch Win32-Funktionen erreichbar: extern fn … link „kernel32.dll“ landet in der Import-Tabelle, mehrere DLLs nebeneinander sind zulässig, und Umgebungsvariablen (EnvLookupRaw, std.datetime, std.os) lesen den Windows-Umgebungsblock (#1671, #1674, #1676, #1677). Auch die frühere Größengrenze ist weg: eine Relokationstabelle über 0x1000 machte die Datei vorher unstartbar (#1672).
RISC-V (Embedded-Controller, HiFive, VisionFive)
# Generisches RISC-V RV64GC:
lyxc main.lyx --target=riscv -o controller.elf
# Linux-spezifisch (mit Linux ABI-Optimierungen):
lyxc main.lyx --target=linux-riscv64 -o app.elf
Einschränkung: Kein Dynamic Linking für RISC-V — immer statisch linken (–static) oder -l-Flags vermeiden.
ARM Cortex-M (Bare-Metal Microcontroller)
Für STM32, nRF52, RP2040 und kompatible Cortex-M4/M33-Systeme:
# Cortex-M4 / Cortex-M33:
lyxc main.lyx --target=arm-cm4 --no-libc-init -o firmware.elf
# Generisch Cortex-M (M0/M3):
lyxc main.lyx --target=arm_cm --no-libc-init -o firmware.elf
Wichtig: Kein Hardware-FPU-Support — Fließkomma läuft als Software-Emulation. Für DO-178C mit FPU: die betroffenen Funktionen mit @flight_crit annotieren — ein –no-fp-fold gibt es nicht.
Das ELF32-Format enthält e_flags = EABI5 | Thumb-interwork — direkt flashbar mit objcopy oder openocd.
ESP32 / ESP32-S3 (Xtensa LX6 — experimentell)
lyxc main.lyx --target=esp32 --no-libc-init -o app.elf
lyxc main.lyx --target=esp32s3 --no-libc-init -o app_s3.elf
Status: läuft, aber ohne Hardware-Nachweis. Die Defektkette, die hier früher als „Segfault bei komplexen Programmen„ stand, ist mit lyxc 1.1.9N behoben (#1782); Division und Modulo, die still 0 lieferten, mit 1.1.10G (#1789). Was bleibt:
- Kein
f64-Rechnen — Addition und Vergleich brechen beim Übersetzen ab (nachgemessen mit 1.1.11B) - Kein Dynamic Linking
- Die Multiplikation ist nicht ausführbar geprüft — in keinem verfügbaren QEMU-Kern gibt es
Mul32(#1790). Das gilt sinngemäß für das ganze Ziel: geprüft ist, was der Compiler erzeugt, nicht was der Chip daraus macht.
Android (.so + JNI)
Android-Bibliotheken werden als .so erzeugt:
lyxc mylib.lyx --target=android --output-type=shared-lib -o libmylib.so
# Android API-Level explizit setzen (Default: 26 = Android 8.0, Minimum: 21):
lyxc mylib.lyx --target=android --android-api=29 --output-type=shared-lib -o libmylib.so
# Android x86_64 (Emulator):
lyxc mylib.lyx --target=android-x86_64 --output-type=shared-lib -o libmylib_x86.so
--android-apihat derzeit keine Wirkung (#1526). Alle Werte erzeugen dasselbe Programm — auch ein ungültiges wie 99, das ohne Beanstandung angenommen wird. Der Schalter ist also weder wirksam noch prüfend; das API-Level ist beim Einbinden auf Android-Seite sicherzustellen.
JNI-Funktionen werden mit @jni annotiert — der Compiler mangelt den Namen automatisch in das Java-Format Java_<class>_<method>:
import std.io;
@jni(class="com/example/MyApp", method="hello")
pub fn hello(): void {
PrintLn("Hello from Lyx via JNI!");
}
@jni(class="com/example/MyApp", method="add")
pub fn add(a: int64, b: int64): int64 {
return a + b;
}
Das erzeugte .so enthält ein .note.android.ident-Section mit dem API-Level — erforderlich für den Android-Loader.
Bare-Metal: kein Betriebssystem
Für Microcontroller und Custom-Bootloader ohne OS-Unterstützung:
lyxc main.lyx --target=arm-cm4 --no-libc-init --static -o bare.elf
Was –no-libc-init bewirkt:
- Kein
__libc_start_main-Aufruf - Kein
DT_NEEDED libc.so.6im ELF - 12-Byte
BL main + exit-Stub statt 56-Byte Standard-_start main()wird direkt als Einstiegspunkt verwendet
Compiler ist vollständig self-contained
lyxc benötigt keinen externen Linker (kein ld, lld, arm-none-eabi-ld). Der Compiler schreibt das fertige Binary (ELF64/ELF32/PE32+/Mach-O) direkt — Parsing, Codegen und Linking in einem Schritt.
Externe C-Bibliotheken werden trotzdem unterstützt:
# C-Bibliothek linken (lyxc löst Symbole selbst auf):
lyxc main.lyx -lm -lpthread -o app
# Eigene Shared Library einbinden:
lyxc main.lyx -L/opt/mylibs -lmylib -o app
Bekannte Einschränkungen
| Plattform | Einschränkung | Workaround |
|---|---|---|
| ESP32 / Cortex-M | Kein f64-Rechnen — Addition und Vergleich brechen beim Übersetzen ab | Ganzzahl-Arithmetik, Festkomma |
| ARM Cortex-M | alloc/new gibt es nicht — der Compiler weist beide ab (alloc/new gibt es auf einem freistehenden Ziel nicht) | Statische Puffer, feste Feldgrößen |
| ESP32 | Kein Dynamic Linking | Immer statisch kompilieren |
| RISC-V | Kein Dynamic Linking | –static verwenden |
| ARM Cortex-M | Kein Hardware-FPU | Software-Float; für DO-178C: @flight_crit an der Funktion |
| ARM Cortex-M | TrustZone nur Stub | Nur M-Mode nutzen |
Die frühere Zeile „ESP32: Segfault bei komplexen Programmen“ ist entfallen. Die Ursache war eine Defektkette im xtensa-Backend — Argumente, Param-Spill, Vorwärtsaufrufe, Feld-Offsets, Einstiegspunkt —, behoben mit lyxc 1.1.9N (#1782, #1765). Dazu kamen Division und Modulo, die vorher still 0 lieferten (#1789, 1.1.10G), sowie Zeichenkettenliterale und PC-relative Adressierung (#1786). Dieselbe Kette auf riscv war #1740 (1.1.9A) — dort waren es acht Defekte auf einmal, weil das Backend bis dahin nie ausgeführt geprüft worden war.
Was je Ziel übersetzt
Gemessen mit lyxc 1.1.11B: je ein Minimalprogramm pro Zeile, für jedes Ziel übersetzt. ✓ heißt „der Compiler erzeugt ein Binary„, nicht „auf dem Gerät nachgerechnet“ — für arm-cm4 und esp32 fehlt uns dafür die Hardware.
| Was | linux | arm64 | riscv | arm-cm4 | esp32 |
|---|---|---|---|---|---|
f64-Variable anlegen | ✓ | ✓ | ✓ | ✓ | ✓ |
f64-Addition | ✓ | ✓ | ✓ | bricht ab | bricht ab |
f64-Vergleich | ✓ | ✓ | ✓ | bricht ab | bricht ab |
| Ganzzahl-Division | ✓ | ✓ | ✓ | ✓ | ✓ |
| Multiplikation | ✓ | ✓ | ✓ | ✓ | ✓ |
Zeichenkettenliteral + PrintLn | ✓ | ✓ | ✓ | ✓ | ✓ |
Print(variable) | ✓ | ✓ | ✓ | ✓ | ✓ |
| globale Variable lesen/schreiben | ✓ | ✓ | ✓ | ✓ | ✓ |
con-Konstante | ✓ | ✓ | ✓ | ✓ | ✓ |
alloc/free | ✓ | ✓ | ✓ | abgewiesen | ✓ |
Gegenüber dem Stand 1.0.21A hat sich das Bild grundlegend gedreht:
arm64undriscvrechnenf64vollständig. Der frühere Ausfall beim Vergleich (arm64, Opcode 42) und bei der Addition (riscv, Opcode 20) ist weg.con-Konstanten waren auf allen IR-Zielen 0 — die Vorab-Erfassung der Modul-Globalen kannte nurNK_VAR_DECL, nichtNK_CON_DECL(#1751, 1.1.8A). Ein reiner Übersetzungstest wäre grün gewesen; der Fehler saß im erzeugten Code. Nachgezogen:f64-Konstanten (#1801),~0im Anfangswert (#1799) und negativef64(#1803).import std.ioträgt jetzt auf allen IR-Zielen. Die frühere Aussage „scheitert für alle IR-Ziele, dort fehlen 194 Builtins„ (#1388) gilt nicht mehr.- Virtueller und Interface-Dispatch laufen auf dem IR-Pfad über einen type-id-Switch statt über eine VMT (#1773, 1.1.9G) — kein eigenes Layout, keine Interface-Tabellen.
Die Backends weisen laut ab, statt still falschen Code zu erzeugen (#1339):
> error: arm-cm4-Backend kennt Opcode 20 nicht — er wuerde stillschweigend verschwinden (#1339). > error: arm-cm: alloc/new gibt es auf einem freistehenden Ziel nicht. >
Das ist die richtige Reihenfolge: ein Abbruch beim Übersetzen ist einer Rechnung vorzuziehen, die auf dem Zielgerät stillschweigend nichts tut.
Der Wächter hat allerdings eine Lücke, die #1789 offengelegt hat: aufesp32standenIRO_DIVundIRO_MODals „behandelt“ in der Liste — behandelt wurden sie auch, nur mit einem Rumpf, der still 0 zurückgab. Eine Liste, die Vorhandensein prüft, sagt nichts über Richtigkeit. Dafür braucht es Ausführung.
Letzte Aktualisierung: 2026-08-27 — Einschränkungstabelle und Ziel-Matrix gegen lyxc 1.1.11B neu erhoben (je ein Minimalprogramm pro Zeile und Ziel übersetzt): #1740, #1751, #1765, #1782, #1786, #1789, #1799, #1801, #1803 behoben, #1388 hinfällig.
Vorherige Aktualisierung: 2026-08-19 — Windows-Abschnitt um DLL-Aufrufe, Umgebungsvariablen und die Relokationsgrenze ergänzt (#1671, #1672, #1674, #1676, #1677), unter Wine mit lyxc 1.1.3I nachgemessen.
Vorherige Aktualisierung: 2026-08-15 — Gleitkomma-Matrix gegen 1.0.20A gemessen (#1339)
