Rust Bare Bones

From OSDev Wiki
Jump to navigation Jump to search

This page is under construction! This page or section is a work in progress and may thus be incomplete. Its content may be changed in the near future.

This article is a stub! This page or section is a stub. You can help the wiki by accurately contributing to it.

Difficulty level
Difficulty 2.png
Medium

WAIT! Have you read Getting Started, Beginner Mistakes, and some of the related OS theory?

Kernel Designs
Models
Other Concepts

Cross Compiling

Just like when building a OS in another language, such as C, you need to build a cross compiler. In contrast to GCC, this is much easier with Rust and its surrounding tools. Here's a list of the tools you'll need:

  • Rustup for managing your toolchain (https://rustup.rs/).
  • Rustc (installable via rustup)
  • Cargo (installable via rustup)

The cross compiler and core library will automatically be fetched, built, and installed when you build the project the right way.

Creating a target

In order to specify how to build the cross compiler and what features to include, you have to make a target.json file. The specification can be found here: https://book.avr-rust.com/005.1-the-target-specification-json-file.html.

An example might be as follows (for an x86_64 platform):

{
  "llvm-target": "x86_64-unknown-none",
  "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
  "arch": "x86_64",
  "target-endian": "little",
  "target-pointer-width": "64",
  "target-c-int-width": "32",
  "os": "none",
  "executables": true,
  "linker-flavor": "ld.lld",
  "linker": "rust-lld",
  "panic-strategy": "abort",
  "disable-redzone": true,
  "features": "-mmx,-sse,+soft-float",
  "dynamic-linking": false,
  "relocation-model": "pic",
  "code-model": "kernel",
  "exe-suffix": ".elf",
  "has-rpath": false,
  "no-default-libraries": true,
  "position-independent-executables": false,
  "pre-link-args": {
    "ld.lld": ["--script=<your linker script>"]
  }
}

Please note that you also need to provide a linker script. This is more or less analogous to what you could provide for a kernel built in a more conventional language like C. This is pretty dependent on which executable format and which bootloader you use (among other things), so this isn't listed here.

A minimal executable

This assumes you have at least some background with Rust (e.g. you've built some command line tools or crates before). It does not assume you know about the internals of the Rust language. It also assumes you will be using Cargo for your build system (you can call it from a Makefile if you need to do other things).

Please note that this depends on which bootloader you are using. For the purposes of this example, we assume you are working with Limine, over the Limine protocol, with an ELF binary. This will be different if you are using a different setup.

First, you must prepare your source. In general, rust tries to avoid command line flags in favour of in-source changes. As such, there is not Cargo or Rust flag to disable the standard library and add a custom panic handler. The below source takes care of that:

#![no_std]

#[no_mangle]
fn _start() {
    loop {}
}

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    loop {}
}

The above code tells the compiler to not include the Rust standard library (the core library is kept), not to mangle the _start symbol (Rust uses name mangling by default), and to use the panic() function as the panic handler. This approach by the Rust team generally keeps your rustc invocations simpler and easier to debug if anything goes wrong.

Another thing to keep in mind is that Rust uses stack unwinding by default, however, this is dependent on an underlying OS, which we obviously don't have (hey, you're here for a reason). To disable stack unwinding and just abort on panic (after the panic handler is called, of course), modify your Cargo.toml:

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"

Run cargo build to build your kernel, and do whatever you wish with the resulting ELF binary (look at your bootloaders instructions on how to build a bootable image).

Notes

  • There aren't as many resources for using rust to build a kernel. Be aware of this if this is your first attempt.
  • The rust libcore can easily be included in a kernel, it's analogous to C's freestanding headers
  • Enable `--gc-sections` in lld to avoid having to define symbols you don't need (e.g. floating point math functions)
  • Always use a target json file (Flexible Target Specifications)
  • If you're building a UEFI app, disable function sections as well
    • rustc puts every item in its own section by default. The PE format has a limit of 96 sections. So unless you disable function sections, your project will most likely fail to load.


Examples