Make Your Own Bootable Thumbdrive with Buildroot

From Sfvlug

At the meeting on Saturday, March 31, 2012, I got the opportunity to show off the fruits of my latest project to a few members. Kurt and Mark asked me some more specific details on how to reproduce this, so here it is for them and any others who may be interested in making their own bootable thumbdrives.

A little bit of background, first. This project will actually result in a build more similar to an embedded Linux project like OpenWrt or similar because it uses many of the same tools. So it is important to understand what we are doing here. We are going to build a cross-compiling tool chain then populate a file system with specially built software. This means two things. First, we don't have to build for our native processor type if we so choose, and second, binaries from our native operating system will not work because they do not link against the cross-compiled libraries. Also, this project will result in a system containing uClibc instead of the standard glibc against which your regular operating system binaries will be linked, and busybox which will replace coreutils, util-linux, and several other base packages.


Setting Up the Cross-Compiling Environment

Make sure you have what you will need installed first. Even though my final image is only about 50MB, unpacking all the source code and compiling has taken over 5GB, so make sure you have plenty of disk space. Additionally, you will need make and gcc to build your cross-compiler. It also helps to have patch and sed already installed. RPM based distros should have @development-tools installed, and DEB based distros should have build-essentials.

Browse on over to and click the link on the left for Download. Pick either a daily snapshot or the latest stable release, which is 2012.02 at this time. New stable releases are every three months, invariably on the last day of that month, so the next one will be on May 31.

Pick a directory where to unpack the downloaded tarball, preferably somewhere under your home directory. I used linux-uclibc, and unpacking results in a subdirectory like buildroot-2012.02.

During my build I chose to use a look-aside build directory so I could use the same sources for multiple builds if I want to. This means buildroot will build outside of the directory created by unpacking the tarball. The default behavior is to put all the resultant files into a subdirectory called output. Instead I told it to do something else for output, put it in ../i686-2012.02. Now I can also make another output directory called ../arm-2012.02 or x86_64-2012.02 if I want to. You might want to skip this for your first build but if you're ambitious enough to want to try multiple projects, go ahead and follow my lead. I started with a default config with the following:

make defconfig O=../i686-2012.02

Now I can work exclusively from my look-aside directory. I only customized a few items at first.

cd ../i686-2012.02
make menuconfig

If you have ever built the Linux kernel from source then you are probably familiar with the make menuconfig operation. Buildroot uses the same interface ported from the kernel. It also has xconfig and gconfig targets available if you prefer Qt or Gtk to the ncurses interface.

Some specifics to turn on for the first run. As you can see, defconfig has defaulted to i386 for the target architecture, and i686 for the target variant. You could now select a different processor type. I am building on a quad-core processor, so under Build Options I set the "number of jobs to run simultaneously" to 5. Under Target Optimizations, I turned on "enable large file support," "enable IPv6 support," "enable RPC support," "enable WCHAR support," and "enable C++ support." The latter will build a cross-compiling g++ as well as gcc. Under System Configuration, I set "/dev management" to "udev." Doing so now avoids having a bunch of /dev entries which are hard to get rid of in the final product. Also, I set the "port to run a getty on" to "tty1." Leaving it as a serial port doesn't work well for the thumbdrive. Finally, under Kernel, turn on "Linux Kernel" and set "defconfig name" to "i386." Also select "install kernel image to /boot in target." Pick "exit" a few times until it returns to the shell prompt.


Now just let it build. This could take a few hours, so go to sleep or work or lunch or whatever. We have left most of the options very basic, so there should be no problems building.

Customizing a Target Image

Believe it or not, we are now very close to having a bootable image available. Only a few options remain but first I want to discuss a few more customizations. Busybox, uClibc, and the Linux kernel are all customizable using the same menuconfig option.

make busybox-menuconfig
make uclibc-menuconfig
make linux-menuconfig

You may choose to change some options in any of these. Indeed, you probably want to add some drivers for some network cards to the kernel. The Intel e1000 is useful for running in qemu or VirtualBox. I didn't really need to alter uClibc beyond its defaults, but I turned on a few more applets in busybox and added bzip2 and xzip support. After customizing these things, run make again. I prefer to do incremental changes and run make after small sets of changes to make sure if I turn on any incompatible options, I can easily back them out.

Inside your output or look-aside directory, there is a subdirectory named target. This is just about everything that will be built into your final image. You can customize anything in here now, particularly files in target/etc. You probably want to fix up the /etc/fstab so that /dev/sdb1 gets mounted as root in read-write mode.

