chroot in ARM Linux mit ARM-Binärinterpreter

Posted on Sunday, 26 February 2017 in Linux

Ich hatte bereits von dem Problem mit PHP 7.1.2 mit älteren Kerneln geschrieben. Und die schlechte Idee erwähnt, einen anderen Kernel zu installieren. Schlecht deswegen, weil ich bis gestern keinen direkten Zugang zum Server hatte. Und der Wechsel des Kernels den Rechner am Booten gehindert hat. Weswegen ich erst heute das Problem beheben konnte, indem ich ein Downgrade des Linux-Kernels auf die vorherige Version durchgeführt habe. Dies hat sich insgesamt als schwieriger herausgestellt als vorerst angenommen.

x86_64 mag keine ARM-Instruktionen

Das Hauptproblem dabei war, dass ein simples chroot in das System meines Servers und ein Downgrade so nicht funktionierten. Grund ist, dass bei einem chroot in ein anderes Wurzelverzeichnis auch die dort beheimateten Anwendungen auf dem lokalen Prozessor ausgeführt werden. Handelt es sich bei beiden Systemem um ein x86_64-System, stellt dies kein Problem dar. Anders jedoch im Fall meines Servers, der einen ARMv7 Prozessor verwendet. Somit wird nach dem chroot versucht, die ARM-Binärdaten auf einem Prozessor mit x86-Instruktionsset auszuführen. Was so natürlich nicht funktioniert.

Interpreter für fremde Binärformate

Das Problem lässt sich umgehen, wenn ein Emulator für den unbekannten Instruktionssatz zwischengeschaltet wird. Dazu habe ich das Paket qemu-user-static aus dem AUR installiert. Dieses bringt das statisch kompilierte Binärpaket qemu-arm-static mit sich, womit ARM-Anwendungen auch auf dem aktuellen Sysetm ausgeführt werden können. Wird dieses auf das zu reparierende System kopiert, können nach dem chroot die Anwendungen ausgeführt werden. Eine detaillierte Beschreibung dazu habe ich hier hinterlegt.

Interpreter beim Kernel registrieren

Wie auf der verlinkten Wiki-Seite beschrieben, kann der Interpreter qemu-arm-static auch direkt beim Kernel mittels dessen binfmt_misc Kernel-Modul registriert werden. Dies ist nur dem Systemadministrator erlaubt, weswegen im Folgenden erst mit einem su der Benutzer gewechselt wird:

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

Auf diese Weise wird dem Kernel mitgeteilt, wie der ELF-Header für das ARM-Binärformat aussieht und wie er dieses ausführen soll. Nachdem das ARM-System nun gemountet ist, kann mit einem sudo chroot /mnt direkt in das System gewechselt werden, ohne Fehler aufgrund von unbekannten Binärdaten zu erhalten.

Anschließendes Kernel-Downgrade

Das anschließende Kernel-Downgrade ist dabei die leichteste Übung. Da keine Zeit war, den Cache zu leeren, befindet sich der zuvor verwendete Kernel noch im Verzeichnis /var/cache/pacman/pkg. In meinem Fall ist es das Paket linux-odroid-u2-3.8.13.30-5-armv7h.pkg.tar.xz, welches ich mit pacman wieder installiere:

sudo pacman -U /var/cache/pacman/pkg/linux-odroid-u2-3.8.13.30-5-armv7h.pkg.tar.xz

Die Installation eines Kernels triggert das Skript mkinitcpio, welches eine "initial ramdisk"-Umgebung zum Starten des Kernels während des Boot-Vorgangs einrichtet. Ohne die Registrierung eines Binärinterpreters würde dies nicht funktionieren, da jeder Befehl, der in dem Bash-Skript aufgerufen wird, mit einem qemu-arm-static <Binärpfad> maskiert werden müsste.