讓我們開始吧!
(題圖來自:adafruit.com)
<a target="_blank"></a>
在開始編譯前要進行很多準備工作。最主要的就是找到并配置好配置檔案,<code>make</code> 指令要使用到的參數都需要從這些配置檔案擷取。現在就讓我們深入核心的根 <code>makefile</code> 吧
<code>version = 4</code>
<code>patchlevel = 2</code>
<code>sublevel = 0</code>
<code>extraversion = -rc3</code>
<code>name = hurr durr i'ma sheep</code>
這些變量決定了目前核心的版本,并且被使用在很多不同的地方,比如同一個 <code>makefile</code> 中的 <code>kernelversion</code>:
<code>kernelversion = $(version)$(if $(patchlevel),.$(patchlevel)$(if $(sublevel),.$(sublevel)))$(extraversion)</code>
接下來我們會看到很多<code>ifeq</code> 條件判斷語句,它們負責檢查傳遞給 <code>make</code> 的參數。核心的 <code>makefile</code> 提供了一個特殊的編譯選項 <code>make help</code> ,這個選項可以生成所有的可用目标和一些能傳給 <code>make</code> 的有效的指令行參數。舉個例子,<code>make v=1</code> 會在建構過程中輸出詳細的編譯資訊,第一個 <code>ifeq</code> 就是檢查傳遞給 make 的<code>v=n</code> 選項。
<code>ifeq ("$(origin v)", "command line")</code>
<code>kbuild_verbose = $(v)</code>
<code>endif</code>
<code>ifndef kbuild_verbose</code>
<code>kbuild_verbose = 0</code>
<code></code>
<code>ifeq ($(kbuild_verbose),1)</code>
<code>quiet =</code>
<code>q =</code>
<code>else</code>
<code>quiet=quiet_</code>
<code>q = @</code>
<code>export quiet q kbuild_verbose</code>
如果 <code>v=n</code> 這個選項傳給了 <code>make</code> ,系統就會給變量 <code>kbuild_verbose</code> 選項附上 <code>v</code> 的值,否則的話<code>kbuild_verbose</code> 就會為 <code>0</code>。然後系統會檢查 <code>kbuild_verbose</code> 的值,以此來決定 <code>quiet</code> 和<code>q</code> 的值。符号 <code>@</code> 控制指令的輸出,如果它被放在一個指令之前,這條指令的輸出将會是 <code>cc scripts/mod/empty.o</code>,而不是<code>compiling .... scripts/mod/empty.o</code>(lctt 譯注:cc 在 makefile 中一般都是編譯指令)。在這段最後,系統導出了所有的變量。
下一個 <code>ifeq</code> 語句檢查的是傳遞給 <code>make</code> 的選項 <code>o=/dir</code>,這個選項允許在指定的目錄 <code>dir</code> 輸出所有的結果檔案:
<code>ifeq ($(kbuild_src),)</code>
<code>ifeq ("$(origin o)", "command line")</code>
<code>kbuild_output := $(o)</code>
<code>ifneq ($(kbuild_output),)</code>
<code>saved-output := $(kbuild_output)</code>
<code>kbuild_output := $(shell mkdir -p $(kbuild_output) && cd $(kbuild_output) \</code>
<code>&& /bin/pwd)</code>
<code>$(if $(kbuild_output),, \</code>
<code>$(error failed to create output directory "$(saved-output)"))</code>
<code>sub-make: force</code>
<code>$(q)$(make) -c $(kbuild_output) kbuild_src=$(curdir) \</code>
<code>-f $(curdir)/makefile $(filter-out _all sub-make,$(makecmdgoals))</code>
<code>skip-makefile := 1</code>
<code>endif # ifneq ($(kbuild_output),)</code>
<code>endif # ifeq ($(kbuild_src),)</code>
系統會檢查變量 <code>kbuild_src</code>,它代表核心代碼的頂層目錄,如果它是空的(第一次執行 makefile 時總是空的),我們會設定變量 <code>kbuild_output</code> 為傳遞給選項 <code>o</code> 的值(如果這個選項被傳進來了)。下一步會檢查變量 <code>kbuild_output</code> ,如果已經設定好,那麼接下來會做以下幾件事:
将變量 <code>kbuild_output</code> 的值儲存到臨時變量 <code>saved-output</code>;
嘗試建立給定的輸出目錄;
檢查建立的輸出目錄,如果失敗了就列印錯誤;
如果成功建立了輸出目錄,那麼就在新目錄重新執行 <code>make</code> 指令(參見選項<code>-c</code>)。
下一個 <code>ifeq</code> 語句會檢查傳遞給 make 的選項 <code>c</code> 和 <code>m</code>:
<code>ifeq ("$(origin c)", "command line")</code>
<code>kbuild_checksrc = $(c)</code>
<code>ifndef kbuild_checksrc</code>
<code>kbuild_checksrc = 0</code>
<code>ifeq ("$(origin m)", "command line")</code>
<code>kbuild_extmod := $(m)</code>
系統還會檢查變量 <code>kbuild_src</code>,如果 <code>kbuild_src</code> 沒有被設定,系統會設定變量 <code>srctree</code> 為<code>.</code>:
<code>srctree := .</code>
<code>objtree := .</code>
<code>src := $(srctree)</code>
<code>obj := $(objtree)</code>
<code>export srctree objtree vpath</code>
這将會告訴 <code>makefile</code> 核心的源碼樹就在執行 <code>make</code> 指令的目錄,然後要設定 <code>objtree</code> 和其他變量為這個目錄,并且将這些變量導出。接着就是要擷取 <code>subarch</code> 的值,這個變量代表了目前的系統架構(lctt 譯注:一般都指cpu 架構):
<code>subarch := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \</code>
<code>-e s/sun4u/sparc64/ \</code>
<code>-e s/arm.*/arm/ -e s/sa110/arm/ \</code>
<code>-e s/s390x/s390/ -e s/parisc64/parisc/ \</code>
<code>-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \</code>
<code>-e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ )</code>
<code>ifeq ($(arch),i386)</code>
<code>srcarch := x86</code>
<code>ifeq ($(arch),x86_64)</code>
<code>hdr-arch := $(srcarch)</code>
注意:<code>arch</code> 是 <code>subarch</code> 的别名。如果沒有設定過代表核心配置檔案路徑的變量 <code>kconfig_config</code>,下一步系統會設定它,預設情況下就是 <code>.config</code> :
<code>kconfig_config ?= .config</code>
<code>export kconfig_config</code>
<code>config_shell := $(shell if [ -x "$$bash" ]; then echo $$bash; \</code>
<code>else if [ -x /bin/bash ]; then echo /bin/bash; \</code>
<code>else echo sh; fi ; fi)</code>
接下來就要設定一組和編譯核心的編譯器相關的變量。我們會設定主機的 <code>c</code> 和 <code>c++</code> 的編譯器及相關配置項:
<code>hostcc = gcc</code>
<code>hostcxx = g++</code>
<code>hostcflags = -wall -wmissing-prototypes -wstrict-prototypes -o2 -fomit-frame-pointer -std=gnu89</code>
<code>hostcxxflags = -o2</code>
接下來會去适配代表編譯器的變量 <code>cc</code>,那為什麼還要 <code>host*</code> 這些變量呢?這是因為 <code>cc</code> 是編譯核心過程中要使用的目标架構的編譯器,但是 <code>hostcc</code> 是要被用來編譯一組 <code>host</code> 程式的(下面我們就會看到)。
然後我們就看到變量 <code>kbuild_modules</code> 和 <code>kbuild_builtin</code> 的定義,這兩個變量決定了我們要編譯什麼東西(核心、子產品或者兩者):
<code>kbuild_modules :=</code>
<code>kbuild_builtin := 1</code>
<code>ifeq ($(makecmdgoals),modules)</code>
<code>kbuild_builtin := $(if $(config_modversions),1)</code>
在這我們可以看到這些變量的定義,并且,如果們僅僅傳遞了 <code>modules</code> 給 <code>make</code>,變量 <code>kbuild_builtin</code> 會依賴于核心配置選項 <code>config_modversions</code>。
下一步操作是引入下面的檔案:
<code>include scripts/kbuild.include</code>
<code>as = $(cross_compile)as</code>
<code>ld = $(cross_compile)ld</code>
<code>cc = $(cross_compile)gcc</code>
<code>cpp = $(cc) -e</code>
<code>ar = $(cross_compile)ar</code>
<code>nm = $(cross_compile)nm</code>
<code>strip = $(cross_compile)strip</code>
<code>objcopy = $(cross_compile)objcopy</code>
<code>objdump = $(cross_compile)objdump</code>
<code>awk = awk</code>
<code>...</code>
在這些定義好的變量後面,我們又定義了兩個變量:<code>userinclude</code> 和 <code>linuxinclude</code>。他們包含了頭檔案的路徑(第一個是給使用者用的,第二個是給核心用的):
<code>userinclude := \</code>
<code>-i$(srctree)/arch/$(hdr-arch)/include/uapi \</code>
<code>-iarch/$(hdr-arch)/include/generated/uapi \</code>
<code>-i$(srctree)/include/uapi \</code>
<code>-iinclude/generated/uapi \</code>
<code>-include $(srctree)/include/linux/kconfig.h</code>
<code>linuxinclude := \</code>
<code>-i$(srctree)/arch/$(hdr-arch)/include \</code>
以及給 c 編譯器的标準标志:
<code>kbuild_cflags := -wall -wundef -wstrict-prototypes -wno-trigraphs \</code>
<code>-fno-strict-aliasing -fno-common \</code>
<code>-werror-implicit-function-declaration \</code>
<code>-wno-format-security \</code>
<code>-std=gnu89</code>
這并不是最終确定的編譯器标志,它們還可以在其他 makefile 裡面更新(比如 <code>arch/</code> 裡面的 kbuild)。變量定義完之後,全部會被導出供其他 makefile 使用。
下面的兩個變量 <code>rcs_find_ignore</code> 和 <code>rcs_tar_ignore</code> 包含了被版本控制系統忽略的檔案:
<code>export rcs_find_ignore := \( -name sccs -o -name bitkeeper -o -name .svn -o \</code>
<code>-name cvs -o -name .pc -o -name .hg -o -name .git \) \</code>
<code>-prune -o</code>
<code>export rcs_tar_ignore := --exclude sccs --exclude bitkeeper --exclude .svn \</code>
<code>--exclude cvs --exclude .pc --exclude .hg --exclude .git</code>
這就是全部了,我們已經完成了所有的準備工作,下一個點就是如果建構<code>vmlinux</code>。
<code>all: vmlinux</code>
<code>include arch/$(srcarch)/makefile</code>
不要操心我們略過的從 <code>export rcs_find_ignore.....</code> 到 <code>all: vmlinux.....</code> 這一部分 makefile 代碼,他們隻是負責根據各種配置檔案(<code>make *.config</code>)生成不同目标核心的,因為之前我就說了這一部分我們隻讨論建構核心的通用途徑。
<code>vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) force</code>
第二個目标是 <code>vmlinux-deps</code>,它的定義如下:
<code>vmlinux-deps := $(kbuild_lds) $(kbuild_vmlinux_init) $(kbuild_vmlinux_main)</code>
它是由核心代碼下的每個頂級目錄的 <code>built-in.o</code> 組成的。之後我們還會檢查核心所有的目錄,<code>kbuild</code> 會編譯各個目錄下所有的對應 <code>$(obj-y)</code> 的源檔案。接着調用 <code>$(ld) -r</code> 把這些檔案合并到一個 <code>build-in.o</code> 檔案裡。此時我們還沒有<code>vmlinux-deps</code>,是以目标 <code>vmlinux</code> 現在還不會被建構。對我而言 <code>vmlinux-deps</code> 包含下面的檔案:
<code>arch/x86/kernel/vmlinux.lds arch/x86/kernel/head_64.o</code>
<code>arch/x86/kernel/head64.o arch/x86/kernel/head.o</code>
<code>init/built-in.o usr/built-in.o</code>
<code>arch/x86/built-in.o kernel/built-in.o</code>
<code>mm/built-in.o fs/built-in.o</code>
<code>ipc/built-in.o security/built-in.o</code>
<code>crypto/built-in.o block/built-in.o</code>
<code>lib/lib.a arch/x86/lib/lib.a</code>
<code>lib/built-in.o arch/x86/lib/built-in.o</code>
<code>drivers/built-in.o sound/built-in.o</code>
<code>firmware/built-in.o arch/x86/pci/built-in.o</code>
<code>arch/x86/power/built-in.o arch/x86/video/built-in.o</code>
<code>net/built-in.o</code>
下一個可以被執行的目标如下:
<code>$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;</code>
<code>$(vmlinux-dirs): prepare scripts</code>
<code>$(q)$(make) $(build)=$@</code>
就像我們看到的,<code>vmlinux-dir</code> 依賴于兩部分:<code>prepare</code> 和 <code>scripts</code>。第一個 <code>prepare</code> 定義在核心的根<code>makefile</code> 中,準備工作分成三個階段:
<code>prepare: prepare0</code>
<code>prepare0: archprepare force</code>
<code>$(q)$(make) $(build)=.</code>
<code>archprepare: archheaders archscripts prepare1 scripts_basic</code>
<code>prepare1: prepare2 $(version_h) include/generated/utsrelease.h \</code>
<code>include/config/auto.conf</code>
<code>$(cmd_crmodverdir)</code>
<code>prepare2: prepare3 outputmakefile asm-generic</code>
第一個目标是 makefile 生成的系統調用清單(syscall table)中的 <code>archheaders</code> :
<code>archheaders:</code>
<code>$(q)$(make) $(build)=arch/x86/entry/syscalls all</code>
第二個目标是 makefile 裡的 <code>archscripts</code>:
<code>archscripts: scripts_basic</code>
<code>$(q)$(make) $(build)=arch/x86/tools relocs</code>
<code>scripts_basic:</code>
<code>$(q)$(make) $(build)=scripts/basic</code>
<code>scripts/basic/makefile</code> 包含了編譯兩個主機程式 <code>fixdep</code> 和 <code>bin2</code> 的目标:
<code>hostprogs-y := fixdep</code>
<code>hostprogs-$(config_build_bin2c) += bin2c</code>
<code>always := $(hostprogs-y)</code>
<code>$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep</code>
執行 make 之後,終端的第一個輸出就是 <code>kbuild</code> 的結果:
<code>$ make</code>
<code>hostcc scripts/basic/fixdep</code>
<code>hostcc arch/x86/tools/relocs_32.o</code>
<code>hostcc arch/x86/tools/relocs_64.o</code>
<code>hostcc arch/x86/tools/relocs_common.o</code>
<code>hostld arch/x86/tools/relocs</code>
在編譯完 <code>relocs.c</code> 之後會檢查 <code>version.h</code>:
<code>$(version_h): $(srctree)/makefile force</code>
<code>$(call filechk,version.h)</code>
<code>$(q)rm -f $(old_version_h)</code>
我們可以在輸出看到它:
<code>chk include/config/kernel.release</code>
以及在核心的根 makefiel 使用 <code>arch/x86/include/generated/asm</code> 的目标 <code>asm-generic</code> 來建構 <code>generic</code>彙編頭檔案。在目标 <code>asm-generic</code> 之後,<code>archprepare</code> 就完成了,是以目标 <code>prepare0</code> 會接着被執行,如我上面所寫:
<code>build := -f $(srctree)/scripts/makefile.build obj</code>
或者在我們的例子中,它就是目前源碼目錄路徑:<code>.</code>:
<code>$(q)$(make) -f $(srctree)/scripts/makefile.build obj=.</code>
<code>include $(kbuild-file)</code>
首先,我們先來了解一下 <code>vmlinux-dirs</code> 都包含了那些東西。在我們的例子中它包含了下列核心目錄的路徑:
<code>init usr arch/x86 kernel mm fs ipc security crypto block</code>
<code>drivers sound firmware arch/x86/pci arch/x86/power</code>
<code>arch/x86/video net lib arch/x86/lib</code>
<code>vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \</code>
<code>$(core-y) $(core-m) $(drivers-y) $(drivers-m) \</code>
<code>$(net-y) $(net-m) $(libs-y) $(libs-m)))</code>
<code>init-y := init/</code>
<code>drivers-y := drivers/ sound/ firmware/</code>
<code>net-y := net/</code>
<code>libs-y := lib/</code>
這裡我們借助函數 <code>patsubst</code> 和 <code>filter</code>去掉了每個目錄路徑裡的符号 <code>/</code>,并且把結果放到 <code>vmlinux-dirs</code>裡。是以我們就有了 <code>vmlinux-dirs</code> 裡的目錄清單,以及下面的代碼:
符号 <code>$@</code> 在這裡代表了 <code>vmlinux-dirs</code>,這就表明程式會遞歸周遊從 <code>vmlinux-dirs</code> 以及它内部的全部目錄(依賴于配置),并且在對應的目錄下執行 <code>make</code> 指令。我們可以在輸出看到結果:
<code>cc init/main.o</code>
<code>chk include/generated/compile.h</code>
<code>cc init/version.o</code>
<code>cc init/do_mounts.o</code>
<code>cc arch/x86/crypto/glue_helper.o</code>
<code>as arch/x86/crypto/aes-x86_64-asm_64.o</code>
<code>cc arch/x86/crypto/aes_glue.o</code>
<code>as arch/x86/entry/entry_64.o</code>
<code>as arch/x86/entry/thunk_64.o</code>
<code>cc arch/x86/entry/syscall_64.o</code>
每個目錄下的源代碼将會被編譯并且連結到 <code>built-io.o</code> 裡:
<code>$ find . -name built-in.o</code>
<code>./arch/x86/crypto/built-in.o</code>
<code>./arch/x86/crypto/sha-mb/built-in.o</code>
<code>./arch/x86/net/built-in.o</code>
<code>./init/built-in.o</code>
<code>./usr/built-in.o</code>
<code>+$(call if_changed,link-vmlinux)</code>
<code>link vmlinux</code>
<code>ld vmlinux.o</code>
<code>modpost vmlinux.o</code>
<code>gen .version</code>
<code>upd include/generated/compile.h</code>
<code>ld init/built-in.o</code>
<code>ksym .tmp_kallsyms1.o</code>
<code>ksym .tmp_kallsyms2.o</code>
<code>ld vmlinux</code>
<code>sortex vmlinux</code>
<code>sysmap system.map</code>
<code>vmlinux</code> 和<code>system.map</code> 生成在核心源碼樹根目錄下。
<code>$ ls vmlinux system.map</code>
<code>system.map vmlinux</code>
<code>all: bzimage</code>
<code>bzimage: vmlinux</code>
<code>$(q)$(make) $(build)=$(boot) $(kbuild_image)</code>
<code>$(q)mkdir -p $(objtree)/arch/$(uts_machine)/boot</code>
<code>$(q)ln -fsn ../../x86/boot/bzimage $(objtree)/arch/$(uts_machine)/boot/$@</code>
在這裡我們可以看到第一次為 boot 目錄執行 <code>make</code>,在我們的例子裡是這樣的:
<code>boot := arch/x86/boot</code>
<code>$(obj)/setup.elf: $(src)/setup.ld $(setup_objs) force</code>
<code>$(call if_changed,ld)</code>
我們已經在目錄 <code>arch/x86/boot</code> 有了連結腳本 <code>setup.ld</code>,和擴充到 <code>boot</code> 目錄下全部源代碼的變量<code>setup_objs</code> 。我們可以看看第一個輸出:
<code>as arch/x86/boot/bioscall.o</code>
<code>cc arch/x86/boot/cmdline.o</code>
<code>as arch/x86/boot/copy.o</code>
<code>hostcc arch/x86/boot/mkcpustr</code>
<code>cpustr arch/x86/boot/cpustr.h</code>
<code>cc arch/x86/boot/cpu.o</code>
<code>cc arch/x86/boot/cpuflags.o</code>
<code>cc arch/x86/boot/cpucheck.o</code>
<code>cc arch/x86/boot/early_serial_console.o</code>
<code>cc arch/x86/boot/edd.o</code>
<code>$(obj)/header.o: $(obj)/voffset.h $(obj)/zoffset.h</code>
第一個頭檔案 <code>voffset.h</code> 是使用 <code>sed</code> 腳本生成的,包含用 <code>nm</code> 工具從 <code>vmlinux</code> 擷取的兩個位址:
<code>#define vo__end 0xffffffff82ab0000</code>
<code>#define vo__text 0xffffffff81000000</code>
<code>$(obj)/zoffset.h: $(obj)/compressed/vmlinux force</code>
<code>$(call if_changed,zoffset)</code>
<code>lds arch/x86/boot/compressed/vmlinux.lds</code>
<code>as arch/x86/boot/compressed/head_64.o</code>
<code>cc arch/x86/boot/compressed/misc.o</code>
<code>cc arch/x86/boot/compressed/string.o</code>
<code>cc arch/x86/boot/compressed/cmdline.o</code>
<code>objcopy arch/x86/boot/compressed/vmlinux.bin</code>
<code>bzip2 arch/x86/boot/compressed/vmlinux.bin.bz2</code>
<code>hostcc arch/x86/boot/compressed/mkpiggy</code>
<code>vmlinux.bin</code> 是去掉了調試資訊和注釋的 <code>vmlinux</code> 二進制檔案,加上了占用了 <code>u32</code> (lctt 譯注:即4-byte)的長度資訊的 <code>vmlinux.bin.all</code> 壓縮後就是 <code>vmlinux.bin.bz2</code>。其中 <code>vmlinux.bin.all</code> 包含了<code>vmlinux.bin</code> 和<code>vmlinux.relocs</code>(lctt 譯注:vmlinux 的重定位資訊),其中 <code>vmlinux.relocs</code> 是<code>vmlinux</code> 經過程式 <code>relocs</code> 處理之後的 <code>vmlinux</code> 鏡像(見上文所述)。我們現在已經擷取到了這些檔案,彙編檔案 <code>piggy.s</code> 将會被 <code>mkpiggy</code> 生成、然後編譯:
<code>mkpiggy arch/x86/boot/compressed/piggy.s</code>
<code>as arch/x86/boot/compressed/piggy.o</code>
這個彙編檔案會包含經過計算得來的、壓縮核心的偏移資訊。處理完這個彙編檔案,我們就可以看到 <code>zoffset</code>生成了:
<code>zoffset arch/x86/boot/zoffset.h</code>
<code>as arch/x86/boot/header.o</code>
<code>cc arch/x86/boot/main.o</code>
<code>cc arch/x86/boot/mca.o</code>
<code>cc arch/x86/boot/memory.o</code>
<code>cc arch/x86/boot/pm.o</code>
<code>as arch/x86/boot/pmjump.o</code>
<code>cc arch/x86/boot/printf.o</code>
<code>cc arch/x86/boot/regs.o</code>
<code>cc arch/x86/boot/string.o</code>
<code>cc arch/x86/boot/tty.o</code>
<code>cc arch/x86/boot/video.o</code>
<code>cc arch/x86/boot/video-mode.o</code>
<code>cc arch/x86/boot/video-vga.o</code>
<code>cc arch/x86/boot/video-vesa.o</code>
<code>cc arch/x86/boot/video-bios.o</code>
所有的源代碼會被編譯,他們最終會被連結到 <code>setup.elf</code> :
<code>ld arch/x86/boot/setup.elf</code>
或者:
<code>ld -m elf_x86_64 -t arch/x86/boot/setup.ld arch/x86/boot/a20.o arch/x86/boot/bioscall.o arch/x86/boot/cmdline.o arch/x86/boot/copy.o arch/x86/boot/cpu.o arch/x86/boot/cpuflags.o arch/x86/boot/cpucheck.o arch/x86/boot/early_serial_console.o arch/x86/boot/edd.o arch/x86/boot/header.o arch/x86/boot/main.o arch/x86/boot/mca.o arch/x86/boot/memory.o arch/x86/boot/pm.o arch/x86/boot/pmjump.o arch/x86/boot/printf.o arch/x86/boot/regs.o arch/x86/boot/string.o arch/x86/boot/tty.o arch/x86/boot/video.o arch/x86/boot/video-mode.o arch/x86/boot/version.o arch/x86/boot/video-vga.o arch/x86/boot/video-vesa.o arch/x86/boot/video-bios.o -o arch/x86/boot/setup.elf</code>
最後的兩件事是建立包含目錄 <code>arch/x86/boot/*</code> 下的編譯過的代碼的 <code>setup.bin</code>:
<code>objcopy -o binary arch/x86/boot/setup.elf arch/x86/boot/setup.bin</code>
以及從 <code>vmlinux</code> 生成 <code>vmlinux.bin</code> :
<code>objcopy -o binary -r .note -r .comment -s arch/x86/boot/compressed/vmlinux arch/x86/boot/vmlinux.bin</code>
<code>arch/x86/boot/tools/build arch/x86/boot/setup.bin arch/x86/boot/vmlinux.bin arch/x86/boot/zoffset.h arch/x86/boot/bzimage</code>
實際上 <code>bzimage</code> 就是把 <code>setup.bin</code> 和 <code>vmlinux.bin</code> 連接配接到一起。最終我們會看到輸出結果,就和那些用源碼編譯過核心的同行的結果一樣:
<code>setup is 16268 bytes (padded to 16384 bytes).</code>
<code>system is 4704 kb</code>
<code>crc 94a88f9a</code>
<code>kernel: arch/x86/boot/bzimage is ready (#5)</code>
全部結束。
這就是本文的結尾部分。本文我們了解了編譯核心的全部步驟:從執行 <code>make</code> 指令開始,到最後生成<code>bzimage</code>。我知道,linux 核心的 makefile 和建構 linux 的過程第一眼看起來可能比較迷惑,但是這并不是很難。希望本文可以幫助你了解建構 linux 核心的整個流程
本文來自雲栖社群合作夥伴“linux中國”,原文釋出日期:2015-09-11