To Initramfs or Not to Initramfs

2021-03-23

Once upon a time booting Linux was simpler. Well, everything was simpler to be honest. But the bootloader, after initializing a few key things, basically just passed control over to the kernel, which was expected to have capability built in to mount the root filesystem without having to load any further modules.

This can actually still be done. It is a matter of compiling your own kernel with support for your specific hardware. Unfortunately, if one wishes to compile a kernel for distribution this can lead to a rather kitchen sink configuration in order to include support for every possible sane hardware configuration. This in turn leads to a larger kernel image.

The solution employed by most distributions is the use of an initramfs, which is basically a minimal root filesystem which is loaded into memory, which then loads whatever modules are required to bootstrap your system, mounts the real root and then calls the switch_root binary to move into the real root and run init. The initramfs is generally a compressed cpio archive on disk, and will usually include at minimum a statically compiled busybox executable, an init script and some kernel modules.

Often, however, it is desirable to also start udev while still in the initramfs so that we can do things like specify the root disk by uuid in /etc/fstab, as the kernel only makes the device nodes /dev/sdx, and udev then creates the links in /dev/disk/by-uuid. However, udev also pulls in libkmod from the kmod package and libblkid from util-linux. So we end up with those libraries plus glibc in the initrd. It's at this point that the 'minimal' description begins to be a stretch. I did attempt to compile udev statically. However, kmod stright refuses to be compiled statically with it's current build system so that was a show stopper right off the bat. Is there a technical reason? Well I haven't dug too deaply yet but I suspect the functionality to build a static library was just never implemented due to static linking being frowned upon my most conventional thinking these days.

Right now I'm toying with a compromise approach, and compiling all of the needed libraries and programs against musl libc. Overall this shrinks the size from around 20M uncompressed (on my Arch installation) to around 10M. I'm basing the init script off of the one used by Artix Linux ,which is a fork of Arch without systemd. I think this is a nice gain, as it's half the amount of data for the kernel to decompress and musl linked binaries do actually run a little faster.

I have a separate project which is not yet online to build a range of cross compilers which is suitable for building against musl on every arch that HitchHiker supports. However, at the moment I have not automated the building of kmod, libblkid, eudev and busybox, or the creation of an initrd. Even when I do, I don't think that code will ship with the default source tree, as I would like to encourage users to build a kernel for their machine and run without an initramfs. I will most likely ship the default install tarballs with an initramfs however, just to make booting a little bit more generic and support a wider range of hardware. If and when I have the initrd creation a little more automated I'll post it online as a separate project. I'll probably also make the toolchains available in binary form as compressed tarballs at that time.


Tags for this post:

Boot Initramfs Cross Compile