Now, to build a final image, enter into menuconfig again. Under Filesystem images, choose "tar the root filesystem." I left the "compression method" as "no compression." Also, you might like to play around with the "ext2 root filesystem." Under Bootloaders, I chose to use grub and I will explain how to use it below. At one point I tried to use syslinux but I could not successfully build it, so if you get it working, let me know. Choose "exit" a few more times and get back to the shell and run make again.

Building A Bootable Thumbdrive

If all has gone well, you now have a rootfs.tar under output/images. You also have rootfs.ext2 if you chose that option. You can loopback mount this and chroot to it if you like.

We're going to set buildroot aside for a moment and prepare the thumbdrive. Insert it into a USB socket. Check /proc/partitions and see what new partitions exist. Mine came up as /dev/sdb. Do the following as root.

fdisk /dev/sdb

Use 'd' to delete any existing partition from the drive and 'c' to create a new one. I accepted the defaults and created a partition of maximum size on the drive. Make sure it is type 0x83 - Linux. Use 'w' to write the new partition table to the drive and exit fdisk. Make a new filesystem on the drive, mount it, and unpack the rootfs.tar onto it.

mkfs /dev/sdb1
mount /dev/sdb1 /mnt
cd /mnt
tar xvf $OUTPUT/images/rootfs.tar

Next we will make sure to install the grub which is now on the thumbdrive onto the boot sector of the thumbdrive.

grub-install --root-directory=/mnt /dev/sdb

You will probably need an initrd in order to boot properly. My distro uses dracut to produce this and I found it worked perfectly.

dracut -k /mnt/lib/modules /mnt/boot/initrd.img 3.2.6

Finally, you need to tell grub how to boot the kernel. Make the following in /mnt/boot/grub/menu.lst:


title		Linux
	root	(hd0,0)
	kernel	/boot/bzImage ro root=/dev/sdb1
	initrd	/boot/initrd.img

Going Further

Now that you have your first bootable thumbdrive and have booted it a few times to make sure it works, you have probably noticed it is quite boring at this point. It has vi and an /etc/passwd file and not much else. You could probably use it as rescue media for another computer but that's it.

If you made a module for your network card, modprobe it and run udhcpc eth0. If you built busybox with ping you can test your network and if you added nc you can probe some ports.

Get back into menuconfig and add some more packages under Package Selection For The Target. I made my initial thumbdrive into a general purpose firewall. I added ebtables, iptables, ipset, dropbear, and dnsmasq. These are enough to give me at least the functionality of OpenWrt or others. I also added net-snmp and nfs-utils. Just run make after selecting the packages and unpack the tarball as above. You will need to run dracut again if you added to your kernel but it is not necessary to mess with grub any more.

Adding A Custom Package

Here's the most advanced thing I have done with this. I added a new package to buildroot. In order to add more advanced features to my firewall, I added xtables-addons. Check out for more information about what this does. In short, it's some extensions for iptables which add portknocking, tarpitting, and portscan detection to name a few.

Adding a package consists of just a few steps. First, I added the xtables-addons subdirectory under my buildroot directory's package subdirectory. In this case it was buildroot-2012.02/package/xtables-addons. You will see that all available packages have a subdirectory under package. Inside here, I added with the following contents:

        bool "xtables-addons"
          Extensions targets and matches for iptables

Before "bool" and "help" that's one tab. The other two indented lines are one tab and two spaces. I think this is important. Second, I added another file to the same location, with the following:

# xtables-addons

XTABLES_ADDONS_CONF_OPT = --with-kbuild=$(LINUX_DIR) --with-xtlibdir=/lib/xtables/
XTABLES_ADDONS_DEPENDENCIES = host-pkg-config iptables linux

$(eval $(call AUTOTARGETS))

And finally, I added a line to package/

source "package/xl2tp/"
source "package/xtables-addons/"

This final addition makes it show up in menuconfig. But don't select it in menuconfig because I still have a problem with it. Instead, from the shell just run this:

make xtables-addons

That will build the package and install it in output/target. When you next run make, it will be added to your images. But when it is built initially, it fails because it tries to alter some files on your host system, files which only root may change, which is why it is important to build as a normal user and not root. Once I figure out how to avoid this package doing this, I will update this document, and perhaps submit the package to the buildroot team as an addition.

I also might add mini_snmpd from as well because net-snmp is a lot bigger than is necessary for these needs.

Jeff 23:57, 6 April 2012 (UTC)

Your Ad Here
Personal tools