This page documents the main Make targets and variables used to build Neutron. The Makefile is in the project root.

Default Target

Invoking make with no arguments runs the default target all, which depends on bootloader, kernel, and (when not embedding the kernel) sd-image. The bootloader reads per-build settings from build.cfg; see Build Configuration.

A full build typically produces:

  • bin/kernel8.img (bootloader binary; may embed the packed kernel when embed-kernel = true in build.cfg)
  • bin/CUSTOM.bin (packed test kernel; built by the kernel target)
  • bin/sd.img (64 MiB FAT32 image; only built when embed-kernel = false. The file name on the FAT32 root is set by kernel_filename in build.cfg.)

Phony Targets

all
Builds bootloader, kernel, and (when embed-kernel = false in build.cfg) SD image. When embed-kernel = true, the kernel is embedded into kernel8.img and the SD image is not built. Prints a short summary and mentions make qemu-rpi.

bootloader
Builds only the bootloader. Produces build/neutron.elf and bin/kernel8.img. The Makefile regenerates internal/config.h and build.mk from build.cfg when needed. Dependencies: boot assembly and C sources, linker script, and (indirectly) build.cfg. See Build Configuration.

kernel
Builds the test kernel and packs it. Produces build/kernel_raw.elf, build/kernel_raw.bin, and bin/CUSTOM.bin. The pack step is done by pack_kernel.py with default load/entry 0x200000. When K_BIN is passed on the command line (USE_PREBUILT_KERNEL=1), this target instead validates the prebuilt file exists (and, when PACK=1 is also set, packs the raw binary into K_BIN_BUILT first). In that case test_kernel sources are not compiled.

sd-image
Creates the SD image from a packed kernel image. K_BIN_SRC determines the source kernel: by default it is the bundled test kernel (bin/CUSTOM.bin); if K_BIN is passed on the command line it uses that prebuilt packed image; if K_BIN is passed together with PACK=1 it first packs the raw binary and then uses the result. Requires parted, mtools, and a working mformat and mcopy. Produces bin/sd.img (64 MiB, MBR, one FAT32 partition from 1 MiB to end, volume label NEUTRON). The kernel file name on the FAT32 root is taken from kernel_filename in build.cfg (e.g. CUSTOM.BIN). When embed-kernel = true in build.cfg, the default all target does not build the SD image. See Build Configuration.

debug
Generates comprehensive debug artifacts for both bootloader and kernel, compiled with -O0 -g -DDEBUG flags for maximum debuggability. Creates a debug/ directory tree with:

  • Disassembly: Full bootloader and kernel disassemblies with source line information (debug/disasm/)
  • Symbol tables: Complete symbol tables with line and size information (debug/symbols/)
  • ELF information: Full ELF headers, debug info dumps, and metadata (debug/elf/)
  • Linker maps: Memory layout and section mapping (debug/linkermap/)
  • Per-module disassembly: Individual driver object file disassemblies (debug/drivers/)
  • Size analysis: Section-by-section size breakdowns (debug/bootloader/size, debug/kernel/size)
  • Binary info: Entry points and binary format metadata
  • DEBUG_MANIFEST.txt: Build timestamp, flags, and project configuration snapshot

Useful for stepping through code in a debugger, analyzing memory layout, or investigating compiler optimizations. Use make clean-debug to remove debug artifacts without cleaning build outputs.

clean
Removes all files under build/, bin/ and debug/. Use this before a fresh build or to free space.

clean-debug
Removes debug artifacts generated by make debug. Clears the entire debug/ directory. Does not remove the debug/ directory.

qemu-rpi
Depends on all. Invokes QEMU with following flags (if embed_kernel=false):

-machine raspi3b -cpu cortex-a53 -m 1G
-kernel bin/kernel8.img
-drive file=bin/sd.img,if=sd,format=raw
-serial mon:stdio -display none

Invokes QEMU with following flags (if embed_kernel=true)

