For a school assigment I had to do, I chosed the creation of a minimal linux live cd that boots up to some sort of a shell. I also got bonus points if I can get it under 10MB.
As a longtime Gentoo user I created a chroot and first tried trimming the standard base system down. But as I soon found out, that won't work, at least not to the extend I wished to. And then I found the Tiny Gentoo wiki page describing an alternative option.
Unfortunately the TinyGentoo wiki page is a bit of a mess and sometimes outdated. (I'm still thinking if I should clean it up or create a separate wiki page based on my work.) Because of that I encountered quite some bugs/problems and solved/hacked together a solution for most of them.
So basicly, what do you _really_ need for a Linux system to boot up. Ofcourse, you need the Linux kernel and a bootloader (Grub2 was my choice). For the next stage, you need a C library and a Shell with some standard commands/utilities. I chose those two from a standard embedded setup, namely uClibc and BusyBox. Next thing you need is some kind of a init system. BusyBox has it built in, but I chosed to go with OpenRC/baselayout. Now that we know all the "components" we will need, let's try to compile and glue everything together.
As previously stated, this guide is heavly based on TinyGentoo and work of judepereira. It is also recommended that you have previous experiance with Gentoo and Gentoo installed on your workstation, but it is not a requirement.
In the process of writing this, it came to my attention that someone has recently done a similar tutorial: http://www.anticore.org/ratgentoo/
First, you need to create a working directory, you don't need too much space - a few GB should be enough. Let's call this directory $MINI_ROOT.
Download the latest x86 uClibc stages from judepereira's blog and extract it to $MINI_ROOT.
Now we need to do some standard chores before we can chroot:
- "cp -L /etc/resolv.conf $MINI_ROOT/etc/resolv.conf" - for internet connectivity
- "mkdir -p $MINI_ROOT/usr/portage"
- Now if you have Gentoo installed on your workstation:
- "mount --bind /usr/portage $MINI_ROOT/usr/portage" - to avoid downloading the distfiles
- From the closest Gentoo mirror download the lasted portage snapshot
- Extract it in $MINI_ROOT/usr/portage
- "mount --bind /dev $MINI_ROOT/dev"
- "mount -t proc proc $MINI_ROOT/proc"
Now before we're ready to chroot, there are some files you'll need later in that chroot, and I advise you copy them there beforehand. Download the package that contains the portage files, patched uclibc ebuild, inittab and init script. Extract the archive to $MINI_ROOT and this will copy the appropriate files.
Now it's time to chroot:
- "chroot $MINI_ROOT /bin/bash"
- "source /etc/profile"
And voila, you're in your uClibc chroot. First we need to update the system:
- "ln -snf /usr/portage/profiles/uclibc/x86 /etc/make.profile" - symlink the appropriate profile
- "emerge -uDNav world" - we carefully update world. Please READ all the messages portage gives you and act accordingly.
So, our chroot is up to date and almost ready to rumble. First we need to install some additional packages we will need - "emerge -av cdrtools grub cpio".
Now we are ready to preapare our root FS. Create a directory inside your chroot where the root FS will reside. Let's call this directory $TARGET_ROOT. You also need to create "$TARGET_ROOT/proc" and "$TARGET_ROOT/sys".
Now it's time to install the packages.
"ROOT=$TARGET_ROOT USE=make-symlinks emerge -avkN baselayout uclibc busybox openrc mingetty" - this can take some time. Make sure the patched uclibc gets pulled in [insert patch notes here].
When portage finishes, you have to do some minor corrections:
- "cp /usr/lib/gcc/i386-gentoo-linux-uclibc/4.5.3/libgcc_s.so.1 $TARGET_ROOT/usr/lib/" - because even with static cimpiling, openrc doesen't compile fully staticly.
- "cp /misc/inittab $TARGET_ROOT/etc/" - copy the modified inittab
- "cp /misc/init $TARGET_ROOT/" - copy the init script
- edit "/etc/fstab", by default nothing in there is needed
This now allows us to to chroot into our target with "chroot $TARGET_ROOT /bin/ash". After that we have to do some elementary maintainance that can be skipped if needed:
- "adduser -h / -s /bin/ash -G users USERNAME"
- "rc-update del netmount default"
- "rc-update del net.lo boot"
- "rc-update del sysctl boot"
We exit the chroot and start preparing for iso making:
- "mkdir -p /tmp/iso/grub/" - create the directory structure
- "cp /boot/grub/stage2_eltorito /tmp/iso/grub/" - copy grub stage 2
- Create menu.lst in the grub directory, there is a sample in /misc/
- "cp -r $TARGET_ROOT /tmp/$TARGET_ROOT" - copy the target root for additional cleaning
- "rm -r /tmp/$TARGET_ROOT/var/*/* /tmp/$TARGET_ROOT/tmp/*" - more aggresive cleaning
- "cd /tmp/$TARGET_ROOT && find . | cpio -H newc -o | gzip -9 > /tmp/iso/initramfs-tinygentoo.igz && cd .." - Create initramfs
Now we need to compile the linux kernel, nothing exotic:
- "emerge -av gentoo-sources"
- "cd /usr/src/linux"
- "make menuconfig" - the only thing you really need is initramfs support
- "cp arch/i368/boot/bzImage /tmp/iso/boot/vmlinuz"
- "make INSTALL_MOD_PATH=$TARGET_ROOT modules_install" - if you have any modules, you need to recreate initramfs after this.
Now finally, we can create the iso image: "cd /tmp/ && mkisofs -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 4 -boot-info-table -o cd.iso iso"
Some general tips:
- "dispatch-conf $TARGET_ROOT" - update config files in $TARGET_ROOT
- Use saved-config for tweaking (busybox/uclibc)
Notes about the custom ebuilds
There is a custom uClibc ebuild for version 0.9.30.1 that applies a simple patch that makes the uClibc compile. I would suggest that you first try to compile the latest stable and unstable uClibc ebuilds and resort to this ebuild (uclibc 0.9.30.1-r2).
Also, there are some sample configs for busybox and uclibc versions provided, because sometimes busybox doesen't compile and the easiest solution is to remove some parts of it.
Note: this guide relies on the saved-config of busybox to compile with mdev support, the same could be achived with mdev use flag.