Customize Linux from the Bottom—Building Your Own Linux Base System
A curious reader might ask why we do this and what it is good for. Like many others, we want to run some complex software on a box, something that can be generalized as a multitasking application on a typical PC-like machine without hard disk or monitor. We need an OS kernel and some elements as the base experimental platform. This platform should be robust, maintainable and customizable. Writing a good OS kernel for this purpose is too scaring for many. Thanks to Linux and the Open Source community, we now have an excellent option.
Basic materials are ready and available for free. Now it is time to pick up pieces we need, assemble our own engine and control it. Then, it is time to enjoy.
Before we start, we need to know the answer to some key questions: How to compile the kernel? How to compile a shared library? How to create an initial root file system? How to put the kernel image and compressed file system onto a floppy or EPROM? How to run an application using shared libraries? How to debug? There are many questions like these. The answers are already documented, not, as far as we know, in a single place, but scattered over a wide range of documents. We don't want to write a comprehensive document for these questions but, rather, tell our story and major part of our answers.
Once the plan has been made for customization, detailed steps can be put into action. General steps in our work are described in the following.
1. Setup the development environment Install a full Linux distribution such as Red Hat 6.0 as the development platform. Make sure it's gcc-supported. To make things easy, assume the target machine on which we run our customized system and the host machine, that is, the development machine, are using the same type of CPUs, in our case Intel x86. Otherwise, we have to prepare a cross-compiler. 2. Customize the kernel Get the latest stable kernel source (version 2.2.13 at this article's writing). How to configure and compile the kernel are well documented in the source files. We don't want to repeat the details here. But we need to choose support for initial ramdisk, loadable modules and other necessary options. If we use the serial console port, choose serial console support. If we have a piece of hardware we want to set up, like an Ethernet card, we can select them as modules. Then we can install and use these modules in our base system. 3. Prepare the standard libraries Get the latest stable standard library glibc (versions 2.2.1). This includes almost everything we want: the dynamic loader, the standard C library and math lib, etc. Although we don't need all that glibc provides, it's better to build them all together, then choose what we want. The documents in the glibc source tree tell in detail how to compile it. Because we use it for our target machine, we have to compile it using the kernel header files consistent with Step 2. This can be specified using --with-headers during configuration. Also, we have to install all library header files so that the compilation of other components for the target machine can use them. 4. Let the compiler know how to cross-compile After installing kernel and glibc header files, we need to prepare the compiler, gcc, to use them. Glibc2-HOWTO describes this in more detail. Briefly, we need to tell gcc where to find specifications using the option -b. In our case, because the target and the host machine are basically the same, we need to use only the host machine specs. This can be found by using the command gcc -v. For example, on my box, the reply is:
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/specs gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)Compile other components of the system by adding the option -b as:
gcc -b i386-redhat-linux5. Select and prepare utilities and other libraries To compile a utility program such as mount or libraries other than those in glibc, such as termcap, consistently, we have to tell the compiler where to find all the header (include) files and the libraries. First, we tell the compiler not to search header files in default paths by specifying --nostdinc. Second, tell the compiler we are compiling sources for the target machine by using option -b $MACHINE, as described in Step 2. Third, state exactly where to find the kernel header files and the standard library header files by specifying option -I. Fourth, tell the loader what libraries to use and where to find them by using -L and -l options. 6. Build applications Like compiling for utilities and libraries, we need to cross-compile our applications in order to use them on the base system. There are no particular considerations needed from the system view, except to make sure everything on which the application is dependent is installed already. 7. Package things together Once all the components are ready, we need to arrange them in such a way that the system be booted, and any everything can be correctly located. This issue is discussed in more detail in the following section. 8. Move on Use the base system as a start point for whatever you want to do. More features can be added, as needed, over the base system. A separate section is devoted to this issue.