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 to facilitate a lightweight implementation of common programs like
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.
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
/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
/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.