At a recent functional programming meetup I was discussing with a colleague about how nice it would be to be able to use Rust in Gecko. This made me curious if it was possible to implement libraries in Rust and call them from C. After the meeting I asked in #rust and got pointed to some projects that showed the way.

This lead me to trying to come up with as simple example as possible of compiling a Rust file into an object file, linking it to a C program and running it without the Rust runtime. The code is in my rust-from-c-example github repository. It can be cloned and built with:

$ git clone http://github.com/doublec/rust-from-c-example $ cd rust-from-c-example $ make $ ./test

To avoid issues with integrating with the Rust runtime I've opted to not use it. This means no threads and limits the standard library usage. This example is very simple, only demonstrating adding two numbers. Extending from this will be an interesting exercise to see how much Rust can be used.

The Rust code is:

#[crate_type = "lib"]; #[no_std]; #[allow(ctypes)]; #[no_mangle] pub extern fn add(lhs: uint, rhs: uint) -> uint { lhs + rhs }

The first three lines ensure that the file is compiled as a library, does not use the standard library and can use C types. The no_mangle declaration stops the Rust default of mangling function names to include their module and version information. This means that add in Rust is exported as add for C programs. The extern makes the function available from C and defaults to cdecl calling format.

To generate a .o file that can be linked into a C program:

$ rustc -c mylib.rs

The C program creates an extern declaration for add and calls it:

#include <stdio.h> extern unsigned int add(unsigned int lhs, unsigned int rhs); int main() { printf("add(40,2) = %u

", add(40,2)); return 0; }

Unfortunately we can't just compile and link with the mylib.o file. This results in a linker error:

mylib.o: In function `add': mylib.rc:(.text+0x4f): undefined reference to `upcall_call_shim_on_rust_stack' collect2: error: ld returned 1 exit status

Some searching pointed me to armboot which had a stub implementation for this in zero.c. Compiling and linking to that worked successfully. A cut down variant of zero.c is included in the project.

There's a bunch of limitations with this approach. We're basically using Rust as a higher level C. This post on embedding Rust in Ruby details some of the limitations:

When calling Rust code you will not be executing in a Rust task and will not have access to any runtime services that require task-local resources. Currently this means you can't use the local heap, nor can you spawn or communicate with tasks, nor call fail!() to unwind the stack. I/O doesn't work because core::io (unfortunately, and incorrectly) uses @-boxes. Even logging does not work. Calling any code that tries to access the task context will cause the process to abort. Because code is not executing in a task, it does not grow the stack, and instead runs on whatever stack the foreign caller was executing on. Recurse too deep and you will scribble on random memory.

Hopefully some of these limitations will go away or 'zero runtime' libraries will appear to make this sort of usage easier. Some resources that helped putting this together were: