QEMU User Emulation

This page describes how to setup and use QEMU user emulation in a "transparent" fashion, allowing execution of non-native target executables just like native ones (i.e. ./program).

In this text, "target" means the system being emulated, and "host" means the system where QEMU is running.

Note this setup is incompatible with Scratchbox (both use the binfmt_misc module to register the same formats), so it's recommended to remove it (or stop its init script) before continuing.

Installing packages

The binfmt-support package contains a helper script to easily register/unregister binary formats with the kernel using the binfmt_misc module.

  1. Install qemu, binfmt-support, and qemu-user-static:

# apt install qemu binfmt-support qemu-user-static
  1. Check whether the binfmt entries were successfully registered:

# update-binfmts --display

Adjusting the system

Depending on the your kernel settings, you may need to set 'vm.mmap_min_addr=0' sysctl option to allow a program being run under a regular user, not root.

Running dynamically linked executables

With the instructions above, you should be able to run statically linked target executables. To be able to run dynamically linked binaries, QEMU needs to have access to the target ELF interpreter. The libc6 package for the target architecture contains the target's ELF interpreter used by QEMU.

Installing this can be done with multiarch from wheezy onwards, or with dpkg-cross on earlier (pre-multiarch) releases.

Installing the target C libraries with multiarch

For example purposes, let's assume the target system is "armhf".

sudo dpkg --add-architecture armhf
sudo apt update
sudo apt install libc6:armhf

That's it.

Installing the target C libraries with dpkg-cross

If the target Debian package cannot be installed directly on the host, we need to use dpkg-cross to "cross-install" the package.

For example purposes, let's assume the target system is "armel".

  1. Install the dpkg-cross package:

# apt install dpkg-cross
  1. Now download the target libc6 package from one of the Debian mirrors and install it using dpkg-cross:

# dpkg-cross -i -a arm libc6_<version>_armel.deb

Point QEMU to the target linux loader

Under multiarch the target arch loader is in the usual place (/lib/<triplet>) so nothing special is needed. If using dpkg-cross it's installed in a non-standard path so you need to tell QEMU about that.

for example, for the armel architecture: add the line

EXTRA_OPTS="-L /usr/arm-linux-gnueabi"

to the /etc/qemu-binfmt.conf.

for armhf add: EXTRA_OPTS="-L /usr/arm-linux-gnueabihf"

Testing the emulation environment

We will use the "hello" ARM Debian package to test the new environment.

  1. Download the hello package (e.g. from http://deb.debian.org/debian/pool/main/h/hello/hello_version_armel.deb)

  2. Unpack it with the command:

$ dpkg -x hello_version_armel.deb /tmp/hello_armel
  1. Finally, run the hello executable with:

    $ /tmp/hello_armel/usr/bin/hello

It should print "Hello, world!".

That's it! You can now run non-native executables transparently, as long as QEMU supports the system calls used by it.

Appendix: chrooting into target file systems

To be able to chroot into a target file system, the qemu emulator for the target CPU needs to be available. You need first to install the qemu-user-static package:

# apt install qemu-user-static

If you are using stretch or earlier, it also needs to be accessible from inside the chroot jail. From Debian buster and later, this isn't necessary because Linux will use the qemu binary from the host system instead of from the chroot. This means that you can use the dynamically linked qemu on buster or later but not stretch or earlier because the host libraries will not be accessible from inside the chroot.

If you are using stretch or earlier, make the emulator available for the target architecture inside the chroot at the path registered by binfmt-support. You can either bind-mount the binary into the chroot, or just copy it. Copying it means you won't have to copy it again, but it also means it won't get updates, while bind-mounting means you have to bind-mount For example, for an ARM target file system, you need to do one of the following:

# mount --bind /usr/bin/qemu-arm-static /target_fs/usr/bin
# cp /usr/bin/qemu-arm-static /target_fs/usr/bin

If you are using systemd, then you can use systemd-nspawn to start a container:

# apt install systemd-container
# systemd-nspawn -D /target_fs/

If you aren't using systemd-nspawn, then you may want to mount special filesystems for devices, terminals and processes from the host in the chroot:

for f in dev dev/pts sys proc run ; do mount --bind /$f tmp/$f ; done

You should now be able to chroot into the file system:

# chroot /target_fs/

See Also

TODO


CategoryEmdebian CategoryNotNative