Skip to main content

Building from Source

This guide walks through building Aeolos from source, giving you a complete development environment for kernel hacking.
Building Aeolos requires a Linux environment. Cross-compilation from other platforms is possible but not officially supported.

Prerequisites

Required Tools

Aeolos requires a modern Linux distribution with the following packages:
sudo apt-get install -y \
  nasm \
  gcc \
  make \
  mtools \
  xorriso \
  git

GCC

C compiler for kernel code (gnu2x standard)

NASM

Assembler for SMP trampoline code

xorriso

ISO 9660 image creation tool

mtools

Utilities for manipulating disk images

Optional: QEMU for Testing

# Debian/Ubuntu
sudo apt-get install qemu-system-x86

# Fedora/RHEL  
sudo dnf install qemu-system-x86

# Arch Linux
sudo pacman -S qemu-system-x86

Clone the Repository

1

Clone with submodules

Aeolos uses git submodules for third-party dependencies (Limine bootloader and SAF archive tools).
git clone --recursive https://github.com/chocabloc/aeolos.git
cd aeolos
If you already cloned without --recursive, initialize submodules:
git submodule update --init --recursive
2

Verify submodules

Check that the third-party dependencies are present:
ls -la thirdparty/
You should see:
  • limine-bin/ - Bootloader binaries
  • saf/ - SAF archive format tools for initrd

Build Process

Complete Build

The simplest way to build everything:
make
This will:
  1. Build the SAF tools (thirdparty/saf)
  2. Compile the kernel (kernel/kernel.elf)
  3. Generate kernel symbols
  4. Package the initrd (image/boot/initrd.saf.gz)
  5. Create the bootable ISO (os.iso)
The build process uses a two-pass linking approach to embed kernel symbols for debugging.

Understanding the Build

The root Makefile orchestrates the build:
# From Makefile
KERNELFILE = kernel/kernel.elf
INITRDFILE = image/boot/initrd.saf.gz
IMAGEFILE = os.iso

all: $(IMAGEFILE)

$(IMAGEFILE): $(KERNELFILE) $(INITRDFILE)
    @echo Generating Hard Disk Image...
    @$(GENIMG)
1

Kernel Compilation

The kernel is built from C and assembly sources in kernel/:
make -C kernel
Compiler flags (from kernel/Makefile):
CFLAGS = -std=gnu2x \
         -ffreestanding \
         -fno-pic \
         -fno-omit-frame-pointer \
         -fno-stack-protector \
         -mcmodel=kernel \
         -mno-red-zone \
         -mno-80387 \
         -mno-mmx \
         -mno-sse \
         -mno-sse2 \
         -Wall \
         -Wextra \
         -Og
These flags ensure:
  • Freestanding environment (no standard library)
  • No position-independent code
  • Kernel code model for higher-half addressing
  • No red zone (required for interrupt handlers)
  • No x87/MMX/SSE (kernel shouldn’t use FP)
2

SMP Trampoline

The 16-bit real mode trampoline for starting secondary CPUs:
nasm sys/smp/trampoline.nasm -o sys/smp/trampoline.bin
This binary blob is embedded in the kernel for SMP initialization.
3

Symbol Generation

After initial linking, the gensym tool extracts symbols:
./gensym kernel.elf
This generates _symbols.gen which is compiled and linked back into the kernel, enabling runtime symbol lookup for debugging.
4

Initrd Creation

The initial ramdisk is created from files in initrd/:
./thirdparty/saf/saf-make initrd image/boot/initrd.saf -q
gzip -f image/boot/initrd.saf
The SAF (Simple Archive Format) file is compressed with gzip and loaded by the bootloader.
5

ISO Generation

The genimg script creates a bootable ISO:
#!/bin/bash
# From genimg script

# Copy Limine bootloader files
cp thirdparty/limine-bin/limine.sys image/boot/
cp thirdparty/limine-bin/limine-cd.bin image/boot/
cp thirdparty/limine-bin/limine-eltorito-efi.bin image/boot/limine-efi.bin
cp thirdparty/limine-bin/BOOTX64.EFI image/EFI/BOOT/

# Copy kernel
cp kernel/kernel.elf image/boot/

