分类

使用 multiarch 运行其它架构的程序

2019-08-26 15:30 linux

Debian 提供的 multiarch 功能对于调试跨架构的应用来说非常方便. 尽管 multiarch 常用于在 x86_64 的系统里面运行 x86 的程序.

以下操作以在 x86_64 的系统里面编译并运行 arm64 的应用为示例来演示一下 multiarch.

安装交叉编译的工具链

apt 仓库里面提供了多个架构的 gcc 工具链, 如果编译 arm64 平台的, 可以安装:

$ sudo apt install gcc-aarch64-linux-gnu

该工具包提供了包括编译器和链接器等:

/usr/bin/aarch64-linux-gnu-gcc
/usr/bin/aarch64-linux-gnu-gcc-ar
/usr/bin/aarch64-linux-gnu-gcc-nm
/usr/bin/aarch64-linux-gnu-gcc-ranlib
/usr/bin/aarch64-linux-gnu-gcov
/usr/bin/aarch64-linux-gnu-gcov-dump
/usr/bin/aarch64-linux-gnu-gcov-tool

测试工具链

以下的 hello.c 用于测试 arm64 工具链是否可用:

#include <stdio.h>

int main(void) {
  printf("Hello, world\n");
  return 0;
}

编译:

$ aarch64-linux-gnu-gcc hello.c -o hello

会生成 hello 文件, 查看一下 hello 这个可执行文件的格式:

$ file hello
hello: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=752dcd6a165921bedbfba81dd8ecba4530bf7c5a, for GNU/Linux 3.7.0, not stripped

安装 qemu

上面生成的 arm64 版的程序, 并不能在我们的 x86_64 系统里直接运行:

$ ./hello
bash: ./hello: cannot execute binary file: Exec format error

但是可以使用 qemu 这个模拟器来运行:

$ sudo apt install qemu-user-static

其中, qemu-user-static 会调用 update-binfmt 命令, 向内核中注册很多 binfmt 格式:

    aarch64_magic='\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00'
     aarch64_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
      alpha_magic='\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90'
       alpha_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
        arm_magic='\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00'
         arm_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
      armeb_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28'
       armeb_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
       cris_magic='\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x4c\x00'
        cris_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
       hppa_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x0f'
        hppa_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
       i386_magic='\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00'
        i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
       m68k_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x04'
        m68k_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
 microblaze_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xba\xab'
  microblaze_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
       mips_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08'
        mips_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
     mipsel_magic='\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00'
      mipsel_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xfe\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
     mips64_magic='\x7f\x45\x4c\x46\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08'
      mips64_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
   mips64el_magic='\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00'
    mips64el_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xfe\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
        ppc_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14'
         ppc_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
      ppc64_magic='\x7f\x45\x4c\x46\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15'
       ppc64_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
 ppc64abi32_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15'
  ppc64abi32_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
    ppc64le_magic='\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15\x00'
     ppc64le_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\x00'
    riscv32_magic='\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'
     riscv32_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
    riscv64_magic='\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'
     riscv64_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
      s390x_magic='\x7f\x45\x4c\x46\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x16'
       s390x_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
        sh4_magic='\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a\x00'
         sh4_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
      sh4eb_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a'
       sh4eb_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
      sparc_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02'
       sparc_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
sparc32plus_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x12'
 sparc32plus_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
    sparc64_magic='\x7f\x45\x4c\x46\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2b'
     sparc64_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
     x86_64_magic='\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00'
      x86_64_mask='\xff\xff\xff\xff\xff\xfe\xfe\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
     xtensa_magic='\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x5e\x00'
      xtensa_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
   xtensaeb_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x5e'
    xtensaeb_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'

这种操作就像 wine 一样, 可以让 linux 系统支持 windows 平台的 PE 格式.

安装好之后, 在终端运行一下:

$ ./hello
/lib/ld-linux-aarch64.so.1: No such file or directory

这里报错的意思是找不到 ld-linux-aarch64.so.1 文件, 该共享库是由 libc6:arm64 包 提供的:

$ sudo apt install libc64:arm64

再次运行一下:

$ ./hello
Hello, world

现在可以运行 arm64 的程序了.

debootstrap 创建隔立的 rootfs

以上演示的, 是在宿主系统中启用 arm64multiarch 的方式来实现的. 但可能会让 宿主系统混乱. 另外这种做法只支持 debian 系的系统.

可以用 debootstrap 创建一个 aarm64 rootfs, 然后 chroot 到这个环境里. 这种做法, 即 qemu + debootstrap 可以运行在大多数 linux 发行版里, 包括那些不支持 multiarch, 不支持 arm64 的系统里.

首先安装 debootstrap:

$ sudo apt install debootstrap

然后创建 arm64 rootfs:

$ sudo debootstrap --arch arm64 buster arm64-rootfs http://ftp.cn.debian.org/debian

以上命令中:

  • --arch arm64, 指定目标架构是 `arm64
  • buster, 指定要安装的版本, 是 debian 10 - buster
  • arm64-rootfs, 指定本地的 rootfs 目录
  • http://ftp.cn.debian.org/debian, 要使用的 apt 仓库地址

经过一段时间的运行, 就会生成 arm64 rootfs:

$ ls arm64-rootfs
bin  boot  dev  etc  home  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
$ file arm64-rootfs/bin/dpkg
arm64-root/bin/dpkg: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]=836ed47b5d716f4b5d033975d2dafd8b95ce59c9, stripped

之后, 我们挂载几个目录:

$ cd buster-rootfs
$ sudo mount -t proc proc ./proc
$ sudo mount -t sysfs sysfs ./sys
$ sudo mount --bind /dev ./dev
$ sudo mount -t devpts devpts ./dev/pts

然后 chroot 到这个目录:

$ sudo chroot .

之后就可以在这个 arm64 chroot 环境里正常操作了.

在 arm64 rootfs 里编译 rust 程序

下面我们测试一下在刚刚创建的 rootfs 里面编译一个 rust 程序, 先安装 rust 编译器:

# apt install cargo rustc

之后, 安装一下 fd 命令:

# cargo install fd-find
  .....................................
  Compiling regex v1.2.1
   Compiling globset v0.4.4
   Compiling fd-find v7.3.0
   Compiling ignore v0.4.10
    Finished release [optimized] target(s) in 9m 26s
  Installing /root/.cargo/bin/fd
warning: be sure to add `/root/.cargo/bin` to your PATH to be able to run the installed binaries

编译速度挺慢的, 等编译完成之后, 就在会 $HOME/.cargo/bin 目录里生成 arm64 版的 fd 可执行文件.

参考

  • https://www.instructables.com/id/Uniform-Development-by-Docker-QEMU/
  • https://git.savannah.gnu.org/cgit/binfmt-support.git
  • https://nongnu.org/binfmt-support/
  • https://wiki.debian.org/SupportedArchitectures
  • https://wiki.debian.org/Multiarch/HOWTO
  • https://www.freedesktop.org/software/systemd/man/binfmt.d.html
  • https://en.wikipedia.org/wiki/Binfmt_misc