4.2 KiB
NOS - Experimental Assembly Runtime
This project is a low-level runtime environment written in x86_64 assembly (NASM) for Linux. It implements a custom memory manager and basic list data structures.
Project overview
The main goal is to understand how computers work at a low level by building a runtime environment from scratch. This project serves as a "bare metal" experiment in manual resource management and data representation.
Key mechanisms:
- Custom Memory Allocation: It uses the
mmapsystem call to reserve 64 MB of RAM. - Cons Structure: It implements value pairs (cons cells), which are the foundation of languages like Lisp.
- Direct Kernel Interface: The code bypasses the standard C library (
libc) entirely. It relies on the x86_64 ABI to invoke Linux system calls directly.
Project structure
The files are organized as follows:
src/nos.asm: The main program logic and allocator implementation.include/syscalls.inc: Definitions of Linux system call numbers.include/mmap.inc: Constants and flags for memory management.Makefile: The script for building and testing the project.
Requirements
To run this, you need a 64-bit Linux system and the following tools:
nasm(Assembler)ld(Linker)make(Build tool)gf2(Optional, for debugging)
Usage
You control the project using the Makefile.
-
Build: Compiles the executable into the
bindirectory.make -
Run: Compiles (if necessary) and runs the program.
make run -
Debug: Runs the program inside a debugger (default is gf2).
make debug
How it works
When you run the program, it performs these steps:
- Heap Initialization: Requests 64 MB of memory using
SYS_MMAP. - Allocation: Creates a
conscell with values0xAAAAand0xBBBB. - Output: Prints the following to stdout in hexadecimal:
- The memory address of the new structure.
- The first value (head/car).
- The second value (tail/cdr).
- Exit: Terminates the process cleanly.
Philosophy
This project adopts a "constructivist" approach to systems programming. We avoid the standard C library (libc) not because it is bad, but because it hides the machinery we want to study.
-
Total transparency: By avoiding standard libraries, we remove the "black box". Every instruction executed by the CPU is explicit in our source code. There is no hidden initialization code running before
_start. -
Memory control: General-purpose allocators (like
malloc) are optimized for average use cases. Building a language runtime requires precise control over the heap layout. We achieve this by managing raw memory pages viammapdirectly. -
The trade-off: We accept a trade-off: portability for clarity. The code is tightly coupled to the Linux kernel ABI. However, this dependency allows us to interact with the operating system without any abstraction layers blurring the picture.
Educational value
This project serves as a case study in system software engineering. It explores several core computer science concepts:
- Memory Management: It demystifies the heap. It shows how to request memory pages directly from the kernel without
malloc. - Data Structures: The
consfunction shows how abstract linked lists are physically represented in RAM. - Binary Interface (ABI): The code strictly follows System V AMD64 ABI conventions. It requires manual management of registers and the stack.
- System Dependency: It demonstrates how software interacts with the OS kernel boundary without any abstraction layers.
TODO - Future plans
The current version is a prototype. Future development will focus on:
- Garbage Collection (GC): The current linear allocator only consumes memory. A mechanism (like Mark and Sweep) is needed to reclaim unused cells.
- Better I/O: The
print_hexfunction is only for debugging. Decimal numbers and string support are needed. - REPL: The goal is to build a Read-Eval-Print Loop for interactive commands.
- Type Tagging: Currently, everything is a raw 64-bit value. Pointer tagging is needed to distinguish integers from pointers.
License
MIT