# Build ISO with xorriso
xorriso -as mkisofs \
  -J -joliet-long \
  -rock \
  -b boot/limine-cd.bin \
  -c boot/limine-cd.cat \
  -no-emul-boot -boot-load-size 4 -boot-info-table \
  -eltorito-alt-boot \
  -e boot/limine-efi.bin \
  -no-emul-boot -isohybrid-gpt-basdat \
  image \
  -o os.iso
This creates a hybrid BIOS/UEFI bootable ISO using the Limine bootloader.

Kernel Structure

The kernel source tree is organized as follows:
kernel/
├── boot/          # Bootloader interface and initrd
├── dev/           # Device drivers (fb, serial, term)
├── fs/            # Filesystem implementations (vfs, ramfs, ttyfs)
├── lib/           # Kernel library functions
├── mm/            # Memory management (pmm, vmm)
├── proc/          # Process/task management
├── sys/           # System hardware (ACPI, APIC, CPU, GDT, IDT, SMP)
├── kmain.c        # Kernel entry point
├── linker.ld      # Linker script for higher-half kernel
└── Makefile       # Build configuration

Build Targets

The Makefile provides several targets:
# Build everything and create os.iso
make

Running in QEMU

The make run target builds and tests the OS:
make run
This is equivalent to:
make
qemu-system-x86_64 -cdrom os.iso -m 128 -no-reboot -no-shutdown -smp 4 -cpu host -M accel=kvm:tcg

Boot Configuration

The Limine bootloader is configured via image/boot/limine.cfg:
:Aeolos

PROTOCOL=stivale2
RESOLUTION=800x600x32
KERNEL_PATH=boot:///boot/kernel.elf
MODULE_PATH=boot:///boot/initrd.saf.gz
MODULE_STRING=initrd
TIMEOUT=0

Stivale2 Protocol

Modern boot protocol with clean handoff to the kernel

Framebuffer Mode

800x600x32 graphical mode for terminal output

Kernel Module

Compressed initrd loaded as a boot module

No Timeout

Boots immediately without menu

Development Workflow

1

Make code changes

Edit source files in kernel/, initrd/, etc.
2

Rebuild

make clean && make
You can often omit make clean for incremental builds, but use it if you encounter linking issues.
3

Test in QEMU

make run
Or use custom QEMU flags:
qemu-system-x86_64 -cdrom os.iso -m 128 -smp 4 -serial stdio -d int
4

Debug output

Use the kernel logging functions:
klog_info("Message: %d\n", value);
klog_ok("Success\n");
klog_warn("Warning\n");
klog_err("Error\n");
Output goes to both serial port and framebuffer.

Troubleshooting

Missing build dependencies.Solution: Install all required packages from the Prerequisites section.
Git submodules weren’t initialized.Solution:
git submodule update --init --recursive
Symbol generation might have failed.Solution: Clean and rebuild:
make clean
make
The image might be corrupted or bootloader not properly installed.Solution: Ensure xorriso and mtools are installed, then:
make clean
make
The initrd needs to be regenerated.Solution: Remove the old initrd:
rm image/boot/initrd.saf.gz
make

Advanced: Custom Kernel Configuration

You can modify the kernel build by editing kernel/Makefile:

Debug Symbols

Add debug symbols for GDB:
CFLAGS += -g

Optimization Levels

Change from -Og (debug-friendly) to -O2 (optimized):
CFLAGS = ... -O2
Higher optimization levels may make debugging more difficult and can expose latent bugs.

Memory Configuration

Adjust QEMU memory allocation in root Makefile:
QEMUFLAGS = -m 256  # Increase to 256MB

CI/CD Build

The GitHub Actions workflow builds Aeolos automatically:
# From .github/workflows/c-cpp.yml
name: C/C++ CI

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout repo and submodules
      uses: actions/checkout@v2
      with:
        submodules: recursive
    - name: Install dependencies
      run: sudo apt-get install -y nasm gcc mtools xorriso
    - name: make
      run: make
    - uses: actions/upload-artifact@v4
      with:
        name: os.iso
        path: os.iso
This ensures every commit produces a working os.iso artifact.

Next Steps

Run Aeolos

Test your build in QEMU

Architecture Overview

Learn about the kernel internals
The kernel entry point is kmain() in kernel/kmain.c. Start there to understand the initialization sequence and modify OS behavior.

Build docs developers (and LLMs) love