Subscribe to this blog


I have started working on a port of Rust to L4Re, Fiasco.OC or simply to the L4 variant of the TU Dresden. It's a third-generation microkernel with many nice features, most notably strong safety guarantees through complete isolation of components and virtualisation of processes, but it lacks Rust support.

Why Rust

Rust brings safety, concurrency and zero-cost abstractions to a world, where normally programming in unsafe languages like C and partly C++ is the norm. On the other hand, L4Re is a microkernel built on top of isolation and offers many safety features, not found in other operating systems. Complementing this with Rust's safety guarantees is the right thing to do.

Rust has proven that it can be as efficient as C++ or even close to C. Various projects already demonstrate Rusts usefulness (e.g. Servo) and it is even used in systems programming by the Rust OS Redox and as a language for the userland of the SEl4 microkernel.

Roadmap

In the first phase, I have integrated Rustc into BID (the L4Re build system). Phase two encompasses patching Rust's standard library. I'm in the middle of this very process, the first patches have already landed in liblibc (the libc binding library).\ In the third phase, I will map L4Re's inter-process communication primitives (IPC) to Rust's strong type system. This crucial for a microkernel OS, because virtually all system services have to communicate in a safe, yet efficient manner.

What Works

My aim is to bring Rust support to L4re, but also bring L4re support to Rust. I have patched BID, so that Rust programs and libraries can be compiled for L4re. This is in an early stage and is going to change in the future.

The standard library of Rust consists of many small libraries, all united under the std crate. std has not been ported yet, but all other libraries already run smoothly.

Practical Insights

All of this is ready-to-use in a GitHub repository in the branch stdlib. Below, I'll explain what I did / how to reproduce this.

If you are curious and want to try out Rust on L4re, you can already do so. You will need a computer with GNU Make, GCC, GCC Multilib and Rust. I've tested it on a GNU/Linux, but you might have success on other platforms, too.

First of all, a L4Re snapshot is required, which you can download from http://l4re.org/download.html. This snapshot also contains l4linux which is not required and causes trouble if you don't have the Linux headers available. I hence suggest

$ cd *snapshot* && rm -rf src/l4linux

You have to configure and build your build tree then:

$ make setup
$ make -j8

To avoid headache, you should set the OBJ variable, which points to the build tree of L4/L4Re. Assuming that the current working directory is the root of the snapshot, this can be accomplished like this

$ echo O=`pwd`/obj/l4/amd64 >> src/l4//conf/Makeconf.local

NOTE:

  1. It could be that the above setting is not enough. When you later compile a program and it cannot find the object directory, change O= to OBJ_BASE=.
  2. Please do not use spaces in your path.
  3. Please DO NOT use ~/ to denote your home directory. This will lead to errors with no recognizable relation to the issue.

With a basic L4Re system in place, adding a patch to build simple Rust binaries is actually not too complicated. The patch is only 113 lines long. You can view the patch or download it straight away:

$ curl -o /tmp/l4.diff paste.debian.net/download/976734
$ cd path/to/l4/snapshot
$ patch -p0 < /tmp/l4.diff

Most of the relevant changes are in the src/l4/mk directory, in the files rules.mk and binary.mk, used by prog.mk. I won't explain all the changes here. If you are familiar with make, you'll understand the changes after some digging.

An example (C) program is located in src/l4/pkg/hello. CChange to this directory, remove the C source and add a main.rs with the following content:

#![no_std]
#![feature(collections)]
#![feature(lang_items)]
#![feature(panic_unwind)]

extern crate panic_unwind;
extern crate libc;
#[macro_use]
extern crate collections;

use libc::*;

/// Empty panic formatter...
#[lang="panic_fmt"]
#[no_mangle]
pub fn panic_fmt() -> ! {
    loop { } // no handling
}

#[no_mangle]
pub extern "C"
fn main(_: i32, _: *const *const u8) -> i32 {
    let nums = vec![1, 2, 3];
    unsafe {
        printf("I can count: \0".as_ptr());
    }
    for elem in nums {
        unsafe {
            printf("%i, \0".as_ptr(), elem);
        }
    }
    unsafe {
        printf("\n\0".as_ptr());
    }
    0
}

This program does nothing spectacular, in fact, it's pretty boring and verbose. This is because most of the convenience from the std crate is missing.\ The Makefile could look like this:

```make^ PKGDIR ?= . L4DIR ?= $(PKGDIR)/../..

TARGET = hello SRC_RS = main.rs

REQUIRE_LIBS += libpanic-rust libcollections-rust libcore-rust liballoc-rust export RUSTC_BOOTSTRAP=1

include $(L4DIR)/mk/prog.mk ```

Right, now you expect you could run that program? Not quite. Currently, Rust needs custom core library versions to run on L4Re. The source code of these libraries need to be packaged and built. This is automated by a script. If you're curious, change to src/l4/pkg/libstd-rust in the mentioned GitHub repository and read the README. I won't detail that here, because it's a temporary solution. But if the build files have been generated, building the library is as easy as typing make in the generated build/<libname> directory.

Rust Is Already A Cross Compiler!

If you are more familiar with the Rust ecosystem, you might wonder why the integration is not happening within the Rust compiler, because this one already provides cross-compilation. The answer is a bit more difficult. The BID is a sophisticated build system and using Cargo or Xargo would require bypassing it. Interoperability would become a nightmare. L4re has custom startup- and teardown code and custom linker flags; it is best, if all of that is handled by BID itself. The Rustc compiler should receive it's linking instructions from BID and carry out the operation.

In the future, if you grab a snapshot from L4Re.org, your Rust compiler will automatically be used when you write Rust code within the L4Re source tree, only the target "l4re" would need to be activated using Rustup.

Integrating Rust into the BID doesn't mean that you have to throw away Cargo and all its convenience. In fact, Cargo integration is a long-term goal. This is not easy, because both BID and Cargo handle dependency management and define a "package" differently.

L4re + Rust support is evolving, so stay tuned!



Comments