Skip to content
Switch branches/tags

Latest commit

…019, and has continued as part of that project.

Its purpose was always "build a tiny toybox-based system", and being able to
do so without having to download toybox source simplified things a bit.

I also came up with a more compact CONFIG=name,name,name,name syntax for
the miniconfig symbols which shrunk the whole build script down to 250 lines
of bash, and added an expansion directory (scripts/root) that can contain
additional build scripts to add packages to the root filesystem...

I.E. development continues, just as part of toybox.

Git stats


Failed to load latest commit information.
Latest commit message
Commit time
THIS PROJECT HAS BEEN MERGED INTO TOYBOX. This version is retained for
historical purposes, new development occurs in the built-in toybox version.


mkroot - simple linux system builder

  Compiles a toybox-based root filesystem and kernel that can boot under qemu.

  Mailing list:

The tl;dr invocation is probably something like this, but you need to
build musl-cross-make and set up the mcm symlink first, as explained below:

  $ ./ sh4 ./mkroot dropbear distcc kernel
  $ (cd output/sh4 && ./

This project is a successor to
and shares most of the same goals, with a much simpler implementation.

--- Quick start

Running ./ with no arguments and no $CROSS_COMPILE environment
variable builds a root filesystem for the host.

  $ ./ 

This downloads toybox into the "download" directory (if it's not already
there), and builds a minimal root filesystem in output/host. You can
then chroot into it like this:

  $ sudo chroot output/host/root /init
    $ ls -l
    $ exit

--- Adding build modules

You can build additional things from the "module" directory by listing
them on the command line:

  $ ./ dropbear distcc kernel

Use "-n" as the first argument to mkroot if you're adding to an existing
root filesystem. (The default is to delete and rebuild each time.)

  $ ./
  $ ./ -n dropbear distcc
  $ ./ -n kernel

We provide a few module build scripts to get you started, but aren't interested
in turning mkroot into a Linux distro. If you want to cross-compile more
packages, fork the repo and add your own modules. As long as you don't modify
existing files you should be able to cleanly pull updates into your fork
(without even a merge commit if you pull --ff).

The "Linux From Scratch" ( and
"Beyond Linux From Scratch" ( projects are
a good source of additional package build instructions.

--- Cross compiling

To build for a different target, specify a cross compiler prefix using
the CROSS_COMPILE environent variable, ala:

  $ CROSS_COMPILE=armv5l-linux-musl- ./ kernel

which builds a root filesystem and kernel, and writes a qemu boot script.
If you have QEMU installed, you can then go:

  $ cd output/armv5l
  $ ./
    $ cat /proc/cpuinfo
    $ exit

In theory you can use many different cross compilers. In practice the project
is developed and tested against musl-cross-make:

Running the included script in the musl-cross-make project
directory builds cross and native compilers for all supported targets
(which takes quite a while).

  $ cd ~
  $ git clone mcm
  $ cd mcm
  $ ~/mkroot/
  $ cd ~/mkroot
  $ CROSS_COMPILE=~/mcm/output/sh4-linux-musl-cross/bin/sh4-linux-musl- \
    ./ kernel

Or you can download the resulting binary tarballs from:

If you create an "mcm" symlink under mkroot pointing to your musl-cross-make
output directory, you can use the wrapper script to quickly select
the target to build, or use the target "all" to build all available targets.

  $ ln -s cross ~/mcm/output mcm
  $ ./ # lists available targets
  $ ./ i686
  $ tail output/i686/log.txt
  $ (cd output/i686 && ./
    $ exit
  $ ./ && ./ all ./ distcc kernel

All does is set $CROSS_COMPILE to an appropriate value, then call
the rest of its command line. To see this value, try:

  $ ./ i686 env | grep CROSS_COMPILE

--- Building kernels for QEMU

Most modules add files to the target's root filesystem, but the "kernel"
module doesn't modify the root filesystem. Instead it packages up that root
filesystem into a cpio.gz archive, builds a bootable Linux kernel
for the target, creates a qemu-$ launch script to plug them
both into QEMU, and also saves the $TARGET.miniconfig file used to configure
the kernel build.

Modules are processed in order, so kernel should always come last or it won't
package up anything you add after it runs. The module/kernel script
recognizes all the musl-cross-make prefixes, and uses "uname -m" when building
for the host. When using an unknown cross compiler, you can set the environment
variable "TARGET" to override this (ala "./ TARGET=mips kernel").

The standard test that all available hardware is working is that you get a
shell prompt, can type at it, the date is set right, the network is working,
the hard drive images work, and the emulator quits when you exit.

  $ ./ armv5l ./ dropbear kernel
  $ cd output/armv5l
  $ ./
    $ ls -l
    $ cat /proc/cpuinfo 
    $ ifconfig
    $ date
    $ exit

Updating QEMU or kernel versions can break any of that, so retest if you do.

The qemu-$ script appends any extra arguments to the QEMU command line,
like so:

  $ ./ -hda armv5l.miniconf -hdb
    $ tail /dev/?da
    $ tail /dev/?db
  $ exit

--- Hermetic build

Mkroot uses toybox's "make install_airlock" to implement a "hermetic build"
when cross compiling, which means it tries to insulate itself from variations
in host systems by providing its own build prerequisites.

This means the first thing builds when it sees CROSS_COMPILE set
is an "airlock" directory full of all the binaries it will use (most supplied
by toybox, plus symlinks to the host's native toolchain). Then it changes the
$PATH for the rest of the build to point to just this airlock directory
(plus the cross compiler if you didn't provide an absolute path for that).
Doing this prevents autoconf from finding things like python on your host
that won't be on the target.

A hermetic build also blanks all the environment variables, except for $HOME,
$PATH, and $CROSS_COMPILE. If you need to specify more, set them on the
mkroot command line (name=value arguments are interpreted as environment
variables instead of modules).

If you need to add extra things to the airlock, delete the old one (if any,
./ will do this) the set HOST_EXTRA="list of commands" on the mkroot
command line (space separated if there's more than one). For example:

  ./ && ./ i686 ./ HOST_EXTRA=mkimage kernel

--- Downloading and building packages

Source code for package builds is stored in the "download" directory,
either as extracted source or as tarballs. The "download" function verifies
each tarball's sha1sum, fetching a new one from the URL via wget as necessary.
To download/verify source packages without building yet, use the "-d" option.

If you provide a directory under download matching a package's name (often
via "git clone"), the build will use that instead of fetching tarballs.
Since mkroot doesn't apply patches to tarballs, the easy way to build modified
source is to provide your own source directory under download.

For example:

  mkdir download
  cd download
  git clone git://

Using extracted source is noticeably faster when building multiple targets.
(Re-extracting the Linux kernel source tarball takes a while, and copies
of source directories are made with cp -s which creates a tree of symlinks
to the original source, saving disk space and cache memory.)


Simple Linux build, bootable under qemu for multiple architectures.




No packages published