ABI Guide
0x0 ABI version 0.1 is the first native runtime boundary for direct ELF
programs, compiler-emitted relocatable objects, compatibility object output,
linked executables, and the current lib0x0 archive object slice. It is
intentionally small and Linux x86-64 only. A release may claim ABI 0.1 only
when make abi-check passes for the released compiler artifacts.
Target
- Target triple:
x86_64-unknown-linux-0x0. - System: Linux.
- Architecture: x86-64.
- Object format: ELF64.
- Endianness: little-endian.
- Executable format: current direct whole-ELF output uses one
PT_LOAD
segment; linked executable output uses separate PT_LOAD segments for RX
text/read-only data and RW data/zero-filled .bss.
- Object path: compiler-emitted ELF64
ET_RELobjects for the production
object slice; compatibility GAS assembly remains comparison coverage.
Version Marker
Generated direct ELF binaries append a NUL-terminated marker:
0x0 ABI 0.1
Compatibility object assembly emits:
.section .note.0x0.abi,"a",@note
.asciz "0x0 ABI 0.1"
Compiler-emitted ELF64 ET_REL objects carry the same marker in
.note.0x0.abi.
The marker is the enforced ABI identity for version 0.1. Direct ELF binaries,
released compiler binaries, compiler-emitted objects, compatibility object
assembly, and linked executable outputs must contain it. Later ABI versions may
add structured metadata, but they must not weaken marker enforcement.
The independent compiler's direct ELF packager uses the same marker for every
supported runtime executable slice. Its backend gate inspects the marker with
zero-elf-info before running each emitted runtime executable.
The 0x0 linker rejects input objects with a different marker and appends the
accepted marker to linked executable output, so linked artifacts remain
auditable without re-reading their input objects.
Value Model
Runtime values are represented as a payload plus a tag:
payload: rax
tag: r15
Current tags:
0 = Unit / nil
1 = I64 or Bool payload
2 = NUL-terminated UTF-8 Text pointer
3 = list/cons pointer
Unit uses payload 0 and tag 0.
I64 uses tag 1 and the signed 64-bit payload in rax or the corresponding
argument/stack payload slot.
Bool uses tag 1 with payload 0 for false and 1 for true.
Byte values are represented as I64 in ABI 0.1. Dedicated byte-array values
belong to the lib0x0/runtime milestone.
Source-level Option, Result, and Error values are list-backed library
conventions in ABI 0.1. They do not yet have separate runtime object tags.
Calls
Function return values use rax and r15.
The direct ELF backend passes up to six arguments as payload/tag register pairs.
The current source compiler rejects direct ELF calls or function definitions
with more than six arguments. This rejection is part of ABI 0.1; stack-passed
arguments are reserved for a future ABI version.
Payload registers:
arg0 rdi
arg1 rsi
arg2 rdx
arg3 rcx
arg4 r8
arg5 r9
Tag registers:
arg0 r10
arg1 r11
arg2 r12
arg3 r13
arg4 r14
arg5 r15
Function prologues save argument payloads and tags to stack slots. Stack slots
store payload and tag separately.
Caller-owned registers are all argument registers plus rax, r10, r11, and
r15 after a call. Callee code may use them while producing the return
payload/tag pair. Callee prologues preserve only the stack frame state needed by
the generated function body.
The generated code keeps the stack 16-byte aligned at process entry and adjusts
stack space in 16-byte payload/tag pairs for locals and saved arguments.
Text
Text values are pointers to NUL-terminated UTF-8 bytes. Current runtime text
operations scan to NUL for length and equality. Embedded NUL text is not part of
ABI 0.1.
Text-producing runtime helpers allocate writable buffers from the direct ELF
runtime arena. There is no garbage collector in ABI 0.1; allocated runtime
values are process lifetime allocations.
If the direct ELF arena cannot grow with mmap, the generated process exits
with status 1 before exposing a partial runtime value. ABI 0.1 does not
provide recoverable allocation errors at the source level.
Text ownership is immutable by convention once a value is returned. Runtime
helpers may allocate new text but must not mutate caller-provided text.
Lists
Lists use tag 3. Empty list is payload 0 with tag 3.
Cons nodes are 24-byte records:
offset 0 = car payload
offset 8 = car tag
offset 16 = cdr payload
Cons nodes are allocated from the direct ELF runtime arena.
List ownership follows the same process-lifetime rule as text. Cons nodes are
immutable after construction.
Symbols And Modules
Source imports are resolved before backend emission. Unaliased imports append
the imported functions to the compile unit. Aliased imports expose exported
functions under alias.name.
ABI 0.1 has no dynamic module initialization hook. Module forms identify the
source module for emitted OISA and diagnostics; direct ELF output lays out
functions in deterministic source/import order and enters the wrapper for
main.
Legacy object assembly names functions with zfn_ plus a sanitized source
symbol. Direct ELF whole-program output uses absolute intra-image addresses
rather than exported symbol names.
Process Boundary
Program startup preserves process metadata for argv and env. Current direct
ELF startup records the initial stack frame and runtime helpers read arguments
and environment variables from that state.
Program result printing is part of the executable wrapper, not the user
function ABI. User functions return value/tag pairs; the wrapper prints integer
or text results for current examples.
Compiler artifacts have a different executable wrapper: they read source and
output paths from process arguments, compile through the ABI 0.1 value model,
write the output file, and exit with status 0 on success.
Errors
Runtime panic exits the process with status 1. Normal generated compiler
artifacts exit with status 0 after writing output.
Structured error values in lib/core/error.0x0 are source-level conventions in
ABI 0.1; they are not yet separate runtime object layouts.
Syscall Boundary
Direct ELF output uses Linux syscalls directly for process exit, file open,
file read/write, stdin, stdout, mmap-backed allocation, and stack-limit growth.
The syscall boundary is not exposed as a stable source-level FFI in ABI 0.1;
it is a runtime implementation detail guarded by ABI tests.
Compatibility
ABI 0.1 may be consumed only by artifacts that declare the same ABI marker.
The object/linker path rejects incompatible ABI markers instead of attempting
best-effort linking.
The compatibility rule for this release is exact match: 0.1 links or imports
only with 0.1. Older or newer markers must produce a clear incompatibility
diagnostic in the current object linker.
The current object/linker ABI mismatch diagnostic is:
abi mismatch: <object> has <actual-marker>, expected 0x0 ABI 0.1
make abi-object-mismatch-check mutates a deterministic compiler-compatible
ELF64 relocatable object to ABI 9.9 and requires zero-link to reject it with
that diagnostic. The gate does not invoke the compiler; it is a lightweight
diagnostic compatibility check.
make abi-source-check is the lightweight source contract gate. It does not
compile; it verifies that abi/VERSION, this document, compiler ABI emitters,
object/assembly ABI note spellings, linker expectations, and ABI mismatch
diagnostics all agree on the same marker.
The machine-checked compatibility matrix lives at compat/abi-matrix.tsv.
make compatibility-matrix-check verifies that the matrix records ABI 0.1 as
the supported direct/object ABI and records non-0.1 object-link input as
rejected by the mismatch gate.