Skip to content

x86_64 Target (Linux / QEMU) ​

The x86_64 target compiles Yog programs to Linux ELF64 executables that make raw syscalls.

  • On Linux x86_64: run the binary directly — no emulator, no VM.
  • On macOS (Apple Silicon or Intel): run via qemu-x86_64 (user-mode QEMU — translates Linux syscalls to the host OS without a full VM).
  • On Windows (WSL): run natively inside the WSL2 environment.

This is the fastest target for iterating on Yog programs during development: compile, run, see output — all in your terminal.

Entry Point ​

x86_64 programs define a single main() function, same as the ARM64 target:

ts
function main(): void {
    uart_init();              // no-op on x86_64
    uart_print("Hello!\n");  // → write(stdout, ...) syscall
}

uart_print maps to the Linux write(2) syscall (fd=1, stdout). uart_init is a no-op — no UART to initialise, stdout is already open.

Compile ​

sh
yogc --target x86_64 examples/x86_64-hello/main.yog app.elf

Output:

[yogc] OK  examples/x86_64-hello/main.yog → app.elf  (target: x86_64)
[yogc]     312 bytes

Run ​

Linux x86_64 (native) ​

sh
chmod +x app.elf
./app.elf

macOS or ARM (user-mode QEMU) ​

Install QEMU (if not already):

sh
brew install qemu

Run the Linux binary:

sh
qemu-x86_64 app.elf

qemu-x86_64 intercepts Linux syscalls and forwards them to the macOS kernel — no full VM, no boot time, near-native speed.

Technical Details ​

Output Format ​

PropertyValue
FormatELF64 (ET_EXEC)
ArchitectureEM_X86_64 (0x3E)
Load address0x400000
Code starts at0x400078 (file offset 120 = 64 ELF header + 56 PT_LOAD phdr)
SegmentSingle PT_LOAD (R|X) from file offset 0

Boot Sequence ​

The Linux kernel loads and maps the ELF file, then jumps to _start:

asm
_start:
    call  main          ; call user's main()
    mov   edi, 0        ; exit code = 0
    mov   eax, 60       ; SYS_exit
    syscall             ; exit(0)

Stack Frame Convention ​

The System V AMD64 ABI requires RSP to be 16-byte aligned before any CALL. Each compiled function follows this pattern:

asm
main:
    push rbp            ; re-aligns RSP to 16 bytes (CALL pushed return addr)
    mov  rbp, rsp
    ; ... body ...
    pop  rbp
    ret

uart_print Encoding ​

asm
; uart_print("Hello!\n") expands to:
lea  rsi, [rip + <str_offset>]  ; 7 bytes — RIP-relative address of string
mov  edx, 8                      ; 5 bytes — string length
mov  edi, 1                      ; 5 bytes — stdout fd
mov  eax, 1                      ; 5 bytes — SYS_write
syscall                          ; 2 bytes

String literals are stored after the code section in the same LOAD segment. LEA RSI, [RIP+disp32] is used to address them position-independently.

Instructions Used ​

InstructionEncodingPurpose
CALL rel32E8 [rel32] (5 bytes)Call function
PUSH RBP55 (1 byte)Save frame pointer
MOV RBP, RSP48 89 E5 (3 bytes)Set up frame
POP RBP5D (1 byte)Restore frame
RETC3 (1 byte)Return
MOV EAX, imm32B8 [imm32] (5 bytes)Syscall number
MOV EDI, imm32BF [imm32] (5 bytes)First argument
MOV EDX, imm32BA [imm32] (5 bytes)Third argument
LEA RSI, [RIP+disp32]48 8D 35 [disp32] (7 bytes)String address
SYSCALL0F 05 (2 bytes)Enter kernel

Roadmap ​

  • Phase 5+: macOS native target (Mach-O format, BSD syscalls)
  • Phase 5+: Windows native target (PE32+ format, Win32 API or NT syscalls)
  • Phase 8: LLVM IR backend — delegate to LLVM for full optimisation and all platforms

Yog — union of JS syntax and silicon.