Raspberry Piでx86_64の起動ディスクを修正・・・できる・・だと?
昨日、Raspberry Pi ( armvl64 / aarch64 ) なことを失念したまま、x86のubuntuのディスクにChrootしたんですよ。
x86_64のUSBディスクにchrootしても動くんですよ。えええ。
RaspberryPi(arm)から USB-ubuntu(x86)へchroot ができてる。
マウントしてchrootして・・・
takuya@raspi-ubuntu:~ $ sudo mount /dev/sda1 /mnt takuya@raspi-ubuntu:~ $ sudo chroot /mnt root@raspi-ubuntu:/# ## 動いてる。。。
エラーにならないんですよ。CPUアーキテクチャが異なるので、エラーになるかと思いきや、動いてるんです。意味わかんないwwww。
x86へchrootしたときのuname
慌てて、CPUアーキテクチャを調べました。
root@raspi-ubuntu:/# uname -a Linux raspi-ubuntu 5.15.0-1027-raspi #29-Ubuntu SMP PREEMPT Mon Apr 3 10:12:21 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
CPUは ARM / Cortex-A72 なのに x86_64
CPUを見ると Architecture: x86_64
となっている。マジですか。
root@raspi-ubuntu:/# lscpu Architecture: x86_64 CPU op-mode(s): 32-bit Byte Order: Little Endian CPU(s): 4 On-line CPU(s) list: 0-3 Vendor ID: ARM Model name: Cortex-A72 Model: 3 Thread(s) per core: 1 Core(s) per socket: 4 Socket(s): 1 Stepping: r0p3 CPU max MHz: 1800.0000 CPU min MHz: 600.0000 BogoMIPS: 108.00 Flags: fp asimd evtstrm crc32 cpuid Caches (sum of all): L1d: 128 KiB (4 instances) L1i: 192 KiB (4 instances) L2: 1 MiB (1 instance) Vulnerabilities: Itlb multihit: Not affected L1tf: Not affected Mds: Not affected Meltdown: Not affected Mmio stale data: Not affected Retbleed: Not affected Spec store bypass: Vulnerable Spectre v1: Mitigation; __user pointer sanitization Spectre v2: Vulnerable Srbds: Not affected Tsx async abort: Not affected
どうやって動いているのか。
落ち着いて、プロセスを確認した。
/usr/libexec/qemu-binfmt/x86_64-binfmt-P
というQEMU経由で動作していました。
そういえば、このRaspberryPi4は、以前にDockerでX86バイナリを動かすために、qemuをいれていた。
sudo apt-get install qemu binfmt-support qemu-user-static
chrootでqemu が自動的に動くようです。
つまり、chrootするときに、/bin/bashのアーキテクチャを見て、x86_64のバイナリなので、qemu経由で動作している。
再現方法
qemuと binfmt-supportを入れる。
sudo apt install qemu-system binfmt-support qemu-user-static qemu-user-static:i386
適当な、x86バイナリを持ってくる
scp ubunt-x86:/usr/bin/bash ./bash-x86
動かしてみる
./bash-x86
ファイルが見つからないエラー。
takuya@raspi-ubuntu:~$ ./bash-x86 x86_64-binfmt-P: Could not open '/lib64/ld-linux-x86-64.so.2': No such file or directory
動いてます。gccのlib ファイルが見つからないというだけです。CPUアーキテクチャ相違エラーが出ません。
バイナリのフォーマットを見て、自動的にqemuを使って起動してくれるんですね。
chroot で動かす /mnt/bin/bashがqemu経由になる、
bashがx86なので、armなRaspberry Piからchrootしてもbashにあわせて、binfmtが発動しQEMUが起動するというわけだ。
qemu-binfmt/x86_64-binfmt-P があるからだ。
なるほど、これで冒頭のなんで動くの?という謎がとけた
binfmt-supportがあれば、x86のLinuxのHDDをRaspberry Piからでもメンテナンスできるんですね。おもしれぇ
動作の速度
ただし、動くけど恐ろしく遅い。crypttab/fstabの修正くらいなら待ち時間に耐えられる。update-initramfsや grub-installはかなり辛抱強く我慢が必要(通常の10倍位、1分で終わる処理が10分くらいかかった)。vim でファイルを修正や passwdを書き換えるくらいならchrootがqemuで起動してることに気づかないくらいだ。
追記
chroot / binfmt / qemuで調べたら、次の記事が見つかった。
Raspberry Pi のセルフビルド環境を QEMU で作る
ここに詳しい動作機序が解説してあった。どうやら、chrootする先にもQEMUがインストールされている必要がある様だ。
いか抜粋(一部)
binfmt はqemu-arm-static を実行しようとするが、すでに chroot してしまっている。なのでchroot 先にも、binfmt 設定とおりに qemu-arm-static を配置しておく必要がある。
いか抜粋
binfmt-support
実行可能ファイルのヘッダを見て、必要なランタイムやインタプリタ等を準備する。 スクリプトの #! 行を見てインタプリタ経由で実行させたり、ELF バイナリのヘッダを見てダイナミックリンカ (ld-linux.so) 経由で実行させたりしているのはこの仕組み。このパッケージはこの仕組みを設定ファイルで拡張可能にする。
qemu-arm-static
ARM 機械語の部分を x86 機械語に翻訳して実行し、システムコールはホストのものを呼び出すようになっている。通常の QEMU と違ってハードウェアをエミュレートしなくて良い分、高速に動作する。 スタティックリンク版を使う理由は後述。
ELF バイナリのヘッダにはアーキテクチャを示す項目があるので、「ARM アーキテクチャな ELF ヘッダを見つけたら qemu-arm-static を使って実行する」ように binfmt に設定すれば、ARMバイナリを x86 でも透過的に実行できるようになるはずである。
Linux のダイナミックリンカは /lib/ld-linux.so という名前なのだが、ARM も x86 も同じ名前で参照しようとするところ、実際にこのパスにあるのは x86 版なわけなのでエラーになる。このファイルは x86 バイナリの実行に必須なため置き換えることもできない。同様の問題が libc (libc.so) にもあてはまる。 chroot 先のディレクトリに ARM バイナリ一式(ランタイムも)と qemu-arm-static を置くことで、以下の流れが成立する。
chroot
chroot がルートディレクトリを変更する(元のディレクトリにはもう戻れない)
↓
(たとえば)chroot 先の /bin/bash を実行する
↓
ARM バイナリなので binfmt が /usr/bin/qemu-arm-static 経由で実行しようとする
↓
chroot 先の /usr/bin/qemu-arm-static を実行(これは x86 バイナリ)
↓
ARM 版のダイナミックリンカやランタイムを chroot 先から探すようになる
↓
bash 起動。この bash から起動したコマンドはすべて chroot の下で動作する。binfmt は元々のルートディレクトリのつもりで qemu-arm-static を実行しようとするが、この場合すでに chroot してしまっているので、chroot 先のディレクトリにも、binfmt の設定に書かれているパスのとおりに qemu-arm-static を配置しておく必要がある。