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/).
I have a x86 based ubuntu 12.04 desktop running inside VirtualBox, which is used to do this exercise.
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:
First, I download busybox and extract it.
Now let’s configure busybox. It has same method as linux kernel configuration menu. Nice!
The only thing I need to change is CONFIG_DESKTOP=n.
After “make install”, the files need to be packed are placed inside directory “_install”.
Now we need copy those files into “initramfs” staging area:
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:
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).
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.
Now we need create /init, which can be called by kernel at the last stage of boot process:
Now we have everything ready, and it can be packed into cpio file now:
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.
Now we can configure linux kernel by :
The only thing we need to change is to inclue the initramfs directory path, see below:
After exist from menuconfig, we can build kernel, and copy the final result bzImage:
Run it using qemu