Writing an OS in Rust

Philipp Oppermann's blog

Writing an OS in pure Rust

Over the past six months we’ve been working on a second edition of this blog. Our goals for this new version are numerous and we are still not done yet, but today we reached a major milestone: It is now possible to build the OS natively on Windows, macOS, and Linux without any non-Rust dependendencies.

The first edition required several C-tools for building:

  • We used the GRUB bootloader for booting our kernel. To create a bootable disk/CD image we used the grub-mkrescue tool, which is very difficult to get to run on Windows.
  • The xorriso program was also required, because it is used by grub-mkrescue.
  • GRUB only boots to protected mode, so we needed some assembly code for entering long mode. For building the assembly code, we used the nasm assembler.
  • We used the GNU linker ld for linking together the assembly files with the rust code, using a custom linker script.
  • Finally, we used make for automating the various build steps (assembling, compiling the Rust code, linking, invoking grub-mkrescue).

We got lots of feedback that this setup was difficult to get running under macOS and Windows. As a workaround, we added support for docker, but that still required users to install and understand an additional dependency. So when we decided to create a second edition of the blog - originally because the order of posts led to jumps in difficulty - we thought about how we could avoid these C-dependencies.

There are lots of alternatives to make, including some Rust tools such as just and cargo-make. Avoiding nasm is also possible by using Rust’s global_asm feature instead. So there are only two problems left: the bootloader and the linker.

A custom Bootloader

To avoid the dependency on GRUB and to make things more ergonomic, we decided to write our own bootloader using Rust’s global_asm feature. This way, the kernel can be significantly simplified, since the switch to long mode and the initial page table layout can already be done in the bootloader. Thus, we can avoid the initial assembly level blog posts in the second edition and directly start with high level Rust code.

The bootloader is still an early prototype, but it is already capable of switching to long mode and loading the kernel in form of an 64-bit ELF binary. It also performs the correct page table mapping (with the correct read/write/execute permissions) as it’s specified in the ELF file and creates an initial physical memory map.

The plan for the future is to make the bootloader more stable, add documentation, and ultimately add a “Writing a Bootloader” series to the blog, which explains in detail how the bootloader works.

Linking with LLD

With our custom bootloader in place, the last remaining problem is platform independent linking. Fortunately there is LLD, the cross-platform linker from the LLVM project, which is already very stable for the x86 architecture. As a bonus, LLD is now shipped with Rust, which means that it can be used without any extra installation.

The new Posts

The second edition is already live at https://os.phil-opp.com/second-edition. Please tell us if you have any feedback on the new posts! We’re planning to move over the content from the first edition iteratively, in a different order and with various other improvements.

Many thanks to everyone who helped to make Rust an even better language for OS development!


Comments

Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's code of conduct. This comment thread directly maps to a discussion on GitHub, so you can also comment there if you prefer.

Instead of authenticating the giscus application, you can also comment directly on GitHub.