====== Lyx – ABI & Calling Conventions ====== Die ABI (Application Binary Interface) legt fest, wie Funktionen auf Binärebene kommunizieren: welche Register Argumente tragen, wie der Stack aufgebaut ist, wer Register sichert und wie der Rückgabewert transportiert wird. Lyx folgt den nativen Konventionen der jeweiligen Zielplattform, um Kompatibilität mit Betriebssystem-Syscalls und C-Bibliotheken ohne Wrapper zu gewährleisten. Dieses Wissen ist notwendig für: * **FFI-Grenzen** – C-Funktionen korrekt aufrufen und exportieren * **Inline-Assembly** – Register-Belegung vor und nach ''unsafe''-Blöcken * **Debugging** – Stack-Traces und Disassemblies interpretieren * **Safety-Nachweis** – ''--call-graph''-Reports bei DO-178C-Zertifizierung ===== 1. Datentyp-Größen & Alignment ===== Alle Typen folgen ihrer natürlichen Ausrichtung (Alignment = Größe, maximal 8 Byte). Das gilt auf allen drei unterstützten Architekturen. ^ Typ ^ Größe (Byte) ^ Alignment ^ Registerklasse ^ | ''bool'' | 1 | 1 | Integer (GPR), Wert 0 oder 1 | | ''int8'' / ''uint8'' | 1 | 1 | Integer (GPR) | | ''int16'' / ''uint16'' | 2 | 2 | Integer (GPR) | | ''int32'' / ''uint32'' | 4 | 4 | Integer (GPR) | | ''int64'' / ''uint64'' | 8 | 8 | Integer (GPR) | | ''isize'' / ''usize'' | 8 | 8 | Integer (GPR) | | ''f32'' | 4 | 4 | Float (FPU/SIMD) | | ''f64'' | 8 | 8 | Float (FPU/SIMD) | | ''qbool'' | 8 | 8 | **Float (FPU)** – wie f64 | | ''pchar'' / ''%%^T%%'' | 8 | 8 | Integer (GPR) – Pointer = int64 | | ''char'' | 1 | 1 | Integer (GPR) | > **qbool in der ABI:** > Obwohl ''qbool'' logisch wie ein ''bool'' wirkt, wird er als ''f64'' behandelt. Wahrscheinlichkeitswerte fließen direkt durch die Floating-Point-Einheiten – ohne GPR-zu-FPU-Konvertierung. ==== Struct-Alignment ==== Structs werden so ausgerichtet, dass jedes Feld sein natürliches Alignment erfüllt. Der Compiler fügt Padding-Bytes ein: type Example = struct { a: uint8; // Offset 0, 1 Byte // 7 Byte Padding b: int64; // Offset 8, 8 Byte c: uint32; // Offset 16, 4 Byte // 4 Byte Padding }; // Gesamt: 24 Byte Mit ''@packed'' entfällt das Padding (wichtig für Hardware-Register-Maps und Protokoll-Frames): @packed type PackedExample = struct { a: uint8; // Offset 0 b: int64; // Offset 1 c: uint32; // Offset 9 }; // Gesamt: 13 Byte – kein Padding → Details: [[lyx_-_programmiersprache:sprache:ffi|FFI – @packed und Struct-Layout]] ===== 2. x86_64 – System V AMD64 (Linux / macOS) ===== Die Standard-Konvention auf Linux und macOS. Lyx verwendet sie für alle Nicht-Windows-Targets. ==== Integer-Register ==== Zuordnung der Integer-Register nach x86-64 System V ABI: ^ Zweck ^ Register ^ Gesichert von ^ | Argument 1 | RDI | Aufrufer (Caller-saved) | | Argument 2 | RSI | Aufrufer | | Argument 3 | RDX | Aufrufer | | Argument 4 | RCX | Aufrufer | | Argument 5 | R8 | Aufrufer | | Argument 6 | R9 | Aufrufer | | Argument 7+ | Stack | Aufrufer | | Rückgabewert (primär) | RAX | Aufrufer | | Rückgabewert (sekundär) | RDX | Aufrufer | | Scratch (temporär) | R10, R11 | Aufrufer | | Callee-saved | RBX, RBP, R12–R15 | Aufgerufener | ==== Float-Register (XMM / YMM) ==== Zuordnung der Floating-Point-Register: ^ Zweck ^ Register ^ | Float-Argumente 1–8 | XMM0 – XMM7 | | Float-Rückgabewert | XMM0 | | Caller-saved | XMM0 – XMM15 | ==== Prolog / Epilog (x86_64) ==== Standard-Funktionsrahmen bei x86_64: ; Funktionsprolog (Lyx-generiert) push rbp ; Sichert alten Frame-Pointer (Callee-saved) mov rbp, rsp ; Neuer Frame-Pointer sub rsp, N ; Platz für lokale Variablen (N = nächstes Vielfaches von 16) ; … Funktionskörper … ; Funktionsepilog mov rsp, rbp ; Stack-Pointer wiederherstellen pop rbp ; Frame-Pointer wiederherstellen ret ; Springt zur Adresse auf dem Stack (von 'call' abgelegt) ==== Konkretes Beispiel ==== fn Add(a: int64, b: int64): int64 { return a + b; } Generierter Assembler: Add: push rbp mov rbp, rsp mov rax, rdi ; a kommt in RDI → ins Ergebnis-Register RAX add rax, rsi ; b kommt in RSI → addieren pop rbp ret ==== Rekursion auf x86_64 ==== Bei rekursiven Aufrufen muss nichts extra gesichert werden – ''call'' legt die Rücksprungadresse automatisch auf den Stack. RBP wird im Prolog gesichert. Factorial: push rbp mov rbp, rsp cmp rdi, 1 jle .base push rdi ; RDI sichern (Caller-saved, wird durch rekursiven call überschrieben) dec rdi call Factorial ; RDI = n-1 pop rdi ; RDI (= n) wiederherstellen imul rax, rdi ; Ergebnis = n * Factorial(n-1) jmp .done .base: mov rax, 1 .done: pop rbp ret ===== 3. x86_64 – Microsoft x64 (Windows) ===== Windows verwendet eine eigene Calling Convention. Der Hauptunterschied: nur vier Argument-Register, Shadow Space auf dem Stack. ==== Integer-Register ==== Zuordnung der Integer-Register nach x86-64 System V ABI: ^ Zweck ^ Register ^ Gesichert von ^ | Argument 1 | RCX | Aufrufer | | Argument 2 | RDX | Aufrufer | | Argument 3 | R8 | Aufrufer | | Argument 4 | R9 | Aufrufer | | Argument 5+ | Stack | Aufrufer | | Rückgabewert | RAX | Aufrufer | | Callee-saved | RBX, RBP, RDI, RSI, R12–R15 | Aufgerufener | ==== Float-Register ==== Zuordnung der Floating-Point-Register: ^ Zweck ^ Register ^ | Float-Argumente 1–4 | XMM0 – XMM3 | | Float-Rückgabewert | XMM0 | | Caller-saved | XMM0 – XMM5 | | Callee-saved | XMM6 – XMM15 | > **Wichtiger Unterschied zu System V:** > Auf Windows sind XMM6–XMM15 **callee-saved**. Wenn Lyx-Code diese Register nutzt (z. B. für f64-Operationen), muss der generierte Prolog sie sichern. Der Lyx-Compiler erledigt das automatisch beim Windows-Target. ==== Shadow Space ==== Vor jedem Funktionsaufruf reserviert der Aufrufer **32 Byte Shadow Space** auf dem Stack – direkt unterhalb der Rücksprungadresse. Windows-API-Funktionen nutzen diesen Bereich intern zum Sichern von RCX, RDX, R8, R9. ; Windows-Aufruf: Funktion mit 2 Argumenten sub rsp, 40 ; 32 Byte Shadow Space + 8 Byte Alignment mov rcx, arg1 ; Argument 1 mov rdx, arg2 ; Argument 2 call SomeCFunction add rsp, 40 ; Shadow Space freigeben Lyx generiert den Shadow Space automatisch bei allen ''@extern''-Aufrufen unter Windows-Target. ==== @stdcall ==== ''@stdcall'' ist die Aufrufkonvention älterer Win32-APIs (vor Vista weit verbreitet). Der wesentliche Unterschied: **Der Aufgerufene räumt den Stack auf** (nicht der Aufrufer). @extern @stdcall fn MessageBoxA(hwnd: int64, text: pchar, caption: pchar, flags: uint32): int32; ===== 4. ARM64 / AArch64 (AAPCS64) ===== ARM64 verwendet den AAPCS64-Standard (Procedure Call Standard for the Arm 64-bit Architecture). Dieser gilt auf Linux-ARM, Apple Silicon (macOS/iOS) und Embedded-ARM-Targets. ==== Integer-Register (X0–X30) ==== Zuordnung der Integer-Register nach x86-64 System V ABI: ^ Register ^ Alias ^ Zweck ^ Gesichert von ^ | X0–X7 | – | Argumente 1–8 / Rückgabewert 1–2 | Aufrufer | | X8 | XR | Indirekter Ergebnis-Zeiger (große Structs) | Aufrufer | | X9–X15 | – | Temporäre Register | Aufrufer | | X16–X17 | IP0, IP1 | Intra-Procedure-Call Scratch | Aufrufer | | X18 | PR | Platform-Register (OS-intern, nicht für Lyx) | – | | X19–X28 | – | Callee-saved Register | Aufgerufener | | X29 | FP | Frame-Pointer | Aufgerufener | | X30 | LR | Link-Register (Rücksprungadresse) | Aufgerufener | | SP | – | Stack-Pointer (16-Byte-Aligned) | – | ==== Float-/SIMD-Register (V0–V31) ==== ^ Register ^ Zweck ^ Gesichert von ^ | V0–V7 | Float-Argumente / Float-Rückgabewert | Aufrufer | | V8–V15 | Callee-saved | Aufgerufener | | V16–V31 | Temporäre SIMD-Register | Aufrufer | ==== Prolog / Epilog (ARM64) ==== ; ARM64 Funktionsprolog stp x29, x30, [sp, #-16]! ; Frame-Pointer (x29) und Link-Register (x30) sichern ; '!' = Pre-Decrement: SP wird zuerst um 16 verringert mov x29, sp ; Neuen Frame-Pointer setzen ; … Funktionskörper … ; ARM64 Funktionsepilog ldp x29, x30, [sp], #16 ; FP und LR wiederherstellen; SP += 16 ret ; Springt zu Adresse in X30 > **Häufigster ARM64-Bug bei Rekursion:** > Das Link-Register X30 wird beim ''bl''-Befehl (Branch with Link) mit der Rücksprungadresse des **aktuellen** Aufrufs überschrieben. Ohne ''stp x29, x30'' am Anfang verliert die Funktion ihre eigene Rücksprungadresse, sobald sie eine andere Funktion aufruft. Das führt zu einem Absturz beim ''ret''. ==== Konkretes Beispiel: Drei-Argument-Funktion ==== fn Clamp(val: int64, lo: int64, hi: int64): int64 { if (val < lo) { return lo; } if (val > hi) { return hi; } return val; } Clamp: ; val → X0, lo → X1, hi → X2 stp x29, x30, [sp, #-16]! mov x29, sp cmp x0, x1 bge .check_hi mov x0, x1 ; return lo b .done .check_hi: cmp x0, x2 ble .done mov x0, x2 ; return hi .done: ldp x29, x30, [sp], #16 ret ==== Ergebnis-Register bei großen Structs ==== Structs bis 16 Byte werden in X0–X1 zurückgegeben (zwei 8-Byte-Hälften). Größere Structs: Der Aufrufer reserviert Platz und übergibt den Zeiger in X8 (Indirect Result Register). Der Aufgerufene schreibt das Ergebnis dorthin. type BigResult = struct { a: int64; b: int64; c: int64; }; // 24 Byte fn GetResult(): BigResult { return BigResult { a: 1, b: 2, c: 3 }; // Compiler: Aufrufer reserviert 24 Byte, Adresse in X8 // Aufgerufener schreibt nach [X8] } ===== 5. RISC-V64 (RV64GC) ===== Für RISC-V nutzt Lyx die offizielle RISC-V calling convention (psABI, Integer + FP extension). ==== Register-Übersicht ==== ^ ABI-Name ^ Reg.-Nr. ^ Zweck ^ Gesichert von ^ | ''zero'' | x0 | Konstante 0 (hardwired) | – | | ''ra'' | x1 | Return Address (Rücksprungadresse) | Aufgerufener | | ''sp'' | x2 | Stack Pointer | Aufgerufener | | ''gp'' | x3 | Global Pointer (GOT-Zugriff) | – | | ''tp'' | x4 | Thread Pointer (TLS) | – | | ''t0–t2'' | x5–x7 | Temporäre Register | Aufrufer | | ''s0 / fp'' | x8 | Saved Register / Frame Pointer | Aufgerufener | | ''s1'' | x9 | Saved Register | Aufgerufener | | ''a0–a7'' | x10–x17 | Argumente / Rückgabewerte (a0–a1) | Aufrufer | | ''s2–s11'' | x18–x27 | Saved Register | Aufgerufener | | ''t3–t6'' | x28–x31 | Temporäre Register | Aufrufer | ==== Float-Register (F-Extension) ==== ^ ABI-Name ^ Zweck ^ Gesichert von ^ | ''fa0–fa7'' | Float-Argumente / Rückgabe | Aufrufer | | ''fs0–fs11'' | Saved Float-Register | Aufgerufener | | ''ft0–ft11'' | Temporäre Float-Register | Aufrufer | ==== Prolog / Epilog (RISC-V64) ==== ; RISC-V64 Funktionsprolog addi sp, sp, -16 ; Stack-Pointer verringern sd ra, 8(sp) ; Rücksprungadresse sichern sd s0, 0(sp) ; Frame-Pointer sichern addi s0, sp, 16 ; Neuen Frame-Pointer setzen ; … Funktionskörper … ; RISC-V64 Funktionsepilog ld ra, 8(sp) ; Rücksprungadresse wiederherstellen ld s0, 0(sp) ; Frame-Pointer wiederherstellen addi sp, sp, 16 ; Stack freigeben ret ; = jalr x0, ra, 0 Ähnlich wie ARM64: ''ra'' muss im Prolog gesichert werden, bevor eine andere Funktion aufgerufen wird. ===== 6. Stack-Layout & Alignment ===== Alle drei Architekturen verlangen ein **16-Byte-Alignment** des Stack-Pointers zum Zeitpunkt eines Funktionsaufrufs. Der Lyx-Compiler stellt dies im Prolog sicher. ==== Stack-Frame-Struktur (x86_64, System V) ==== Höhere Adressen (Richtung Basis) ┌──────────────────────────────────┐ │ Argument 8+ (vom Aufrufer) │ ← RSP vor dem 'call' ├──────────────────────────────────┤ │ Rücksprungadresse (von 'call') │ ├──────────────────────────────────┤ ← RBP zeigt hier │ Gesicherter alter RBP │ ├──────────────────────────────────┤ │ Lokale Variable 1 │ │ Lokale Variable 2 │ │ … │ ├──────────────────────────────────┤ │ Padding (für 16-Byte-Alignment) │ └──────────────────────────────────┘ ← RSP (aktuelle Position) Niedrigere Adressen (Stack wächst ↓) ==== @integrity – Canary-Bytes ==== In Modulen mit ''@integrity'' oder ''@redundant'' fügt der Lyx-Compiler zusätzliche **Canary-Bytes** zwischen Stack-Frames ein. Diese werden beim Epilog geprüft; eine Änderung signalisiert einen Pufferüberlauf oder Bit-Flip. @integrity(mode: scrubbed) @dal(B) fn ProcessFrame(data: ^uint8) { var buf: [128]uint8; // Compiler: legt Canary-Wert oberhalb von 'buf', prüft ihn im Epilog } ===== 7. Rückgabewerte ===== Konventionen für Rückgabewerte nach ABI: ==== Primitive Typen ==== ^ Typ ^ x86_64 ^ ARM64 ^ RISC-V64 ^ | ''int8''…''int64'', Pointer | RAX | X0 | a0 | | ''f32'', ''f64'', ''qbool'' | XMM0 | V0 | fa0 | | Zweiter Rückgabewert | RDX | X1 | a1 | ==== Tupel-Rückgabe ==== Lyx unterstützt Tupel-Rückgaben: ''(int64, bool)''. Beide Werte werden in den zwei primären Rückgabe-Registern transportiert: fn Divide(a: int64, b: int64): (int64, bool) { if (b = 0) { return (0, false); } return (a / b, true); } ; x86_64: RAX = Quotient, RDX = ok (0 oder 1) ; ARM64: X0 = Quotient, X1 = ok ; RISC-V: a0 = Quotient, a1 = ok ==== Große Structs (> 16 Byte) ==== Structs über 16 Byte werden nicht in Registern zurückgegeben. Der Aufrufer alloziert den Speicher und übergibt einen versteckten Zeiger: ^ Architektur ^ Zeiger-Register ^ | x86_64 (System V) | RDI (erstes Argument – verschiebt alle anderen um +1) | | ARM64 | X8 (Indirect Result Register – kein Argument verschoben) | | RISC-V64 | a0 (erstes Argument – verschiebt alle anderen um +1) | ===== 8. Variadic Functions (@variadic) ===== Variadische Funktionen (C-''printf''-Stil) erfordern in System V, dass ''AL'' (lower byte of RAX) die Anzahl der genutzten XMM-Register enthält, bevor die Funktion aufgerufen wird. @extern @variadic fn printf(fmt: pchar): int32; ; x86_64 System V: printf("Wert: %d\n", 42) mov rdi, fmt_str ; Format-String mov rsi, 42 ; Erstes variadisches Argument xor eax, eax ; AL = 0 (keine XMM-Register genutzt) call printf Auf Windows (Microsoft x64) entfällt die AL-Konvention; variadische Argumente folgen den normalen Argument-Registern (RCX, RDX, R8, R9, dann Stack). ===== 9. Systemaufruf-Konvention (Syscalls) ===== Syscalls nutzen eine eigene Konvention und umgehen die normale Calling Convention. ''std.os'' und ''std.fs'' kapseln diese Aufrufe. ^ Aspekt ^ x86_64 (Linux) ^ ARM64 (Linux) ^ RISC-V64 (Linux) ^ | Syscall-Nummer | RAX | X8 | a7 | | Argument 1 | RDI | X0 | a0 | | Argument 2 | RSI | X1 | a1 | | Argument 3 | RDX | X2 | a2 | | Argument 4 | R10 | X3 | a3 | | Argument 5 | R8 | X4 | a4 | | Argument 6 | R9 | X5 | a5 | | Rückgabewert | RAX | X0 | a0 | | Instruktion | ''syscall'' | ''svc #0'' | ''ecall'' | // std.os kapselt Syscalls – direkter Einsatz nur in unsafe + FFI: unsafe { // write(1, msg, len) → Syscall Nr. 1 auf x86_64 var result: int64; // ... inline asm oder std.os.Write() verwenden } ===== 10. Plattform-Vergleich: Wichtigste Unterschiede ===== ^ Merkmal ^ System V (Linux/macOS) ^ Microsoft x64 ^ ARM64 (AAPCS64) ^ RISC-V64 ^ | Argument-Register (Int) | 6 (RDI,RSI,RDX,RCX,R8,R9) | 4 (RCX,RDX,R8,R9) | 8 (X0–X7) | 8 (a0–a7) | | Argument-Register (Float) | 8 (XMM0–XMM7) | 4 (XMM0–XMM3) | 8 (V0–V7) | 8 (fa0–fa7) | | Rückgabe-Register | RAX (+RDX) | RAX | X0 (+X1) | a0 (+a1) | | Float-Rückgabe | XMM0 | XMM0 | V0 | fa0 | | Shadow Space | Nein | **32 Byte** | Nein | Nein | | Callee-saved XMM | Keine | XMM6–XMM15 | V8–V15 | fs0–fs11 | | Rücksprung-Adresse | Stack (''call'') | Stack (''call'') | X30 (LR) | x1 (ra) | | Stack-Alignment | 16 Byte | 16 Byte | 16 Byte | 16 Byte | | Struct > 16 Byte | RDI (arg 0) | Stack | X8 (IR) | a0 (arg 0) | ===== 11. Auswirkung auf Lyx-Code ===== Praktische Auswirkungen der ABI-Regeln auf Lyx-Programmcode: ==== Funktionen mit vielen Parametern ==== Mehr als 6 (System V) / 4 (Windows) / 8 (ARM64/RISC-V) Integer-Argumente gehen auf den Stack – mit Aufruf-Overhead. Die Lyx-Empfehlung: Struct-Pointer übergeben. // Schlecht: viele Parameter → Stack-Overflow-Risiko bei @flight_crit fn ProcessData(a: int64, b: int64, c: int64, d: int64, e: int64, f: int64, g: int64) { ... } // Besser: Struct-Pointer – ein Register, kein Stack-Argument type DataParams = struct { a: int64; b: int64; c: int64; d: int64; e: int64; f: int64; g: int64; }; fn ProcessData(p: ^DataParams) { ... } ==== float vs. int in Argumentposition ==== Werden Float- und Integer-Argumente gemischt, belegen sie **separate** Registerbänke und blockieren sich nicht gegenseitig: fn Mix(i: int64, f: f64, j: int64): f64 { // x86_64 System V: i→RDI, f→XMM0, j→RSI // ARM64: i→X0, f→V0, j→X1 return f * (i as f64) + (j as f64); } ==== @export und C-Kompatibilität ==== Mit ''@export'' erzeugt der Lyx-Compiler ein C-kompatibles Symbol ohne Name-Mangling. Die Konvention folgt der Plattform-ABI: @export pub fn lyx_add(a: int64, b: int64): int64 { return a + b; } // Verwendung aus C: extern long lyx_add(long a, long b); long result = lyx_add(3, 4); // 7 ===== 12. ABI in Safety-Code ===== Bei DO-178C-Zertifizierung muss die ABI-Konformität nachgewiesen werden. Der ''--call-graph''-Pass erzeugt einen Bericht über alle Funktionsaufrufe und deren Register-Belegung: lyxc --call-graph --provenance --target arm64 main.lyx Call-Graph-Auszug: ProcessFlightData(val: f64) Plattform: ARM64 (AAPCS64) Argumente: val → V0 (f64, Float-Register) Rückgabe: X0 (int64) Callee-saved genutzt: X19, X20 Sichere Register: X19, X20 korrekt gesichert (stp/ldp im Prolog/Epilog) unsafe-Blöcke: keine @redundant-Variablen: heading (TMR-gesichert) ✅ ABI-konform ===== 13. macOS x86_64 – Mach-O Backend-Besonderheiten ===== Der ''--target=macosx64''-Backend erzeugt **Mach-O**-Binaries statt ELF. Die Calling Convention ist identisch mit System V AMD64 (Abschnitt 2), aber es gibt plattformspezifische Unterschiede im generierten Maschinencode und in den Syscall-Nummern. ==== _start-Stub und Helper-Offsets ==== Der macOS-''_start''-Stub ist **79 Bytes** lang (Linux: 65 Bytes). Die drei eingebetteten Helper-Symbole liegen an exakt denselben Offset-Positionen wie auf Linux: ^ Symbol ^ Offset im Binary ^ | ''strlen'' | Byte 79 | | ''printstr'' | Byte 94 | | ''printint'' | Byte 120 | Der ''rel32''-Patch für den ''call main''-Sprung verwendet Bytes **63–66** (nicht 49–52 wie bei ''CG_ARGC'' — ein kritischer Bug in der ersten Implementierung, der falsche Sprungziele erzeugte). ==== Mach-O Data-Section ==== ''cg.data'' (Strings und VMT-Tabellen) wird **nach** dem Code-Segment in die Mach-O-Datei geschrieben. VMT-Zeiger, die für Linux-Basisadressen berechnet wurden, werden beim Schreiben auf die macOS-Ladeadresse umgerechnet (**VMT-Patching: Linux-Base → macOS-Base**). ==== Syscall-Nummern: macOS vs. Linux ==== macOS x86_64 verwendet andere Syscall-Nummern als Linux. Die folgende Tabelle zeigt die 9 Socket-Syscalls, die der Backend-Code anpasst: ^ Syscall ^ Linux x86_64 ^ macOS x86_64 ^ | ''socket'' | 41 | 97 | | ''connect'' | 42 | 98 | | ''bind'' | 49 | 104 | | ''listen'' | 50 | 106 | | ''accept'' | 43 | 30 | | ''sendto'' | 44 | 133 | | ''recvfrom'' | 45 | 29 | | ''setsockopt'' | 54 | 105 | | ''shutdown'' | 48 | 134 | ==== Plattformkonstanten ==== ^ Konstante ^ Linux ^ macOS ^ | ''MAP_ANON'' | ''0x20'' | ''0x1002'' | Der Unterschied betrifft alle ''mmap''-Aufrufe für anonyme Speicherzuordnungen (''alloc'', Heap, Stack-Extension). Ein falscher ''MAP_ANON''-Wert führt zu ''EINVAL'' beim Systemaufruf. ---- **Weiterführende Seiten:** * [[lyx_-_programmiersprache:sprache:ffi|FFI – @extern, @export, @packed, Callbacks]] * [[lyx_-_programmiersprache:sprache:pointer-inlining|Pointer & Inlining – unsafe, @volatile, @inline]] * [[lyx_-_programmiersprache:sprache:memory-management|Memory Management – Stack-Layout, @stack_limit]] * [[lyx_-_programmiersprache:sprache:rekursion|Rekursion – Prolog/Epilog bei rekursiven Aufrufen]] * [[lyx_-_programmiersprache:tools:lyx-compiler-selbst-kompilieren|Compiler selbst kompilieren – Cross-Compilation-Targets]] * [[lyx_-_programmiersprache:guides:do-178c|DO-178C – Call-Graph-Nachweis und ABI-Verifikation]]