3 Comments

Embedded Rust Right Now!

I’ve been very excited about the Rust programming language for a while now, as evidenced by some previous posts.

Rust recently released 1.0-alpha, which mean it’s stable enough that I might actually consider using it for things. One of the killer features of Rust (at least for me) is that you can call Rust from C and C from Rust. This means that we don’t have to take an all-or-nothing approach to using Rust in our projects. We can mix and match as it makes sense. This also means that we don’t have to wait for peripheral libraries and RTOS’s to be ported to Rust. We can try out Rust on out embedded projects alongside existing C libraries right now! Here’s how.

1. Get the Rust Compiler

It turns out getting Rust setup for cross-compiling to an embedded platform is actually really easy. Because Rust uses LLVM as its code generator, the Rust compiler is a cross-compiler right out of the box. The only extra step is compiling a version of Rust’s libcore for your target architecture. This turns out to quite straightforward.

If you haven’t already done so, get a copy of the Rust compiler for your platform.

2. Get a Target Spec for your Target Architecture

To do a cross-compile you’re going to need a description of target architecture that Rust can use to tell LLVM how it should generate code. You’re going to need to find (or make) a target spec for your target architecture.

Save the spec without the .json extension in the same directly that you’re going to be invoking rustc from (usually the root of your project).

3. Build libcore

It’s important that the libcore you use matches your compiler version. You can get the git hash used to build your compiler like this:

$ rustc -v --version
rustc 1.0.0-nightly (8903c21d6 2015-01-15 22:42:58 +0000)
binary: rustc
commit-hash: 8903c21d618fd25dca61d9bb668c5299d21feac9
commit-date: 2015-01-15 22:42:58 +0000
host: x86_64-apple-darwin
release: 1.0.0-nightly

And then clone the Rust repository and checkout that specific commit:

git clone git@github.com:rust-lang/rust.git
cd rust
git checkout 8903c21d618fd25dca61d9bb668c5299d21feac9
cd ..

Your commit-hash will almost certainly be different than what I have here. Don’t just copy what I have :)

Then to build libcore you just do:

mkdir libcore-thumbv7m
rustc -C opt-level=2 -Z no-landing-pads --target thumbv7m-none-eabi -g rust/src/libcore/lib.rs --out-dir libcore-thumbv7m

Now you’re ready for cross-compiling.

4. Start Using Rust on your Embedded Platform

At this point you can start compiling .rs files into object files that can be fed to your linker just like you’d do with object file generated from C.

For example:

#![feature(no_std)]
#![feature(core)]
#![no_std]
#![crate_type="staticlib"]


// **************************************
// These are here just to make the linker happy
// These functions are just used for critical error handling so for now we just loop forever
// For more information see: https://github.com/rust-lang/rust/blob/master/src/doc/trpl/unsafe.md

#![feature(lang_items)]

extern crate core;

#[lang="stack_exhausted"] extern fn stack_exhausted() {}
#[lang="eh_personality"] extern fn eh_personality() {}

#[lang="panic_fmt"]
pub fn panic_fmt(_fmt: &core::fmt::Arguments, _file_line: &(&'static str, usize)) -> ! {
  loop { }
}

#[no_mangle]
pub unsafe fn __aeabi_unwind_cpp_pr0() -> () {
  loop {}
}

// **************************************
// **************************************

// And now we can write some Rust!


#[no_mangle]
pub fn main() -> () {
  
  // Do stuff here
  loop {}

}

Then to compile:

rustc -C opt-level=2 -Z no-landing-pads --target thumbv7m-none-eabi -g --emit obj -L libcore-thumbv7m  -o my_rust_file.o my_rust_file.rs

Now you can link my_rust_file.o into your project just like you would any other .o file.