---
title: 'Building Bare-Metal .NET: Exploring NativeAOT Patching'
url: 'https://kaleb.au/blog/building-bare-metal-net-exploring-nativeaot-patching'
markdown: 'https://kaleb.au/blog/building-bare-metal-net-exploring-nativeaot-patching.md'
date: '2026-04-24'
description: 'If you have ever wondered how far .NET can be pushed outside the traditional runtime model and down into kernels, bootloaders, and bare metal environments, then the project nativeaot-patcher is worth a serious look. Repository: https://github.com/valentinbreiz/nativeaot-patcher This project is esse…'
---

# [Building Bare-Metal .NET: Exploring NativeAOT Patching](https://kaleb.au/blog/building-bare-metal-net-exploring-nativeaot-patching)

   24 Apr 2026  

If you have ever wondered how far .NET can be pushed outside the traditional runtime model and down into kernels, bootloaders, and bare metal environments, then the project `nativeaot-patcher` is worth a serious look.

Repository: <https://github.com/valentinbreiz/nativeaot-patcher>

This project is essentially a proof of concept for a next generation Cosmos style toolchain that compiles C# directly into a bootable kernel using .NET NativeAOT and IL patching.

It demonstrates something many developers assume is impossible.

**Using modern .NET tooling to build an operating system kernel without a traditional managed runtime.**

---

## The Big Idea

At a high level, the project combines:

- .NET NativeAOT for ahead of time compilation
- IL patching using Mono.Cecil
- Custom build tooling
- Low level assembly integration
- Standard bootloader packaging

The result is a workflow that turns C# code into:

```text
C# -> IL -> patched IL -> native objects -> ELF kernel -> bootable ISO
```

This pipeline is fully automated through MSBuild and custom tasks, ultimately producing a bootable kernel image that can run in a VM or on hardware.

In other words:

**It treats C# as a systems programming language.**

---

## Why This Matters

Most developers think of .NET like this:

```text
App -> CLR -> OS
```

But NativeAOT enables a different architecture:

```text
App -> native binary -> hardware
```

Projects like this take that one step further:

```text
Kernel -> native binary -> hardware
```

No runtime. No JIT. No operating system dependency.

Just compiled code.

---

## How the Build Pipeline Works

The build process is surprisingly structured and transparent.

### 1) Assembly Compilation

Low level routines are written in assembly and compiled into object files.

Examples include:

- Hardware I/O
- Interrupt handling
- Runtime imports

These are assembled using:

```bash
yasm -felf64
```

---

### 2) C# Compilation

Standard .NET tooling compiles the kernel code into an IL assembly.

At this stage:

- Roslyn compiles the code
- Static analyzers validate plug rules

Nothing unusual yet.

---

### 3) IL Patching

This is where things get interesting.

The patcher:

- Loads the compiled assembly
- Finds methods marked with `[Plug]`
- Replaces implementations with the methods marked with `[Plug]`

This allows:

- Runtime methods to be overridden
- Platform specific implementations
- Missing runtime functionality to be injected

The patched assembly is then saved.

---

### 4) NativeAOT Compilation

The patched IL is compiled into native machine code.

This step performs:

- Tree shaking
- Dependency trimming
- Native code generation

Output:

```text
.obj / .o files
```

---

### 5) Linking

All native objects are linked into a single executable:

```text
Kernel.elf
```

A linker script defines:

- Memory layout
- Entry point
- Symbol resolution

---

### 6) ISO Creation

Finally, the system packages everything into a bootable image:

```text
Kernel.iso
```

This includes:

- Bootloader
- Configuration
- Kernel binary

The ISO can then be:

- Booted in QEMU
- Run in VirtualBox
- Written to USB
- Executed on hardware

All from a standard:

```bash
dotnet build
```

pipeline.

---

## The Real Innovation: Plug Based Runtime Replacement

The most powerful concept in this project is the plug system.

Instead of relying on the standard runtime, the toolchain allows you to replace functionality directly at the IL level.

For example:

```csharp
Console.WriteLine()
```

could be replaced with:

```csharp
SerialPort.Write()
```

Or:

```text
Memory allocation
Thread scheduling
Hardware access
```

All implemented manually.

This makes the runtime:

- Minimal
- Deterministic
- Hardware aware
- Fully customizable

---

## Comparison to Traditional Cosmos

Traditional Cosmos uses:

```text
IL -> custom compiler -> assembly
```

This project uses:

```text
IL -> NativeAOT -> native objects
```

That shift matters.

NativeAOT provides:

- Modern compiler optimizations
- Better tooling compatibility
- Smaller binaries
- Faster startup
- Predictable execution

And importantly:

It leverages the official .NET toolchain instead of a custom compiler stack.

---

## Where This Gets Interesting for Real Systems

This is not just about hobby operating systems.

The same architecture could be used for:

### Embedded Systems

- Routers
- IoT devices
- Industrial controllers

### Infrastructure Components

- Hypervisors
- Boot environments
- Recovery systems

### High Reliability Systems

- Deterministic services
- Minimal attack surface
- Fast cold start workloads

Given the kind of infrastructure heavy environments many of us run, especially around virtualization and low level networking, this model is particularly relevant for:

- Custom appliance OS builds
- Secure boot environments
- Minimal control plane runtimes

---

## Strengths

### Uses the Official .NET Toolchain

No custom compiler required.

### Fully Native Output

No runtime dependency.

### Deterministic Behavior

No JIT or runtime variability.

### Modern Build System

Everything integrates with:

```text
dotnet build
MSBuild
NuGet
```

---

## Limitations

This is still experimental.

You should expect:

### Limited Runtime Support

Many standard APIs require manual implementation.

### Architecture Constraints

Hardware support must be written explicitly.

### Debugging Complexity

You are debugging:

```text
native kernel code
```

not managed applications.

---

## Why Developers Should Watch This Project

This is one of the clearest demonstrations that:

**.NET is no longer just an application framework.**

It can be:

- A systems language
- A kernel language
- An embedded language

And that shift is happening now.

---

## Getting Started

The easiest way to explore the project:

### Install dependencies

```bash
.NET SDK 9+
LLVM (lld)
yasm
xorriso
```

### Clone the repository

```bash
git clone https://github.com/valentinbreiz/nativeaot-patcher
cd nativeaot-patcher
```

### Build

```bash
dotnet build
```

You will get:

```text
Kernel.iso
```

Boot it in:

```bash
qemu-system-x86_64 -cdrom Kernel.iso
```

And you are running a C# kernel.

---

## Final Thoughts

Projects like this challenge long held assumptions about what managed languages can do.

They show that:

- Modern compilers
- Aggressive trimming
- IL rewriting
- Ahead of time compilation

can turn high level languages into true systems languages.

Not in theory.

In practice.

---

## Navigation

- Parent: [Blog](https://kaleb.au/blog.md)
- Previous: [Bunny CDN Review](https://kaleb.au/blog/bunny-cdn-review.md)
- Next: [Using MXroute as a Relay](https://kaleb.au/blog/using-mxroute-as-a-relay.md)
