您的位置:首页 > 移动开发 > Android开发

[Android]构建boot.img:root目录与ramdisk.img的生成

2014-04-23 13:43 441 查看
以TCC88XX为例,当在Android顶层源码目录使用make编译完成后,会生成这样一个目录:

out/target/product/tcc8800,该目录内部有我们需要的boot.img和system.img,boot.mg

使用kernel和out/target/product/tcc8800/root目录打包而成(广义的ramdisk),也就是说,

boot.img是由kernel和ramdisk.img生成得到,在本文中主要分析root目录和ramdisk.img的生成,

在Android编译框架中,把许多固定的、反复用到的目录路径定义为宏变量,而上述生成的目录

out/target/product/tcc8800的宏即为:PRODUCT_OUT

out/target/product/tcc8800/system的宏即为:TARGET_OUT

而out/target/product/tcc8800/root的宏即为:TARGET_ROOT_OUT,

out/target/product/tcc8800/root主要是由system/core/rootdir目录拷贝得到的,

为此我分析了system/core/rootdir目录中的Android.mk文件,具体情况是这样的:

copy_from := etc/dbus.confetc/hosts

copy_from += etc/vold.fstab

以上内容将需要拷贝的文件添加到copy_from变量中,以便后续处理。

拷贝到那里呢? 在看看copy_to的定义:

copy_to := $(addprefix $(TARGET_OUT)/,$(coby_from))

该语句即为copy_from中每个字符串片段添加一个TARGET_OUT前缀(即system),这样copy_to的

内容就很明了:

copy_to :=out/target/product/tcc8800/system/etc/dbus.conf ...之类,在此略掉。

之后,给copy_from添加路径前缀:

