使用 chroot 和 Docker 搭建 multiarch 调试环境

微信扫一扫,分享到朋友圈

使用 chroot 和 Docker 搭建 multiarch 调试环境

最近需要一个环境调试一些非 x86 的二进制和学习一下其他体系架构的汇编,最基本的思路是 qemu 启动 qcow2 虚拟机(如果需要模拟调试 MIPS 路由器等设备,可能需要这种办法,现成的 qcow2 镜像可以从类似 https://people.debian.org/~jcowgill/qemu-mips/ 的地方找到),但是我觉得有点太麻烦了, 调试二进制文件其实只需要一个干净的 rootfs 和一套指令翻译就行了,并不需要虚拟机提供的全套 I/O 虚拟化、网络虚拟化、外设驱动等等。

使用 debootstrap + chroot

实际发现使用 debootstrap+chroot 就足够, 也可以使用multistrap,区别是可以使用配置文件,理论上更加灵活但是我目前的应用场景不需要了,参见: https://wiki.debian.org/EmDebian/CrossDebootstrap

唯一需要注意的是,跨体系结构的 debootstrap 步骤稍有不同,需要将 --foreign--second-stage 两步分开,简单地说就是需要 qemu 参与 stage2,所以我们要找个时间把 qemu 丢进 chroot 的目录里,参见: https://askubuntu.com/questions/287789/what-is-debootstrap-second-stage-for

apt install binfmt-support qemu qemu-user-static debootstrap
mkdir debian-mips
debootstrap --arch=mips --foreign buster ./debian-mips http://mirrors.uestc.cn/debian # 随便换个什么速度快的镜像源
cp `which qemu-mips-static` ./debian-mips/usr/bin/
chroot ./debian-mips /debootstrap/debootstrap --second-stage

遇到提示 is /dev/pts mounted 的时候手动执行以下 mount -t devpts devpts /dev/pts ,如果需要 procfs 、sysfs 也需要手动 mount 一次,可以写在 chroot 里的 bashrc / zshrc 里,借助 zlogout 可以在出入 chroot 环境的时候自动挂载/解挂相关 fs,注意 loginshell 和 nonloginshell 的问题。

binfmt_misc ( https://zh.wikipedia.org/wiki/Binfmt_misc ) 是一个由内核提供、根据判断 magic number 判断文件类型而使用指定打开方式的机制(如指定Java程序直接使用JVM虚拟机打开),是一个内核模块,所有注册过的信息会保存至 /proc/sys/fs/binfmt_misc 目录下,部分发行版不需要手动 mount,如 Debian 的 binfmt-suppport 包含了几个启动脚本和 systemd-unit,binfmt-support 也在 qemu-user-static 的推荐里,可见 Debian 自动注册了相关的 magic number 到 qemu:

-> # cat /proc/sys/fs/binfmt_misc/qemu-mips
enabled
interpreter /usr/bin/qemu-mips-static
flags: OCF
offset 0
magic 7f454c4601020100000000000000000000020008
mask ffffffffffffff00fefffffffffffffffffeffff

如果发行版没有帮你做这些步骤,可能需要手动操作:

modprobe binfmt_misc
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc

另外注意到在 Debian 中,flag 字段是 OCF , 其中的 F 会禁用 lazy spawn 特性,让 binfmt_misc 在 mount namespace 环境下正常工作,详细参考: https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html

使用 hexdump 可以读出原始的开头格式(ELF 的 magic number 是 0x7f 0x45 0x4c 0x46 ),可以观察到 MIPS 和 amd64 的大小端不一样导致读到的字节顺序是不同的,同时也可以确认 chroot 环境中确实运行着其他架构的二进制。

但是即使告诉了系统使用 qemu 执行跨架构的 ELF,没有 chroot 环境直接执行依然是会报错的,提示是: /lib/ld.so.1: No such file or directory ,这个报错的意思实际是 ld 找不到需要的 ld.so 和 libc.so(这个报错同样出现在没有安装 i386 运行库的 amd64 系统执行 i386 ELF 的时候)。

readelf 可见 ELF 中写死了 interpreter 的路径,因此不能简单地通过 LD_PRELOAD 在 x64 执行 MIPS 的 ELF,如:

但是如果是完全静态链接的 MIPS ELF,指定了 qemu 作为解释器后则可以直接在 x64 下执行。

使用 Docker

chroot 简单轻量,但需要自己处理 mount procfs 之类的问题,如果目标架构(如 ARMv8)已经有现成的 image 可用,也可以使用 docker,可参见: https://www.stereolabs.com/docs/docker/building-arm-container-on-x86/

系统中存在 qemu-user-static 则 Docker 可以直接开箱即用的使用其他架构的 image:

apt install qemu binfmt-support qemu-user-static
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes  # 不必要,取决于发行版
docker run -it arm64v8/ubuntu

更多的 image 可以在类似 https://hub.docker.com/_/debian 的页面中找到,且注意到上一节中,Debian 的 binfmt-support 已经注册过,这使得 Debian 使用 Docker 打开跨架构二进制不需要执行上述步骤的第二步。

交叉编译

qemu-user-static 提供的二进制翻译效率很差(顾名思义,qemu-user-static 是一个用户态的二进制翻译,也不像 KVM 有指令级加速),因此如果需要编译大型 ELF,不要在 chroot 内直接编译,而是使用交叉编译,如在 x64 上编译 i386:

apt install gcc-multilib
apt install libc6-dev-i386
gcc -m32 hello.c -o i386.o

又如直接在x64编译MIPS,参见 https://stackoverflow.com/questions/4175450/is-there-a-way-to-use-gcc-to-convert-c-to-mips

apt install crossbuild-essential-mips
mips-linux-gnu-gcc hello.c -o mips.o

本文链接:本站允许也欢迎您:自由地对本文进行复制、分享或基于本文进行创作但您需同意并遵守:对本文署名并标记来源、使用相同方式共享、不将其用于商业用途

更严谨和完整的声明请参见

微信扫一扫,分享到朋友圈

使用 chroot 和 Docker 搭建 multiarch 调试环境

蚂蚁战配基金已售罄三只 累计关注人数超720万

上一篇

新建果园的选址及栽植管理技术

下一篇

你也可能喜欢

使用 chroot 和 Docker 搭建 multiarch 调试环境

长按储存图像,分享给朋友