I found a guide for cross-compiling Rust which basically involves
configuring it with a --target
option. The target needs to include the
cross-compile target and the host target.
$ wget https://static.rust-lang.org/dist/rustc-1.5.0-src.tar.gz
$ tar xf rustc-1.5.0-src.tar.gz
$ ./configure --target=arm-unknown-linux-gnueabi,x86_64-unknown-linux-gnu --prefix "$(PWD)"/../bin
$ make -j$(nproc) install
This didn't work with my target arm-unknown-linux-gneabi
as at some point in
the build it started trying to use arm-linux-gneabi-gcc
and
arm-linux-gneabi-ar
, which didn't work as they are called
arm-unknown-linux-gneabi-gcc
and arm-unknown-linux-gneabi-gcc
. I guess this
is a problem with the build script, but I couldn't easily work out a fix, so I
just created some symlinks with the names it was expecting. This worked well.
$ export LD_LIBRARY_PATH=~/projects/arm-rust-cross-compiler/bin/lib/
$ export PATH=$PATH:~/projects/arm-rust-cross-compiler/bin/bin/
$ bin/bin/rustc --version
rustc 1.5.0-dev
Cargo can also be built from source. I don't think Cargo needs to be cross-compiled as it supports compiling for different targets and I don't actually want to run Cargo on the target machine.
$ git clone https://github.com/rust-lang/cargo
$ cd cargo
$ git submodule update --init
$ python -B src/etc/install-deps.py
$ ./configure --local-rust-root="$PWD"/../bin --prefix "$(PWD)"/../bin
$ make
$ make install
To cross-compile with Cargo, a linker needs to be specified for the target. In my case:
$ cat > ~/.cargo/config << EOF
[target.arm-unknown-linux-gnueabi]
linker = "arm-unknown-linux-gnueabi-gcc-5.2.0"
EOF
Now Cargo can be used to create a new project and build a binary for an ARM target.
$ cargo new --bin hello-rust
$ cd hello
$ cargo build --target=arm-unknown-linux-gnueabi
$ file target/arm-unknown-linux-gnueabi/debug/hello-rust
target/arm-unknown-linux-gnueabi/debug/hello: ELF 32-bit LSB shared object,
ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter
/lib/ld-linux.so.3, for GNU/Linux 4.3.0, not stripped
I was pretty confident at this point, until I tried to run it in my emulator and the program immediately terminated with the message "Illegal instruction". So far, my cross-compiler toolchain and emulator had worked well. Why is now generating illegal instructions?
I eventually found that I wasn't specifying a CPU type and qemu
was using the
default ARM926 processor on the Versatile board. This can be fixed by adding
--cpu arm1176
(which is the CPU on the first generation Raspberry Pi which I
am trying to emulate). However, now the Kernel doesn't boot as it was built for
an ARM926 processor as specified by versatile_defconfig
.
The Linux ARM Versatile config restricts the possible CPU targets to CPUs that
can actually fit in the board. Fortunately I found this patch which removes
this restriction. Now make ARCH=arm versatile_defconfig && make ARCH=arm
menuconfig
allows the System Type -> Support ARM V6 processor
option to be
selected.
Finally, after rebuilding the Kernel and adding the Rust built hello
binary to
the filesystem, the program can run in the emulator.
$ qemu-system-arm -M versatilepb -cpu arm1176 -m 128M -kernel zImage -initrd ./rootfs.img.gz -append "root=/dev/ram rdinit=/sbin/init"