Générer des Programmes Liés Dynamiquement avec Cargo
Des Gros Programmes, liés Statiquement
Dans un article précédent, Cargo avait été utilisé pour générer un programme Rust: hello-rust.
$ cd $HOME/src/hello-rust $ stat -c "%s" target/arm-buildroot-linux-gnueabihf/release/hello-rust 218732
La taille du fichier binaire généré est de 213 Ko. Une fois les symboles enlevés, elle descendra jusqu'à 115 Ko. C'est vraiment un "Hello World!" imposant! Étant donné l'architecture du "runtime", tout le code de gestion des entrées/sorties est inclus dans tout binaire lié statiquement, ce qui est le comportement par défaut de rustc (l'exécution de strings sur le fichier donne un résultat effrayant).
Si plusieurs programmes Rust doivent être inclus dans le système de fichiers d'un système embarqué, ce serait un gâchis de place.
Il est possible d'obtenir un fichier plus petit en ajoutant -C prefer-dynamic lors de l'invocation de rustc. Mais pour que le programme puisse s'exécuter correctement, les versions des bibliothèques partagées contenant les symboles manquants, construites avec le compilateur croisé, doivent être copiées dans le système de fichiers de la cible.
Ces bibliothèques ont été construites en même temps que le compilateur Rust et
sont disponible dans
$HOME/build/demo-rust/qemu/arm/host/usr/lib/rustlib/arm-buildroot-linux-gnueabihf/lib/
.
Generating Dynamically Linked Programs with Cargo
Il est possible de générer des programmes liés dynamiquement avec Cargo. Mais cela requiert une fonctionnalité uniquement disponible dans la version 0.10.0, qui n'est pas encore disponible.
Construction de Cargo depuis la Branche "master"
Allez dans le répertoire de base de Buildroot et récupérez la version courante de Cargo (branche "master").
git clone --recursive https://github.com/rust-lang/cargo \ $HOME/build/demo-rust/qemu/arm/build/host-cargo-lastest
Configurez, construisez et installez:
export PATH=$HOME/build/demo-rust/qemu/arm/host/usr/bin:$PATH export PKG_CONFIG=$HOME/build/demo-rust/qemu/arm/host/usr/bin/pkgconf export LIBRARY_PATH=$HOME/build/demo-rust/qemu/arm/host/usr/lib pushd $HOME/build/demo-rust/qemu/arm/build/host-cargo-latest ./configure --prefix=$HOME/build/demo-rust/qemu/arm/host/usr \ --localstatedir=$HOME/build/demo-rust/qemu/arm/host/var/lib \ --sysconfdir=$HOME/build/demo-rust/qemu/arm/host/etc make make install popd
Construction du Programme de Test
Ensuite, créez une nouvelle version du fichier de configuration de Cargo pour le projet "hello-world":
cd $HOME/src/hello-rust cat <<EOF > .cargo/config [target.arm-buildroot-linux-gnueabihf] linker = "arm-buildroot-linux-gnueabihf-gcc" [build] rustflags = ["-C", "prefer-dynamic"] EOF
Grâce aux deux dernières lignes, Cargo est configuré pour passer une option additionnelle au compilateur Rust. Cette option est assez explicite.
Indiquez au compilateur Rust où la spécification de la cible peut être trouvée:
Maintenant, lancez la construction du projet:
Une fois la construction finie, vérifiez la taille du fichier binaire généré:
Le fichier binaire a beaucoup rétréci! Une fois les symboles enlevés, il rétrécira jusqu'à 3680 octets. Copiez-le dans le système de fichiers de la cible:
Copie des Bibliothèques Manquantes
D'habitude, ldd est utilisé pour lister les dépendances d'un programme lié
dynamiquement. Malheureusement, cet outil n'est pas disponible dans
l'environement Buildroot. Mais il est possible d'utiliser la version de
ld.so
, l'éditeur/chargeur dynamique, construite avec le compilateur croisé,
pour parvenir au même but.
Sur la cible, le chargeur dynamique est
HOME/build/demo-rust/qemu/arm/target/lib/ld-2.22.so
. Comme il est uniquement
exécutable sur une machine ARM, l'émulateur espace utilisateur de QEMU sera
utilisé:
$ 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
Comme illustré, le programme dépend de la bibliothèque C (libc.so.6
), la
bibliothèque bas niveau de GCC (libgcc_s.so.1) et
libstd-ca1c970e.so
, la bibliothèque standard de Rust.
Les dépendances sont affichées, mais les bibliothèques partagées ne sont pas trouvées, l'inspection est incomplète. Voici le résultat en passant le chemin des bibliothèques:
$ 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 \ > --library-path $HOME/build/demo-rust/qemu/arm/host/usr/lib/rustlib/arm-buildroot-linux-gnueabihf/lib:$HOME/build/demo-rust/qemu/arm/target/lib \ > target/arm-buildroot-linux-gnueabihf/release/hello-rust libstd-ca1c970e.so => /home/stewie/build/demo-rust/qemu/arm/host/usr/lib/rustlib/arm-buildroot-linux-gnueabihf/lib/libstd-ca1c970e.so (0xf64be000) libgcc_s.so.1 => /home/stewie/build/demo-rust/qemu/arm/target/lib/libgcc_s.so.1 (0xf6492000) libc.so.6 => /home/stewie/build/demo-rust/qemu/arm/target/lib/libc.so.6 (0xf6356000) libdl.so.2 => /home/stewie/build/demo-rust/qemu/arm/target/lib/libdl.so.2 (0xf6342000) libpthread.so.0 => /home/stewie/build/demo-rust/qemu/arm/target/lib/libpthread.so.0 (0xf6319000) libm.so.6 => /home/stewie/build/demo-rust/qemu/arm/target/lib/libm.so.6 (0xf629f000) /lib/ld-linux-armhf.so.3 => /home/stewie/build/demo-rust/qemu/arm/target/lib/ld-2.22.so (0xf6fcf000) Version information: target/arm-buildroot-linux-gnueabihf/release/hello-rust: libgcc_s.so.1 (GCC_3.5) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libgcc_s.so.1 libc.so.6 (GLIBC_2.4) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libc.so.6 /home/stewie/build/demo-rust/qemu/arm/host/usr/lib/rustlib/arm-buildroot-linux-gnueabihf/lib/libstd-ca1c970e.so: libdl.so.2 (GLIBC_2.4) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libdl.so.2 libgcc_s.so.1 (GCC_4.3.0) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libgcc_s.so.1 libgcc_s.so.1 (GLIBC_2.0) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libgcc_s.so.1 libgcc_s.so.1 (GCC_3.0) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libgcc_s.so.1 libgcc_s.so.1 (GCC_4.0.0) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libgcc_s.so.1 libgcc_s.so.1 (GCC_3.3.1) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libgcc_s.so.1 libgcc_s.so.1 (GCC_3.5) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libgcc_s.so.1 libm.so.6 (GLIBC_2.4) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libm.so.6 libpthread.so.0 (GLIBC_2.4) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libpthread.so.0 libc.so.6 (GLIBC_2.17) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libc.so.6 libc.so.6 (GLIBC_2.4) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libc.so.6 /home/stewie/build/demo-rust/qemu/arm/target/lib/libgcc_s.so.1: libc.so.6 (GLIBC_2.4) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libc.so.6 /home/stewie/build/demo-rust/qemu/arm/target/lib/libc.so.6: ld-linux-armhf.so.3 (GLIBC_2.4) => /home/stewie/build/demo-rust/qemu/arm/target/lib/ld-2.22.so ld-linux-armhf.so.3 (GLIBC_PRIVATE) => /home/stewie/build/demo-rust/qemu/arm/target/lib/ld-2.22.so /home/stewie/build/demo-rust/qemu/arm/target/lib/libdl.so.2: ld-linux-armhf.so.3 (GLIBC_PRIVATE) => /home/stewie/build/demo-rust/qemu/arm/target/lib/ld-2.22.so libc.so.6 (GLIBC_PRIVATE) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libc.so.6 libc.so.6 (GLIBC_2.4) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libc.so.6 /home/stewie/build/demo-rust/qemu/arm/target/lib/libpthread.so.0: ld-linux-armhf.so.3 (GLIBC_2.4) => /home/stewie/build/demo-rust/qemu/arm/target/lib/ld-2.22.so ld-linux-armhf.so.3 (GLIBC_PRIVATE) => /home/stewie/build/demo-rust/qemu/arm/target/lib/ld-2.22.so libc.so.6 (GLIBC_PRIVATE) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libc.so.6 libc.so.6 (GLIBC_2.4) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libc.so.6 /home/stewie/build/demo-rust/qemu/arm/target/lib/libm.so.6: libc.so.6 (GLIBC_PRIVATE) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libc.so.6 libc.so.6 (GLIBC_2.4) => /home/stewie/build/demo-rust/qemu/arm/target/lib/libc.so.6
C'est une liste exhaustive! Mais elle montre que seule libstd-ca1c970e.so
est requise, étant donné que les autres dépendances sont déjà présentes dans le
système de fichiers de la cible.
Pour la copier, exécutez:
cp $HOME/build/demo-rust/qemu/arm/host/usr/lib/rustlib/arm-buildroot-linux-gnueabihf/lib/libstd-ca1c970e.so \ $HOME/build/demo-rust/qemu/arm/target/usr/lib chmod a+wx $HOME/build/demo-rust/qemu/arm/target/usr/lib/libstd-ca1c970e.so
Comme cette bibliothèque était à l'origine installée dans
$HOME/build/demo-rust/qemu/arm/host/usr/lib
, le chemin de recherche à
l'exécution ("rpath") dans le fichier
ELF est incorrect. Pour le vérifier et le modifier, construisez patchelf:
La valeur courante de rpath est:
$ patchelf --print-rpath $HOME/build/demo-rust/qemu/arm/target/usr/lib/libstd-ca1c970e.so /home/stewie/build/demo-rust/qemu/arm/host/usr/lib/rustlib/arm-buildroot-linux-gnueabihf/lib
Pour la remplacer par un chemin vide, exécutez:
Exécution du Programme de Test sur le Système
Reconstruisez l'image système:
Maintenant, vous pouvez démarrer votre système avec QEMU:
qemu-system-arm \ -M vexpress-a9 \ -m 256 \ -kernel $HOME/build/demo-rust/qemu/arm/images/zImage \ -dtb $HOME/build/demo-rust/qemu/arm/images/vexpress-v2p-ca9.dtb \ -drive file=$HOME/build/demo-rust/qemu/arm/images/rootfs.ext2,if=sd,format=raw \ -append "console=ttyAMA0,115200 root=/dev/mmcblk0" \ -serial stdio \ -serial mon:stdio \ -net nic,model=lan9118 \ -net user
Connectez-vous en tant que "root" (pas de mot de passe) et exécutez le programme de test:
Excellent!