2026-01-19 17:35:37 +01:00
2026-01-19 17:35:37 +01:00
2026-01-19 17:35:37 +01:00
2026-01-19 17:35:37 +01:00
2026-01-19 17:35:37 +01:00
2026-01-19 17:35:37 +01:00
2026-01-19 17:35:37 +01:00

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 mmap system 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.

  1. Build: Compiles the executable into the bin directory.

    make
    
  2. Run: Compiles (if necessary) and runs the program.

    make run
    
  3. Debug: Runs the program inside a debugger (default is gf2).

    make debug
    

How it works

When you run the program, it performs these steps:

  1. Heap Initialization: Requests 64 MB of memory using SYS_MMAP.
  2. Allocation: Creates a cons cell with values 0xAAAA and 0xBBBB.
  3. 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).
  4. 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 via mmap directly.

  • 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 cons function 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_hex function 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

Description
Experimental x86_64 assembly runtime for Linux. Features custom memory allocation via direct system calls, with zero reliance on libc.
Readme 34 KiB
Languages
Assembly 98.2%
Makefile 1.8%