In my Formula Student team I’m using a Teltonika RUT956 router for relaying telemetry data over UDP to a base station outside the car. This thing has a 32-bit little-endian MIPS CPU without a floating-point unit (FPU). You can install Python on it, but the RUT956 isn’t very powerful, so Python is too slow for handling real-time telemetry data.
I figured I’d give Rust a shot, which brings us to the topic of today’s post: how do you compile Rust code for a MIPS target without FPU support?
After spending weeks recompiling the Rust standard library and staring at assembly in gdb,
I finally got it working and thought I’d write up a guide to save you (and future me) some pain.
Step 0: Set up your project
If you don’t already have one, make a new project.
cargo new --bin rust-mips-softfloat Put this into src/main.rs to test floating-point functionality:
// src/main.rs
use std::io;
fn main() {
if let Some(Ok(line)) = io::stdin().lines().next() {
let x: f32 = line.trim().parse().unwrap();
println!("x = {x}");
println!("sin(x) = {}", x.sin());
}
} Step 1: Get the cross-compilation toolchain
I’m using musl as the C standard library here. You can try your luck with glibc, but that’s on you to figure out.
Since my MIPS CPU is little-endian, I’m using the mipsel target and toolchain.
If you have a big-endian MIPS CPU, use mips instead for the target and cross-compilation toolchain.
To link Rust code for MIPS, you’ll need a cross-compilation toolchain. musl.cc conveniently provides precompiled toolchains for various targets,
including mipsel-linux-muslsf-cross, which is the one we need.
That toolchain name means:
mipsel: A MIPS little-endian CPUlinux: running a Linux OSmuslsf: using the musl C standard library and soft float
You could download it, extract it, add the bin directory to your PATH, and so on,
but here’s a Dockerfile that does the work for you without cluttering your pristine system (you did clear out your downloads folder in the last year, right?).
# builder/Dockerfile
FROM rust:1
# Install the cross-compilation toolchain.
RUN apt update \
&& apt install -y --no-install-recommends curl ca-certificates tar \
&& rm -rf /var/lib/apt/lists/*
ARG TOOLCHAIN_ROOT=/opt/toolchains
ARG TOOLCHAIN=mipsel-linux-muslsf-cross
ARG LINKER=mipsel-linux-muslsf-gcc
RUN mkdir -p ${TOOLCHAIN_ROOT} \
&& curl -fsSL https://musl.cc/${TOOLCHAIN}.tgz | tar -xzf - -C ${TOOLCHAIN_ROOT}
ENV PATH="${TOOLCHAIN_ROOT}/${TOOLCHAIN}/bin:${PATH}"
ENV CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_MUSL_LINKER=${LINKER}
# Install a nightly Rust toolchain.
RUN rustup toolchain install nightly --component rust-src
RUN useradd -ms /bin/bash builder
USER builder
WORKDIR /workspace Step 2: Create a builder container
To streamline the build process, create a docker-compose.yaml file:
# docker-compose.yaml
volumes:
cargo-cache:
services:
builder:
build: ./builder
working_dir: /workspace
volumes:
- cargo-cache:/usr/local/cargo
- .:/workspace
command: cargo +nightly build --release --target mipsel-unknown-linux-musl -Z build-std Step 3: Build your code
Now you can simply run
docker compose run --rm builder and your binary will be at target/mipsel-unknown-linux-musl/release/rust-mips-softfloat.
Step 4: Check the binary
To verify that the binary is actually using soft float,
you can use readelf from the cross-compilation toolchain to check the attributes.
docker compose run --rm builder bash -c "mipsel-linux-muslsf-readelf -A target/mipsel-unknown-linux-musl/release/rust-mips-softfloat | head -n 12" You should see something like this:
Attribute Section: gnu
File Attributes
Tag_GNU_MIPS_ABI_FP: Soft float
MIPS ABI Flags Version: 0
ISA: MIPS32r2
GPR size: 32
CPR1 size: 0
CPR2 size: 0
FP ABI: Soft float
ISA Extension: None If you see “Hard float” instead,
double-check you’re using the mipsel-unknown-linux-musl target and mipsel-linux-muslsf-gcc linker.
Step 5: Test it on your device
Copy the binary over to your MIPS device and give it a spin. It should calculate the sine of any input float without crashing due to illegal instructions.
$ ./rust-mips-softfloat
4.2↵
x = 4.2
sin(x) = -0.87157565 Conclusion
That’s it! I hope this guide helped you get your Rust code running on MIPS.
If you ran into any issues, noticed a mistake, or just want to share your thoughts, drop a comment below or shoot me an email.
Happy tinkering!