-machine raspi3b -cpu cortex-a53 -m 1G
-kernel bin/kernel8.img
-serial mon:stdio -display none

size
Prints section sizes for build/neutron.elf and build/kernel_raw.elf. Useful to check code and data size.

help
Prints a short help message listing the main targets and the CROSS variable.

Variables

CROSS
Toolchain prefix. Default: aarch64-none-elf-. Must include the trailing hyphen. Example: make CROSS=aarch64-none-elf- all.

CC, AS, LD, OBJCOPY, OBJDUMP, SIZE
Derived from CROSS (e.g. $(CROSS)gcc, $(CROSS)ld). The assembler is invoked via the C compiler ($(CROSS)gcc) for preprocessing of .S files.

BUILD_DIR, BIN_DIR
Directories for object files and final binaries. Defaults: build, bin.

QEMU, QEMU_MACHINE, QEMU_CPU, QEMU_MEM
Used to form the QEMU command. Values: qemu-system-aarch64, raspi3b, cortex-a53, 1G.

SD_SIZE_MB
Size of the SD image in MiB. Default: 64.

K_BIN
Optional. If provided on the command line, treated as a path to a prebuilt packed NKRN image. The kernel target skips building test_kernel and validates/uses this path directly. sd-image and the embed path both consume it via K_BIN_SRC. When using neutron.sh / neutron.ps1, prefer --kernel <path> instead — the scripts copy the file into the bind-mounted bin/ directory and set K_BIN automatically.

PACK
Optional. Default: 0. Set to 1 alongside K_BIN=<path> to treat the provided file as a raw (unpacked) binary — the kernel target will run pack_kernel.py on it before it is used for the SD image or embed path. When using neutron.sh / neutron.ps1, use --pack instead of PACK=1 directly. Example: make all K_BIN=out/raw.bin PACK=1.

Per-build settings (kernel filename on FAT32, load/staging addresses, log level, embed-kernel, etc.) are in build.cfg, not Make variables. The Makefile runs gen_config.py to generate internal/config.h (C macros) and build.mk (Makefile variables) when building the bootloader. See Build Configuration.

Output Files

FileTargetDescription
build/neutron.elfbootloaderLinked bootloader ELF
bin/kernel8.imgbootloaderBootloader binary (objcopy from neutron.elf); may embed packed kernel when embed-kernel = true
build/kernel_raw.elfkernelLinked test kernel ELF
build/kernel_raw.binkernelTest kernel raw binary
bin/CUSTOM.binkernelPacked test kernel (NKRN header + kernel_raw.bin)
bin/sd.imgsd-imageFAT32 disk image; kernel filename from build.cfg. Not built when embed-kernel = true.

Compiler and Linker Flags

  • ARCH_FLAGS: -march=armv8-a -mtune=cortex-a53 -mgeneral-regs-only -mlittle-endian
  • CFLAGS: ARCH_FLAGS plus -ffreestanding -fno-builtin -fno-stack-protector -fno-pie -fno-pic -nostdlib -nostdinc -std=gnu11 -Wall -Wextra -Werror -O2 -g and include paths.
  • ASFLAGS: ARCH_FLAGS plus -ffreestanding -nostdlib and include path, with -D__ASSEMBLER__.
  • BL_LDFLAGS: -nostdlib -T linker/bootloader.ld -Map=build/neutron.map --no-dynamic-linker --no-warn-rwx-segments
  • K_LDFLAGS: -nostdlib -T test_kernel/linker/kernel.ld -Map=build/kernel.map --no-dynamic-linker

The bootloader is linked with ld directly; the kernel is linked with ld as well. No startup files or C library are linked. The kernel pack step is separate (Python script) and must be run for the bootloader to load the kernel; the Makefile runs it as part of the kernel target.

For build configuration (build.cfg, internal/config.h, build.mk, embed-kernel) see Build Configuration. For building on Linux/macOS see Linux and macOS; for Windows with Docker see Windows.