Linux Kernel image

  • 学习Linux Kernel image in different forms

1.内核镜像介绍

  通常一个可启动的内核镜像 (bootable kernel image) 是经过算法压缩的,2.6.30 之后采用 LZMA 或者 BZIP2,vmlinuz 最后的 z 表示内核是压缩的,这也意味着内核中会有一段解压程序

  内核中包含了各种内核镜像的格式,如 vmlinux、zImage、bzImage、uImage 等,首先介绍内核中常见内核文件。

  • vmlinux是最原始,静态链接的,可执行的,不能bootable的,未压缩的内核镜像。vm代表Virtual Memory。Linux支持虚拟内存,因此得名vm。它是通过源码经过编译汇编, 链接而成的 ELF 文件。

  • zImage是 vmlinux 经过 gzip 压缩后的文件,适用于小内核。

  • bzImage 是 vmlinux 经过 gzip 压缩后的文件,适用于大内核,”bz” 表示 “big zImage”。

  • uImage 是 uboot 专用的镜像文件,它是在 zImage 之前加上一个长度为 0x40 的头信息,包括了该镜像文件的类型、加载位置、生成时间、大小等信息。

2.内核镜像构建
在这里插入图片描述
2.1.文件描述

1.vmlinux
  vmlinux是最原始,未压缩的内核镜像。vm代表Virtual Memory。Linux支持虚拟内存,因此得名vm。它是通过源码经过编译汇编, 链接而成的 ELF 文件。因此这个 vmlinux 文件包含了 ELF 的属性,以及各种调试信息等,因此这个阶段的内核镜像 vmlinux 特别大,而且不能直接在 arm 上直接运行。

2.Image
  由于 vmlinux 镜像体积巨大而且不能在 arm 上运行,因此需要使用 objcopy工具将不需要 的 section 从 vmlinux 里面剥离出来,最终在 arch/arm64/boot/目录下生成 Image 文件, 此时 Image 是可以在 arm 平台上运行的,该镜像文件也是未压缩。由于历史原因,当年制作出 Image 的大小正好比一个软盘大一点,为了让内核镜像能够装在一张软盘上,所以就将 Image 进行压缩, 生成 piggy.gz 或者 piggy_data.

3.piggy.gz/piggy_data

  The file Image compressed with gzip.一开始只支持 gzip 压缩方法,所以将压缩之后的 Image 称为 piggy.gz,但随着内核的不断 发展,内核支持更多的压缩算法,因此把压缩之后的 Image 称为 piggy_data.

4.piggy.o

  之前说过 Image 可以在 arm 上运行,当不能直接运行,因为 Image 运行前需要一些已知 初始化环境,这就需要特定功能的代码实现这些功能,这里称这些代码为 bootstrap。 于是内核在 arch/arm/boot/compressed/ 目录下增加了 bootstrap 功能的代码。和制作 vmlinux 一样,需要将这个目录下的源文件编译汇编成目标文件,然后再链接成一个文件。 为了构造这个,内核将 piggy_data 直接塞到了一个汇编文件 piggy.S 中,然后这个文件 经过汇编之后,就生成了 piggy.o。

2.2.vmlinux 构建过程

  vmlinux 文件是 Kbuild 编译系统将源码经过编译链接所获得的目标文件,所以它是一个 ELF 文件,因此 vmlinux 文件包含了各种调试信息和各种有用的 section。vmlinux 文件的链接过程由 arch/$(ARCH)/kernel/vmlinux.lds.S 链接脚本决定,可以通过该文件知道 vmlinux 文件的内部布局。
在这里插入图片描述在这里插入图片描述
let’s look at how vmlinux is produced on x86-64:
在这里插入图片描述
内核编译:
在这里插入图片描述
2.3.Image 构建
  Image文件是 vmlinux 使用 objcopy 工具转换后得到的二进制文件。由于 vmlinux 不能直接在 arm 上运行,需要丢弃一些与运行无关的 section,所以使用 objcopy 工具完成这个任务。Image 文件相比 vmlinux,除了格式不同之外,vmlinux 的调试信息和 许多注释以及与运行无关的 section 都被移除,所以体积会变小很多。构建过程如下所示:

arch/arm/boot/Makefile :
$(obj)/Image: vmlinux FORCE
  $(call if_changed,objcopy)

  Image 就是通过 vmlinux objcopy 获得,这里 objcopy 对应的命令是 位于 scripts/Makefile.lib 文件中获得,定义如下:

quiet_cmd_objcopy = OBJCOPY $@
cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@

  测试:在 Image 生成过程中添加打印消息,以此查看整个 object 过程, 添加调试代码如下:

$(obj)/Image: vmlinux FORCE
  $(warning "OBJCOPYFLAGS: $(OBJCOPYFLAGS)")
  $(warning "OBJCOPYFLAGS_$(@F): $(OBJCOPYFLAGS_$(@F))")
  $(call if_changed,objcopy)

