kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

Kernel build and image pipeline

This roadmap tracks the work needed for kit to build freestanding kernels from C and assembly sources and emit kernel images that can be passed to QEMU's direct loaders. It deliberately does not cover VM execution, bootloader generation, UEFI application layout, or kit-provided startup code. Kernel startup, privilege-mode entry, page-table setup, stack setup, TLS setup, and boot-protocol compliance are the kernel author's responsibility.

Related: ../DRIVER.md, ../LINK.md, ../OBJ.md, ../RUNTIME.md, LINKER-COMPAT.md, PORT.md.

Scope

The supported path is:

C / asm sources + objects + archives
  -> kit build-obj / build-exe
  -> freestanding static kernel ELF
  -> kit image / objcopy
  -> ELF / flat binary / ROM-style payload / section-concatenated payload

The first targets are freestanding ELF:

The output artifacts should be usable with QEMU features such as -kernel, -bios, or -device loader,file=..., depending on the target machine and the kernel's own entry contract. Kit should not decide or implement the boot protocol.

Current baseline

Useful pieces already exist:

The desired user-facing build shape stays on the existing tools:

kit build-exe -target x86_64-none-elf \
  -ffreestanding -nostdlib -static -no-pie \
  -mcmodel=kernel -mno-red-zone \
  -ffunction-sections -fdata-sections \
  -T kernel.ld -e _start \
  -Wl,--gc-sections \
  --map kernel.map \
  --symbols kernel.sym \
  -o kernel.elf \
  boot.S kernel.c mm.c

Compile and build-driver support

Do not add a separate kit kernel compile command. Keep build-obj and build-exe as the compile/link front doors, and make their flag surface complete enough for kernel authors.

Required flag support and behavior:

Driver parity to improve:

Linker work

Linker-script subset

Kernel links need a larger structured GNU-ld-compatible subset. The goal is not to interpret arbitrary linker scripts blindly, but to support the constructs needed for deterministic kernel memory layouts with precise diagnostics.

Add support for:

Existing KEEP(...) support must continue to interact correctly with --gc-sections.

Link output side files

Add linker-produced side outputs, available from both ld and build-exe:

The link map should include:

Linker flags and policy

Add or normalize:

Freestanding kernel validation should reject, by default:

Image command

Add a new kit image command for kernel-image emission. Also add a minimal objcopy -O binary path for compatibility, backed by the same lower-level image emitter.

objcopy should remain an object transformer:

kit objcopy -O binary kernel.elf kernel.bin

kit image should own image-building policy:

kit image --format bin kernel.elf -o kernel.bin

kit image --format bin --from segments --segment PT_LOAD \
  --addr paddr --base 0x80000000 --fill 0x00 --align 4096 \
  --pad-to 2M --max-size 8M \
  --metadata kernel.image.json \
  kernel.elf -o kernel.bin

Image formats

Optional later embedded formats, such as Intel HEX, S-record, or UF2, are out of the first kernel-focused pass.

Flat kernel Image (arm64 / riscv64)

QEMU's -kernel path on arm64 and riscv consumes the flat Image format: a raw loadable binary prefixed with a fixed 64-byte header the kernel's loader reads to place and size the image. The first 32 bytes are common to both arches:

offset field notes
0 code0 (u32) first instruction (branch to entry; "MZ" low half if EFI)
4 code1 (u32) second instruction
8 text_offset u64 LE — load offset from a 2 MiB-aligned base
16 image_size u64 LE — effective image size including BSS
24 flags u64 LE — bit 0 endianness; arm64 bits 1-2 page size, bit 3 placement

The tails differ. arm64: three reserved u64s, magic = "ARM\x64" (0x644d5241) at offset 56, then a u32 PE-offset slot. riscv64: a version u32 (currently 0x2), reserved words, a deprecated "RISCV\0\0\0" magic at 48, and magic2 = "RSC\x05" (0x05435352) at 56.

There are two ways to support this, and they have different costs:

Because flags encodes boot semantics (endianness, page size, placement), header synthesis is an explicit opt-in, never a default. Those fields are surfaced as explicit options (e.g. --image-endian, --image-page-size, --image-text-offset) rather than invented; kit fills magic/version and image_size deterministically and does not choose a boot policy.

Image flags

Selection:

Addressing:

Holes and layout:

ELF/debug:

Validation:

Reporting:

Image semantics

Segment-based flat image emission should:

Section-based emission should:

ROM-style emission should:

Initramfs and archive packaging

The kernel-boot pipeline needs one packaging format kit does not yet emit: the SVR4 newc cpio archive the Linux kernel unpacks as its initramfs. An initramfs is a cpio -H newc archive (magic 070701, or 070702 for the CRC variant), optionally compressed, that the kernel's built-in extractor reads at boot; the early-microcode convention is just an uncompressed cpio concatenated ahead of the compressed main archive. This is an archive format, not a boot protocol — the direct analogue of the existing ar and tar paths — so it belongs in kit's byte-utility tool family, not the image emitter. kit packages and inspects the archive; it does not build, mount, or boot it, and does not invent its contents.

Add a kit cpio tool, reusing the src/dist/tar.c patterns and the public kit/compress.h codecs:

Gate the tool in driver/main.c (KIT_TOOL_CPIO_ENABLED) alongside the other archive utilities.

Implementation shape

Add a shared image-emission layer rather than burying policy in objcopy:

driver/cmd/image.c        CLI policy for kit image
driver/cmd/objcopy.c      simple -O binary compatibility path
include/kit/image.h       public image-emission API, if we want embedders to use it
src/api/image.c           public wrapper
src/obj/image.c           object/ELF-to-image implementation

The image API should consume already-read object bytes or an opened KitObjFile view, plus explicit options. It should not read the filesystem directly and should not run QEMU or inspect host bootloader installs.

Map/symbol side outputs should be linker-owned rather than image-owned. The image metadata file can reference link-map facts, but it should be a report about the image transform, not a replacement for --map.

Phasing

  1. Raw binary baseline

    • Add objcopy -O binary.
    • Add kit image --format bin --from segments.
    • Support --base, --fill, --fail-on-holes, --pad-to, --max-size.
    • Add focused ELF fixtures for x64, aa64, rv64, and rv32.
  2. Build/link parity for kernels

    • Add missing build-exe direct flag parity with ld.
    • Add -mno-red-zone, -mgeneral-regs-only, builtin policy, and stack protector policy.
    • Add strict freestanding undefined/dynamic-artifact diagnostics.
  3. Link map and symbols

    • Add --map FILE.
    • Add --symbols FILE.
    • Add deterministic tests for section layout, symbols, and discarded sections.
  4. Script growth

    • Add MEMORY, region placement, VMA/LMA split, and region overflow checks.
    • Add PHDRS once memory regions are stable.
    • Add ASSERT, PROVIDE, EXTERN, richer input patterns, and orphan handling.
  5. Image formats beyond bin

    • Add --format sections.
    • Add --format rom.
    • Add --metadata FILE.
    • Add --format elf normalization/strip/split-debug behavior if it proves cleaner than routing those cases through objcopy and strip.

Acceptance criteria

For each first-pass freestanding target, kit should be able to:

The validation suite should stay targeted:

Remaining work

Phases 1-5 are landed. Phases 1-4 (flag parity, undefined-symbol policy, --map/--symbols, the linker-script parse surface, kit image --format bin / objcopy -O binary) plus all of the items below shipped; an adversarial review of the integrated diff found and fixed a further set of correctness bugs (coalesced-PT_LOAD bss/perms, --defsym ordering, exact --section-start addressing, NOLOAD relocations, memory-usage accounting, --cref imports, image metadata accuracy, and cc/ld report parity). The checklist below is closed out.

Image formats beyond bin (Phase 5)

Linker-script layout (now honored at layout)

Linker flags and policy

Freestanding strict validation (Phase 2 gap)

Tests and fixtures

Newly scoped: initramfs and Image packaging

Added on top of the closed-out Phase 1-5 work; not yet started.

Initramfs / cpio (archive packaging, sibling to ar) — landed as kit cpio. The newc codec lives driver-local in driver/cmd/cpio.c (only the tool consumes it; the driver has no -Isrc, so it mirrors tar.c's stateless append/finish/ iter shape rather than living in the dist subsystem). Create needed two additive host shims: driver_readlink (read symlink targets) and driver_path_lstat (no-follow operand classification + the source executable bit). Bidirectionally interop-verified against host bsdcpio.

Flat kernel Image header (arm64 / riscv64) — landed via the --image-header modifier on --format bin/rom (not separate formats); it overlays the first 64 bytes of the first loadable segment rather than prepending, so the entry branch (code0/code1) is preserved and the output stays the same size as a plain bin.