Before perusing Linux code, we should get some basic idea about how Linux is composed, compiled and linked. A straightforward way to achieve this goal is to understand Linux makefiles. Check Cross-Referencing Linux if you prefer online source browsing.
Here are some well-known targets in this top-level makefile:
xconfig, menuconfig, config, oldconfig:
generate kernel configuration file
linux/.config
;
depend, dep: generate dependency files, like
linux/.depend
,
linux/.hdepend
and
.depend
in subdirectories;
vmlinux: generate resident kernel image
linux/vmlinux
, the most important target;
modules, modules_install:
generate and install modules in
/lib/modules/$(KERNELRELEASE)
;
tags: generate tag file
linux/tags
, for source browsing with
vim.
Overview of linux/Makefile
is outlined below:
include .depend include .config include arch/i386/Makefile vmlinux: generate linux/vmlinux /* entry point "stext" defined in arch/i386/kernel/head.S */ $(LD) -T $(TOPDIR)/arch/i386/vmlinux.lds -e stext /* $(HEAD) */ + from arch/i386/Makefile arch/i386/kernel/head.o arch/i386/kernel/init_task.o init/main.o init/version.o init/do_mounts.o --start-group /* $(CORE_FILES) */ + from arch/i386/Makefile arch/i386/kernel/kernel.o arch/i386/mm/mm.o kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o /* $(DRIVERS) */ drivers/... char/char.o block/block.o misc/misc.o net/net.o media/media.o cdrom/driver.o and other static linked drivers + from arch/i386/Makefile arch/i386/math-emu/math.o (ifdef CONFIG_MATH_EMULATION) /* $(NETWORKS) */ net/network.o /* $(LIBS) */ + from arch/i386/Makefile arch/i386/lib/lib.a lib/lib.a --end-group -o vmlinux $(NM) vmlinux | grep ... | sort > System.map tags: generate linux/tags for vim modules: generate modules modules_install: install modules clean mrproper distclean: clean up build directory psdocs pdfdocs htmldocs mandocs: generate kernel documents include Rules.make rpm: generate an rpm
"--start-group" and "--end-group" are ld command line options to resolve symbol reference problem. Refer to Using LD, the GNU linker: Command Line Options for details.
Rules.make
contains rules which are shared
between multiple Makefiles.
After compilation, ld combines a number of
object and archive files, relocates their data and
ties up symbol references.
linux/arch/i386/vmlinux.lds
is designated by
linux/Makefile
as the linker script used
in linking the resident kernel image linux/vmlinux
.
/* ld script to make i386 Linux kernel * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>; */ OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") OUTPUT_ARCH(i386) /* "ENTRY" is overridden by command line option "-e stext" in linux/Makefile */ ENTRY(_start) /* Output file (linux/vmlinux) layout. * Refer to Using LD, the GNU linker: Specifying Output Sections */ SECTIONS { /* Output section .text starts at address 3G+1M. * Refer to Using LD, the GNU linker: The Location Counter */ . = 0xC0000000 + 0x100000; _text = .; /* Text and read-only data */ .text : { *(.text) *(.fixup) *(.gnu.warning) } = 0x9090 /* Unallocated holes filled with 0x9090, i.e. opcode for "NOP NOP". * Refer to Using LD, the GNU linker: Optional Section Attributes */ _etext = .; /* End of text section */ .rodata : { *(.rodata) *(.rodata.*) } .kstrtab : { *(.kstrtab) } /* Aligned to next 16-bytes boundary. * Refer to Using LD, the GNU linker: Arithmetic Functions */ . = ALIGN(16); /* Exception table */ __start___ex_table = .; __ex_table : { *(__ex_table) } __stop___ex_table = .; __start___ksymtab = .; /* Kernel symbol table */ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; .data : { /* Data */ *(.data) CONSTRUCTORS } /* For "CONSTRUCTORS", refer to * Using LD, the GNU linker: Option Commands */ _edata = .; /* End of data section */ . = ALIGN(8192); /* init_task */ .data.init_task : { *(.data.init_task) } . = ALIGN(4096); /* Init code and data */ __init_begin = .; .text.init : { *(.text.init) } .data.init : { *(.data.init) } . = ALIGN(16); __setup_start = .; .setup.init : { *(.setup.init) } __setup_end = .; __initcall_start = .; .initcall.init : { *(.initcall.init) } __initcall_end = .; . = ALIGN(4096); __init_end = .; . = ALIGN(4096); .data.page_aligned : { *(.data.idt) } . = ALIGN(32); .data.cacheline_aligned : { *(.data.cacheline_aligned) } __bss_start = .; /* BSS */ .bss : { *(.bss) } _end = . ; /* Output section /DISCARD/ will not be included in the final link output. * Refer to Using LD, the GNU linker: Section Definitions */ /* Sections to be discarded */ /DISCARD/ : { *(.text.exit) *(.data.exit) *(.exitcall.exit) } /* The following output sections are addressed at memory location 0. * Refer to Using LD, the GNU linker: Optional Section Attributes */ /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } }
linux/arch/i386/Makefile
is included by
linux/Makefile
to provide i386 specific
items and terms.
All the following targets depend on target vmlinux
of linux/Makefile
.
They are accomplished by making corresponding targets in
linux/arch/i386/boot/Makefile
with some options.
Table 1. Targets in linux/arch/i386/Makefile
Target | Command |
---|---|
zImage [a] | @$(MAKE) -C arch/i386/boot zImage [b] |
bzImage | @$(MAKE) -C arch/i386/boot bzImage |
zlilo | @$(MAKE) -C arch/i386/boot BOOTIMAGE=zImage zlilo |
bzlilo | @$(MAKE) -C arch/i386/boot BOOTIMAGE=bzImage zlilo |
zdisk | @$(MAKE) -C arch/i386/boot BOOTIMAGE=zImage zdisk |
bzdisk | @$(MAKE) -C arch/i386/boot BOOTIMAGE=bzImage zdisk |
install | @$(MAKE) -C arch/i386/boot BOOTIMAGE=bzImage install |
[a] zImage alias: compressed; [b] "-C" is a MAKE command line option to change directory before reading makefiles; Refer to GNU make: Summary of Options and GNU make: Recursive Use of make. |
It is worth noticing that this makefile redefines
some environment variables which are exported by
linux/Makefile
, specifically:
OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
The effect will be passed to subdirectory makefiles and will change the tool's behavior. Refer to GNU Binary Utilities: objcopy for objcopy command line option details.
Not sure why $(LIBS) includes "$(TOPDIR)/arch/i386/lib/lib.a" twice:
LIBS := $(TOPDIR)/arch/i386/lib/lib.a $(LIBS) $(TOPDIR)/arch/i386/lib/lib.a
It may be employed to work around linking problems with some toolchains.
linux/arch/i386/boot/Makefile
is somehow
independent as it is not included by either
linux/arch/i386/Makefile
or linux/Makefile
.
However, they do have some relationship:
linux/Makefile
: provides resident kernel image
linux/vmlinux
;
linux/arch/i386/boot/Makefile
:
provides bootstrap;
linux/arch/i386/Makefile
:
makes sure linux/vmlinux
is ready
before the bootstrap is constructed,
and exports targets (like bzImage)
to linux/Makefile
.
$(BOOTIMAGE) value, which is for target zdisk, zlilo
or zdisk, comes from
linux/arch/i386/Makefile
.
Table 2. Targets in linux/arch/i386/boot/Makefile
Target | Command |
---|---|
zImage |
$(OBJCOPY) compressed/vmlinux compressed/vmlinux.out tools/build bootsect setup compressed/vmlinux.out $(ROOT_DEV) > zImage |
bzImage |
$(OBJCOPY) compressed/bvmlinux compressed/bvmlinux.out tools/build -b bbootsect bsetup compressed/bvmlinux.out $(ROOT_DEV) \ > bzImage |
zdisk |
dd bs=8192 if=$(BOOTIMAGE) of=/dev/fd0 |
zlilo |
if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi cat $(BOOTIMAGE) > $(INSTALL_PATH)/vmlinuz cp $(TOPDIR)/System.map $(INSTALL_PATH)/ if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi |
install |
sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map "$(INSTALL_PATH)" |
tools/build builds boot image
zImage from
{bootsect, setup, compressed/vmlinux.out}, or
bzImage from
{bbootsect, bsetup, compressed/bvmlinux,out}.
linux/Makefile
"export ROOT_DEV = CURRENT".
Note that $(OBJCOPY) has been redefined by
linux/arch/i386/Makefile
in Section 2.3, “linux/arch/i386/Makefile”.
Table 3. Supporting targets in linux/arch/i386/boot/Makefile
Target: Prerequisites | Command |
---|---|
compressed/vmlinux: linux/vmlinux | @$(MAKE) -C compressed vmlinux |
compressed/bvmlinux: linux/vmlinux | @$(MAKE) -C compressed bvmlinux |
tools/build: tools/build.c | $(HOSTCC) $(HOSTCFLAGS) -o $@ $< -I$(TOPDIR)/include [a] |
bootsect: bootsect.o | $(LD) -Ttext 0x0 -s --oformat binary bootsect.o [b] |
bootsect.o: bootsect.s | $(AS) -o $@ $< |
bootsect.s: bootsect.S ... | $(CPP) $(CPPFLAGS) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ |
bbootsect: bbootsect.o | $(LD) -Ttext 0x0 -s --oformat binary $< -o $@ |
bbootsect.o: bbootsect.s | $(AS) -o $@ $< |
bbootsect.s: bootsect.S ... | $(CPP) $(CPPFLAGS) -D__BIG_KERNEL__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ |
setup: setup.o | $(LD) -Ttext 0x0 -s --oformat binary -e begtext -o $@ $< |
setup.o: setup.s | $(AS) -o $@ $< |
setup.s: setup.S video.S ... | $(CPP) $(CPPFLAGS) -D__ASSEMBLY__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ |
bsetup: bsetup.o | $(LD) -Ttext 0x0 -s --oformat binary -e begtext -o $@ $< |
bsetup.o: bsetup.s | $(AS) -o $@ $< |
bsetup.s: setup.S video.S ... | $(CPP) $(CPPFLAGS) -D__BIG_KERNEL__ -D__ASSEMBLY__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ |
[a] "$@" means target, "$<" means first prerequisite; Refer to GNU make: Automatic Variables; [b] "--oformat binary" asks for raw binary output, which is identical to the memory dump of the executable; Refer to Using LD, the GNU linker: Command Line Options. |
Note that it has "-D__BIG_KERNEL__" when compile
bootsect.S
to bbootsect.s
, and
setup.S
to bsetup.s
.
They must be Place Independent Code (PIC), thus what "-Ttext" option is
doesn't matter.
This makefile handles image (de)compression mechanism.
It is good to separate (de)compression from bootstrap. This divide-and-conquer solution allows us to easily improve (de)compression mechanism or to adopt a new bootstrap method.
Directory
linux/arch/i386/boot/compressed/
contains two source files:
head.S
and misc.c
.
Table 4. Targets in linux/arch/i386/boot/compressed/Makefile
Target | Command |
---|---|
vmlinux[a] | $(LD) -Ttext 0x1000 -e startup_32 -o vmlinux head.o misc.o piggy.o |
bvmlinux | $(LD) -Ttext 0x100000 -e startup_32 -o bvmlinux head.o misc.o piggy.o |
head.o | $(CC) $(AFLAGS) -traditional -c head.S |
misc.o |
$(CC) $(CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c misc.c[b] |
piggy.o | tmppiggy=_tmp_$$$$piggy; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \ $(OBJCOPY) $(SYSTEM) $$tmppiggy; \ gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ echo "SECTIONS { .data : { input_len = .; \ LONG(input_data_end - input_data) input_data = .; \ *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ $(LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 \ -T $$tmppiggy.lnk; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk |
[a]
Target vmlinux here is different from
that defined in [b] "subst" is a MAKE function; Refer to GNU make: Functions for String Substitution and Analysis. |
piggy.o
contains
variable input_len
and gzipped linux/vmlinux
.
input_len is at the beginning of
piggy.o
, and it is equal to the size of
piggy.o
excluding
input_len itself. Refer to
Using LD, the GNU linker: Section Data Expressions
for "LONG(expression)" in piggy.o linker script.
To be exact, it is not linux/vmlinux
itself
(in ELF format) that is gzipped but its binary image,
which is generated by objcopy command.
Note that $(OBJCOPY) has been redefined by
linux/arch/i386/Makefile
in
Section 2.3, “linux/arch/i386/Makefile” to output raw binary
using "-O binary" option.
When linking {bootsect, setup} or {bbootsect, bsetup}, $(LD) specifies "--oformat binary" option to output them in binary format. When making zImage (or bzImage), $(OBJCOPY) generates an intermediate binary output from compressed/vmlinux (or compressed/bvmlinux) too. It is vital that all components in zImage or bzImage are in raw binary format, so that the image can run by itself without asking a loader to load and relocate it.
Both vmlinux and bvmlinux
prepend head.o
and misc.o
before piggy.o
,
but they are linked against different start addresses (0x1000 vs 0x100000).
linux/arch/i386/tools/build.c
is a host utility to
generate zImage or bzImage.
In linux/arch/i386/boot/Makefile
:
tools/build bootsect setup compressed/vmlinux.out $(ROOT_DEV) > zImage tools/build -b bbootsect bsetup compressed/bvmlinux.out $(ROOT_DEV) > bzImage
"-b" means is_big_kernel, used to check whether system image is too big.
tools/build outputs the following components to stdout, which is redirected to zImage or bzImage:
bootsect or bbootsect: from
linux/arch/i386/boot/bootsect.S
, 512 bytes;
setup or bsetup: from
linux/arch/i386/boot/setup.S
,
4 sectors or more, sector aligned;
compressed/vmlinux.out or compressed/bvmlinux.out, including:
head.o: from
linux/arch/i386/boot/compressed/head.S
;
misc.o: from
linux/arch/i386/boot/compressed/misc.c
;
piggy.o: from input_len
and gzipped linux/vmlinux
.
tools/build will change some contents of bootsect or bbootsect when outputting to stdout:
Table 5. Modification made by tools/build
Offset | Byte | Variable | Comment |
---|---|---|---|
1F1 (497) | 1 | setup_sectors | number of setup sectors, >=4 |
1F4 (500) | 2 | sys_size | system size in 16-bytes, little-endian |
1FC (508) | 1 | minor_root | root dev minor |
1FD (509) | 1 | major_root | root dev major |
In the following chapters, compressed/vmlinux will be referred as vmlinux and compressed/bvmlinux as bvmlinux, if not confusing.
Linux Kernel Makefiles:
linux/Documentation/kbuild/makefiles.txt