内核 镜像 解析

版权声明:本文为博主原创文章,任何组织或者个人可以在任何媒介上发表或转载我的文章、图片等.且转载后必须注明出处和邮箱,博客地址(https://blog.csdn.net/u011011827),本人邮箱([email protected]) https://blog.csdn.net/u011011827/article/details/71057018
前言
本文对编译 linux 生成的各种镜像进行解读
  • 各种镜像简介
最终编译结束,可能会生成多种内核镜像

1. vmlinux
  
编译的时候make 工具读取.config文件,然后在顶层目录生成了vmlinux文件.
vmlinux是未压缩的内核,vmlinuz是vmlinux的压缩文件。
vmlinux 是ELF文件,即编译出的最原始的文件,或者称之为基本内核

2. Image

Image是由内核顶层目录下的 vmlinux 二进制化后得到的
ELF文件vmlinux经过objcopy后得到binary文件Image


3. zImage

linux的作者们觉得Image还是太大了所以对Image进行了压缩,并且在image压缩后的文件的前端附加了一部分解压缩代码,构成了一个压缩格式的镜像就叫zImage。解压的时候,通过zImage镜像头部的解压缩代码进行自解压,然后执行解压出来的内核镜像。
zImage的是elf格式的arch/arm/boot/compressed/vmlinux二进制化得到的

zImage和bzImage是vmlinuz的存在形式,在x86系统中,我们是直接通过将bzImage拷贝为/boot/vmlinuz获得vmlinuz的。


4. bzImage

bzImage是压缩的内核映像,需要注意,bzImage不是用bzip2压缩 的,bzImage中的bz容易引起误解,bz表示“big zImage”。 bzImage中的b是“big”意思。 

zImage(vmlinuz)bzImage(vmlinuz)都是用gzip压缩的。

5. vmlinuz (统称,不对应实物.包括 zImage 和 bzImage)

vmlinuz(zImage或bzImage)Image 经过压缩后的文件。

----

10. uImage

uboot为了启动linux内核,还发明了一种内核格式叫uImage。uImage是由zImage加工得到的,uboot中有一个工具,可以将zImage加工生成uImage。注意:uImage不关linux内核的事,linux内核只管生成zImage即可,然后uboot中的mkimage工具再去由zImage加工生成uImage来给uboot启动。这个加工过程其实就是在zImage前面加上64字节的uImage的头信息即可。


---


$ find . -name vmlinux
./arch/arm/boot/compressed/vmlinux  // 这个是压缩过的 大小 7218420
./vmlinux  // 这个是没压缩的, 大小 83725768

$ find . -name zImage  // 大小 7128944
./arch/arm/boot/zImage

$ find . -name Image 
./arch/arm/boot/Image //大小 16864200

各种镜像的生成过程(逆序)


uImage — arch/arm/boot/uImage

uImage 是由 一个脚本做出来的.
scripts/mkuboot.sh 脚本

整体的命令是
scripts/mkuboot.sh -A arm -O linux -C none -T kernel -a 0x80008000 -e 0x80008000 -n 'Linux-3.10.0' -d arch/arm/boot/zImage arch/arm/boot/uImage

命令解析:
	-A arm 架构是arm
	-O linux 系统是linux
	-C none 没压缩
	-T kernel 类型是kernel
	-a 0x80008000 加载地址是0x80008000
	-e 0x80008000 入口地址是0x80008000
	-n 'Linux-3.10.0' 名字是Linux-3.10.0
	-d arch/arm/boot/zImage 输入数据文件是arch/arm/boot/zImage
	arch/arm/boot/uImage 输出文件是arch/arm/boot/uImage

scripts/mkuboot.sh 对mkimage命令进行判断是否存在,然后就调用了 mkimage $@,然后就制作出来了 arch/arm/boot/uImage , 把 选项中的参数 写到了uImage的前64字节,例如加载地址和入口地址


zImage — arch/arm/boot/zImage

uImage 是依靠mkimage命令 和 zImage 做出来的.命令可以在uboot源码tools目录编译得到,也可以直接安装得到.

下面看一下arch/arm/boot/zImage是怎么做出来的

他是由arch/arm/boot/compressed/vmlinux 二进制化得到的
arch/arm/boot/.zImage.cmd中写到

arm-hisiv300-linux-objcopy -O binary -R .comment -S  arch/arm/boot/compressed/vmlinux arch/arm/boot/zImage

命令解析:
	-O binary 	输出目标为二进制文件
	-R .comment 不拷贝.comment段
	-S 					不拷贝重定位信息和符号信息到输出文件(目的文件)中去
	arch/arm/boot/compressed/vmlinux 输入文件
	arch/arm/boot/zImage 输出文件

vmlinux — arch/arm/boot/compressed/vmlinux

然后我们看一下arch/arm/boot/compressed/vmlinux 是怎么做出来的
arch/arm/boot/compressed/.vmlinux.cmd中写到

arm-hisiv300-linux-ld 	-EL  \
  											--defsym _kernel_bss_size=138792 \
  											--defsym zreladdr=0x80008000 \
  											-p \
  											--no-undefined \
  											-X \
  											-T arch/arm/boot/compressed/vmlinux.lds \
  											arch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.gzip.o \
  											arch/arm/boot/compressed/misc.o arch/arm/boot/compressed/decompress.o \
  											arch/arm/boot/compressed/string.o arch/arm/boot/compressed/hyp-stub.o \
  											arch/arm/boot/compressed/lib1funcs.o arch/arm/boot/compressed/ashldi3.o\
  											-o arch/arm/boot/compressed/vmlinux

命令解析:
	-EL 连接little-endian对象. 这会影响缺省输出格式
	--defsym _kernel_bss_size=138792 在输出文件中定义一个全局变量 _kernel_bss_size 值为 138792
	--defsym zreladdr=0x80008000 在输出文件中定义一个全局变量 zreladdr 值为 0x80008000
	-p 动态库的问题
	--no-undefined 好像是符号重定义的问题,不清楚
	-X Delete all temporary local symbols
	-T arch/arm/boot/compressed/vmlinux.lds 用arch/arm/boot/compressed/vmlinux.lds来替换默认链接脚本
	arch/arm/boot/compressed/head.o 输入文件
	-o arch/arm/boot/compressed/vmlinux 输出文件

-rwxrwxr-x 1 linux linux 3.2M  425 20:33 vmlinux*
-rw-rw-r-- 1 linux linux 3.1M  425 20:33 piggy.gzip.o

vmlinux — arch/arm/boot/compressed/vmlinux

  • 这个vmlinux是arch/arm/boot/compressed/中的vmlinux,注意区分文章后段中的vmlinux

vmlinux.lds是怎么做出来的? 没有命令的备份,打印信息里也没有,只能找Makefile了

arch/arm/boot/compressed/Makefile中写到

SEDFLAGS    = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/

$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile $(KCONFIG_CONFIG)
    @sed "$(SEDFLAGS)" < $< > $@

解析上面的命令,就是替换 vmlinux.lds.in 中的两个字符串,然后放入vmlinux.lds中.

vmlinux.lds解析:

ENTRY(_start) 表示,将符号_start设置成入口地址,入口地址(entry point)是指进程执行的第一条用户空间的指令在进程地址空间的地址
SECTIONS
{
  . = 0;  								把定位器符号置为0
  _text = .;							将_text符号放在 0位置

  .text : {
    _start = .;						将_start符号放在0位置
    *(.start)							将所有的输入文件的.start段放入输出文件的.text段
    *(.text)							将所有的输入文件的.text段放入输出文件的.text段
    *(.text.*)
    *(.fixup)
    *(.gnu.warning)
    *(.glue_7t)
    *(.glue_7)
  }
}

我们之前看到
-rwxrwxr-x 1 linux linux 3.2M 4月 25 20:33 vmlinux
-rw-rw-r-- 1 linux linux 3.1M 4月 25 20:33 piggy.gzip.o
所以怀疑内核大部分在piggy.gzip.o文件中.


piggy.gzip.o

所以下面对piggy.gzip.o分析,根据 Makefile中 这一句

$(obj)/piggy.$(suffix_y).o:  $(obj)/piggy.$(suffix_y) FORCE

判 断,取决于piggy.gzip

然后看下一句

$(obj)/piggy.$(suffix_y): $(obj)/../Image FORCE
    $(call if_changed,$(suffix_y))

很明显,这句就是说当依赖更新时,重新调用gzip命令,也就是将Image压缩成了piggy.gzip


Image — arch/arm/boot/Image

查看arch/arm/boot/.Image.cmd

arm-hisiv300-linux-objcopy -O binary -R .comment -S  vmlinux arch/arm/boot/Image

命令解析:
	-O binary 	输出目标为二进制文件
	-R .comment 不拷贝.comment段
	-S 					不拷贝重定位信息和符号信息到输出文件(目的文件)中去
	vmlinux 顶层的vmlinux为输入文件
	arch/arm/boot/Image 输出文件

vmlinux — 顶层vmlinux

  • 这个vmlinux是顶层的vmlinux,注意区分文章前段的vmlinux
    查看.vmlinux.cmd
/bin/bash scripts/link-vmlinux.sh arm-hisiv300-linux-ld -EL  -p --no-undefined -X --build-id


scripts/link-vmlinux.sh解析

首先用了下面这一个函数链接了vmlinux.o

modpost_link()
{
    ${LD} ${LDFLAGS} -r -o ${1} ${KBUILD_VMLINUX_INIT}                   \   
        --start-group ${KBUILD_VMLINUX_MAIN} --end-group
}

然后用scripts/mod/modpost检查了vmlinux.o 并生成了Module.symvers

${MAKE} -f "${srctree}/scripts/Makefile.modpost" vmlinux.o

然后链接了init目录下的内容

${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init

然后就用到了执行了三次 kallsyms

每次都是 vmlinux_link 生成一个文件,然后kallsyms使用这个文件,生成一个二进制文件.

最后生成一个 kallsymso ,然后在用kallsymso生成vmlinux

vmlinux_link

vmlinux_link 解析

ld \
-m elf_i386 --emit-relocs --build-id \
-o $(2) \
-T \
arch/x86/kernel/head_32.o arch/x86/kernel/head32.o arch/x86/kernel/head.o init/built-in.o \
--start-group usr/built-in.o arch/x86/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o test/built-in.o lib/lib.a arch/x86/lib/lib.a lib/built-in.o arch/x86/lib/built-in.o drivers/built-in.o sound/built-in.ofirmware/built-in.o arch/x86/pci/built-in.o arch/x86/power/built-in.oarch/x86/video/built-in.o net/built-in.o --end-group \
$(1)

kallsysms

kallsyms解析
kallsyms系统编译过程


built-in.o

查看 init/.built-in.o.cmd

arm-hisiv300-linux-ld -EL -r -o init/built-in.o init/main.o init/version.o init/mounts.o init/initramfs.o init/calibrate.o init/init_task.o 

命令解析:
	-EL 连接little-endian对象. 这会影响缺省输出格式
	-r 产生可重定位的输出
	-o init/built-in.o 输出文件
	init/main.o init/version.o init/mounts.o init/initramfs.o init/calibrate.o init/init_task.o  出入文件

其他目录下的built-in.o 也是这样子生成的.例如 fs 目录 前期在子目录下生成了fs/exofs/built-in.o文件,然后链接到了一起,生成了fs/fs/built-in.o 文件

uImage和zImage 的 具体区别

uIamge是由zImage 经工具mkimage制作而来的,制作的过程就是压缩
uboot目前只能支持uImage启动,不支持zImage启动
bootm加载linux镜像,加载的是uIamge,bootm需要先对uIamge解压,解压地址为内核入口地址。当解压完成时uIamge和zIamge几乎是相同的,差别是uImage比zImage多64字节

//多出的64字节
2705 1956 fad0 6c19 58d0 99f5 0030 c588
8000 8000 8000 8000 c959 2892 0502 0200
4c69 6e75 782d 332e 302e 3800 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
// 这64字节对应的结构体 // u-boot-2017.05/include/image.h
typedef struct image_header {                                                        
    __be32      ih_magic;   /* Image Header Magic Number    */                       
    __be32      ih_hcrc;    /* Image Header CRC Checksum    */                       
    __be32      ih_time;    /* Image Creation Timestamp */                           
    __be32      ih_size;    /* Image Data Size      */                               
    __be32      ih_load;    /* Data  Load  Address      */                           
    __be32      ih_ep;      /* Entry Point Address      */                           
    __be32      ih_dcrc;    /* Image Data CRC Checksum  */                           
    uint8_t     ih_os;      /* Operating System     */                               
    uint8_t     ih_arch;    /* CPU architecture     */                               
    uint8_t     ih_type;    /* Image Type           */                               
    uint8_t     ih_comp;    /* Compression Type     */                               
    uint8_t     ih_name[IH_NMLEN];  /* Image Name       */                           
} image_header_t;

参考资料

lds语法解析
arch/arm/boot/compressed/vmlinux生成过程
解析 Linux 内核可装载模块的版本检查机制
kallsyms系统编译过程

猜你喜欢

转载自blog.csdn.net/u011011827/article/details/71057018