Skip to Content

Extracting Dracut Built initramfs

It's been a hot second since I've dived into the lands of initramfs and since then it seems like things have gotten more complicated. This is the way of things in tech and usually has a good reason. The simple way that used to work wonders (and is still required) to start with, was to identify if the file is compressed:

$ file /boot/initramfs-current.img
/boot/initramfs-current.img: ASCII cpio archive (SVR4 with no CRC)

In this case the file appears to be entirely uncompressed which is convenient and likely exactly what you'll experience for reasons I'm about to get to. This could indicate that there is a type of compression in place (such as gzip or the like) in which case your initramfs has likely not been generated by a modern version of Dracut and this post isn't right for you.

Next let's extract the contents into a new temporary directory:

$ mkdir init_tmp
$ cd init_tmp
$ cpio -mvid < /boot/initramfs-current.img
.
early_cpio
kernel
kernel/x86
kernel/x86/microcode
kernel/x86/microcode/AuthenticAMD.bin
62 blocks

This is where things got weird and I got caught up. No /init? No tools for dealing with LVM, filesystems, device scanning? No core system directories like /dev, /sys, or /proc? What is going on here. We can also quickly see the size doesn't match what we extracted:

$ du --max-depth=1 -h
32K     ./kernel
36K     .
$ ls -lh /boot/initramfs-current.img
-rwxr-xr-x 1 root root 13M Feb 18 15:13 /boot/initramfs-current.img

Turns out this is an early optimization by dracut to get updated microcode to the processor before we pass control over to any userspace programs. It's actually pretty slick and I'll have to figure out how this works in some future post. To get to the real initramfs we simply need to skip the first one using dd.

Previosly when we extracted the CPIO archive it told us how large it was (The 62 blocks at the end). We simply need to skip those then we should be able to decode the inner archive.

Side note for clarity with the following commands, I switched back to my home directory and emptied out the extracted contents of the init_tmp directory we created earlier as that's where we want to put the extracted inner initramfs contents.

$ dd if=/boot/initramfs-current.img bs=512 skip=62 of=inner-initramfs-current.img
25995+1 records in
25995+1 records out
13309841 bytes (13 MB, 13 MiB) copied, 0.0848529 s, 157 MB/s

Once again we need to identify if compression is present:

$ file inner-initramfs-current.img
inner-initramfs-current.img: LZ4 compressed data (v0.1-v0.9)

From here we can decompress it and extract the inner archive much like before using the appropriate utility (lz4cat does the trick here, your compression may vary).

$ cd init_tmp
$ lz4cat ../inner-initramfs-current.img | cpio -mvid
.
bin
bin/bash
bin/cat
bin/chown
bin/chroot
...<contents remove for brevity>
var
var/lock
var/run
var/tmp
54581 blocks

Bam! That is a lot more like it. I'm really curious how the kernel boot process works with that early microcode but this got me where I needed to be and I hope this helps someone else out.

Additional note: I missed this but apparently dracut ships with a utility called skipcpio which you can pipe one of these initramfs files through to skip the extra dd step. Had a friend point that out after I wrote this up…