(五) openwrt打包过程

本文详细解析了OpenWRT的编译流程,重点介绍了如何将各组件组织成最终的bin文件,包括内核编译、文件系统打包及最终镜像的生成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

标签(空格分隔): Makefile


本周是成胖子每周一博第六周,更好地阅读体验,请点击这里


前言

前面我们已经讲了openwrt编译的大部分过程,包括大致的编译步骤,ipk的编译等.今天是我这个系列的最后一篇,我们来看看openwrt的各个部分是如何组织成为最后的bin文件的.
最后一个步骤,我们的执行的目标是target/install.

执行过程推导

我们首先通过Makefile来分析下我们的依赖关系.

通过前几篇的分析,我们应当知道此时target/install将依赖于target/linux/install.
如下是target/linux/Makefile的节选:

export TARGET_BUILD=1

prereq clean download prepare compile install menuconfig nconfig oldconfig update refresh: FORCE
    @+$(NO_TRACE_MAKE) -C $(BOARD) $@

我们可以看出,此时执行compile将会进入对应的目标平台执行目标compile,我们以mt7620a为例.它属于ramips平台.
如下是target/linux/ramips/Makefile的节选:

include $(INCLUDE_DIR)/target.mk

$(eval $(call BuildTarget))

在这个Makefile文件中,我们找不到目标compile,它被文件target.mk封装.我们在ipk的编译中,已经见过类似的用法.
如下是include/target.mk的节选:

ifeq ($(TARGET_BUILD),1)
  include $(INCLUDE_DIR)/kernel-build.mk
  BuildTarget?=$(BuildKernel)
endif

从上面的节选我们看到变量TARGET_BUILD为1,所以这里函数BuildTarget其实等于在kernel-build.mk定义的变量BuildKernel.
如下是include/kernel-build.mk的节选:

define BuildKernel
  $(if $(QUILT),$(Build/Quilt))
  $(if $(LINUX_SITE),$(call Download,kernel))

  $(STAMP_CONFIGURED): $(STAMP_PREPARED) $(LINUX_KCONFIG_LIST) $(TOPDIR)/.config
    $(Kernel/Configure)
    touch $$@

  $(LINUX_DIR)/.image: $(STAMP_CONFIGURED) $(if $(CONFIG_STRIP_KERNEL_EXPORTS),$(KERNEL_BUILD_DIR)/symtab.h) FORCE
    $(Kernel/CompileImage)
    $(Kernel/CollectDebug)
    touch $$@
    
  install: $(LINUX_DIR)/.image
    +$(MAKE) -C image compile install TARGET_BUILD=

endef

我们绕来绕去,终于找到了我们要的目标,install依赖于$(LINUX_DIR)/.image,

$(LINUX_DIR)/.image的依赖和执行就在节选中,我们略过,

同时将进入image文件夹下,执行目标compileinstall.下面我来看看进入image文件夹下,发生的故事.
打开tareget/linux/ramips/image/Makefile,我们找了一圈,也没找到我们要的目标compileinstall;如果我们是完整编译,其实目标compile已经被执行过了.此时我们以install为目标.玄机就在最后一句$(eval $(call BuildImage))中,函数BuildImage被定义在include/image.mk中,以下为节选:

define BuildImage

  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)))

  install: kernel_prepare
    $(foreach fs,$(TARGET_FILESYSTEMS),
        $(call Image/Build,$(fs))
    )
    $(call Image/mkfs/ubifs)
    $(call Image/Checksum,md5sum --binary,md5sums)
    $(call Image/Checksum,openssl dgst -sha256,sha256sums)

endef

这里我们看到了我们的目标install和它的依赖关系及执行语句,这里$(TARGET_FILESYSTEMS)通常为squashfs.其中大部分函数定义在我们对应平台的image文件夹下的Makefile中.

上文中第28行需稍加注意,这里调用函数BuildImage/mkfs,定义如下:

define BuildImage/mkfs
  install: mkfs-$(1)
  .PHONY: mkfs-$(1)
  mkfs-$(1): mkfs_prepare
    $(Image/mkfs/$(1))
    $(call Build/mkfs/default,$(1))
    $(call Build/mkfs/$(1),$(1))
  $(KDIR)/root.$(1): mkfs-$(1)
endef

这里可以看到目标install依赖于mkfs-(1).
如此我们可以得到整个依赖关系图如下:
依赖关系图
图片不清晰可以下载下来查看.

