Building linux system (kernel + root file system) from scratch

I always feel good if I can build something from scratch and works! I love Linux.
I will show how to build a Linux system with bleeding edge linux kernel and latest stable busybox release, and run linux in [qemu] (http://www.qemu.org/).

Host machine

I have a x86 based ubuntu 12.04 desktop running inside VirtualBox, which is used to do this exercise.

weng@weng-VirtualBox:/scratch/$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 12.04.3 LTS
Release:	12.04
Codename:	precise
weng@weng-VirtualBox:/scratch/$ uname -a
Linux weng-VirtualBox 3.2.51 #5 SMP Fri Jun 6 19:57:45 PDT 2014 i686 i686 i386 GNU/Linux
weng@weng-VirtualBox:/scratch/$ 

What will be built?

To run a linux system, as a minimum requirement, it needs:

  • bootloader: e.g. GRUB/LILO (for x86 CPU), u-boot (for ARM, PowerPC, MIPS). Since I will run linux systrem in qemu, I will skip bootloader.
  • kernel: it can be obtained from [kernel.org] (http://www.kernel.org). I will use the latest version 4.0., which is released very recently.
  • root file system: it is a first root file system image that is mounted at / while the Linux boots. The purpose of the initial root file system is to provide kernel modules and necessary utilities that might be needed in order to bring up the real root filesystem. This is a temporary file system for linux system like desktop, or server, during boot, linux system will detect disk, and switch to file system installed in disk. But for embedded system, it might just stay in this initial root file system.
    There are two schemes to implement the intial root file system: initrd and initramfs
  • initrd scheme: the image may be a file system image (optionally compressed),
    which is made available in a special block device (/dev/ram) that is then mounted as the initial root file system. The driver for that file system must be compiled statically into the kernel. Many distributions originally used compressed ext2 file system images. Others (including Debian 3.1) used cramfs in order to boot on memory-limited systems, since the cramfs image can be mounted in-place without requiring extra space for decompression. Once the initial root file system is up, the kernel executes /linuxrc as its first process. When it exits, the kernel assumes that the real root file system has been mounted and executes /sbin/init to begin the normal user-space boot process.
  • initramfs scheme: (available in Linux 2.6.13 onwards), the image may be a cpio archive (optionally compressed). The archive is unpacked by the kernel into a special instance of a tmpfs that becomes the initial root file system. This scheme has the advantage of not requiring an intermediate file system or block drivers to be compiled into the kernel. Some systems use the dracut package to create an initramfs image.
    In the initramfs scheme, the kernel executes /init as its first process, which is not expected to exit. For more information about initial ramdisk and initramfs, check [initrd wikipedia] (http://en.wikipedia.org/wiki/Initrd) and [initramfs wikipedia] (http://en.wikipedia.org/wiki/Initramfs).
    I will use initramfs scheme. [busybox] (http://www.busybox.net) has a minimum memory footprint, it is very popular in embedded linux.
    I will be using the latest stable release 1.23.1.

Let’s do it! As a first step, we want to create directories:

weng@weng-VirtualBox:/scratch$ mkdir linux-kernel-rootfs-from-scratch
weng@weng-VirtualBox:/scratch$ cd linux-kernel-rootfs-from-scratch
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch$ 
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch$ mkdir initramfs

Build BusyBox

First, I download busybox and extract it.

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch$ wget http://www.busybox.net/downloads/busybox-1.23.1.tar.bz2
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch$ tar xf busybox-1.23.1.tar.bz2
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch$ rm busybox-1.23.1.tar.bz2

Now let’s configure busybox. It has same method as linux kernel configuration menu. Nice!

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/busybox-1.23.1$ make menuconfig

The only thing I need to change is CONFIG_DESKTOP=n.

](/uploads/linux-kernel-rootfs-scratch/busybox-menuconfig.png)

Compile:

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/busybox-1.23.1$ make
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/busybox-1.23.1$ make install

After “make install”, the files need to be packed are placed inside directory “_install”.
Now we need copy those files into “initramfs” staging area:

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/busybox-1.23.1$ sudo cp -avR _install/* ../initramfs/

Now we need check what is dependancies of “busybox”, meaning what libraries are needed for it to be able to execute.
For this, we use ldd to find what are needed:

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/busybox-1.23.1$ ldd busybox
	linux-gate.so.1 =>  (0xb774c000)
	libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb7704000)
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb755a000)
	/lib/ld-linux.so.2 (0xb774d000)

From the output, there are four shared library files needed, but in fact there are only three, let me explain why:

  • linux-gate.so.1: this file actually we won’t find anywhere even we do a find on the whole system. It is exposed to each process by the running kernel as the mechanism for making system calls.
    There is an excellent [blog] (http://www.trilithium.com/johan/2005/08/linux-gate/) explaining linux-gate.so.
  • libm.so.6: this math library, we need handle.
  • libc.so.6: this is C library, we need handle.
  • ld-linux.so.2: this is the kernel handler for ELF binary to be launched to execute.
    The main purpose of this program is to map the binary into memory, load any referenced libraries in the program (e.g. the libm.so.6 previously mentioned), and then hand off control to the starting address of the binary being executed. This program is defined as part of the structure of the ELF file, in the INTERP section of the program header. For 32bit linux binaries, this is the typical name of the 32bit interpreter.
    For 64bit binaries, you’ll find it is typically called ld-linux-x86_64.so.2 (for 64bit x86 platforms).
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/busybox-1.23.1$ readelf -l busybox

Elf file type is EXEC (Executable file)
Entry point 0x804d94c
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0xba50c 0xba50c R E 0x1000
  LOAD           0x0baf08 0x08103f08 0x08103f08 0x00817 0x02b34 RW  0x1000
  DYNAMIC        0x0baf1c 0x08103f1c 0x08103f1c 0x000d0 0x000d0 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x00044 0x00044 R   0x4
  GNU_EH_FRAME   0x0ba280 0x08102280 0x08102280 0x00074 0x00074 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4
  GNU_RELRO      0x0baf08 0x08103f08 0x08103f08 0x000f8 0x000f8 R   0x1
..........

Note that in this exercise, I use host compiler to build busybox because the target is assumed to be x86 also.
The following what I’m doing is pretty hacky. It is not right way, but works in this case. The correct way would be cross compile those three shared libraries.

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/initramfs$ mkdir lib/i386-linux-gnu
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/initramfs$ cp -av /lib/i386-linux-gnu/lib[mc].so.6 lib/i386-linux-gnu
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/initramfs$ cp -av /lib/i386-linux-gnu/lib[mc]-2.15.so lib/i386-linux-gnu
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/initramfs$ cp -av /lib/ld-linux.so.2 lib/
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/initramfs$ cp -av /lib/i386-linux-gnu/ld-2.15.so lib/i386-linux-gnu

Now we need create /init, which can be called by kernel at the last stage of boot process:

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/initramfs$ cat init 
#!/bin/sh

/bin/mount -t proc none /proc
/bin/mount -t sysfs sysfs /sys

cat <<'EOF'

Welcome to Wenwei Weng's mini
  _ _                  
 | (_)_ __  _   ___  __
 | | | '_ \| | | \ \/ /
 | | | | | | |_| |>  < 
 |_|_|_| |_|\__,_/_/\_\
                       

EOF
echo 'Enjoy this little puppy!'
echo '~~~~~~~~~~~~~~~~~~~~~~~~'
echo ''

/bin/sh

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/initramfs$ ls -l init 
-rwxrwxr-x 1 weng weng 345 May  1 21:38 init

Now we have everything ready, and it can be packed into cpio file now:

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/initramfs$ find . -print0 | cpio --null -ov --format=newc > ../weng-initramfs.cpio

In fact, this is not really needed in this exercise, as we will package initramfs directly into kernel. See step below.

Build Linux kernel

First, I download linux kernel bleeding edge version 4.0 and extract it.

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch$ wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.0.tar.xz
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch$ tar -jxf linux-4.0.tar.xz
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch$ rm linux-4.0.tar.xz

Now we can configure linux kernel by :

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/linux-4.0$make menuconfig

The only thing we need to change is to inclue the initramfs directory path, see below:

](/uploads/linux-kernel-rootfs-scratch/kernel-menuconfig.png)

After exist from menuconfig, we can build kernel, and copy the final result bzImage:

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/linux-4.0$make -j8
weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch/linux-4.0$cp arch/x86/boot/bzImage ../bzImage.4.0 

Run it using qemu

weng@weng-VirtualBox:/scratch/linux-kernel-rootfs-from-scratch$ qemu-system-i386 -kernel bzImage.4.0

](/uploads/linux-kernel-rootfs-scratch/linux-running.png)