Did Google bring you here? Anyways, I'd like to inform you that this document is DEPRECATED, that doesn't mean that this information is wrong, but it could be; it means that this document won't receive updates or fixes in the case it has wrong information.
And, I'd like to point you to my new cross compilation guide: rust-cross. It's way more extensive than this document, contains several updates about recent and future improvements in tooling, and, most importantly, it's being maintained.
-- @japaric, 2016/02/05
You can see this document rendered at:
https://github.com/japaric/ruststrap/blob/master/1-how-to-cross-compile.md
"Compiling 'Hello, world!' on my (single core) ARM device takes more than 5 seconds!"
If developing on your ARM device is intolerable due to the long compilation times, there's an alternative for you: cross compilation (which comes with its own set of problems, btw).
Cross compilation in Rust almost works out of the box, this how-to gives you the missing bits of information needed to make it work.
Note: In this how-to I'll use arm-unknown-linux-gnueabihf
as the target
triple (the cross compilation target), and x86_64-unknown-linux-gnu
as the
host triple (my PC, where everything is compiled). But, it should be possible
to use different target/host triples with a little tweak of the instructions
outlined here. For example, I've used these instruction to cross compile from
x86_64 to i686 in Linux.
Cross compiling should be as easy as passing the --target=$TRIPLE
flag to
rustc
, right?
// frestanding.rs
#![crate_type = "lib"]
#![feature(no_std)]
#![no_std]
$ rustc --target=arm-unknown-linux-gnueabihf freestanding.rs && echo OK
OK
It worked! Because it has no dependencies, but that's not an interesting crate
unless you want to build your own core
crate. If you then try the classic
"Hello, world!":
// hello.rs
fn main() {
println!("Hello, world!");
}
$ rustc --target=arm-unknown-linux-gnueabihf hello.rs
hello.rs:1:1: 1:1 error: can't find crate for `std`
hello.rs:1 // hello.rs
It doesn't work!
What the compiler really wants to say here is that there isn't a std
crate
for the arm-unknown-linux-gnueabihf triple. In other words, you need to have
a std
crate that was compiled for ARM installed somewhere in your system. But
the nightly you have installed only comes with native crates.
You can cross compile the ARM version of std
and friends from Rust's source
code, you just need to pass an extra flag to the configure
script:
$ ./configure --target=arm-unknown-linux-gnueabihf,x86_64-unknown-linux-gnu
$ make -j$(nproc)
$ sudo make install
If you don't feel like spending 1 hour or more bootstrapping the compiler, I got good news for you. My unofficial ARM nightlies contain the ARM crates you need. And the official i686 nightly contains the i686 crates needed to cross compile to i686, etc.
You only need to install the static (.rlib
) libraries from the foreign
nightly into your native installation:
$ tree /usr/local/lib
.
|-- bin
| `-- rustc
`-- lib
|-- libstd-deadbeef.so
|-- (..)
`-- rustlib
|-- arm-unknown-linux-gnueabihf <-- Copy this folder from the foreign nightly
| `-- lib
| |-- libstd-deadbeef.rlib
| `-- (..)
`-- x86_64-unknown-linux-gnu <-- This comes with the native nightly
`-- lib
|-- libstd-deadbeef.rlib
`-- (..)
If you are using multirust, your toolchain is installed at:
~/.multirust/toolchains/nightly
Once you've installed the ARM crates, you can now cross compile static libraries:
// lib.rs
#![crate_type = "lib"]
pub fn hello() {
println!("Hello, world!");
}
$ rustc --target=arm-unknown-linux-gnueabihf lib.rs && ls lib*
liblib.rlib
But, if you try the "Hello, world!" crate again:
$ rustc --target=arm-unknown-linux-gnueabihf hello.rs
error: linking with `cc` failed: exit code: 1
note: "cc" '"-Wl,--as-needed"' (plus a bunch of other flags)
note: /usr/bin/ld: hello.o: Relocations in generic ELF (EM: 40)
/usr/bin/ld: hello.o: Relocations in generic ELF (EM: 40)
hello.o: error adding symbols: File in wrong format
collect2: error: ld returned 1 exit status
error: aborting due to previous error
It still doesn't work!
What went wrong this time is that rustc
is using cc
to link the binary,
and this doesn't work because cc
is a symlink to your native compiler
(gcc
). (See rust-lang/rust#9328)
The solution is to tell rustc
to use the right linker:
# install an ARM cross-compiler, if you don't have one installed already
# this command is for Ubuntu/Debian, use whatever is appropiate for your OS
$ apt-get install gcc-arm-linux-gnueabihf
# tell rustc the name of the cross-compiler (which must be in your PATH)
$ rustc -C linker=arm-linux-gnueabihf-gcc-4.8 --target=arm-unknown-linux-gnueabihf hello.rs
# check that the produced binary is really an ARM binary
$ file hello
hello: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), (..)
# sanity check: the binary should work on an ARM device
$ scp hello me@arm:~
$ ssh me@arm ./hello
Hello, world!
Note: If you cross compiling to i686 on a x86_64 PC, then using the native
compiler (cc
) is OK, because the native compiler is also a cross compiler for
i686. But chances are you'll get a different error due to missing 32bit
libraries (e.g. ld cannot find crti.o
), you'll need to install the
libc6-dev-i386
package (Debian/Ubuntu) or equivalent to fix that error.
To tell cargo to cross compile you also have to pass the --target
flag to the
build
command. But, there is something you need to change before it will
Just Work.
By default, cargo
(just like rustc
) will use cc
as the default linker for
native and cross compilation. You can override this behavior using Cargo's
configuration file.
# you can set a different linker for each target
$ cat ~/.cargo/config
[target.arm-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc-4.8"
# sanity check
$ cargo new --bin hello
$ cd hello
$ cargo build --target=arm-unknown-linux-gnueabihf
$ file target/arm-unknown-linux-gnueabihf/hello
hello: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), (..)