刚刚接触到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 1月 26 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引导成功,算是比较满意的收获吧!
搞定,收工!