OpenWRT 镜像生成过程分析
综述
根据前面的分析,我们知道系统最终调用了make -C target/linux compile/install等进行编译,我们接着分析target的生成过程,在target/linux的Makefile只有一句话如下:
#
# Copyright (C) 2006-2007 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/target.mk
export TARGET_BUILD=1
prereq clean download prepare compile install menuconfig nconfig oldconfig update refresh: FORCE
@+$(NO_TRACE_MAKE) -C $(BOARD) $@
在此处我们使用atheros的PB44参考设计进行分析,BOARD为ar71x,我们可以看到这个Makefile直接进入到切换到BOARD执行,ar71xx的Makefile为:
#
# Copyright (C) 2008-2011 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
ARCH:=mips
BOARD:=ar71xx
BOARDNAME:=Atheros AR7xxx/AR9xxx
FEATURES:=mips16
CPU_TYPE=34kc
SUBTARGETS:=generic nand mikrotik
KERNEL_PATCHVER:=3.18
include $(INCLUDE_DIR)/target.mk
DEFAULT_PACKAGES += \
kmod-gpio-button-hotplug swconfig \
kmod-ath9k wpad-mini uboot-envtools
$(eval $(call BuildTarget)
BuildTarget定义在target.mk,我们将其展开才能看到具体的目标是怎么执行
BuildTarget
BuildTarget定义在TOPDIR/include/target.mk中
include $(INCLUDE_DIR)/kernel.mk ifeq ($(TARGET_BUILD),1) include $(INCLUDE_DIR)/kernel-build.mk BuildTarget?=$(BuildKernel) endif
TARGET_BULID在target/linux已经传递进来了,我们可以看到其调用的是BuildKernel,仅需跟踪
BuildKernel
BuildKernel定义在TOPDIR/include/kernel-build.mk中
define BuildKernel
$(if $(QUILT),$(Build/Quilt))
$(if $(LINUX_SITE),$(call Download,kernel))
.NOTPARALLEL:
$(STAMP_PREPARED): $(if $(LINUX_SITE),$(DL_DIR)/$(LINUX_SOURCE))
-rm -rf $(KERNEL_BUILD_DIR)
-mkdir -p $(KERNEL_BUILD_DIR)
$(Kernel/Prepare)
touch $$@
$(KERNEL_BUILD_DIR)/symtab.h: FORCE
rm -f $(KERNEL_BUILD_DIR)/symtab.h
touch $(KERNEL_BUILD_DIR)/symtab.h
+$(MAKE) $(KERNEL_MAKEOPTS) vmlinux
find $(LINUX_DIR) $(STAGING_DIR_ROOT)/lib/modules -name \*.ko | \
xargs $(TARGET_CROSS)nm | \
awk '$$$$1 == "U" { print $$$$2 } ' | \
sort -u > $(KERNEL_BUILD_DIR)/mod_symtab.txt
$(TARGET_CROSS)nm -n $(LINUX_DIR)/vmlinux.o | grep ' [rR] __ksymtab' | sed -e 's,........ [rR] __ksymtab_,,' > $(KERNEL_BUILD_DIR)/kernel_symtab.txt
grep -Ff $(KERNEL_BUILD_DIR)/mod_symtab.txt $(KERNEL_BUILD_DIR)/kernel_symtab.txt > $(KERNEL_BUILD_DIR)/sym_include.txt
grep -Fvf $(KERNEL_BUILD_DIR)/mod_symtab.txt $(KERNEL_BUILD_DIR)/kernel_symtab.txt > $(KERNEL_BUILD_DIR)/sym_exclude.txt
( \
echo '#define SYMTAB_KEEP \'; \
cat $(KERNEL_BUILD_DIR)/sym_include.txt | \
awk '{print "KEEP(*(___ksymtab+" $$$$1 ")) \\" }'; \
echo; \
echo '#define SYMTAB_KEEP_GPL \'; \
cat $(KERNEL_BUILD_DIR)/sym_include.txt | \
awk '{print "KEEP(*(___ksymtab_gpl+" $$$$1 ")) \\" }'; \
echo; \
echo '#define SYMTAB_DISCARD \'; \
cat $(KERNEL_BUILD_DIR)/sym_exclude.txt | \
awk '{print "*(___ksymtab+" $$$$1 ") \\" }'; \
echo; \
echo '#define SYMTAB_DISCARD_GPL \'; \
cat $(KERNEL_BUILD_DIR)/sym_exclude.txt | \
awk '{print "*(___ksymtab_gpl+" $$$$1 ") \\" }'; \
echo; \
) > $$@
$(STAMP_CONFIGURED): $(STAMP_PREPARED) $(LINUX_KCONFIG_LIST) $(TOPDIR)/.config
$(Kernel/Configure)
touch $$@
$(LINUX_DIR)/.modules: $(STAMP_CONFIGURED) $(LINUX_DIR)/.config FORCE
$(Kernel/CompileModules)
touch $$@
$(LINUX_DIR)/.image: $(STAMP_CONFIGURED) $(if $(CONFIG_STRIP_KERNEL_EXPORTS),$(KERNEL_BUILD_DIR)/symtab.h) FORCE
$(Kernel/CompileImage)
$(Kernel/CollectDebug)
touch $$@
mostlyclean: FORCE
$(Kernel/Clean)
define BuildKernel
endef
download: $(if $(LINUX_SITE),$(DL_DIR)/$(LINUX_SOURCE))
prepare: $(STAMP_CONFIGURED)
compile: $(LINUX_DIR)/.modules
$(MAKE) -C image compile TARGET_BUILD=
oldconfig menuconfig nconfig: $(STAMP_PREPARED) $(STAMP_CHECKED) FORCE
rm -f $(STAMP_CONFIGURED)
$(LINUX_RECONF_CMD) > $(LINUX_DIR)/.config
$(_SINGLE)$(MAKE) -C $(LINUX_DIR) $(KERNEL_MAKEOPTS) $$@
$(LINUX_RECONF_DIFF) $(LINUX_DIR)/.config > $(LINUX_RECONFIG_TARGET)
#在调用install的地方我们发现其切入到image目录下,其依赖与LINUX_DIR下的/.image, LINUX_DIR/.image就是编译好的内核可以在上面看到
#此处不具体分析内核生成过程,详见我们的另一篇文章内核生成,此处要跟踪的是升级镜像的生成过程
#我们进入到image下的Makefile查看,发现最终调用了$(eval $(call BuildImage))
#下面我们继续分析BuildImage
install: $(LINUX_DIR)/.image
+$(MAKE) -C image compile install TARGET_BUILD=
clean: FORCE
ifdef CONFIG_EXTERNAL_KERNEL_TREE
$(if $(wildcard $(LINUX_DIR)), \
make -C $(LINUX_DIR) clean)
else
rm -rf $(KERNEL_BUILD_DIR)
endif
image-prereq:
@+$(NO_TRACE_MAKE) -s -C image prereq TARGET_BUILD=
prereq: image-prereq
endef
BuildImage
BuildImage定义在image.mk中
define BuildImage
download:
prepare:
compile:
clean:
image_prepare:
ifeq ($(IB),)
.PHONY: download prepare compile clean image_prepare mkfs_prepare kernel_prepare install
compile:
$(call Build/Compile)
clean:
$(call Build/Clean)
image_prepare: compile
mkdir -p $(KDIR)/tmp
$(call Image/Prepare)
else
image_prepare:
mkdir -p $(KDIR)/tmp
endif
mkfs_prepare: image_prepare
$(call Image/mkfs/prepare)
kernel_prepare: mkfs_prepare
$(call Image/BuildKernel)
$(if $(CONFIG_TARGET_ROOTFS_INITRAMFS),$(if $(IB),,$(call Image/BuildKernel/Initramfs)))
$(call Image/InstallKernel)
$(foreach device,$(TARGET_DEVICES),$(call Device,$(device)))
$(foreach fs,$(TARGET_FILESYSTEMS) $(fs-subtypes-y),$(call BuildImage/mkfs,$(fs)))
$$(sort $$(_KERNEL_IMAGES)):
@touch $$@
install: kernel_prepare
$(foreach fs,$(TARGET_FILESYSTEMS),
$(call Image/Build,$(fs))
)
$(call Image/mkfs/ubifs_fit,-ipq40xx)
$(call Image/mkfs/ubifs_fit,-ipq806x)
$(call Image/mkfs/ubifs)
$(call Image/Checksum,md5sum --binary,md5sums)
$(call Image/Checksum,openssl dgst -sha256,sha256sums)
endef
我们再次回到target/linux/ar71xx/image/目录下发现compile目标为空什么都不做,那么我们接着分析install方法,install方法依赖kernel_prepare
define Image/BuildKernel
cp $(KDIR)/vmlinux.elf $(VMLINUX).elf
cp $(KDIR)/vmlinux $(VMLINUX).bin
dd if=$(KDIR)/vmlinux.bin.lzma of=$(VMLINUX).lzma bs=65536 conv=sync
dd if=$(KDIR)/vmlinux.bin.gz of=$(VMLINUX).gz bs=65536 conv=sync
$(call MkuImage,gzip,,$(KDIR)/vmlinux.bin.gz,$(UIMAGE)-gzip.bin)
$(call MkuImage,lzma,,$(KDIR)/vmlinux.bin.lzma,$(UIMAGE)-lzma.bin)
cp $(KDIR)/loader-generic.elf $(VMLINUX)-lzma.elf
-mkdir -p $(KDIR_TMP)
$(call Image/Build/Profile/$(IMAGE_PROFILE),buildkernel)
展开$(call Image/Build/Template/64k/buildkernel, PB4x, pb44, $(call mkcmdline, ...))
展开$(call Image/Build/PB4x/buildkernel,,$(2),$(3),$(4),$(5),$(6),$(7),$(8),$(9),$(10))
在文件中定义如下:Image/Build/PB4X/buildkernel=$(call PatchKernelLzma,$(2),$(3))
如要给景象打上CMDLINE的参数,并且压缩,到此内核准备好了并且大小已经以64K对其
endef
下面是Image/Build/Profile的详细分析
通过SingleProfile展开得到,
如下:
$(eval $(call SingleProfile,PB4X,64k,PB44,pb44,PB44,ttyS0,115200))
通过一下方式展开,我们以PB44位例
# $(1) : name of image build method to be used, e.g., TPLINK-LZMA, AthLzma.
# $(2) : name of the build template to be used, e.g. 64k, 64kraw, 128k, etc.
# $(3) : name of the profile to be defined.
# $(4) : board name.
# $(5)~$(7) : arguments for $(mkcmdline)
# board=$(1) console=$(2),$(3)
# $(8)~$(14): extra arguments.
define SingleProfile
# $(1): action name, e.g. loader, buildkernel, squashfs, etc.
define Image/Build/Profile/$(3)
$$(call Image/Build/Template/$(2)/$$(1),$(1),$(4),$$(call mkcmdline,$(5),$(6),$(7)),$(8),$(9),$(10),$(11),$(12),$(13),$(14))
endef
SINGLE_PROFILES += $(3)
endef
得到
define Image/Build/Profile/PB44
$(call Image/Build/Template/64k/$(1),PB4x, pb44, $(call mkcmdline, ...))
endef
$(call Image/Build/Template)通过如下宏生成
# $(1): template name to be defined.
# $(2): squashfs suffix to be used.
# $(3): jffs2 suffix to be used.
define BuildTemplate
# $(1) : name of build method.
# $(2) : board name.
# $(3) : kernel command line.
# $(4)~$(8): extra arguments.
define Image/Build/Template/$(1)/initramfs
$$(call Image/Build/$$(1)/initramfs,initramfs,$$(2),$$(3),$$(4),$$(5),$$(6),$$(7),$$(8),$$(9),$$(10))
endef
define Image/Build/Template/$(1)/loader
$$(call Image/Build/$$(1)/loader,$$(2),$$(3),$$(4),$$(5),$$(6),$$(7),$$(8),$$(9),$$(10))
endef
define Image/Build/Template/$(1)/buildkernel
$$(call Image/Build/$$(1)/buildkernel,,$$(2),$$(3),$$(4),$$(5),$$(6),$$(7),$$(8),$$(9),$$(10))
endef
define Image/Build/Template/$(1)/squashfs
$$(call Image/Build/$$(1),squashfs$(2),$$(2),$$(3),$$(4),$$(5),$$(6),$$(7),$$(8),$$(9),$$(10))
endef
$(if $(3),$(foreach bs,$(3),$(eval $(call Jffs2Template,$(1),$(bs)))))
endef
将如下展开
$(eval $(call BuildTemplate,64k,-64k,64k))
得到
define Image/Build/Template/64/buildkernel
$$(call Image/Build/$(1)/buildkernel,,$(2),$(3),$(4),$(5),$(6),$(7),$(8),$(9),$(10))
endef
SINGLE_PROFILES += PB44
未完待续。。。
浙公网安备 33010602011771号