qemu-riscv64-static + chroot + debootstrapでriscv環境を動作させる

chrootでriscv環境を動かす

chroot + qemu-aarch64-staticでaarch64のバイナリがx86_64のPC上で動くことを最近知った。どうやるかというと

# qemu-user-static(qemu-riscv64-static) is required for second stage debootstrap. see https://www.debian.org/releases/stretch/s390x/apds03.html.ja
$ sudo apt-get install -y debootstrap qemu-user-static
$ mkdir -p ./mnt
$ sudo debootstrap --arch=arm64 --foreign --include=ifupdown,netbase,net-tools,openssh-server,vim,file,sudo stretch ./mnt
$ sudo cp `which qemu-aarch64-static` ./mnt/debian-rootfs/usr/bin
$ ls -ls ~/debian/debootstrap/debootstrap
20 -rwxr-xr-x 1 root root 19076 Jan 21 08:03 /home/vagrant/debian/debootstrap/debootstrap
$ sudo chroot ./mnt/debian-rootfs debootstrap/debootstrap --second-stage
$ sudo chroot ./mnt

用途としては、rasberry pi3などのarmの(非x86_64 architectureの)環境をboard上でなく、自身のPC(x86_64)で整えることができるのでとても便利である。

などを参考にした。


qemu-$(arch)-staticと対応するbinfmtの設定がありさえすれば、他のアーキテクチャでも動くはずである。 ということは、riscv architectureでも同じようなことができそうなので、やってみた。

Note) centos7の場合は、debootstrapはepelレポジトリからとってくると良い:

sudo yum install epel-release
# epel.repoが新しくできる。
yum --enablerepo=epel install debootstrap

Reference

本記事は、アーキテクチャによらず、debootstrap--foreign--second-stageを用いれば、統一的にクロス開発環境を整えられるという点でメリットが有る。

Preparation

vagrantでホスト環境を整えておく。ubuntu 18.04を入れてるほかは変わったことはしてない:

Vagrant.configure("2") do |config|

  config.vm.box = "ubuntu/bionic64"
  config.vm.synced_folder ".", "/home/vagrant/shared"
  config.vm.provider "virtualbox" do |vb|
    vb.cpus = 2
    vb.memory = "4096"
  end
  config.vm.provision "shell", inline: <<-SHELL
  SHELL
end

debootstrap first stage

$ sudo apt-get install -y debootstrap
$ mkdir -p ./riscv
# debootstrap (option) (version) (path) (MIRROR)
# debian-ports-archive-keyring : for `apt-get install`
$ sudo debootstrap --arch=riscv64 --foreign \
--include=debian-ports-archive-keyring,ifupdown,netbase,net-tools,openssh-server,file,wget,openssl,sudo \
sid ./riscv http://ftp.ports.debian.org/debian-ports
# or add option`--keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg`

stretchはなかったので、debianのsid にしている。keyringオプションはパッケージの署名でつけたほうがよいが、つけなくても動く。

reigster binfmt

  • chrootの先で/usr/bin/qemu-riscv64-staticが実行できること。
  • riscv64が実行できるbinfmtが準備されていること。

install qemu-riscv64-static

Ubuntu 18.04環境では、sudo apt-get -y qemu-user-staticqemu-riscv64-staticが取得できなかったので、直接ソースからビルドする。

$ update-binfmts --display | grep riscv
# if nothing, we have to build `qemu-riscv64-static` from source.
# see https://wiki.qemu.org/Hosts/Linux#Required_additional_packages
$ sudo apt-get install -y pkg-config git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev
$ git clone https://git.qemu.org/git/qemu.git -b v4.2.0 --depth=1
$ cd qemu && mkdir build && cd build
$ mkdir out
# You have to build as a static binary.
$ ../configure --static --target-list=riscv64-linux-user --prefix=$(pwd)/out
$ make && make install
$ cd ../../

configure binfmt

# Note that `qemu-riscv64` is from x86_64S
$ readelf -h ./qemu/build/out/bin/qemu-riscv64 | grep Machine
  Machine:                           Advanced Micro Devices X86-64
$ sudo cp ./qemu/build/out/bin/qemu-riscv64 ./riscv/usr/bin/qemu-riscv64-static
$ sudo apt-get install -y binfmt-support
$ cat > /tmp/riscv64 <<- EOF
> package qemu-user-static
> type magic
> offset 0
> magic \x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00
> mask \xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff
> interpreter /usr/bin/qemu-riscv64-static
> EOF

update-binfmtsするところのmagicはELFファイルのheader.

$ sudo update-binfmts --import /tmp/riscv64
update-binfmts: warning: /tmp/riscv64: no executable /usr/bin/qemu-riscv64-static found, but continuing anyway as you request
# Any labels will do under binfmt_misc directory
$ ls /proc/sys/fs/binfmt_misc/riscv64
/proc/sys/fs/binfmt_misc/riscv64
# if the file doesn't exist, `sudo systemctl binfmt-support` or restart vbox

Note) qemu-riscv64-staticを./riscv/usr/bin/qemu-riscv64-staticに置いているが、今回の目的であれば、/usr/bin/qemu-riscv64-staticに直におく必要はない。chrootの先で/usr/bin/qemu-riscv64-staticというバイナリが存在しさえすればいいだけなので。

configure under chroot

$ sudo chroot ./riscv debootstrap/debootstrap --second-stage
# mount
$ sudo mount --bind /sys ./riscv/sys
$ sudo mount --bind /dev ./riscv/dev
$ sudo mount --bind /proc ./riscv/proc
# Without this, sudo command would `no tty present and no askpass program specified`..
$ sudo mount --bind /dev/pts ./riscv/dev/pts
$ sudo chroot ./riscv
# dive into riscv64
root@ubuntu-bionic:/# file `which bash`
/bin/bash: ELF 64-bit LSB shared object, UCB RISC-V, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-riscv64-lp64d.so.1, BuildID[sha1]=02650defdd2fb0ebdf17ba89a28040bf403790d7, for GNU/Linux 4.15.0, stripped

ということで、riscv64のバイナリが実行できている!

といってもこのままだと、apt-getとかできないので少し修正。

# inside chroot environment
$ echo "deb http://ftp.ports.debian.org/debian-ports sid main" >> /etc/apt/sources.list
$ apt-get update
# for test
$ apt-get install -y curl

cleanup

$ sudo umount -v ./riscv/{sys,dev,proc}

で後始末しておく。