The Loup Freestanding Book
For Loup 1.25. Freestanding support is experimental in this release; features the Language Reference specifies but the toolchain does not implement yet are marked ⚠ Planned.
This book teaches @mode(freestanding): writing kernels, boot loaders, firmware and other bare-metal programs in Loup. A freestanding image runs with no operating system, no libc and no Loup runtime underneath it — when it starts, the only code in memory is what you wrote and what the toolchain linked for you (memcpy and similar helpers). Loup's type system, ownership verbs, unique values and lifetimes all work there, together with the features bare metal needs: inline assembly, volatile MMIO, interrupt handlers, naked functions, layout control and linker sections.
Who this book is for
- You are writing an operating-system kernel, a boot loader, a hypervisor payload or microcontroller firmware.
- You know what a linker script, an interrupt vector and a memory-mapped register are.
- You have written some Loup, or are willing to pick up the syntax from The Loup Scripting Book or The Loup Native Book — the hosted books are the gentler place to learn the language and its ownership system.
The mode directive
Freestanding source files use the .lpfree extension and begin with:
@mode(freestanding)
const UART0_BASE: uint64 = 0x0900_0000u64
The directive must be the file's first token (only comments may precede it). There is no main: the image's entry point is a function marked @entry.
Installing the toolchain
Loup is installed from the official releases at github.com/code-craft-ai-company/loup-releases:
# Linux and macOS
curl -fsSL https://github.com/code-craft-ai-company/loup-releases/releases/latest/download/loup-install.sh | sh
# Windows (PowerShell)
irm https://github.com/code-craft-ai-company/loup-releases/releases/latest/download/loup-install.ps1 | iex
The installer puts the tools in ~/.loup/bin (or %LOCALAPPDATA%\Programs\Loup) and adds them to PATH; no administrator rights are needed. To pin a version, run the script with sh -s -- --version 1.25.1 or set LOUP_VERSION. After a browser download on macOS, clear the quarantine flag with xattr -dr com.apple.quarantine <dir>.
What each platform can do with freestanding code in 1.25:
- Type-checking (
loup checkin a project;loup-bootstrap checkon Linux x86-64) works on every platform. - Building images (
loup build --target freestanding --arch …) works on Linux x86-64 and needsld.lld. The default back end is Loup's own clang-freellbe;--backend llvmuses clang. - Running images needs QEMU (
qemu-system-x86_64,qemu-system-aarch64,qemu-system-riscv64,qemu-system-arm) or real hardware. x86_64, aarch64, riscv64, armv7 and Cortex-M images are boot-tested under QEMU.
Hello, bare metal
Three files print a line on the serial console of QEMU's AArch64 virt machine. src/main.lpfree:
@mode(freestanding)
# QEMU `virt` (AArch64): the PL011 UART's data register.
let UART0_DATA = Volatile[uint8].at(0x0900_0000)
func uartWrite(s: string): void {
for (c in s) {
UART0_DATA.write(c as uint8)
}
}
func kernelMain(): void {
uartWrite("Hello from Loup freestanding\n")
while (true) {
asm { "wfi" clobbers: [] }
}
}
@entry
@naked
@unsafe("naked: runs before any stack exists; sets sp from __stack_top")
@section(".text.boot")
func _start(): void {
asm {
"adrp x0, __stack_top"
"add x0, x0, :lo12:__stack_top"
"mov sp, x0"
"bl kernelMain"
"1: wfe"
"b 1b"
clobbers: ["memory"]
}
}
kernel.ld:
ENTRY(_start)
PHDRS {
text PT_LOAD FLAGS(7);
}
SECTIONS {
. = 0x40080000;
.text.boot : { KEEP(*(.text.boot)) } :text
.text : { *(.text .text.*) } :text
.rodata : { *(.rodata .rodata.*) } :text
.data : { *(.data .data.*) } :text
.bss : {
*(.bss .bss.*)
. = ALIGN(16);
. = . + 65536;
__stack_top = .;
} :text
}
hello.lppkg:
name = "hello"
version = "0.1.0"
[build]
entry = "src/main.lpfree"
target = "freestanding"
mode = "freestanding"
arch = "aarch64"
panic = "halt"
[linker]
script = "kernel.ld"
entry = "_start"
[freestanding]
stack_size = 65536
Build on Linux x86-64 and boot:
loup build
qemu-system-aarch64 -M virt -cpu cortex-a53 -m 256M -nographic -kernel ./hello
Hello from Loup freestanding
Chapters 1 and 2 explain every line; chapter 21 builds a larger kernel for x86_64.
The Loup books
| Mode | What it is for | Book |
|---|---|---|
host.scripted |
scripts and tools run by the interpreter | The Loup Scripting Book |
host.dual, host.native |
applications compiled to native binaries (or run by the interpreter) | The Loup Native Book |
web.scripted, web.compiled |
browser applications (the browser runtime is planned) | The Loup Web Book |
freestanding |
kernels, boot loaders, firmware — no runtime | this book |
What freestanding keeps, and what it leaves out
You keep the type system, generics and interfaces (resolved at compile time), match, Result-based error handling, the ownership verbs and unique values, borrows and lifetimes, and compile-time evaluation. You gain @entry, @naked, @callconv, asm { }, Volatile[T] and volatile borrow, @repr, @align, @section, buffer[T, N], onstack, @interrupt, @interruptSafe, InterruptGuard, @panic_handler, @allocator, PartialInit[T], and bare intrinsics for atomics and raw memory.
You leave out everything that needs a runtime: exceptions (throw, catch (T as e)), weak references, dynamic class, defer import, consume, async/await, bigint/decimal, growable collections, template fences, reflection, and the hosted standard library. Chapter 1 lists which of these the 1.25 checker rejects and which it does not yet catch.
Table of contents
Part I: The Bare-Metal Foundation
- What Is Freestanding Mode?
- Building a Bare-Metal Binary
- Integer Types and Wrapping Arithmetic
- Functions and Control Flow
Part II: Data and Types on the Metal
- Structs and Layout Control
- Enums and Pattern Matching
- Classes and Allocators
- Interfaces and Generics
Part III: Safety Without a Runtime
Part IV: Talking to Hardware
- Inline Assembly
- MMIO with Volatile
- Interrupts and Naked Functions
- Entry Points and Panic
- Sections and the Linker
Part V: Putting It Together
- Multicore and Atomics
- The Available Standard Library Subset
- Example — x86_64 Kernel Stub
- Testing and Deploying Firmware
- Memory Safety and Kernel Development Patterns — kernel problems worked through with Loup features: fixed-capacity structures, address newtypes, user-pointer validation, DMA hand-off with
uniqueownership, cleanup withfinally, generation-checked handles, interrupt-safe rings, and the@unsafeaudit. synchronizedClasses and Lock Ordering
Conventions
- Every chapter's first listing shows
@mode(freestanding). Later listings may hide it, along with helper declarations, so the visible code stays focused; every listing marked as compiling is type-checked with the 1.25 toolchain as a completesrc/main.lpfree. - A listing labelled as not compiling is followed by the compiler's actual diagnostic. Listings marked as compiling but behaving wrongly are explained in the text.
> ⚠ **Planned.**marks a feature the Language Reference specifies that Loup 1.25 does not implement yet.- Shell commands are shown in
shblocks, program and compiler output intextblocks, manifests intomlblocks. - Section references such as "§3-3.17b" point into the four-volume Loup Language Reference; freestanding mode is specified in volume 3 (compilation), §3-3, at loup-language-compilation-v1_20.md, and the core language in loup-language-core-v1_20.md. Where the toolchain and the reference disagree, the book shows what the toolchain does and says what the reference specifies.