Writing an OS Kernel from Scratch – Part 9: User Program Loading — ELF Parsing and Simple Filesystem
“Your OS can fork/exec, but where do programs come from? Today, we build the first filesystem, letting the kernel load user programs from ‘disk’!”
In the previous post, we implemented fork and exec, but user programs still had to be hardcoded into the kernel image. That’s clearly not realistic — a real OS must load arbitrary programs from storage devices.
This depends on two core modules:
✅ Filesystem — organizes and manages files on disk
✅ ELF Loader — parses and loads executable files into memory
Today, we implement a minimal but usable filesystem (Initramfs style) and complete ELF program loading, making your exec(“/bin/ls”) actually work!
Why a Filesystem?
Without a filesystem, your OS is like a computer without a hard drive — all programs must be compiled into the kernel, unable to expand dynamically.
A filesystem’s core responsibilities:
- Naming: access files via paths (like /bin/ls)
- Storage: map file content to physical storage (disk/memory)
- Metadata: record file size, permissions, type, etc.
Early on, we don’t need complex disk drivers — we can use Initramfs (Initial RAM Filesystem): package all files into the kernel image, loaded into memory at boot.
ELF File Format Overview
ELF (Executable and Linkable Format) is the standard executable file format on Unix systems.
Key structures:
| Structure | Role |
|———–|——|
| ELF Header | Describes architecture, entry address, program header offset |
| Program Header Table | Describes how to load into memory (PT_LOAD segments) |
| Sections | For compile/link (not needed at runtime) |
The loader only needs to parse Program Headers, ignoring Sections!
Implementing the ELF Loader
Steps:
- Read ELF file from filesystem into kernel buffer
- Verify ELF magic number and type
- Iterate Program Headers, load PT_LOAD segments
- Set entry address
Comments