转载于 https://book.2cto.com/201309/33428.html

组合的规则定义在平台的“顶层”Makefile中:

/arch/x86/Makefile:
boot := arch/x86/boot
...
KBUILD_IMAGE := $(boot)/bzImage
...
bzImage: vmlinux
...
    $(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE)

在将各个变量进行替换后,构建bzImage的命令展开为:

make -f scripts/Makefile.build obj=arch/x86/boot
    arch/x86/boot/bzImage

Makefile.build将包含在arch/x86/boot目录下的Makefile文件组成为最终的Makefile。构建目标arch/x86/boot/bzImage的规则在arch/x86/boot下的Makefile中:

/arch/x86/boot/Makefile:
$(obj)/bzImage: $(obj)/setup.bin $(obj)/vmlinux.bin \
                $(obj)/tools/build FORCE
    $(call if_changed,image)

我们来看看构建bzImage的命令cmd_image,其在arch/x86/boot/Makefile中定义:

/arch/x86/boot/Makefile:
cmd_image = $(obj)/tools/build $(obj)/setup.bin \
    $(obj)/vmlinux.bin > $@

根据cmd_image的定义,表面上就是执行程序build,并传递给程序build两个参数,分别是arch/x86/boot目录下的setup.bin和vmlinux.bin,同时将程序build的标准输出stdout重定向到规则的目标($@),即bzImage。那么程序build究竟做了什么呢?我们来看看它的源码:

/arch/x86/boot/tools/build.c:
1 /* This must be large enough to hold the entire setup */
2 u8 buf[SETUP_SECT_MAX*512];
3 ...
4 int main(int argc, char ** argv)
5 {
6     ...
7     void *kernel;
8     ...
9     file = fopen(argv[1], "r");
10    ...
11    c = fread(buf, 1, sizeof(buf), file);
12    ...
13    fd = open(argv[2], O_RDONLY);
14    ...
15    kernel = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
16    ...
17    if (fwrite(buf, 1, i, stdout) != i)
18    ...
19    if (fwrite(kernel, 1, sz, stdout) != sz)
20    ...
21 }

1)argv[1]对应的是setup.bin,所以第9行代码就是将文件setup.bin打开,第11行代码是将其内容读到数组buf中。由第1行的注释可见,数组buf就是用来存放setup.bin的。

2)argv[2]对应的是vmlinux.bin,所以第13行代码是将文件vmlinux.bin打开。因为vmlinux.bin尺寸较大,build并没有使用与setup.bin相同的方式读取vmlinux.bin,而是将vmlinux.bin映射到build的进程空间中,变量kernel指向了vmlinux.bin映射的基址,如代码第15行所示。也就是说,build通过内存映射的方式读取文件vmlinux.bin。

3)第17行代码是将读取到buf中的setup.bin写入到标准输出(stdout)。而根据cmd_image的定义,build程序已经将其标准输出重定向为bzImage,所以这里并不是将setup.bin显示到屏幕上,而是写入到文件bzImage中。

4)同理第19行代码是将vmlinux.bin写入到文件bzImage中。

可见,程序build就是将setup.bin和vmlinux.bin简单地连接为bzImage。

Logo

更多推荐