copy_from := $(addprefix $(LOCAL_PATH)/, $(copy_from)

之所以要添加前缀的原因是接下来马上要设置的拷贝语句:

1
$(copy_to)
 : $(TARGET_OUT)/% : $(LOCAL_PATH)/% | $(ACP)
2
3
$(transform-prebuilt-to-target)
上述语句会让Android在构建img前,自动完成拷贝工作,其中使用到符号%进行匹配,这也是为什么要

给copy_from添加前缀的原因。

随后,脚本将copy_to变量添加进 ALL_PREBUILT全局宏中:

ALL_PREBUILT += $(copy_to)

最后,在build/core/Makefile中看到copy_to的内容被提取到了另外一个全局宏 ,具体如下:

1
#build/core/Makefile
2
3
INTERNAL_SYSTEMIMAGE_FILES
 := $(filter $(TARGET_OUT)/%,$(ALL_PREBUILT) ......
由于上述4行内容设计到system.img的生成,在此不深究。

看来system/core/rootdir中的部分内容是拷贝到了out/target/product/tcc8800/system中的,并不是

完完全全拷贝到out/target/product/tcc8800/root目录中去的。

我们回头继续查看system/core/rootdir/Android.mk文件,该文件中剩下的内容才是与root密切相关的。

file := $(TARGET_ROOT_OUT)/init.rc

然后也是经典的拷贝设置:

1
$(
file
)
 : $(LOCAL_PATH)/% | $(ACP)
2
3
$(transform-prebuilt-to-target)
接下来的脚本的内容是为生成boot.img而写的。

1
ALL_PREBUILT
 +=$(
file
)
2
3
$(INSTALLED_RAMDISK_TARGET):$(
file
)
看来原理也和上述system的拷贝相同,在build/core/Makefile中是由INTERNAL_RAMDISK_FILE提取的,

具体如下:INTERNAL_RAMDISK_FILES := $(filter $(TARGET_ROOT_OUT)/%, $(ALL_PREBUILT)
...

随后有一段很关键的句子直接道破了ramdisk.img的生成:

1
INSTALLED_RAMDISK_TARGET=$(BUILT_RAMDISK_TARGET)
2
3
$(INSTALLED_RAMDISK_TARGET):$(MKBOOTFS
 $(INTERNAL_RAMDISK_FILES | $(MINIGZIP)
4
5
$(hide)
 $(MKBOOTFS) $(TARGET_ROOT_OUT) | $(MINIGZIP) > $@
如此多的宏,让我们一一列出它们的值:

BUILT_RAMDISK_TARGET = $(PRODUCT_OUT/ramdisk.img 这是我们的目标

INSTALLED_RAMDISK_TARGET = BUILT_RAMDISK_TARGET 目标伪装了一下。

MKBOOTFS = mkbootfs 就是位于out/host/linux-x86/bin目录下的mkbootfs,这东西自然也有后话。

INTERNAL_RAMDISK_FILES = 所有TARGET_ROOT_OUT中的文件

由此可以看出root目录先被打包生成了ramdisk.img,然后才合并进boot.img的。

上文已经对boot.img其中组成部分之一ramdisk.img做了分析,boot.img另外一个重要的组成部分就是kernel了,

这里所说的kernel,可以只理解为位于out/target/product/tcc8800/中的kernel文件,本文主要分析kernel的拷贝

过程以及如何被打包到boot.img中。经过分析得知位于out/target/product/tcc8800/中的kernel文件其实就是内核

编译后的Image文件,位于kernel/arch/arm/boot目录下,线索就是这个Image文件,经过搜索发现一处定义:

LOCAL_KERNEL := kernel/arch/arm/boot/Image

该定义位于devices/telechips/tcc88xx-common/BoardConfigCommon.mk中,紧接着,在同目录的Android.mk中

有以下一段定义:

1
PRODUCT_COPY_FILES
 += \
2
3
$(LOCAL_KERNEL):kernel
意在将Image文件拷贝且重命名为kernel,随后的拷贝设置是在build/core/Makefile中完成的,在此略掉。

那么,拷贝完成后,kernel文件如何被打包到boot.img中呢?同样在build/core/Makefile中有以下一段内容:

INTERNAL_BOOTIMAGE_ARGS := ... --kernel $(INSTALLED_KERNEL_TARGET)

现在的问题就是查看 INSTALLED_KERNEL_TARGET的定义,该宏位于build/target/board/Android.mk中:

INSTALLED_KERNEL_TARGET := $(PRODUCT_OUT)/kernel

内容很明显了,至此,内核Image算是到位了。

另外INSTALL_KERNEL_TARGET定义在build/target/board/Android.mk中有点怪怪的,

build/target/board/Android.mk在main.mk中通过subdir_makefiles抽取得到,并包含进main.mk中

在前两篇同一系列的文章中都提到了以下一段语句:

view
source

print?

1
#build/core/Makefile
2
3
INTERNAL_BOOTIMAGE_ARGS
 := \
4
5
--kernel
 $(INSTALLED_KERNEL_TARGET) \
6
7
--ramdisk
 $(INSTALLED_RAMDISK_TARGET)
显然,boot.img中包含了Image和ramdisk.img文件,但boot.img中的内容远不只这么多,本文将介绍

boot.img中的其它参数,boot.img的生成以及最终boot.img的组成格式.

INTERNAL_BOOTIMAGE_ARGS还包含以下内容:

1.附加的内核命令行(cmdline): BOARD_KERNEL_CMDLINE

同样在build/core/Makefile中,有以下一段内容(strip起到去除空格的作用):

1
BOARD_KERNEL_CMDLINE
 := $(strip $(BOARD_KERNEL_CMDLINE)
2
3
ifdef
 BOARD_KERNEL_CMDLINE
4
5
INTERNAL_BOOTIMAGE_ARGS
 += --cmdline
"$(BOARD_KERNEL_CMDLINE)"
6
7
#endif
而BOARD_KERNEL_CMDLINE则在文件device/telechips/tcc88xx-common/BoardConfigCommon.mk中定义:

1
BOARD_KERNEL_CMDLINE
 := console=ttyTCC, 115200n8
2.内核加载的基地址,BOARD_KERNEL_BASE

同样在build/core/Makefile中,有以下一段内容:

1
BOARD_KERNEL_BASE
 := $(strip $(BOARD_KERNEL_BASE))
2
3
ifdef
 BOARD_KERNEL_BASE
4
5
INTERNAL_BOOTIMAGE_ARGS
 += --base $(BOARD_KERNEL_BASE)
6
7
endif
而BOARD_KERNEL_BASE也在device/telechips/tcc88xx-common/BoardConfigCommon.mk中定义。

1
BOARD_KERNEL_BASE
 := 0x40000000
3.映像的页面大小:BOARD_KERNEL_PAGESIZE

同样在build/core/Makefile中,有以下一段内容:

1
BOARD_KERNEL_PAGESIZE:=
 $(strip $(BOARD_KERNEL_PAGESIZE))
2
3
ifdef
 BOARD_KERNEL_PAGESIZE
4
5
INTERNAL_BOOTIMAGE_ARGS
 += --pagesize $(BOARD_KERNEL_PAGESIZE)
6
7
endif
而BOARD_KERNEL_PAGESIZE 却在device/telechips/tcc8800/BoardConfig.mk中定义:

1
BOARD_KERNEL_PAGESIZE
 := 8192
剩下的内容就是生成boot.img的关键语句,在 build/core/Makefile中,内容如下:

1
INSTALLED_BOOTIMAGE_TARGET
 := $(PRODUCT_OUT)/boot.img
2
3
$(INTALLED_BOOTIMAGE_TARGET)
 : $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILE
4
5
  
$(hide)
 $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS)  --output $@
到此,我们可以知道 INTERNAL_BOOTIMAGE_ARGS的内容是:

1
--kernel
out/target/product/tcc8800/kernel
2
--ramdisk  
 out/target/product/tcc8800/ramdisk.img
3
--cmdline  
 console=ttyTCC,115200n8
4
--base
 0x40000000  --pagesize 8192
而预知boot.img的格式,必须查看MKBOOTIMG这个程序,其实就是out/host/linux-x86/bin/mkbootimg中的mkbootimg程序。

mkbootimg程序由system/core/mkbootimg工程生成得到,为此我们来看看其中的mkbootimg.c文件,其中有这样一段:

01
//信息头部分
02
if
(write(fd,&hdr,
sizeof
(hdr))
 !=
sizeof
(hdr))
goto
fail;
03
if
(write_padding(fd,pagesize,
sizeof
(hdr)))
goto
fail;
04
05
//内核部分
06
if
(write(fd,&kernel_data,
 hdr.kernel_size)!= hdr.kernel_size)
07
goto
fail;
08
if
(write_padding(fd,pagesize,hdr.kernel_size))
goto
fail;
09
10
//文件系统部分
11
if
(write(fd,&ramdisk_data,hdr.ramdisk_size)!=
 hdr.ramdisk_size)
12
goto
fail;
13
if
(wirte_padding(fd,pagesize,hdr.ramdisk_size))
goto
fail;
可见boot.img是由文件头信息,内核数据以及文件系统数据组成,它们之间非页面对齐部分用0填充(可以

查看write_padding的代码),文件头信息的具体结构可以在system/core/mkbootimg/bootimg.h中看到:

01
struct
 boot_img_hdr
02
03
{
04
05
unsigned
 char magic[BOOT_MAGIC_SIZE];
06
07
unsigned 
 kernel_size;
08
09
unsigned 
 kernel_addr;
10
11
unsigned 
 ramdisk_size;
12
13
unsigned 
 ramdisk_addr;
14
15
unsigned 
 second_size;
16
17
unsigned 
 second_addr;
18
19
unsigned 
 tags_addr;
20
21
unsigned 
 page_size;
22
23
unsigned 
 unused[2];
24
25
unsigned 
 char  name[BOOT_NAME_SIZE]
26
27
unsigned 
 char cmdline[BOOT_ARGS_SIZE]
28
29
unsigned
id
[8];
 //存放时间戳,校验和,SHA加密等内容
30
31
}
其它成员也很明了,由此可知boot.img的大致组成结构了。

1
#build/core/Makefile
2
3
INTERNAL_BOOTIMAGE_ARGS
 := \
4
5
--kernel
 $(INSTALLED_KERNEL_TARGET) \
6
7
--ramdisk
 $(INSTALLED_RAMDISK_TARGET)
显然,boot.img中包含了Image和ramdisk.img文件,但boot.img中的内容远不只这么多,本文将介绍

boot.img中的其它参数,boot.img的生成以及最终boot.img的组成格式.

INTERNAL_BOOTIMAGE_ARGS还包含以下内容:

1.附加的内核命令行(cmdline): BOARD_KERNEL_CMDLINE

同样在build/core/Makefile中,有以下一段内容(strip起到去除空格的作用):

1
BOARD_KERNEL_CMDLINE
 := $(strip $(BOARD_KERNEL_CMDLINE)
2
3
ifdef
 BOARD_KERNEL_CMDLINE
4
5
INTERNAL_BOOTIMAGE_ARGS
 += --cmdline
"$(BOARD_KERNEL_CMDLINE)"
6
7
#endif
而BOARD_KERNEL_CMDLINE则在文件device/telechips/tcc88xx-common/BoardConfigCommon.mk中定义:

1
BOARD_KERNEL_CMDLINE
 := console=ttyTCC, 115200n8
2.内核加载的基地址,BOARD_KERNEL_BASE

同样在build/core/Makefile中,有以下一段内容:

1
BOARD_KERNEL_BASE
 := $(strip $(BOARD_KERNEL_BASE))
2
3
ifdef
 BOARD_KERNEL_BASE
4
5
INTERNAL_BOOTIMAGE_ARGS
 += --base $(BOARD_KERNEL_BASE)
6
7
endif
而BOARD_KERNEL_BASE也在device/telechips/tcc88xx-common/BoardConfigCommon.mk中定义。

1
BOARD_KERNEL_BASE
 := 0x40000000
3.映像的页面大小:BOARD_KERNEL_PAGESIZE

同样在build/core/Makefile中,有以下一段内容:

1
BOARD_KERNEL_PAGESIZE:=
 $(strip $(BOARD_KERNEL_PAGESIZE))
2
3
ifdef
 BOARD_KERNEL_PAGESIZE
4
5
INTERNAL_BOOTIMAGE_ARGS
 += --pagesize $(BOARD_KERNEL_PAGESIZE)
6
7
endif
而BOARD_KERNEL_PAGESIZE 却在device/telechips/tcc8800/BoardConfig.mk中定义:

1
BOARD_KERNEL_PAGESIZE
 := 8192
剩下的内容就是生成boot.img的关键语句,在 build/core/Makefile中,内容如下:

1
INSTALLED_BOOTIMAGE_TARGET
 := $(PRODUCT_OUT)/boot.img
2
3
$(INTALLED_BOOTIMAGE_TARGET)
 : $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILE
4
5
  
$(hide)
 $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS)  --output $@
到此,我们可以知道 INTERNAL_BOOTIMAGE_ARGS的内容是:

1
--kernel
out/target/product/tcc8800/kernel
2
--ramdisk  
 out/target/product/tcc8800/ramdisk.img
3
--cmdline  
 console=ttyTCC,115200n8
4
--base
 0x40000000  --pagesize 8192
而预知boot.img的格式,必须查看MKBOOTIMG这个程序,其实就是out/host/linux-x86/bin/mkbootimg中的mkbootimg程序。

mkbootimg程序由system/core/mkbootimg工程生成得到,为此我们来看看其中的mkbootimg.c文件,其中有这样一段:

01
//信息头部分
02
if
(write(fd,&hdr,
sizeof
(hdr))
 !=
sizeof
(hdr))
goto
fail;
03
if
(write_padding(fd,pagesize,
sizeof
(hdr)))
goto
fail;
04
05
//内核部分
06
if
(write(fd,&kernel_data,
 hdr.kernel_size)!= hdr.kernel_size)
07
goto
fail;
08
if
(write_padding(fd,pagesize,hdr.kernel_size))
goto
fail;
09
10
//文件系统部分
11
if
(write(fd,&ramdisk_data,hdr.ramdisk_size)!=
 hdr.ramdisk_size)
12
goto
fail;
13
if
(wirte_padding(fd,pagesize,hdr.ramdisk_size))
goto
fail;
可见boot.img是由文件头信息,内核数据以及文件系统数据组成,它们之间非页面对齐部分用0填充(可以

查看write_padding的代码),文件头信息的具体结构可以在system/core/mkbootimg/bootimg.h中看到:

01
struct
 boot_img_hdr
02
03
{
04
05
unsigned
 char magic[BOOT_MAGIC_SIZE];
06
07
unsigned 
 kernel_size;
08
09
unsigned 
 kernel_addr;
10
11
unsigned 
 ramdisk_size;
12
13
unsigned 
 ramdisk_addr;
14
15
unsigned 
 second_size;
16
17
unsigned 
 second_addr;
18
19
unsigned 
 tags_addr;
20
21
unsigned 
 page_size;
22
23
unsigned 
 unused[2];
24
25
unsigned 
 char  name[BOOT_NAME_SIZE]
26
27
unsigned 
 char cmdline[BOOT_ARGS_SIZE]
28
29
unsigned
id
[8];
 //存放时间戳,校验和,SHA加密等内容
30
31
}
其它成员也很明了,由此可知boot.img的大致组成结构了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: