Table of Contents
chroot zwischen verschiedenen Architekturen
Mittels chroot in ein anderes Linux wechseln, um Reparaturen vorzunehmen, kann eine sehr praktische und zeitsparende Angelegenheit sein. Schwieriger wird es, wenn sich die Prozessorarchitektur des Zielsystems von dem aktuell verwendeten unterscheiden, da nahezu alle Programme als Binärdaten vorliegen, die die Instruktionen entsprechend für den jeweiligen Prozessor verwenden. Nehmen wir beispielsweise die Architektur x86_64, auch bekannt als amd64, die 64 Bit-Variante des x86-Instruktionssatzes, welche auf nahezu jedem aktuellen Computer verwendet wird, und als Zielsystem armv7l, ein Prozessor vom Typ ARMv7 mit 32 Bit, wie er beispielsweise beim Raspberry Pi 3 oder vielen Mobilgeräten Verwendung findet. ARM-Prozessoren mit 64 Bit weisen die Architektur ARMv8 auf. Würden wir einfach mittels chroot in ein solches ARM-System wechseln, würden die dort befindlichen für ARM kompilierten Binärpakete auf dem aktuellen Prozessor ausgeführt, der diese Instruktionen nicht kennt. Ergebnis wären Fehlermeldungen der Form
Kann die Binärdatei nicht ausführen: Fehler im Format der Programmdatei
oder
Konnte execv nicht aufrufen (Fehler im Format der Programmdatei)
Fehler: Befehl konnte nicht korrekt ausgeführt werden
Um dies zu umgehen, kann entweder eine virtuelle Maschine mit einem ARM-System aufgesetzt werden, um die ARM-Instruktionen nativ verarbeiten zu können, oder man bedient sich statischer Binärpakete des QEMU-Projekts für die verschiedensten Architekturen, was im Folgenden erklärt wird.
ARM Binärpakete emulieren
Um dennoch ARM-Instruktionen ausführen zu können, kann das Paket qemu-user-static verwendet werden, welches unter anderem ein statisch kompiliertes Binärpaket zur Emulation von ARM-Befehlen zur Verfügung stellt: qemu-arm-static. Unter Arch Linux befindet sich das Paket im AUR.
Sollte es häufiger vorkommen, fremde ELF Binärpakete ausführen zu müssen, könnte es sich lohnen, einen Blick auf binfmt-support zu werfen, welches das binfmt_misc Kernel-Modul verwendet, um damit Interpreter für verschiedene Binärformate zu registrieren. Ich selbst registriere es später manuell.
chroot zu ARM für simple Aufgaben
Der zuvor erwähnte Fehler, dass das Binärformat von ARM vom aktuellen Prozessor nicht verstanden wird, lässt sich umgehen, indem qemu-arm-static in die chroot-Umgebung kopiert und zum Ausführen von Anwendungen verwendet wird. Angenommen, ein ARM-System ist wie hier beschrieben unter /mnt eingebunden und wir wollen dort einzelne Befehle ausführen können, dann benötigen wir die QEMU-Anwendung unter /mnt/usr/bin:
sudo cp $(which qemu-arm-static) /mnt/usr/bin
Oder, wenn es unter dem Standard-Pfad installiert wurde:
sudo cp /usr/bin/qemu-arm-static /mnt/usr/bin
Um nun mittels chroot die Umgebung zu wechseln, müssen wir chroot mitteilen, was es ausführen soll:
sudo chroot /mnt qemu-arm-static /bin/bash
So landen wir mit einer Shell ohne Fehlermeldungen im gemounteten Verzeichnis des ARM-Systems. Alle Befehle, dir wir nun ausführen wollen, müssen mittels qemu-arm-static emuliert werden. Um uns beispielsweise mit uname Systeminformationen ausgeben zu lassen, müssen wir statt uname -a
qemu-arm-static /usr/bin/uname -a
aufrufen. Die Ausgabe könnte z. B. so aussehen:
Linux my_host 4.9.11-1-ARCH #1 SMP PREEMPT Sun Feb 19 13:45:52 UTC 2017 armv7l GNU/Linux
Auf diese Weise können diverse Reparaturen oder Korrekturen auf dem ARM-System durchgeführt werden.
Diese Methode stößt jedoch an ihre Grenzen, wenn beispielsweise versucht wird, ein Shell-Skript auszuführen, da dort jeder Befehl entsprechend mit qemu-arm-static <Binärpfad> maskiert werden müsste. Ich wollte ein Kernel-Downgrade durchführen, da der neue Kernel, den ich installiert hatte, zu Boot-Problemen geführt hat. Dabei wird auch mkinitcpio ausgeführt, ein komplizierteres Bash-Skript, um alle nötigen Module am Kernel zu registrieren und eine “initial ramdisk” Umgebung (initrd) zur Verfügung zu stellen, um den Kernel und alles, was das System benötigt, starten zu können.
Interpreter für fremde Binärformate registrieren
Der oben beschriebene Fall lässt sich einfach lösen, indem man seinem System mitteilt, wie verschiedene Binärformate interpretiert werden sollen. Dazu dient das binfmt_misc Kernel-Modul, dass über /proc/sys/fs/binfmt_misc eingesehen und manipuliert werden kann. Über das dortige register kann ein neuer Binär-Interpreter direkt im Kernel registriert werden, wodurch der Kernel weiß, wie in diesem Fall ARM-Instruktionen verarbeitet werden sollen, anstatt die eingangs erwähnten Fehler zu produzieren. Konkret funktioniert dies wie folgt:
su echo ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-arm-static:' > /proc/sys/fs/binfmt_misc/register exit
Nur der Systemadministrator darf einen neuen Interpreter registrieren, weswegen sudo nicht funktioniert. Danach wird dem Kernel mitgeteilt, an welchem File-Header einer ELF-Binary er das ARM-Binärformat erkennt und mit welchem Interpreter es ausgeführt werden soll. Zum Schluss verlassen wir die root-Shell wieder. Jetzt sollte die Datei /proc/sys/fs/binfmt_misc/arm erstellt sein, die die obigen Informationen enthält.
Wechseln wir nun mittels sudo chroot /mnt das Wurzelverzeichnis, erhalten wir keine Fehlermeldungen, da der Kernel jetzt weiß, wie er die ARM-Instruktionen zu handhaben hat. Auch ein uname -a oder das Ausführen von Shell-Skripten funktioniert auf diese Weise ohne weiteres.
chroot für komplexere Aufgaben mit Einhängepunkten (mount / mtab)
Ist wie zuvor beschrieben das zu reparierende System eingebunden und ein Binärinterpreter für ARM hinterlegt worden, sollte es möglich sein, nahezu alle Aufgaben zu erledigen. In seltenen Fällen kann es jedoch sein, dass die mount points oder Einhängepunkte benötigt werden, speziell die Datei /etc/mtab. In dieser Datei sind alle aktuellen Einhängepunkte des Systems zu sehen, welche auch bei der Eingabe von mount ausgegeben werden.
Sollte dies der Fall sein, wird man Fehlermeldungen wie
could not open file: /etc/mtab: No such file or directory
oder
could not determine filesystem mount points
erhalten. Probleme dieser Art lassen sich beheben, indem die von Linux verwendeten und benötigten speziellen Verzeichnisse /dev (Geräteinformationen), /proc und /sys (Schnittstellen zum Kernel für die Kommunikation mit laufenden Prozessen und Kernel-Subsystemen) zur Verfügung gestellt werden. Diese virtuellen Dateisysteme können wie folgt eingebunden werden, dabei gehe ich wie weiter oben davon aus, dass das System unter /mnt gemountet wurde:
cd /mnt sudo mount -t proc proc proc/ sudo mount --rbind /sys sys/ sudo mount --rbind /dev dev/
Anschließend kann wie gewohnt mit einem
sudo chroot /mnt
in das eingebundene Linux-System gewechselt werden.
Sollte es nach Beendigung der Arbeiten beim Unmounten Probleme geben, da das Verzeichnis noch verwendet wird oder beschäftigt ist, kann statt beispielsweise einem
sudo umount --recursive /mnt
ein --force angehängt werden. Sollte auch dies nicht helfen, kann als Notlösung ein umount --lazy verwendet werden,
sudo umount --recursive /mnt --lazy
dabei werden die Einhängepunkte ohne Überprüfungen einfach freigegeben. Dies kann jedoch höchstwahrscheinlich zu Problemen auf dem aktuellen Host-System führen, weswegen nach der Verwendung von --lazy nach Möglichkeit so schnell wie möglich ein Neustart durchgeführt werden sollte.
