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
未完待续。。。

 

posted on 2022-03-08 11:27  sudochen  阅读(1048)  评论(0编辑  收藏  举报

导航