Porting Rust to DragonFly
During the last week I spent numerous hours on porting my favorite programming language Rust to my favorite operating system DragonFly BSD. While my first attempt months earlier failed, this time I was successful!
In this article I go through all the problems I had to deal with and how you can build Rust for DragonFly yourself. This might also be of interest to people who want to port Rust to further platforms like NetBSD or OpenBSD.
You find the scripts needed to compile Rust on DragonFly here and my DragonFly branch of Rust here (hopefully to be merged soon).
How to port Rust to a new platform (as of 2014)
In short, what needs to be done to port Rust to a new platform, is the following:
-
Extend the
rustc
compiler to generate code for the platform you are porting to. -
As
rustc
depends on LLVM this might require some patching here too to be able to generate code for the target operating system. In my case I had to extend LLVM to support segmented stacks on DragonFly. -
As Rust (still) requires segmented stacks there needs to be support for it in the operating system. Rust and LLVM need a thread local field where they can store the size of the stack.
-
The Rust libraries (e.g.
liballoc
,liblibc
,libstd
,libnative
) need to be ported. This basically involves adding some#[cfg(target_os = "dragonfly")]
somewhere, but also some system structures need to be adapted.
Once this is done we are ready to cross-compile Rust to DragonFly.
Cross-compiling Rust to DragonFly
After trying to cross-compile Rust by specifying --target x86_64-pc-dragonfly-elf
to Rust’s own configure
script and spending numerous
hours just to note that the build fails, I gave up on this idea. Instead I took
a different approach as shown below. Note that this all requires my dragonfly
branch of rust. All stages depend sequentially on another.
Below we are using the scripts from my
rust-cross-dragonfly project.
Stage 1 (Linux, DragonFly)
-
On Linux: Compile a modified
rustc
on Linux that supports DragonFly as target (rustc natively is able to generate code for different targets!). -
On DragonFly: At the same time we can build the C libraries Rust depends on.
Use scripts stage1-linux.sh
and stage1-dragonfly.sh
respectively.
Actually I tried to cross-compile everything on Linux but it failed for LLVM. So I decided to build everything that involved compiling C or C++ on the target (DragonFly) itself.
Stage 2 (Linux)
We have to copy the outcome of stage1-dragonfly (which is stage1-dragonfly.tgz
)
to the Linux machine and extract it to create stage1-dragonfly/
. This
includes the C libraries required for Rust as well as some system libraries.
Stage2 (sh stage2-linux.sh
) cross-compiles all of Rust’s libraries (e.g.
libstd
, libsyntax
, librustc
) and generates the object file for the
rustc
binary (driver.o
). I don’t generate the executable here itself, as
static initializations were omitted and the generated rustc
was unable to
find statically registered command line options of LLVM. I spent hours to
figure this out! The solution was to just generate the object file for rustc
and together with the Rust libraries pass it on to Stage3, which links the
rustc
binary on DragonFly.
Copy the outcome of Stage2 (stage2-linux.tgz
) back to the DragonFly system.
Stage 3 (DragonFly)
Finally back again on DragonFly. Extract stage2-linux.tgz
to become
stage2-linux
. In this stage we will construct a working rustc
binary (the
Rust compiler) and test to compile a simple Hello World application hw.rs
natively on DragonFly.
All you have to do is to execute sh stage3-dragonfly.sh
.
Stage 4 (DragonFly)
Stage4 is the last stage and it builds my dragonfly branch with the compiler from Stage3. It uses Rust’s own build infrastructure and not my “hacked” build infrastructure.
All you have to do is to execute sh stage4-dragonfly.sh
and wait. It does
not automatically install Rust for you so you have to sudo gmake install
inside directory stage4-dragonfly/rust
yourself.
Problems faced
-
LLVM does not support segmented stacks on DragonFly. So I had to patch LLVM and will submit the patch upstream soon.
-
The LLVM patch implies having a special field in the thread control block to record the stack size. Thanks to Matthew Dillon I was “allowed” to use a yet unused field for it and the patch got accepted.
-
Rust fails with a memory corruption when jemalloc is not used, see my bug report #16071. I noticed that earlier on Linux but ignored it. Later when I had a
rustc
compiler working on DragonFly I got hit by this again as I built it without jemalloc. It cost me a whole night ktracing it and several reboots. The solution was to compile with jemalloc. -
Intially, the ports of
libstd
andliblibc
were basically copies of the related FreeBSD code. Several times I got bitten by differences in system structures and return codes. The right solution would be to generate code like this in an automated fashion. -
Cross-linking, i.e. linking an executable targeted for DragonFly on Linux, has some limitations. Or maybe I did something wrong. Static initializations are handled incorrectly as they are not executed. It took me a while to figure this out after searching through the LLVM code.
-
Last but not least, compiling Rust takes an enormous amount of time. That is, the turn-around times are quite high. Compiling, fixing an error, compiling again. And so on and on.
Conclusion
Rust works on DragonFly, yippey! I work on uploading a snapshot and maybe setting up a regular build and add it to dports. Hopefully this document also helps others in porting Rust to further platforms, mainly OpenBSD and NetBSD.