それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Raspiでx86のLinuxにChrootで入り編集できて驚いた(異なるCPUアーキテクチャへの chroot )

Raspberry Pix86_64の起動ディスクを修正・・・できる・・だと?

昨日、Raspberry Pi ( armvl64 / aarch64 ) なことを失念したまま、x86ubuntuのディスクに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。

x86chrootしたときの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

chrootqemu が自動的に動くようです。

つまり、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/bashqemu経由になる、

bashx86なので、armなRaspberry Piからchrootしてもbashにあわせて、binfmtが発動しQEMUが起動するというわけだ。

qemu-binfmt/x86_64-binfmt-P があるからだ。

なるほど、これで冒頭のなんで動くの?という謎がとけた

binfmt-supportがあれば、x86LinuxのHDDをRaspberry Piからでもメンテナンスできるんですね。おもしれぇ

動作の速度

ただし、動くけど恐ろしく遅い。crypttab/fstabの修正くらいなら待ち時間に耐えられる。update-initramfsや grub-installはかなり辛抱強く我慢が必要(通常の10倍位、1分で終わる処理が10分くらいかかった)。vim でファイルを修正や passwdを書き換えるくらいならchrootqemuで起動してることに気づかないくらいだ。

追記

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 を配置しておく必要がある。