Primary target for native 64-bit Linux applications.
x86_64-unknown-linux-gnu
Rust toolchain: x86_64-unknown-linux-gnu (native)
# Native build
cargo build --release
# Or using forgec
./target/release/forgec input.dev -o output --target x86_64-unknown-linux-gnu
| Property | Value |
|---|---|
| Architecture | x86-64 (AMD64) |
| Format | ELF64 |
| Endianness | Little |
| Pointer Size | 64-bit |
| Page Size | 4 KiB |
| Calling Convention | System V AMD64 ABI |
| Register | Purpose | Saved |
|---|---|---|
| RAX | Return value / syscall # | Volatile |
| RDI | Arg 1 | Volatile |
| RSI | Arg 2 | Volatile |
| RDX | Arg 3 | Volatile |
| RCX | Arg 4 | Volatile |
| R8 | Arg 5 | Volatile |
| R9 | Arg 6 | Volatile |
| RBX | Callee-saved | Preserved |
| RBP | Frame pointer | Preserved |
| R12-R15 | Callee-saved | Preserved |
| RSP | Stack pointer | Preserved |
| RIP | Instruction pointer | N/A |
System V AMD64 ABI:
High Address
├── Arguments (if > 6)
├── Return Address
├── Saved RBP
├── Local Variables
├── Red Zone (128 bytes)
└── RSP
Low Address
Linux syscalls via syscall instruction:
| Syscall | Number | Registers |
|---|---|---|
| read | 0 | RDI=fd, RSI=buf, RDX=count |
| write | 1 | RDI=fd, RSI=buf, RDX=count |
| exit | 60 | RDI=code |
| mmap | 9 | RDI=addr, RSI=len, RDX=prot, R10=flags, R8=fd, R9=off |
| munmap | 11 | RDI=addr, RSI=len |
| socket | 41 | RDI=domain, RSI=type, RDX=protocol |
| connect | 42 | RDI=fd, RSI=addr, RDX=addrlen |
A flat x86_64 binary — not an ELF — for freestanding / boot scenarios loaded
by a stage-1 boot sector + stage-2 loader that switches the CPU to long mode
(see examples/os64). Build it with a custom linker
descriptor rather than the --target preset:
./target/release/forgec kernel.dev -o kernel.raw \
--linker examples/os64/os64-kernel.fld
examples/os64/os64-kernel.fld
contains:
ARCH x86_64
FORMAT raw
HOSTED false
ENTRY _start
LOAD 0x100000
Memory model: the image is linked at LOAD (default 0x100000); the
stage-2 loader is responsible for placing the bytes there and jumping to
ENTRY _start. String literals and other absolute references are fixed up
against LOAD, so keep LOAD in sync with where your loader actually maps
the image.
The x86_16 stage-2 loader uses the _dev_enter_long_mode builtin to run the
whole 16→32→64 switch: A20, GDTs, a 32-bit trampoline that relocates the
kernel staging buffer (0x8000→0x100000), 4-level identity page tables,
PAE/EFER.LME, and the final jump into long mode — probing CPUID first and
printing No 64-bit CPU over COM1 on a 32-bit-only CPU. The freestanding
runtime is reference-driven: only the _dev_* helpers the image actually
references are emitted (_dev_outb, _dev_inb, _dev_outw, _dev_inw,
_dev_outl, _dev_inl, _dev_iret, _dev_sti, _dev_cli, _dev_halt),
exposed through std.hal.
ELF64 with sections:
.text - Executable code.rodata - Read-only data (strings, constants).data - Initialized writable data.bss - Zero-initialized data (heap, GC).symtab / .strtab - Symbols (stripped in release)Hosted mode: _start (runtime) → calls main(argc, argv)
argc at [rsp]argv at [rsp+8]Freestanding: Custom entry via @freestanding + --freestanding flag
0x0000000000000000 - 0x00007FFFFFFFFFFF User space (canonical)
0x00007FFFFFFFFFFF - 0xFFFF800000000000 Non-canonical (hole)
0xFFFF800000000000 - 0xFFFFFFFFFFFFFFFF Kernel space
Typical process layout:
0x0000000000400000 .text (executable)
0x0000000000600000 .data/.bss
0x00007FFFFFFFxxxx Stack (grows down)
0x00007FFFF7xxxxxx Shared libraries
Hosted runtime provides:
_start entry pointmain invocation with argc/argvFull support for all modules:
std.io - Linux syscalls (write/read)std.mem - Optimized REP MOVSB/STOSBstd.string - Pure softwarestd.math - Integer + float (x87/SSE2)std.alloc - First-fit free list + GC (4 MiB)std.fmt - Softwarestd.volatile - MFENCE/LFENCE/SFENCEstd.gc - Conservative mark-and-sweepFull x87/SSE2 support:
float32 / float64# GDB
gdb ./output
# LLDB
lldb ./output
# objdump
objdump -d output | less
# strace
strace ./output