稍加解释

上一节我们分析了依赖关系,执行过程其实就是倒叙执行的过程.
主要编辑过程就是先将内核编译完成,然后将安装ipk的root-ramips文件夹制作为squash格式的二进制文件,然后包括压缩内核,为内核制作头部,最后将加工过的内核部分和文件系统部分组合起来.

尾记

  1. 在网上查找资料的时候,发现大部分资料要么雷同,要么浅尝辄止.
  2. 下面这篇文章在我深入学习的过程中,起了不小的作用.链接;就是排版太差了,我才有了自己写写的冲动.
  3. openwrt的整个编译过程是很复杂的,想想电脑一秒可以做多少次计算,而一次完整编译轻轻松松就要两三个小时.可想而知编译过程之复杂.但是我觉得我把大体的编译过程是弄明白了,也欢迎大家交流.一共用了5篇的篇幅,大体的编译过程也算是交代完了.
  4. 纸上得来终觉浅,下面我觉得我还需要更多的改改写写来加深了解.最近在瞎看看linux内核相关的东西,那本书上也说了要加深学习了解,需要更多的修修补补.与大家共勉.

转载于:https://www.cnblogs.com/chengyi818/p/5118999.html

### OpenWRT 固件的解包与打包教程 #### 工具准备 为了完成 OpenWRT 固件的解包和打包操作,需要准备好必要的工具。常用的工具有 `firmware-mod-kit` 和一些基础命令行工具如 `binwalk`, `mkimage`, `dd` 等[^1]。 #### 解包过程 对于 OpenWRT 的固件解包,通常可以通过以下方法实现: 1. **使用 binwalk 进行初步分析** 使用 `binwalk` 对目标固件进行扫描,识别其中的文件系统和其他重要组件。 ```bash binwalk -e firmware.bin ``` 此命令会提取固件中的各个部分并保存到当前目录下的 `_firmware.bin.extracted` 文件夹中[^2]。 2. **手动分离分区** 如果固件包含多个分区,则需根据其结构进一步拆分。这一步可能涉及读取设备的启动加载程序 (bootloader) 或者根文件系统的特定偏移量。通过查看 `dmesg` 输出或厂商文档来获取这些信息[^1]。 3. **解压压缩数据** 许多固件内部的数据会被 gzip, lzma 或 xz 压缩算法处理过,在解包过程中也需要相应地对其进行解压。例如: ```bash unxz rootfs.xz ``` #### 修改内容 一旦成功解包,就可以对固件的内容进行修改了。常见的改动包括替换配置文件、增加自定义脚本或是更新软件版本等。需要注意的是,任何更改都应保持原有文件权限不变,并确保最终生成的新镜像能够被正确解析[^2]。 #### 打包过程 重新构建经过修改后的固件主要依赖于原始 Makefile 定义以及所使用的 CPU 架构相关信息。 1. **调整压缩方式** 根据需求设定合适的压缩格式,默认情况下可能是 squashfs 配合某种压缩技术(比如 xz)。如果希望改变默认设置,可以在对应的 Makefile 中找到类似下面这样的指令来进行定制化调整: ```makefile define Device/generic KERNEL := kernel-bin | append-dtb | pad-to $$(KERNEL_SIZE) IMAGE/sysupgrade.bin := append-kernel | pad-to 64k | append-rootfs | check-size endef $(eval $(call Device/generic)) ``` 2. **利用 mkimage 创建 uImage** 当涉及到 U-Boot 启动环境时,往往需要用 `mkimage` 将 Linux 内核转换成适合引导的形式: ```bash mkimage -A arm -O linux -T kernel -C none -a 0x80008000 -e 0x80008000 -n 'OpenWrt Kernel' -d vmlinux.bin.u-boot uImage ``` 3. **组合各部分形成完整映像** 最终步骤就是把之前分解出来的所有片段按照正确的顺序拼接起来构成一个新的完整的固件图像。这一环节有时需要用到 dd 命令或者其他专用工具依据具体的硬件平台要求执行写入动作[^1]。 ```python import subprocess def repackage_firmware(output_path, components): with open(output_path, 'wb') as out_file: for component in components: process = subprocess.Popen(['cat', component], stdout=subprocess.PIPE) out_file.write(process.stdout.read()) ``` 以上代码展示了如何简单地将几个二进制文件串联在一起创建新的固件影像。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值