编译内核时生成如下信息:

LD      vmlinux
SORTEX  vmlinux
SYSMAP  System.map
arch/arm/boot/Makefile:61: "OBJCOPYFLAGS: -O binary -R .comment -S"
arch/arm/boot/Makefile:61: "OBJCOPYFLAGS_Image: "
OBJCOPY arch/arm/boot/Image
Building modules, stage 2.
MODPOST 6 modules
Kernel: arch/arm/boot/Image is ready

  vmlinux 使用 objcopy变成 Image 时,使用的参数 是 “-O binary -R .comment -S”,这个参数的意思是:

  • -O binary 表示生成二进制文件
  • -R .comment 表示移除 .comment section
  • -S 表示移除所有的标志以及重定位信息

2.4.piggy_data构建

  piggy_data是 Image 经过压缩之后得到的压缩文件,具体如下:

//arch/arm/boot/compressed/Makefile:
$(obj)/piggy_data: $(obj)/../Image FORCE
  $(call if_changed,$(compress-y))

  通过上面的内容可知,内核采用的压缩方法由 compress-y 变量决定,其定义如下:

//arch/arm/boot/compressed/Makefile :
compress-$(CONFIG_KERNEL_GZIP) = gzip
compress-$(CONFIG_KERNEL_LZO)  = lzo
compress-$(CONFIG_KERNEL_LZMA) = lzma
compress-$(CONFIG_KERNEL_XZ)   = xzkern
compress-$(CONFIG_KERNEL_LZ4)  = lz4

  因此内核支持 gzip,lzo,lzma,xzkern, 和 lz4 的压缩方法,具体使用哪种,因此开发者可以在 命令执行处添加调试代码如下:

$(obj)/piggy_data: $(obj)/../Image FORCE
  $(wraning "compress-y: $(compress-y)")
  $(call if_changed,$(compress-y))

  Image 常采用了gzip 方法,在 scripts/Makefile.lib 文件中获得具体的 gzip 过程,如下:

quiet_cmd_gzip = GZIP    $@
      cmd_gzip = cat $(filter-out FORCE,$^) | gzip -n -f -9 > $@

gizp 的参数含义如下:

  • -n 压缩文件时,不保存原来文件名称以及时间戳
  • -f 强制压缩文件。不理会文件名称或硬链接是否存在以及文件是否为符号链接
  • -9 用 9 调整压缩的速度,-1 或 --fast 表示最快压缩方法 (低压缩比), -9 或者 --best 表示最慢的压缩方法 (高压缩比)

2.5.Bootstrap ELF kernel (vmlinux) 构建过程

  只有纯粹的内核是无法启动的,所以需要在内核的头部加入一些用于 bootstrap loader 功能的代码。 Kbuild 编译系统在 arch/arm/boot/compressed/ 目录下,将 head.S, misc.S, compressed.S 等多个汇编文件汇编成多个可链接的 ELF 目标文件,以此作为内核的 bootstrap loader。在这个步骤,Kbuid 编译系统将这些可链接的目标文件与 piggy.o 文件按链接脚本的内容进行链接,制作出一个带 bootstrap loader 的内核ELF 文件。对于过程要参考 arch/arm/boot/compressed/ 目录下的 Makefile 和 vmlinux.lds.S 文件。 首先通过分析 Makefile 知道链接的文件,具体源码如下:

$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o \
                $(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) \
                $(bswapsdi2) $(efi-obj-y) FORCE
        @$(check_for_multiple_zreladdr)
        $(call if_changed,ld)
        @$(check_for_bad_syms)

2.5.1.vmlinux.lds 理解

  vmlinux.lds总共有两个,分别是:

  • arch/arm/kernel/vmlinux.lds(用于生成未压缩的内核image)
  • arch/arm/boot/compressed/vmlinux.lds(用于生成经过压缩的内核image)

这两个lds 文件中的链接地址区别:

arch/arm/kernel/vmlinux.lds:

