Skip to content

Instantly share code, notes, and snippets.

@Schievel1
Created October 10, 2023 08:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Schievel1/e5e8074342ff8bff56d7e9336dd0fca6 to your computer and use it in GitHub Desktop.
Save Schievel1/e5e8074342ff8bff56d7e9336dd0fca6 to your computer and use it in GitHub Desktop.
Cross compliling Rust with SDK toolchain an GCC
* Cross compiling Rust
Rust mainly is statically linked by default. But when compiled against Glibc (for syscalls etc.) it is dynamically linked against that.
There are several ways to tackle this problem
NOTE: The target-triples (the 'arm-unknown-linux-musleabihf' and such) in the following commands are just examples. The targets need to be adapted accordingly.
** Static linking using Musl
While it is not impossible to statically link against Glibc it is discouraged. (Mostly because Glibc itself makes great use of dlopen and other dynamic libraries.)
Musl is a Glibc alternative that can be statically linked and is often used for that in cross compiling scenarios.
To link statically with musl we need to install the musl rust toolchain:
#+begin_src bash
rustup target add armv7-unknown-linux-musleabihf
#+end_src
Then we could build using a command like
#+begin_src bash
cargo build --target=armv7-unknown-linux-musleabihf --release
#+end_src
But we will get a error about a missing linker most likely. See below what to do about this.
** Using dynamic linking and Glibc
Glibc is usually dynamically linked. This becomes brittle when the Glibc version on the target is not the same as the one on the compiling system.
While glibc is backwards compatible (this means a program compiled against an older glibc can run with a newer version on the target, but not vice versa) things become troublesome when the version a program is built against is newer that the version on the target. This is often the case with embedded systems that usually aren't updated very often. Our dev machines are and often enough we have a Glibc on the target that isn't even available in the distros repos anymore.
Therefore we need to link against an older glibc, preferably exactly the one that is on the target.
*** Installing the toolchain
#+begin_src bash
rustup target add armv7-unknown-linux-gnueabihf
#+end_src
Then we could build using a command like
#+begin_src bash
cargo build --target=armv7-unknown-linux-gnueabihf --release
#+end_src
But we will get a error about a missing linker most likely. Also that glibc of the target isn't available. See below what to do about this.
*** Linking against a different glibc
:PROPERTIES:
:ID: 93c74dae-86e7-4396-bef1-d6cd6dfae122
:END:
First we need to get that library we want. Some distros have some versions in their repos, but most of the time boards come with SDKs. Those are packages that contain everything we need to cross build.
e.g. [[https://www.ti.com/tool/PROCESSOR-SDK-AM335X#downloads][This SDK for TI boards]]
But we need to tell cargo in some way where to find that linker in there. When compiling C we would just do ~arm-linux-gnueabihf-gcc --sysroot=/path/to/sysroot~ and that would be it. For Rust we do something similar
First, we add a executable as linker for our target:
#+begin_src toml (~/.cargo/config)
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-gcc-sysroot"
# if your compiliation need it feel free to add more things here like
#ar = "/path/to/your/toolchain/arm-linux-gnueabihf-gcc-ar"
#+end_src
Then we create a file called arm-gcc-sysroot somewhere in $PATH (I usually use ~~/.cargo/bin~) and make it executeable:
#+begin_src bash
touch ~/.cargo/bin/arm-gcc-sysroot
chmod +x ~/.cargo/bin/arm-gcc-sysroot
#+end_src
In that file we write a script that is basically a wrapper around our linker with a sysroot passed to it:
#+begin_src
#!/bin/bash
/path/to/your/toolchain/usr/bin/arm-linux-gnueabihf-gcc \
--sysroot=/path/to/the/sysroot/ti-processor-sdk-linux-am335x-evm-06.03.00.106/linux-devkit/sysroots/armv7at2hf-neon-linux-gnueabi/ "$@"
#+end_src
** Installing linkers
Rust still wants to use the gcc as the linker to link musl together with our program. If we compile now, we will end up with a linker not found error. So we need to install that linker.
*** Installing the linker
We either install a gcc toolchain to link with. For gentoo that command would be something like
#+begin_src bash
sudo crossdev --target arm-unknown-linux-musleabihf
#+end_src
Other distros often have toolchains for common targets in their repos.
Or we could use clang linker lld instead of gcc. The benefit of that is that it comes for many targets at once.
#+begin_src bash
sudo emerge sys-devel/lld
#+end_src
*** Telling cargo where to find that linker
Usually cargo searches for the linker it $PATH and tries to guess its name. That is often working, but if its not, we can tell cargo which linker to use:
(in this example it would guess armv7-unknown-linux-musleabihf-gcc, but the binary is called arm-unknown-linux-musleabihf-gcc)
#+begin_src toml (~/.cargo/config)
# when using gcc
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-unknown-linux-musleabihf-gcc"
# when using clangs lld
[target.armv7-unknown-linux-gnueabihf]
linker = "lld"
#+end_src
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment