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
未完待续。。。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律