421 OUTPUT_ARCH(arm)
422 ENTRY(stext)
423 jiffies = jiffies_64;
424 SECTIONS
425 {
426  /*
427      * XXX: The linker does not define how output sections are
428      * assigned to input sections when there are multiple statements
429      * matching the same input section name.  There is no documented
430      * order of matching.
431      *
432      * unwind exit sections must be discarded before the rest of the
433      * unwind sections get included.
434      */
435  /DISCARD/ : {
436   *(.ARM.exidx.exit.text)
437   *(.ARM.extab.exit.text)
438
439
440
441
442   *(.exitcall.exit)
443   *(.alt.smp.init)
444   *(.discard)
445   *(.discard.*)
446  }
447  . = 0xC0000000 + 0x00008000;
448  .head.text : {
449   _text = .;
450   *(.head.text)
451  }

arch/arm/boot/compressed/vmlinux.lds:

 10 OUTPUT_ARCH(arm)
 11 ENTRY(_start)
 12 SECTIONS
 13 {
 14   /DISCARD/ : {
 15     *(.ARM.exidx*)
 16     *(.ARM.extab*)
 17     /*
 18      * Discard any r/w data - this produces a link error if we have any,
 19      * which is required for PIC decompression.  Local data generates
 20      * GOTOFF relocations, which prevents it being relocated independently
 21      * of the text/got segments.
 22      */
 23     *(.data)
 24   }
 25
 26   . = 0;
 27   _text = .;

  压缩内核的链接地址是从0开始的,未压缩的内核链接地址是从0xC0000000+0x00008000开始的。

  首先uboot使用到的内核是压缩过的内核也就是uimage,此时内核链接到的是0地址,入口是_start。而arch/arm/boot/compressed/vmlinux.lds中有如下定义:

  .text : {
 30     _start = .;
 31     *(.start)
 32     *(.text)
 33     *(.text.*)
 34     *(.fixup)
 35     *(.gnu.warning)
 36     *(.glue_7t)
 37     *(.glue_7)
 38   }

  所以压缩内核的入口就是链接地址是从0开始的。压缩内核来源于压缩的vmlinux,而压缩的vmlinux是通过head.o, misc.o piggy.o加工而来的。这个压缩的vmlinux连接顺序可以从arch/arm/boot/compressed/Makefile中得到如下信息:

HEAD = head.o
OBJS += misc.o decompress.o
vmlinux: $(obj)/vmlinux.lds ( o b j ) / (obj)/ (obj)/(HEAD) ( o b j ) / p i g g y . (obj)/piggy. (obj)/piggy.(suffix_y).o

  所以其实压缩内核的入口就是arch/arm/boot/compressed/head.s文件,并且这个文件应该也就是uboot跳转到内核之后的总入口,这部分代码是位置无关,所以链接到0地址也没关系。

  内核的入口的这段代码应该还包含搬运内核到高地址空间的代码,搬运的目的地址应该就是arch/arm/kernel/vmlinux.lds文件中的连接地址0xC0000000+0x00008000,这也应该改是内核最终的运行地址。

2.6 zImage构建

  zImage 是通过带 bootstrap loader 的内核 ELF 文件经过 objcopy 命令之后制作生成 的二进制文件,用于在 arm 上直接运行,其生成过程可以查看

arch/arm/boot/Makefile:
$(obj)/zImage:  $(obj)/compressed/vmlinux FORCE
        $(call if_changed,objcopy)

  同原始 vmlinux 转换为 Image 过程一致。制作完 zImage 之后, 可以将 zImage 在 arm 上运行。

  最终在内存SDRAM中的内核镜像是经过压缩的,只是在运行时再将其解压。所以编译时会先使用gzip将镜像文件image进行压缩,再将压缩后的镜像文件和源码中的两个文件(如下所示)一起链接生成压缩后的镜像文件compress/vmlinux,注意,这两个源码文件是解压程序,用于将内存SDRAM中的压缩镜像zImage进行解压,然后再执行kernel 的第一阶段启动代码arch/arm/kernel/head.S。简而言之,在内存中运行内核时,kernel先自身解压,再执行第一阶段启动代码。

  • arch/arm/boot/compressed/head.S
  • arch/arm/boot/compressed/misc.c
    在这里插入图片描述

3.vmlinuz

  vmlinuz是可引导的、压缩的,能bootable的内核。vmlinux是用来生成vmlinuz的中间步骤。vmlinuz是Linux kernel文件的历史名字,它实际上就是zImage或bzImage。

  vmlinuz 的建立有两种方式:

  • 编译内核时执行"make zImage",zImage适用于小内核的情况,它的存在是为了向后的兼容性。

  • 内核编译时通过命令make bzImage创建。

  zImage、bzImage 中均包含一个微型的 gzip 用于解压缩内核并引导,两者的不同之处在于: zImage 解压缩内核到低端内存 (第一个640K),bzImage 解压缩内核到高端内存 (1M以上)。也就是,它们之间最大的差别是对于内核体积大小的限制。

  由于 zImage 内核需要放在实模式 1MB 的内存之内,所以其体积受到了限制,目前采用的内核格式大多采用的是 bzImage ,这种格式没有 1MB 内存限制。arm 中常用的是 zImage,而 x86 中常用的是 bzImage 。

4.Linux 编译流程图
在这里插入图片描述

参考资料:
https://www.jianshu.com/p/d4e9b87c409d
https://jin-yang.github.io/post/kernel-compile.html
https://biscuitos.github.io/blog/ARM-Kernel-Image/

  • 6
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值