Skip to content

Instantly share code, notes, and snippets.

@bluedragon1221
Last active November 2, 2024 13:22
Show Gist options
  • Save bluedragon1221/a58b0e1ed4492b44aa530f4db0ffef85 to your computer and use it in GitHub Desktop.
Save bluedragon1221/a58b0e1ed4492b44aa530f4db0ffef85 to your computer and use it in GitHub Desktop.
Create a minimal linux from scratch with initramfs and busybox.

https://www.youtube.com/watch?v=QlzoegSuIzg

The Three Parts

To build a minimal linux distro, we need three parts:

  1. The Kernel
  2. Userspace (busybox)
  3. Bootloader (syslinux)

When the system boots, it loads the kernel, which loads busybox.

Bootloader -> Kernel -> Userspace

Create a Container

It's bad to install locallly, so we'll do everything in an Ubuntu container.

docker run --privileged -it ubuntu

You'll notice that --privileged flag. It is required for mounting some stuff later on.

Setup the Container

Start by updating the system:

apt update

Then we need to install some packages.

apt install bzip2 git vim make gcc libncurses-dev flex bison bc cpio libelf-dev libssl-dev

Build Linux from Source

First, we need to get a kernel.

We'll start by cloning linux. We'll use the GitHub mirror, because why not?

git clone --depth 1 https://github.com/torvalds/linux.git
cd linux

--depth 1 just means that it won't clone the entire git history. (That would be significantly longer)

Next, we need to make the kernel config. There's a lot of options, and I'm sure you can find some other tutorial for making a kernel config. For this, we'll just use the default options.

make menuconfig

Then arrow over to < Exit >, and save the configuration.

So, without further ado, let's build the kernel.

make -j$(nproc)

This will take a LONG time...

At the end, you should see a line that says:

Kernel at arch/x86/boot/bzImage is ready

This is our kernel binary. Since we'll need it later, let's copy it to another place.

mkdir /boot-files
cp arch/x86/boot/bzImage /boot-files/

Build the Userspace (Busybox)

Busybox is a bundle of many basic linux utilities, for example ls, grep, and vi are all included.

First, let's clone the sources (this process will feel very similar to the Kernel)

git clone --depth 1 https://git.busybox.net/busybox
cd busybox

Now, we can edit the busybox config. In this one, we will need to change one option. Go to Settings ---> and toggle [ ] Build static binary (no shared libraries). This will make our build simpler; not depending on any shared libraries.

Then exit, and save changes, just like in the kernel.

Now we'll build it, just like the kernel:

make -j$(nproc)

It shouldn't take nearly as long, but you never know...

Since busybox will go into our initramfs, let's make a folder for it in /boot-files:

mkdir /boot-files/initramfs

Now, we'll install busybox to that directory:

make CONFIG_PREFIX=/boot-files/initramfs install

This will basically just create a bunch of symlinks in that folder

Also, we can remove linuxrc, because we don't need it.

rm /boot-files/initramfs/linuxrc

Create an init script

The kernel needs something to execute. It looks in /init for this file. On a normal linux system, this is a systemd binary, but here, it's just a shell script.

cd /boot-files/initramfs
echo << EOF > ./init
#!/bin/sh

/bin/sh
EOF
chmod +x ./init

This short little script will launch a shell as soon as we boot into the system.

Create the Initramfs

An initramfs is a cpio archive. This archive needs to contain all of the files in initramfs/. First, let's create a list of all of those files:

find .

Then pass it to cpio to create the archive:

find . | cpio -o -H newc > ../init.cpio

-o creates a new archive, and -H newc specifies the type of the archive.

Create the "filesystem"

We don't want to create a whole partition for this device, and, luckily, we don't have to. We'll create an empty file and format it with fat.

dd if=/dev/zero of=./boot bs=1M count=50

Hopefully you can spare 50M...

To format it, we'll need dosfstools:

apt install dosfstools

Then to format it:

mkfs -t fat boot

Install the bootloader

We'll need the syslinux package:

apt install syslinux

Then we can install syslinux with:

syslinux ./boot

Copy files to the image

We need to mount that image (Here's why our container needed root privileges). Create the mountpoint, then mount it there.

mkdir m
mount boot m

Then move the kernel binary and the initramfs to the image:

cp {bzImage,init.cpio} m/

Now we can umount the filesystem:

umount m

Test it

We can test the image using qemu. Assuming you don't want to install qemu on your container, we'll copy the files to the host system.

docker cp {container_name}:/boot-files ~/boot-files

Now we can run ./boot with qemu:

qemu-system-x86_64 -drive format=raw,file=./boot -display gtk

We'll get a prompt that says boot: . Type,

/bzImage -initrd=/init.cpio

Just wait for it to boot up, then you'll be greeted with a shell prompt!

~ # 

Bonus: Installing binaries

You can install any software that you want to the system using the following procedure:

  1. Build the software, with CONFIG_PREFIX=/boot-files/initramfs
  2. Remount ./boot to m
  3. Delete the old init.cpio and m/init.cpio
  4. Create a new one, and copy it to m/
  5. Umount ./m
@actrawave
Copy link

hello, you've made 2 typos here

with out further adue

should be "without further ado"

sorry for being a nerd :)

@bluedragon1221
Copy link
Author

thanks for catching that. fixed! 😉

@actrawave
Copy link

and another typo

The to format it:

should be "then"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment