Linux 的 binfmt_misc (binfmt) module 介紹

linux 透過 qemu 的動態轉譯(dynamic translation)模式, 能直接執行別架構的程式. 舉例: x86_64 中執行 arm64 架構的程式. 然而每次都必須透過 qemu-aarch64 [program] [args...] 的方式執行, 似乎有點瑣碎. 以下就介紹 linux 是如何使用 binfmt_misc kernel module 來化繁為簡...

qemu 與 binfmt 搭配

測試環境為 x86_64 架構, 使用 aarch64 toolchain 編譯下方 hello.c 為 static 的程式. 免去執行時候 link 尋找 libraries 的問題. 這邊透過 1) 直接執行. 2)藉由 qemu 執行. 3) qemu 搭配 binfmt_misc 來示範結果.

#include <stdio.h>
int main()
{
        printf("hello !!\n");
        return 0;
}

首先安裝 aarch64 的 toolchain 來編譯程式. 並確認編譯出來的格式為 aarch64

# 安裝 arm 版本的 toolchain
yijyun@yijyun-VirtualBox:~$ sudo apt-get install gcc-aarch64-linux-gnu
# 使用 arm-gcc 編譯
yijyun@yijyun-VirtualBox:~$ aarch64-linux-gnu-gcc -static hello.c
# 查看編譯出來的格式
yijyun@yijyun-VirtualBox:~$ aarch64-linux-gnu-objdump -f a.out
a.out:     file format elf64-littleaarch64
architecture: aarch64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000000400cd8
# 顯示本機的架構
yijyun@yijyun-VirtualBox:~$ uname -i
x86_64

1) 執行 aarch64 架構的程式

執行 aarch64 架構的程式, 直接顯示錯誤訊息...

# 執行
yijyun@yijyun-VirtualBox:~$ ./a.out
-bash: ./a.out: cannot execute binary file: Exec format error

2) qemu-aarch64 執行 aarch64 架構的程式

安裝 qemu-user 模擬處理器套件, 藉由 qemu-aarch64 指令來間接執行 aarch64 架構的程式.

# 安裝 qemu-user 
yijyun@yijyun-VirtualBox:~$ sudo apt-get install qemu-user
# 執行
yijyun@yijyun-VirtualBox:~$ qemu-aarch64 a.out
hello !!

3) qemu 搭配 binfmt_misc 來執行 aarch64 架構的程式

先安裝 qemu-user-static 與 binfmt-support. 其中 qemu-user-static 是 qemu-user 的 static 版本. 主要是為了與 binfmt_misc 搭配. 這時候執行 aarch64 程式的時候, 已不需要以 qemu-aarch64 [program] [args...] 的方式. 直接執行也沒有錯誤發生, 是不是方便許多! 不過實際上系統實行的時候是呼叫 qemu-aarch64-static [program] [args...]. 使用者察覺不到而已 XD.

# 安裝相關套件
sudo apt-get install apt-get install qemu-user-static binfmt-support
# 執行
yijyun@yijyun-VirtualBox:~$ ./a.out
hello !!

binfmt_misc 介紹

binfmt_misc 分析

看過上方的執行後, 多少會有疑問. 究竟 kernel 是怎麼判斷的阿!!!
這時候就從 /proc/sys/fs/binfmt_misc 來一探究竟! 可以看到底下有許多不同的架構.
由於程式是基於 aarch64, 就輸出 qemu-aarch64 內容來看 !

# 查看 binfmt_misc 支援的格式
yijyun@yijyun-VirtualBox:~$ ls /proc/sys/fs/binfmt_misc/
jar        qemu-aarch64  qemu-armeb  qemu-microblaze  qemu-mips64el  qemu-ppc64       qemu-s390x  qemu-sparc        register
python2.7  qemu-alpha    qemu-cris   qemu-mips        qemu-mipsel    qemu-ppc64abi32  qemu-sh4    qemu-sparc32plus  status
python3.5  qemu-arm      qemu-m68k   qemu-mips64      qemu-ppc       qemu-ppc64le     qemu-sh4eb  qemu-sparc64      

# 顯示 qemu-aarch64
yijyun@yijyun-VirtualBox:~$ cat /proc/sys/fs/binfmt_misc/qemu-aarch64
enabled
interpreter /usr/bin/qemu-aarch64-static
flags: OC
offset 0
magic 7f454c460201010000000000000000000200b700
mask ffffffffffffff00fffffffffffffffffeffffff

經由 qemu-aarch64 的內容可以看出, 根據 binfmt_misc 設定檔來決定 kernel 是否可以直接執行某架構的程式
而這些架構就根據設定裡的 interpreter, magic 來判斷架構和使用對應的動態轉譯程式, 如qemu.
看到這就有種豁然開朗的感覺 XD

binfmt_misc 與 chroot

binfmt_misc, qemu 最適合的應用就是與 chroot 搭配. 藉由 chroot 切換 / (根目錄) 到另一種架構的 / (根目錄). 在使用 binfmt_misc 和 qemu, 就如同在特定架構運行程式

以下為範例為在 x86_64 架構下切換到 aarch64架構根目錄.
首先從 Ubuntu Base rootfs [2]下載 aarch64 的 rootfs 並解壓縮. 在複製 qemu-aarch64-static 到 aarch64 架構的 rootfs 裡面. 再藉由 chroot 切換到 aarch64的根目錄.

# 列出 base rootfs 內容
root@yijyun-VirtualBox:/home/yijyun/ubuntu-aarch64-rootfs# ls rootfs/
bin  boot  dev  etc  home  lib  media  mnt  opt  proc  root  run  sbin  snap  srv  sys  tmp  usr  var
# 複製 /usr/bin/qemu-aarch64-static 到 rootfs/usr/bin/ 
# 1) qemu-aarch64 interpreter 的路徑為此
# 2) 這也是為什麼要編譯成 static 的原因
root@yijyun-VirtualBox:/home/yijyun/ubuntu-aarch64-rootfs# cp -fa /usr/bin/qemu-aarch64-static rootfs/usr/bin/
# 藉由 chroot 切換到 aarch64 的根目錄
root@yijyun-VirtualBox:/home/yijyun/ubuntu-aarch64-rootfs# chroot rootfs/
# 顯示
root@yijyun-VirtualBox:/# ls
bin  boot  dev  etc  home  lib  media  mnt  opt  proc  root  run  sbin  snap  srv  sys  tmp  usr  var
# 透過 objdump 驗證架構
root@yijyun-VirtualBox:/# objdump -f /bin/ls

/bin/ls:     file format elf64-littleaarch64
architecture: aarch64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000000404cd0

以上環境只要在設定好網路, 並把原本系統相關的掛載點(e.g. dev, proc, 等等) 給轉移到 aarch64 的根目錄底下. 再透過 ubuntu base 內建的 apt-get. 就可以客製化自己的 rootfs[3][4]. 只是需要注意圖形化桌面的套件選擇是否適用於 arm 平台下.

Reference

  1. 透過 binfmt_misc 讓 Linux 可以執行不同格式的執行檔
  2. Ubuntu Base
  3. Build Ubuntu Rootfs via Ubuntu Base(FIXME: TBD)
  4. 在 x86 下 chroot 到 ARM 平台的 rootfs

留言

這個網誌中的熱門文章

yocto recipe : (1) 撰寫 recipe

yocto recipe : (2) 撰寫 bbappend

yocto recipe : (3) 使用 External Source 來編譯軟體