本文对编译 linux 生成的各种镜像进行解读
pc 一般用 bzImage ,并将其 命名 为 vmlinuz
嵌入式 一般用 uImage , 也可以 用 Image
最终编译结束,可能会生成多种内核镜像
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”意思。
bzImage 是为了x86的 大内核(512kB)而作出的一种东西
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的头信息即可。
---
./vmlinux // 这个是没压缩的, 大小 83725768
./arch/arm/boot/Image //大小 16864200
./arch/arm/boot/compressed/vmlinux // 这个是压缩过的 大小 7218420
./arch/arm/boot/zImage // 大小 7128944
./arch/arm/boot/uImage // 大小 7128944 + 64
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字节,例如加载地址和入口地址
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 输出文件
然后我们看一下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 4月 25 20:33 vmlinux*
-rw-rw-r-- 1 linux linux 3.1M 4月 25 20:33 piggy.gzip.o
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分析,根据 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
查看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 输出文件
/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 解析
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)
kallsyms解析
kallsyms系统编译过程
查看 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 文件
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系统编译过程