| name | osdev-toolchain |
| description | OS development toolchain setup and usage. GCC/Clang cross-compiler, linker scripts, NASM/GAS assembly, Makefiles, and QEMU/Bochs debugging. Use when setting up cross-compilation, writing linker scripts, or debugging with emulators. |
| origin | MCC |
OS Development Toolchain
Complete reference for setting up and using an OS development toolchain: cross-compilers, linker scripts, assemblers, build systems, and emulator debugging.
When to Use
- Setting up a GCC or Clang cross-compiler for bare-metal targets
- Writing or debugging linker scripts for kernel layout
- Integrating NASM or GAS assembly with C code
- Building a Makefile for a kernel project
- Configuring QEMU or Bochs for OS debugging
- Diagnosing cross-compilation or linking errors
Cross-Compiler Setup
A cross-compiler runs on your host but produces code for a different target (your OS). Never use the host system compiler for kernel development -- it assumes a hosted environment with libc and OS headers.
Quick Setup (i686-elf)
export PREFIX="$HOME/opt/cross"
export TARGET=i686-elf
export PATH="$PREFIX/bin:$PATH"
mkdir build-binutils && cd build-binutils
../binutils-x.y.z/configure --target=$TARGET --prefix="$PREFIX" \
--with-sysroot --disable-nls --disable-werror
make -j$(nproc)
make install
cd .. && mkdir build-gcc && cd build-gcc
../gcc-x.y.z/configure --target=$TARGET --prefix="$PREFIX" \
--disable-nls --enable-languages=c,c++ --without-headers \
--disable-hosted-libstdcxx
make -j$(nproc) all-gcc
make -j$(nproc) all-target-libgcc
make install-gcc
make install-target-libgcc
Prerequisites
| Dependency | Debian/Ubuntu | Fedora | Arch |
|---|
| Compiler | build-essential | gcc gcc-c++ | base-devel |
| Bison | bison | bison | base-devel |
| Flex | flex | flex | base-devel |
| GMP | libgmp3-dev | gmp-devel | gmp |
| MPFR | libmpfr-dev | mpfr-devel | mpfr |
| MPC | libmpc-dev | libmpc-devel | libmpc |
| Texinfo | texinfo | texinfo | base-devel |
| ISL (optional) | libisl-dev | isl-devel | -- |
Common Targets
| Target | Architecture | Use Case |
|---|
i686-elf | x86 32-bit | Classic OS dev, Bare Bones tutorial |
x86_64-elf | x86 64-bit | 64-bit kernels, long mode |
arm-none-eabi | ARM 32-bit | Embedded ARM, Cortex-M/A |
aarch64-none-elf | ARM 64-bit | ARMv8-A, Raspberry Pi 3/4 |
riscv64-unknown-elf | RISC-V 64-bit | RISC-V development |
Essential Compiler Flags
$TARGET-gcc -ffreestanding -nostdlib -nostdinc -fno-builtin
$TARGET-gcc -fno-stack-protector -fno-pic -fno-pie
$TARGET-gcc -g -O0
$TARGET-gcc -Wall -Wextra -Werror
See references/cross-compiler.md for the full build guide.
Linker Script Anatomy
Linker scripts control the memory layout of your kernel binary. Invoked with ld -T linker.ld.
Minimal Kernel Linker Script
/* kernel.ld -- Kernel linker script for i686-elf */
ENTRY(_start)
OUTPUT_FORMAT(elf32-i386)
SECTIONS
{
/* Load kernel at 1MB (above real-mode memory) */
. = 1M;
_kernel_start = .;
/* Multiboot header must be early in the binary */
.multiboot ALIGN(4) : {
KEEP(*(.multiboot))
}
.text ALIGN(4K) : {
_text_start = .;
*(.text .text.*)
_text_end = .;
}
.rodata ALIGN(4K) : {
_rodata_start = .;
*(.rodata .rodata.*)
_rodata_end = .;
}
.data ALIGN(4K) : {
_data_start = .;
*(.data .data.*)
_data_end = .;
}
.bss ALIGN(4K) : {
_bss_start = .;
*(.bss .bss.*)
*(COMMON)
_bss_end = .;
}
_kernel_end = .;
/* Discard unwanted sections */
/DISCARD/ : {
*(.comment)
*(.eh_frame)
}
}
Key Directives
| Directive | Purpose |
|---|
ENTRY(_start) | First instruction to execute |
OUTPUT_FORMAT(elf32-i386) | Output binary format |
. = 1M; | Set location counter (load address) |
ALIGN(4K) | Page-align sections |
KEEP(...) | Prevent garbage collection of section |
_symbol = .; | Export symbol at current address |
Using Linker Symbols in C
extern char _bss_start[];
extern char _bss_end[];
void zero_bss(void) {
for (char *p = _bss_start; p < _bss_end; p++)
*p = 0;
}
See references/linker-scripts.md for complete reference.
Assembly
NASM vs GAS Syntax
| Feature | NASM (Intel syntax) | GAS (AT&T syntax) |
|---|
| Operand order | mov dst, src | movl %src, %dst |
| Register prefix | none | % prefix |
| Immediate prefix | none | $ prefix |
| Memory access | [eax+4] | 4(%eax) |
| Size suffix | dword, word, byte | l, w, b suffix on mnemonic |
| Sections | section .text | .section .text |
| Constants | equ, %define | .equ, .set |
| Data | db, dw, dd, dq | .byte, .word, .long, .quad |
| Output format | -f elf32, -f elf64 | Matches target of cross-compiler |
NASM Multiboot Header
; boot.asm -- Multiboot header and entry point
section .multiboot
align 4
MAGIC equ 0x1BADB002
FLAGS equ 0x00000003 ; align modules, provide memory map
dd MAGIC
dd FLAGS
dd -(MAGIC + FLAGS) ; checksum
section .bss
align 16
stack_bottom:
resb 16384 ; 16 KB stack
stack_top:
section .text
global _start
extern kernel_main
_start:
mov esp, stack_top
push ebx ; multiboot info pointer
push eax ; multiboot magic number
call kernel_main
cli
.hang:
hlt
jmp .hang
GAS Equivalent
/* boot.S -- GAS syntax equivalent */
.section .multiboot, "a"
.align 4
.long 0x1BADB002 /* magic */
.long 0x00000003 /* flags */
.long -(0x1BADB002 + 0x00000003) /* checksum */
.section .bss
.align 16
stack_bottom:
.skip 16384
stack_top:
.section .text
.global _start
.extern kernel_main
_start:
movl $stack_top, %esp
pushl %ebx
pushl %eax
call kernel_main
cli
1: hlt
jmp 1b
Calling Conventions (cdecl, i386)
- Arguments pushed right-to-left on the stack
- Caller cleans up the stack
- Return value in
eax (32-bit) or eax:edx (64-bit value)
eax, ecx, edx are caller-saved
ebx, esi, edi, ebp, esp are callee-saved
Calling Conventions (System V AMD64 ABI)
- First 6 integer args:
rdi, rsi, rdx, rcx, r8, r9
- Return value in
rax
- Stack must be 16-byte aligned before
call
rbx, rbp, r12-r15 are callee-saved
Build System
Makefile Template
TARGET := i686-elf
CC := $(TARGET)-gcc
AS := nasm
LD := $(TARGET)-ld
CFLAGS := -ffreestanding -nostdlib -fno-stack-protector -fno-pic \
-Wall -Wextra -Werror -g -O2 -std=c11
ASFLAGS := -f elf32 -g
LDFLAGS := -T kernel.ld -nostdlib
C_SRCS := $(shell find src -name '*.c')
ASM_SRCS := $(shell find src -name '*.asm')
S_SRCS := $(shell find src -name '*.S')
C_OBJS := $(C_SRCS:.c=.o)
ASM_OBJS := $(ASM_SRCS:.asm=.o)
S_OBJS := $(S_SRCS:.S=.o)
OBJS := $(ASM_OBJS) $(S_OBJS) $(C_OBJS)
KERNEL := kernel.elf
ISO := myos.iso
.PHONY: all clean run debug iso
all: $(KERNEL)
$(KERNEL): $(OBJS)
$(LD) $(LDFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -MMD -MP -c -o $@ $<
%.o: %.asm
$(AS) $(ASFLAGS) -o $@ $<
%.o: %.S
$(CC) $(CFLAGS) -c -o $@ $<
iso: $(KERNEL)
mkdir -p iso/boot/grub
cp $(KERNEL) iso/boot/
echo 'menuentry { multiboot /boot/kernel.elf }' > iso/boot/grub/grub.cfg
grub-mkrescue -o iso/
qemu-system-i386 -cdrom -serial stdio -m 256M -no-reboot
qemu-system-i386 -cdrom -serial stdio -m 256M \
-no-reboot -s -S -d int,guest_errors &
sleep 1
-gdb -ex
rm -f $(C_SRCS:.c=.d)
rm -rf iso/
$(C_SRCS:.c=.d)
See references/makefile-template.md for the complete template.
QEMU Debugging
Essential Flags
| Flag | Purpose |
|---|
-m 256M | Guest RAM size |
-cdrom os.iso | Boot from ISO |
-drive file=disk.img,format=raw,if=ide | Attach disk image |
-serial stdio | Redirect serial port to terminal |
-no-reboot | Halt on triple fault instead of rebooting |
-no-shutdown | Keep QEMU open on guest shutdown |
-s | Enable GDB stub on localhost:1234 |
-S | Start paused (wait for GDB continue) |
-d int,guest_errors | Log interrupts and guest errors to stderr |
-D logfile | Write debug output to file |
-nographic | No GUI, serial on stdio |
-debugcon stdio | Debug console via port 0xE9 |
-monitor stdio | QEMU monitor on stdio |
GDB Remote Debugging Workflow
qemu-system-i386 -cdrom myos.iso -s -S -serial stdio -no-reboot
i686-elf-gdb kernel.elf
(gdb) target remote :1234
(gdb) break kernel_main
(gdb) continue
(gdb) info registers
(gdb) x/16xw 0x100000
(gdb) layout asm
.gdbinit for Automatic Connection
file kernel.elf
target remote localhost:1234
break kernel_main
continue
QEMU Monitor Commands
| Command | Description |
|---|
info registers | Dump CPU register state |
info mem | Show page table mappings |
info tlb | Show TLB entries |
xp /16xw 0x100000 | Examine physical memory |
cpu N | Switch to CPU N |
gdbserver | Start GDB server |
See references/qemu-debugging.md for complete QEMU configuration.
Common Build Errors
| Error | Cause | Fix |
|---|
undefined reference to _start | Missing entry point | Add global _start in assembly, ENTRY(_start) in linker script |
cannot find -lgcc | libgcc not built for target | Run make all-target-libgcc and make install-target-libgcc |
relocation truncated to fit: R_386_16 | 16-bit relocation in 32-bit code | Use [bits 32] in NASM, check section attributes |
multiple definition of _start | Duplicate entry points | Ensure only one file defines _start |
skipping incompatible | Wrong binary format | Check OUTPUT_FORMAT in linker script matches target |
LOAD segment with RWX permissions | Sections not properly separated/aligned | Add ALIGN(CONSTANT(MAXPAGESIZE)) to each section |
.bss is not within region | BSS exceeds MEMORY region | Increase MEMORY region size or reduce BSS usage |
the directory that should contain system headers does not exist | Normal for cross-compiler build | Safe to ignore, add --with-sysroot to suppress |
_start not found | PATH missing cross-compiler bin | export PATH="$PREFIX/bin:$PATH" |
multiboot header not found | Header not in first 8KB | Use KEEP() in linker script, ensure .multiboot section is first |
Related Skills
rust-patterns -- for Rust-based OS development
cpp-coding-standards -- C++ kernel coding conventions
docker-patterns -- containerized cross-compilation environments