Linux has a mechanism that allows plug-ins to be registered so that the kernel calls an interpreter program when instructed to execute a file: binfmt_misc. Simplifying a bit, when an executable file is executed, the kernel reads the first few bytes and goes like this:
- Does it start with the four bytes
\x7fELF followed by a valid-looking ELF header? If so, use the ELF loader inside the kernel to load the program and execute it.
- Does it start with the two bytes
#! (shebang)? If so, read the first line, parse what's after the
#! and execute that, passing the path to the executable as an argument.
- Does it start with one of the magic values registered through the binfmt_misc mechanism? If so, execute the registered interpreter.
To run foreign-architecture binaries via Qemu, magic values corresponding to an ELF header with each supported architecture are registered through the binfmt_misc mechanism. You can see what is supported by listing the directory
/proc/sys/fs/binfmt_misc/ (which is a special filesystem representing the current set of registered binfmt_misc interpreters in the kernel). For example:
So if an executable
/somewhere/foo starts with the specified magic bytes, if you run
/somewhere/foo arg1 arg2, the kernel will call
/usr/bin/qemu-arm-static /somewhere/foo arg1 arg2.
It is not necessary to use a chroot for this mechanism to work, the executable could be anywhere. A chroot is convenient in order for dynamic executables to work: dynamic executables contain an absolute path to their loader, so if you run e.g. an ARM excutable, it will expect a loader located in
/lib. If the loader is in fact located in
/different-arch-root-to-be, a chroot to that directory is necessary for the executable to find the loader.