# Easy and hygenic RISC-V userspace tesing on Linux
So I was playing around yesterday with [Alex Suykov's QEMU fork][Q] and [Richard
W.M. Jones' Fedora bootstrapping efforts][F] and noticed that they go really
well together. Using binfmt_misc to invoke qemu, I can set up a chroot running
a RISC-V Fedora userland, including *non-cross* gcc compilers.
[Q]:
https://github.com/arsv/riscv-qemu
[F]:
https://github.com/rwmjones/fedora-riscv
Automating the process with Docker (a very fancy chroot manager for Linux with
additional sandboxing facilities), you can now run a sandboxed RISC-V Linux
user environment with just two commands. I've pushed the necessary image to
[Docker hub][I], although it's also surprisingly simple to rebuild (below).
[I]:
https://hub.docker.com/r/sorear/fedora-riscv-wip/
## The two-command way
1. Set up binfmt_misc to pass RISC-V ELF binaries to `/qemu-riscv64` in the
current filesystem root. This configuration is _not_ scoped to the sandbox,
so it will affect the host system; since you don't have a file named
`/qemu-riscv64` on your host system, the only effect of this is that trying
to run a RISC-V binary may result in a spurious ENOENT instead of ENOEXEC.
There are no spaces or newlines in the argument to echo.
Z='\x00\x00\x00\x00\x00\x00\x00'; echo ':riscv64:M::\x7f\x45\x4c\x46'\
$Z$Z'\xf3\x00:\xff\xff\xff\xff'$Z$Z'\xff\xff:/qemu-riscv64:' >
/proc/sys/fs/binfmt_misc/register
2. Use docker to download the chroot environment and run a shell. The chroot
contains a `/qemu-riscv64` binary, so RISC-V executables invoked within it
will be emulated automatically; furthermore, the emulator is inside the
sandbox. This is roughly a 600MB download.
The qemu binary is compiled for amd64; if you are on a different
architecture, you will need to build your own image (below).
docker run -it sorear/fedora-riscv-wip:interp
## Known limitations
* I'm using an incomplete old snapshot of the
bootstrapping process, so there are relatively few tools.
* Job control doesn't work (control-Z does nothing).
* Control-C at the shell doesn't clear the current command line(?)
* Many commands, including `git` and `make`, will segfault immediately if run
from the context of a `docker exec` but work properly from `docker run`.
No idea why.
* There's something wrong with library search paths and gcc doesn't work unless
I set `LD_LIBRARY_PATH=/lib64`; this workaround is included in the image.
## I want to build the image myself.
Say you don't want me in your chain of custody, or you're not on amd64.
1. Download the Fedora stage 3 root image and convert it into a docker image.
wget
http://oirase.annexia.org/riscv/stage3-disk.img.xz
unxz stage3-disk.img.xz
sudo mount -o loop stage3-disk.img.xz stage3-disk
cd stage3-disk
sudo tar -c . | docker import -c 'CMD /bin/bash' - \
sorear/fedora-riscv-wip:pristine
cd ..
sudo umount stage3-disk
2. Download and compile a statically linked riscv64-linux-user qemu.
git clone
https://github.com/arsv/riscv-qemu
cd riscv-qemu
sudo dnf install glibc-static glib2-static libstdc++-static
./configure --prefix=~/opt/riscv-qu --target-list=riscv64-linux-user \
--static
make
cd ..
3. Create an image which combines the two.
mkdir addinterp
cd addinterp
cp ../riscv-qemu/riscv64-linux-user/qemu-riscv64 .
cat >Dockerfile <<END
FROM sorear/fedora-riscv-wip:pristine
COPY qemu-riscv64 .
ENV LD_LIBRARY_PATH=/lib64
END
docker build -t sorear/fedora-riscv-wip:interp .
cd ..
## I don't want Docker.
The previous steps can be easily adapted to build a directory tree instead of
an image, which you can then invoke with a chroot. This results in much weaker
sandboxing, though.
## I'm not using Linux.
You will need a Linux VM for this project. Docker has downloadable VM tools
in the command line package for macOS and Windows; I haven't tried them.
I believe those tools do not give you easy shell access to the VM outside of a
container. The binfmt_misc setup command cannot be run in a normal container
because it's a privileged operation (it affects all containers), but it should
be possible to run it in a container started with `--privileged`, e.g.
docker run --rm -it --privileged alpine /bin/sh