make zImage和make uImage的区别和mkimage工具的使用

时间:2021-04-08 16:36:51

刚刚接触到linux内核编译过程的时候,相信不少人跟我一样,都是按照手册或者网上的教程一步一步的执行,对于其中的很多原理和过程都是一知半解,这其中有一个很常见的问题,就是make image命令。

因为大家都知道,在编译linux内核时,往往会涉及到以下3个命令:

  • make Image
  • make zImage
  • make uImage

那么,这3个命令分别是做什么的?所生成的对象又有什么区别呢?

在网上查阅了很多的资料和博客,发现绝大部分都是千篇一律的说法,在对比上述3个概念的时候,顺带还对vmlinux、vmlinuz、initrd等诸多概念胡乱进行一通比较,概念模糊,根本没把问题解释清楚,很明显是互相摘抄、囫囵吞枣的。算了,还是我自己来捋一捋好了。

1、首先来解释一下前面2个命令的区别。Image为普通的内核映像文件,而zImage为压缩过的内核映像文件(其中的z字母就是压缩的意思)。一般情况下,编译出来的Image大约为4M,而zImage不到2M。

2、然后来解释一下第3个命令uImage。它是uboot专用的映像文件,它是在zImage之前加上一个长度为64字节的“头”,说明这个内核的版本、加载位置、生成时间、大小等信息;其0x40之后与zImage没区别。换句话说,如果直接从uImage的0x40位置开始执行,那么zImage和uImage没有任何区别。

为什么要用uboot 的mkimage工具处理内核映像zImage呢?

因为uboot在用bootm命令引导内核的时候,bootm需要读取一个64字节的文件头,来获取这个内核映象所针对的CPU体系结构、OS、加载到内存中的位置、在内存中入口点的位置以及映象名等等信息。这样bootm才能为OS设置好启动环境,并跳入内核映象的入口点。而mkimage就是添加这个文件头的专用工具。具体的实现请看uboot中bootm的源码和mkimage的源码。

3、另外,要额外说明的是如何来生成uImage。资料上显示,linux2.4之前版本的内核不支持直接生成uImage镜像文件,需要手动来生成。而对于linux2.6之后版本的内核,尽管加入了很多对嵌入式系统的支持,但是uImage的生成也需要单独设置。

那究竟要如何生成uImage呢?这就要从uboot处来想办法了。

uboot源代码的tools/目录下有一个mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。

leon@Ubuntu:~/u-boot-2010.03/tools$ ls
bddb             fdt_ro.o        img2srec.o         mkimage.c
bin2header.c     fdt_rw.o        imls               mkimage.h
bmp_logo         fdt_strerror.o  imximage.c         mkimage.o
bmp_logo.c       fdt_wip.o       imximage.h         mpc86x_clk.c
bmp_logo.o       fit_image.c     imximage.o         ncb.c
crc32.o          fit_image.o     inca-swap-bytes.c  netconsole
default_image.c  gdb             jtagconsole        os_support.c
default_image.o  gen_eth_addr    kwbimage.c         os_support.h
easylogo         gen_eth_addr.c  kwbimage.h         os_support.o
env              gen_eth_addr.o  kwbimage.o         scripts
envcrc           getline.c       logos              setlocalversion
envcrc.c         getline.h       Makefile           sha1.o
envcrc.o         image.o         md5.o              ubsha1.c
env_embedded.o   img2brec.sh     mingw_support.c    updater
fdt_host.h       img2srec        mingw_support.h
fdt.o            img2srec.c      mkimage

leon@Ubuntu:~/u-boot-2010.03/tools$ ll mkimage
-rwxrwxr-x 1 leon leon 62384  126 22:30 mkimage*

leon@Ubuntu:~/u-boot-2010.03/tools$ file mkimage
mkimage: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x366213ad60c7c7fc6793def77d2df59d2ab85d5d, stripped

下面介绍下mkimage这个工具的用法:

参数说明:

  • -A:指定 CPU 的体系结构,可用值有:alpha、arm 、x86、ia64、mips、mips64、 ppc 、s390、sh、sparc 、sparc64、m68k 等;
  • -O:指定操作系统类型,可用值有:openbsd、netbsd、freebsd、4_4bsd、linux、 svr4、esix、solaris、irix、sco、dell、ncr、lynxos、vxworks、psos、qnx、u-boot、rtems、artos;
  • -T:指定映象类型,可用值有:standalone、kernel、ramdisk、multi、firmware、script、filesystem;
  • -C:指定映象压缩方式,可用值有:
    :none 不压缩(一般使用这个,因为 zImage 是已经被 bzip2 压缩过的自解压内核);
    :zip 用 gzip 的压缩方式;
    :bzip2 用 bzip2 的压缩方式;
  • -a:指定映象在内存中的加载地址,映象下载到内存中时,要按照用 mkimage 制作映象 时,这个参数所指定的地址值来下载;
  • -e:指定映象运行的入口点地址,这个地址就是-a 参数指定的值加上 0x40(因为前面有个 mkimage 添加的 0x40 个字节的头);
  • -n:指定映象名;
  • -d:指定制作映象的源文件;

以下是摘自tekkaman的制作内核映像的命令示例:

mkimage -n 'tekkaman' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage zImage.img

其中有几点要注意:

1、关于偏移量的问题(请千万注意这一点)。‘-e’命令之后的地址,必须比’-a’命令后的地址多0x40的偏移量。一旦没有偏移,则会导致系统出现引导失败的问题。而很多博客文章里,居然把这2个值写成一样的,都写成0x30008000。也不知道各位博主们自己到底有没有尝试过,害我一开始折腾了半天才发现这个问题,简直就是浪费我的时间。

