In a previous article, a Rust program was generated using Cargo:
hello-rust.
$ cd $HOME/src/hello-rust
$ stat -c "%s" target/arm-buildroot-linux-gnueabihf/release/hello-rust
218732
The size of the binary file generated is 213 KB. Stripped, it will go down to
115 KB. This is indeed a big "Hello World"! Due to the architecture of the
runtime, all I/O handling code is included in any statically linked binary,
which is the default for rustc (running strings on the file ends up with a
scary result).
If multiple Rust programs should be included in the target root filesystem of an
embedded system, this would be a terrible waste of space.
It is possible to get a smaller file by adding -C prefer-dynamic to the
rustc command line. But for the program to run properly, the cross-compiled
versions of shared libraries with the missing symbols will have to be copied in
the target root file system.
These libraries were built along with the Rust compiler and are available in
$HOME/build/demo-rust/qemu/arm/host/usr/lib/rustlib/arm-buildroot-linux-gnueabihf/lib/.
Generating Dynamically Linked Programs with Cargo
It is possible to generate dynamically linked programs with Cargo. But it
requires a feature only available in version 0.10, which is not available yet.
Build Cargo from Master Branch
Go to the Buildroot base directory and grab the current version of Cargo (master
branch):
Note the last two lines, where Cargo is instructed to pass an additional flag to
the Rust compiler when building the project. The new flag is quite explicit.
Tell the Rust compiler where the target specification can be found:
Usually, ldd is used to list the dependencies of a dynamically linked
program. Unfortunately, this tool is not available in the Buildroot environment.
But it is possible to use the cross-compiled version of ld.so, the
dynamic linker/loader, to achieve the same goal.
On the target, the dynamic loader is
HOME/build/demo-rust/qemu/arm/target/lib/ld-2.22.so. As it is only
executable on an ARM machine, QEMU userspace emulator will be used to run it.
$ qemu-arm -r 4.5.0 -E LD_TRACE_LOADED_OBJECTS=1 -E LD_VERBOSE=1 \
> $HOME/build/demo-rust/qemu/arm/target/lib/ld-2.22.so \
> target/arm-buildroot-linux-gnueabihf/release/hello-rust
libstd-ca1c970e.so => not found
libgcc_s.so.1 => not found
libc.so.6 => not found
Version information:
target/arm-buildroot-linux-gnueabihf/release/hello-rust:
libgcc_s.so.1 (GCC_3.5) => not found
libc.so.6 (GLIBC_2.4) => not found
As shown, the program depends on the C runtime (libc.so.6), the GCC
low-level runtime library
(libgcc_s.so.1) and libstd-ca1c970e.so, the Rust standard library.
The dependencies are listed, but as the shared libraries have not been found,
the inspection is incomplete. Here is the result when passing the location of
the libraries:
That is quite an exhaustive list! But it shows that only libstd-ca1c970e.so
is required, as all the other dependencies are already in the target root
filesystem.
As this library has been originally installed in
$HOME/build/demo-rust/qemu/arm/host/usr/lib, the runtime search path in the ELF file is incorrect. To check
and modify it, build patchelf:
Go back to the Buildroot directory. Before rebuilding the system image, generate
the cache of the loader (this operation is not automagically performed by
Buildroot):