PRoot is a user-space implementation of chroot, mount --bind, and binfmt_misc. This means that users don't need any privileges or setup to do things like using an arbitrary directory as the new root filesystem, making files accessible somewhere else in the filesystem hierarchy, or executing programs built for another CPU architecture transparently through QEMU user-mode. Also, developers can use PRoot as a generic Linux process instrumentation engine thanks to its extension mechanism, see CARE for an example. Technically PRoot relies on ptrace, an unprivileged system-call available in every Linux kernel.
The new root file-system, a.k.a guest rootfs, typically contains a Linux distribution. By default PRoot confines the execution of programs to the guest rootfs only, however users can use the built-in mount/bind mechanism to access files and directories from the actual root file-system, a.k.a host rootfs, just as if they were part of the guest rootfs.
When the guest Linux distribution is made for a CPU architecture incompatible with the host one, PRoot uses the CPU emulator QEMU user-mode to execute transparently guest programs. It's a convenient way to develop, to build, and to validate any guest Linux packages seamlessly on users' computer, just as if they were in a native guest environment. That way all of the cross-compilation issues are avoided.
PRoot can also mix the execution of host programs and the execution of guest programs emulated by QEMU user-mode. This is useful to use host equivalents of programs that are missing from the guest rootfs and to speed up build-time by using cross-compilation tools or CPU-independent programs, like interpreters.
It is worth noting that the guest kernel is never involved, regardless of whether QEMU user-mode is used or not. Technically, when guest programs perform access to system resources, PRoot translates their requests before sending them to the host kernel. This means that guest programs can use host resources (devices, network, ...) just as if they were "normal" host programs.
In the following examples the directories /mnt/slackware-8.0 and /mnt/armslack-12.2/ contain a Linux distribution respectively made for x86 CPUs and ARM CPUs.
To execute a command inside a given Linux distribution, just give proot the path to the guest rootfs followed by the desired command. The example below executes the program cat to print the content of a file:
proot -r /mnt/slackware-8.0/ cat /etc/motd Welcome to Slackware Linux 8.0
The default command is /bin/sh when none is specified. Thus the shortest way to confine an interactive shell and all its sub-programs is:
proot -r /mnt/slackware-8.0/ $ cat /etc/motd Welcome to Slackware Linux 8.0
The bind mechanism enables one to relocate files and directories. This is typically useful to trick programs that perform access to hard-coded locations, like some installation scripts:
proot -b /tmp/alternate_opt:/opt $ cd to/sources $ make install [...] install -m 755 prog "/opt/bin" [...] # prog is installed in "/tmp/alternate_opt/bin" actually
As shown in this example, it is possible to bind over files not even owned by the user. This can be used to overlay system configuration files, for instance the DNS setting:
ls -l /etc/hosts -rw-r--r-- 1 root root 675 Mar 4 2011 /etc/hosts
proot -b ~/alternate_hosts:/etc/hosts $ echo '1.2.3.4 google.com' > /etc/hosts $ resolveip google.com IP address of google.com is 1.2.3.4 $ echo '5.6.7.8 google.com' > /etc/hosts $ resolveip google.com IP address of google.com is 5.6.7.8
Another example: on most Linux distributions /bin/sh is a symbolic link to /bin/bash, whereas it points to /bin/dash on Debian and Ubuntu. As a consequence a #!/bin/sh script tested with Bash might not work with Dash. In this case, the binding mechanism of PRoot can be used to set non-disruptively /bin/bash as the default /bin/sh on these two Linux distributions:
proot -b /bin/bash:/bin/sh [...]
Because /bin/sh is initially a symbolic link to /bin/dash, the content of /bin/bash is actually bound over this latter:
proot -b /bin/bash:/bin/sh $ md5sum /bin/sh 089ed56cd74e63f461bef0fdfc2d159a /bin/sh $ md5sum /bin/bash 089ed56cd74e63f461bef0fdfc2d159a /bin/bash $ md5sum /bin/dash 089ed56cd74e63f461bef0fdfc2d159a /bin/dash
In most cases this shouldn't be a problem, but it is still possible to strictly bind /bin/bash over /bin/sh -- without dereferencing it -- by specifying the ! character at the end:
proot -b '/bin/bash:/bin/sh!' $ md5sum /bin/sh 089ed56cd74e63f461bef0fdfc2d159a /bin/sh $ md5sum /bin/bash 089ed56cd74e63f461bef0fdfc2d159a /bin/bash $ md5sum /bin/dash c229085928dc19e8d9bd29fe88268504 /bin/dash
The two features above can be combined to make any file from the host rootfs accessible in the confined environment just as if it were initially part of the guest rootfs. It is sometimes required to run programs that rely on some specific files:
proot -r /mnt/slackware-8.0/ $ ps -o tty,command Error, do this: mount -t proc none /proc
works better with:
proot -r /mnt/slackware-8.0/ -b /proc $ ps -o tty,command TT COMMAND ? bash ? proot -b /proc /mnt/slackware-8.0/ ? sh ? ps -o tty,command
Actually there's a bunch of such specific files, that's why PRoot provides the option -R to bind automatically a pre-defined list of recommended paths:
proot -R /mnt/slackware-8.0/ $ ps -o tty,command TT COMMAND pts/6 bash pts/6 proot -R /mnt/slackware-8.0/ pts/6 sh pts/6 ps -o tty,command
Some programs will not work correctly if they are not run by the "root" user, this is typically the case with package managers. PRoot can fake the root identity and its privileges when the -0 (zero) option is specified:
proot -r /mnt/slackware-8.0/ -0 # id uid=0(root) gid=0(root) [...] # mkdir /tmp/foo # chmod a-rwx /tmp/foo # echo 'I bypass file-system permissions.' > /tmp/foo/bar # cat /tmp/foo/bar I bypass file-system permissions.
This option is typically required to create or install packages into the guest rootfs. Note it is not recommended to use the -R option when installing packages since they may try to update bound system files, like /etc/group. Instead, it is recommended to use the -S option. This latter enables the -0 option and binds only paths that are known to not be updated by packages:
proot -S /mnt/slackware-8.0/ # installpkg perl.tgz Installing package perl...
PRoot uses QEMU user-mode to execute programs built for a CPU architecture incompatible with the host one. From users' point-of-view, guest programs handled by QEMU user-mode are executed transparently, that is, just like host programs. To enable this feature users just have to specify which instance of QEMU user-mode they want to use with the option -q:
proot -R /mnt/armslack-12.2/ -q qemu-arm $ cat /etc/motd Welcome to ARMedSlack Linux 12.2
The parameter of the -q option is actually a whole QEMU user-mode command, for instance to enable its GDB server on port 1234:
proot -R /mnt/armslack-12.2/ -q "qemu-arm -g 1234" emacs
PRoot allows one to mix transparently the emulated execution of guest programs and the native execution of host programs in the same file-system namespace. It's typically useful to extend the list of available programs and to speed up build-time significantly. This mixed-execution feature is enabled by default when using QEMU user-mode, and the content of the host rootfs is made accessible through /host-rootfs:
proot -R /mnt/armslack-12.2/ -q qemu-arm $ file /bin/echo [...] ELF 32-bit LSB executable, ARM [...] $ /bin/echo 'Hello world!' Hello world! $ file /host-rootfs/bin/echo [...] ELF 64-bit LSB executable, x86-64 [...] $ /host-rootfs/bin/echo 'Hello mixed world!' Hello mixed world!
Since both host and guest programs use the guest rootfs as /, users may want to deactivate explicitly cross-filesystem support found in most GNU cross-compilation tools. For example with GCC configured to cross-compile to the ARM target:
proot -R /mnt/armslack-12.2/ -q qemu-arm $ export CC=/host-rootfs/opt/cross-tools/arm-linux/bin/gcc $ export CFLAGS="--sysroot=/" # could be optional indeed $ ./configure; make
As with regular files, a host instance of a program can be bound over its guest instance. Here is an example where the guest binary of make is overlaid by the host one:
proot -R /mnt/armslack-12.2/ -q qemu-arm -b /usr/bin/make $ which make /usr/bin/make $ make --version # overlaid GNU Make 3.82 Built for x86_64-slackware-linux-gnu
It's worth mentioning that even when mixing the native execution of host programs and the emulated execution of guest programs, they still believe they are running in a native guest environment. As a demonstration, here is a partial output of a typical ./configure script:
checking whether the C compiler is a cross-compiler... no
The source code for PRoot and CARE are hosted in the same repository on GitHub. Previous PRoot releases were packaged at https://github.com/proot-me/proot-static-build/releases, however, that repository has since been archived. The latest builds can be found under the job artifacts for the GitLab CI/CD Pipelines for each commit. The following commands can be used to download the latest x86_64 binary for convenience:
curl -LO https://proot.gitlab.io/proot/bin/proot chmod +x ./proot proot --version
The following URLs contain rootfs archives that can be freely downloaded. Note that mknod errors reported by tar when extracting these archives can be safely ignored since special files are typically bound (see -R option for details).
Technically such rootfs archive can be created by running the following command on the expected Linux distribution:
tar --one-file-system --create --gzip --file my_rootfs.tar.gz /
The following ecosystem has developed around PRoot since it has been made publicly available.
ATOS: find automatically C/C++ compiler options that provide best optimizations.
CARE: archive material used during an execution to make it reproducible on any Linux system.
Debian noroot: use Debian Linux on Android without root access.
GNURoot: use several Linux distros on Android without root access.
JuNest: use Arch Linux on any Linux distros without root access.
OPAM2Debian: create Debian packages which contains a fully compiled OPAM installation.
OpenMOLE: execute programs on distributed computing environments.
Polysquare Travis Container: use several Linux distros on Travis-CI without root access.
Portable PyPy: portable 32 and 64 bit x86 PyPy binaries.
SIO Workers: batch long-term computations with Python.
Binaries from the Downloads section are likely more up-to-date.
articles on Rémi's blog. Rémi (a.k.a Ivoire) is one of the PRoot developers.
presentation "Software engineering tools based on syscall instrumentation" during FOSDEM 2014.
presentation "SW testing & Reproducing a LAVA failures locally using CARE" during Linaro Connect USA 2014
presentation and essay "CARE: the Comprehensive Archiver for Reproducible Execution" (essay) during TRUST 2014
presentation "An Introduction to the CARE tool (dead link)" during HiPEAC CSW 2013
presentation and essay "PRoot: a Step Forward for QEMU User-Mode" (proceedings) during QUF'11
tutorial "How to install nix in home (on another distribution)"
STMicroelectronics
Sony
Ericsson
Cisco
Gogo
Infinite Omicron, LLC.
Feel free to send your questions, bug reports, suggestions, and patches to the mailing-list or to the forum, or chat with us on Gitter; but please be sure that your answer isn't in the user manual first.