2、执行该命令时,要注意一下权限的问题。我一开始时在虚拟机的共享目录下执行,一直提示失败:

leon@Ubuntu:/mnt/share/tmp/zImages$ mkimage -n 'tekkaman' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage zImage.img
mkimage: Can't map zImage.img: Invalid argument

后来跳转到~目录下执行,就一下子成功了。成功后的提示信息如下:

leon@Ubuntu:~$ mkimage -n 'tekkaman' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage zImage.img
Image Name:   leon_20170201
Created:      Wed Feb  1 14:50:47 2017
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    2266576 Bytes = 2213.45 kB = 2.16 MB
Load Address: 30008000
Entry Point:  30008040

将生成的image文件,拷贝到nfs目录下,按照uboot环境变量的预设名称改名,然后启动uboot,系统成功引导启动!

hit any key to stop auto_boot:  0 
DM9000: Found at 0x20000300, id: 0x90000a46 
DM9000: running in 16 bit mode
MAC: 08:08:11:18:12:27
Operating at 100M full duplex mode
Begin to Initiate NFS Process...
NFS Process Using dm9000 device
NFS: File transfer via NFS:
     Srver_Addr: 192.168.100.120;
     Local_Addr: 192.168.100.230;
     filename: '/opt/FriendlyARM/mini2440/rootfs/zImage.img'
     address: 0x30008000
     loading......
...........................................................................................................................................................................................................................................................................................................................................................................................................................................................
                                                                         done
Bytes transferred = 2266640 (229610 hex)
## Booting kernel from Legacy Image at 30008000 ...
   Image Name:   leon_20170201
   Created:      2017-02-01   6:50:47 UTC
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    2266576 Bytes =  2.2 MB
   Load Address: 30008000
   Entry Point:  30008040
   Verifying Checksum ... OK
   XIP Kernel Image ... OK
OK

Starting kernel ...
Uncompressing Linux................................................................................................................................................... done, booting the kernel.
Linux version 2.6.32.2-leon (leon@Ubuntu) (gcc version 4.4.3 (ctng-1.6.1) ) #1 Wed Feb 1 08:33:10 CST 2017
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177
CPU: VIVT data cache, VIVT instruction cache
...(此处略去12345678个字).....
NFS root ...Done

3、将mkimage工具所在目录加入环境变量。可以将mkimage这个工具拷贝到usr/bin等用户目录下,这样直接在命令行输入这个命令就可以运行,而无需每次都手工指定到uboot/tools这个目录下去,提高执行效率。当然,也可以按照我的另一篇博客(linux(ubuntu)编译linux内核提示”mkimage” command not found)中介绍的方案,直接在ubuntu系统中安装一下uboot的mkimage命令,效果更好。

4、关于linux2.6之后版本的系统,是否可以直接make uImage?这一点,虽然绝大部分的网文和博客中都说到要单独设置,但是却没有人明确指出要如何设置。我直接在ubuntu 12.04的系统(Linux Ubuntu 3.13.0-32-generic)中输入make uImage命令,确实能够运行,也确实在指定的目录下生成了uImage文件。

leon@Ubuntu:/opt/FriendlyARM/mini2440/linux-2.6.32.2$ make uImage
  CHK     include/linux/version.h
make[1]:include/asm-arm/mach-types.h”是最新的。
  CHK     include/linux/utsrelease.h
  SYMLINK include/asm -> include/asm-arm
  CALL    scripts/checksyscalls.sh
  CHK     include/linux/compile.h
  Kernel: arch/arm/boot/Image is ready
  Kernel: arch/arm/boot/zImage is ready
  UIMAGE  arch/arm/boot/uImage
Image Name:   Linux-2.6.32.2-leon
Created:      Wed Feb  1 15:48:37 2017
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    2266576 Bytes = 2213.45 kB = 2.16 MB
Load Address: 30008000
Entry Point:  30008000
  Image arch/arm/boot/uImage is ready

但是,遗憾的是,上面生成的镜像文件,导入到开发板上却无法执行!

hit any key to stop auto_boot:  0 
DM9000: Found at 0x20000300, id: 0x90000a46 
DM9000: running in 16 bit mode
MAC: 08:08:11:18:12:27
Operating at 100M full duplex mode
Begin to Initiate NFS Process...
NFS Process Using dm9000 device
NFS: File transfer via NFS:
     Srver_Addr: 192.168.100.120;
     Local_Addr: 192.168.100.230;
     filename: '/opt/FriendlyARM/mini2440/rootfs/zImage.img'
     address: 0x30008000
     loading......
..........................................T .................................................................................................................................................................................................................................................................................................................................................................................................................
 done
Bytes transferred = 2266640 (229610 hex)
## Booting kernel from Legacy Image at 30008000 ...
   Image Name:   Linux-2.6.32.2-leon
   Created:      2017-02-01   7:48:37 UTC
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    2266576 Bytes =  2.2 MB
   Load Address: 30008000
   Entry Point:  30008000
   Verifying Checksum ... OK
   XIP Kernel Image ... OK
OK

Starting kernel ...
(卡在这里不动了)

至于失败的原因,我觉得很有可能就是因为没有配置参数,导致上述第一点中的偏移量没有设置进去。这一点,从上面的Load Address和Entry Point的值就可以看出来。待后续我查找到如何配置make uImage命令的参数时,再进行验证!

不管如何,今天总算是成功搞清楚了make zImage和make uImage命令的区别,也成功编译出了uImage镜像文件,并下载到mini2440开发板上利用nfs引导成功,算是比较满意的收获吧!

搞定,收工!