Such Programming

Tinkerings and Ramblings

Getting BusyBox with It

Following up on the Building a Barebones Linux System post, today I will be adding BusyBox to make the system a smidge more usable.

What is this BusyBox?

BusyBox is a refers to itself as The Swiss Army Knife of Embedded Linux. It is a collection of common Unix tools living within a single binary that uses the magic of argv[0] to facilitate a lightweight implementation of common programs like sh, ls, cat and many more. It is extremely common in the embedded systems community because it is a well maintained open source project and can be built very small.

For my purposes, I will use BusyBox instead of going into a full Linux From Scratch route. This will give me a shell and a few other basic userspace tools needed to allow me to make some use of this bare bones system.

Automating the builds

At this point I have been dabbling with this bare bones setup long enough that I want some additional tooling to help manage rebuilding as necessary. There are many good tools out there for managing custom Linux builds, but for now I’d like to continue using custom tools to learn more of what it takes to build a Linux distro from square one.

I’ve made a repo on Github and a Makefile that will pull in the kernel, build it, and build my initramfs. It also has a runvm command that will help me test my builds quickly.

KERNEL_VERSION=4.13.3
KERNEL_DIRECTORY=linux-$(KERNEL_VERSION)
KERNEL_ARCHIVE=$(KERNEL_DIRECTORY).tar.xz
KERNEL_URL=https://cdn.kernel.org/pub/linux/kernel/v4.x/$(KERNEL_ARCHIVE)


all: vmlinuz initramfs


# Kernel build targets
vmlinuz: $(KERNEL_DIRECTORY)
	cd $(KERNEL_DIRECTORY) && make defconfig && make -j`nproc`
	cp $(KERNEL_DIRECTORY)/arch/x86_64/boot/bzImage vmlinuz

$(KERNEL_DIRECTORY):
	wget $(KERNEL_URL)
	tar xf $(KERNEL_ARCHIVE)


# Initramfs build targets
initramfs: initfs initfs/init
	cd initfs/ && find . | cpio -o --format=newc > ../initramfs

initfs/init: initfs init.c
	gcc -o initfs/init -static init.c

initfs:
	mkdir -p initfs/bin


# Utility targets
runvm: vmlinuz initramfs
	qemu-system-x86_64 -m 2048 -kernel vmlinuz -initrd initramfs

clean:
	rm -rf vmlinuz $(KERNEL_DIRECTORY) $(KERNEL_ARCHIVE)

Configuring and Building BusyBox

BusyBox uses buildroot so its configuration and build process are very similar to the kernel itself. After downloading and extracting the source I’ll use make menuconfig to use the ncurses based config editor.

I’ll be leaving most settings at their default, but will configure the build to generate a static binary since I don’t yet have a libc implementation to link against.

With BusyBox built, I’ll add it to my initfs directory so that my Makefile includes it in my initramfs.

The busybox binary will act differently based on what it is called as, if we renamed it to sh it would act as a shell, or if named ls it will list our files and such. To allow it to serve all of these functions I’ll check what commands my build implements and create symlinks for each program it provides.

Switching the Build to BusyBox

With my process for building BusyBox roughly figured out, I’ll save a copy of my BusyBox config, so that future builds will also be static linked. I’ll also include a small shell script that builds my symlinks.

cd initfs/bin
for i in `./busybox --list`
do
  ln -s busybox $i
done

Now that I have BusyBox in my initramfs, I’ll switch my init process from my custom binary to a shell script that will forever spawn a shell to use.

#!/bin/sh

while true
do
  sh
  sleep 1
done

With this script in place I’ll update my Makefile and repo to include the changes I’ve made here.

And now we test!

There are a few deficiencies in here still, e.g. ps won’t work because I have no procfs mounted and sh will complain about being unable to access tty.

To get a few more things to run sanely, I’ll add /dev, /proc and /sys to my initramfs and modify my init script to mount the procfs and sysfs. I’ll also run mdev -s which is provided by BusyBox to populate /dev and /sys. Lastly, I’ll launch my shell with setsid cttyhack sh so that job control is enabled.

With all that setup, the system is substantially more usable! I’ll push these changes to my repo and call it a wrap.

ad