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

android4.3 解压、打包boot.img新脚本

2014-05-09 18:21 330 查看
解压boot.img需要用到三个文件:mkbootfs、mkbootimg、split_bootimg.pl。前两个是编译完系统后生成的可执行程序,存在于out/host/linux-x86/bin下面。split_bootimg.pl是一个Perl写的脚本。

现在编译一个android4.3的系统,因为要改boot.img中的某个小东西又不想去重新编译boot,就想通过解压boot.img的方式来进行改动。在网上下载了一个split_bootimg.pl(分割boot的工具)文件,然后把系统中编译出来的mkbootfs(解压ramdisk的工具)和mkbootimg(重新打包签名boot.img的工具)拿出来,准备解压。

执行步骤:1. split_bootimg.pl boot.img (解压boot.img,得到boot.img-kernel,boot.img-ramdisk.gz)

2. mkdir ramdisk (解压boot.img.ramdisk.gz)

cd ramdisk

gzip -dc ../boot.img-ramdisk.gz | cpio -i

cd ..

一般我们需要改动的东西都在这个ramdisk当中,比如开机log(开机动画显示前显示的图片)、init.rc(系统启动是开启的服务项)、default.prop(系统配置文件)等等。

3. ./mkbootfs ./ramdisk | gzip > ramdisk-new.gz (使用mkbootfs工具将修改后的ramdisk重新打包)

4. ./mkbootimg --cmdline 'console=115200,n8 androidboot.console=ttyHSL0 androidboot.hardware=qcom
user_debug=31 msm_rtb.filter=0x37' --base 0x00000000 --kernel boot.img-kernel --ramdisk ramdisk-new.gz -o bootnew.img

第四步是使用mkbootimg工具对整个boot.img重新打包,其中的--cmdline后面的引用号里的值是执行第一步就会打印出来的信息。 --base 0x0000000是基址,用于告诉手机从哪个地址开始,是准备给内存盘的入口,哪个地址是给kernel的入口。在boardconfig.h中存在地址偏移的define,一般是这个值,或者是0x00200000、0x20000000。

问题来了,整个过程执行下来也没有报任何错误,但是升级到机器里系统就启动不起来了。原因是在android4.x的boot里面加入了一个叫dt.img(device tree)的东西,少了这个东西boot是启动不起来的。原因也可以在split_bootimg.pl中看到,下面是split_bootimg.pl的代码:

#!/usr/bin/perl

######################################################################

#

# File : split_bootimg.pl

# Author(s) : William Enck <enck@cse.psu.edu>

# Description : Split appart an Android boot image created

# with mkbootimg. The format can be found in

# android-src/system/core/mkbootimg/bootimg.h

#

# Thanks to alansj on xda-developers.com for

# identifying the format in bootimg.h and

# describing initial instructions for splitting

# the boot.img file.

#

# Last Modified : Tue Dec 2 23:36:25 EST 2008

# By : William Enck <enck@cse.psu.edu>

#

# Copyright (c) 2008 William Enck

#

######################################################################

use strict;

use warnings;

# Turn on print flushing

$|++;

######################################################################

## Global Variables and Constants

my $SCRIPT = __FILE__;

my $IMAGE_FN = undef;

# Constants (from bootimg.h)

use constant BOOT_MAGIC => 'ANDROID!';

use constant BOOT_MAGIC_SIZE => 8;

use constant BOOT_NAME_SIZE => 16;

use constant BOOT_ARGS_SIZE => 512;

# Unsigned integers are 4 bytes

use constant UNSIGNED_SIZE => 4;

# Parsed Values

my $PAGE_SIZE = undef; #page 大小

my $KERNEL_SIZE = undef; #kernel 大小

my $RAMDISK_SIZE = undef; #ramdisk 大小

my $SECOND_SIZE = undef; #second 大小

######################################################################

## Main Code

&parse_cmdline();

&parse_header($IMAGE_FN);

=format (from bootimg.h)

** +-----------------+

** | boot header | 1 page

** +-----------------+

** | kernel | n pages

** +-----------------+

** | ramdisk | m pages

** +-----------------+

** | second stage | o pages

** +-----------------+

**

** n = (kernel_size + page_size - 1) / page_size

** m = (ramdisk_size + page_size - 1) / page_size

** o = (second_size + page_size - 1) / page_size

=cut

my $n = int(($KERNEL_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);

my $m = int(($RAMDISK_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);

my $o = int(($SECOND_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);

my $k_offset = $PAGE_SIZE; # kernel的偏移量

my $r_offset = $k_offset + ($n * $PAGE_SIZE); # ramdisk的偏移量

my $s_offset = $r_offset + ($m * $PAGE_SIZE); # second的偏移量

(my $base = $IMAGE_FN) =~ s/.*\/(.*)$/$1/;

my $k_file = $base . "-kernel"; #解压出kernel后添加的名称后缀

my $r_file = $base . "-ramdisk.gz"; # 解压出ramdisk.gz后添加的名称后缀

my $s_file = $base . "-second.gz"; #解压出second.gz后添加的名称后缀

#调用dump_file方法根据kernel的偏移量截取出kernel

# The kernel is always there

print "Writing $k_file ...";

&dump_file($IMAGE_FN, $k_file, $k_offset, $KERNEL_SIZE);

print " complete.\n";

#调用dump_file方法根据ramdisk的偏移量截取出ramdisk

# The ramdisk is always there

print "Writing $r_file ...";

&dump_file($IMAGE_FN, $r_file, $r_offset, $RAMDISK_SIZE);

print " complete.\n";

#调用dump_file方法根据second的偏移量截取出second

# The Second stage bootloader is optional

unless ($SECOND_SIZE == 0) {

print "Writing $s_file ...";

&dump_file($IMAGE_FN, $s_file, $s_offset, $SECOND_SIZE);

print " complete.\n";

}

######################################################################

## Supporting Subroutines

=header_format (from bootimg.h)

struct boot_img_hdr

{

unsigned char magic[BOOT_MAGIC_SIZE];

unsigned kernel_size; /* size in bytes */

unsigned kernel_addr; /* physical load addr */

unsigned ramdisk_size; /* size in bytes */

unsigned ramdisk_addr; /* physical load addr */

unsigned second_size; /* size in bytes */

unsigned second_addr; /* physical load addr */

unsigned tags_addr; /* physical addr for kernel tags */

unsigned page_size; /* flash page size we assume */

unsigned unused[2]; /* future expansion: should be 0 */

unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */

unsigned char cmdline[BOOT_ARGS_SIZE];

unsigned id[8]; /* timestamp / checksum / sha1 / etc */

};

=cut

sub parse_header {

my ($fn) = @_;

my $buf = undef;

open INF, $fn or die "Could not open $fn: $!\n";

binmode INF;

# Read the Magic

read(INF, $buf, BOOT_MAGIC_SIZE);

unless ($buf eq BOOT_MAGIC) {

die "Android Magic not found in $fn. Giving up.\n";

}

# Read kernel size and address (assume little-endian)

read(INF, $buf, UNSIGNED_SIZE * 2);

my ($k_size, $k_addr) = unpack("VV", $buf);

# Read ramdisk size and address (assume little-endian)

read(INF, $buf, UNSIGNED_SIZE * 2);

my ($r_size, $r_addr) = unpack("VV", $buf);

# Read second size and address (assume little-endian)

read(INF, $buf, UNSIGNED_SIZE * 2);

my ($s_size, $s_addr) = unpack("VV", $buf);

# Ignore tags_addr

read(INF, $buf, UNSIGNED_SIZE);

# get the page size (assume little-endian)

read(INF, $buf, UNSIGNED_SIZE);

my ($p_size) = unpack("V", $buf);

# Ignore unused

read(INF, $buf, UNSIGNED_SIZE * 2);

# Read the name (board name)

read(INF, $buf, BOOT_NAME_SIZE);

my $name = $buf;

# Read the command line

read(INF, $buf, BOOT_ARGS_SIZE);

my $cmdline = $buf;

# Ignore the id

read(INF, $buf, UNSIGNED_SIZE * 8);

# Close the file

close INF;

#打印一些基本信息,包括Command line,就是打包boot.img的时候需要的参数

# Print important values

printf "Page size: %d (0x%08x)\n", $p_size, $p_size;

printf "Kernel size: %d (0x%08x)\n", $k_size, $k_size;

printf "Ramdisk size: %d (0x%08x)\n", $r_size, $r_size;

printf "Second size: %d (0x%08x)\n", $s_size, $s_size;

printf "Board name: $name\n";

printf "Command line: $cmdline\n";

# Save the values

$PAGE_SIZE = $p_size;

$KERNEL_SIZE = $k_size;

$RAMDISK_SIZE = $r_size;

$SECOND_SIZE = $s_size;

}

sub dump_file {

my ($infn, $outfn, $offset, $size) = @_;

my $buf = undef;

open INF, $infn or die "Could not open $infn: $!\n";

open OUTF, ">$outfn" or die "Could not open $outfn: $!\n";

binmode INF;

binmode OUTF;

seek(INF, $offset, 0) or die "Could not seek in $infn: $!\n";

read(INF, $buf, $size) or die "Could not read $infn: $!\n";

print OUTF $buf or die "Could not write $outfn: $!\n";

close INF;

close OUTF;

}

######################################################################

## Configuration Subroutines

sub parse_cmdline {

unless ($#ARGV == 0) {

die "Usage: $SCRIPT boot.img\n";

}

$IMAGE_FN = $ARGV[0];

}

以上文件做了点简单的注释,很明显,里面的代码只实现了解压kernel、ramdisk、second这几个文件出来,没有解压出dt.img,所以这个文件过时了,在新系统上不能适应。

通过研究测试,终于把这个文件改好了,我就直接上改好的文件吧,懒得写注释了,文笔又不好,写的乱七八糟。

#!/usr/bin/perl

######################################################################

#

# File : split_bootimg.pl

# Author(s) : William Enck <enck@cse.psu.edu>

# Description : Split appart an Android boot image created

# with mkbootimg. The format can be found in

# android-src/system/core/mkbootimg/bootimg.h

#

# Thanks to alansj on xda-developers.com for

# identifying the format in bootimg.h and

# describing initial instructions for splitting

# the boot.img file.

#

# Last Modified : Tue Dec 2 23:36:25 EST 2008

# By : William Enck <enck@cse.psu.edu>

#

# Copyright (c) 2008 William Enck

#

######################################################################

use strict;

use warnings;

# Turn on print flushing

$|++;

######################################################################

## Global Variables and Constants

my $SCRIPT = __FILE__;

my $IMAGE_FN = undef;

# Constants (from bootimg.h)

use constant BOOT_MAGIC => 'ANDROID!';

use constant BOOT_MAGIC_SIZE => 8;

use constant BOOT_NAME_SIZE => 16;

use constant BOOT_ARGS_SIZE => 512;

# Unsigned integers are 4 bytes

use constant UNSIGNED_SIZE => 4;

# Parsed Values

my $PAGE_SIZE = undef;

my $KERNEL_SIZE = undef;

my $RAMDISK_SIZE = undef;

my $SECOND_SIZE = undef;

my $DT_SIZE = undef;

######################################################################

## Main Code

&parse_cmdline();

&parse_header($IMAGE_FN);

=format (from bootimg.h)

** +-----------------+

** | boot header | 1 page

** +-----------------+

** | kernel | n pages

** +-----------------+

** | ramdisk | m pages

** +-----------------+

** | second stage | o pages

** +-----------------+

** | device tree | p pages

** +-----------------+

**

** n = (kernel_size + page_size - 1) / page_size

** m = (ramdisk_size + page_size - 1) / page_size

** o = (second_size + page_size - 1) / page_size

** p = (dt_size + page_size - 1) / page_size

=cut

my $n = int(($KERNEL_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);

my $m = int(($RAMDISK_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);

my $o = int(($SECOND_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);

my $p = int(($DT_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);

my $k_offset = $PAGE_SIZE;

my $r_offset = $k_offset + ($n * $PAGE_SIZE);

my $s_offset = $r_offset + ($m * $PAGE_SIZE);

my $d_offset = $s_offset + ($o * $PAGE_SIZE);

(my $base = $IMAGE_FN) =~ s/.*\/(.*)$/$1/;

my $k_file = $base . "-kernel";

my $r_file = $base . "-ramdisk.gz";

my $s_file = $base . "-second.gz";

my $d_file = $base . "-dt.img";

# The kernel is always there

print "Writing $k_file ...";

&dump_file($IMAGE_FN, $k_file, $k_offset, $KERNEL_SIZE);

print " complete.\n";

# The ramdisk is always there

print "Writing $r_file ...";

&dump_file($IMAGE_FN, $r_file, $r_offset, $RAMDISK_SIZE);

print " complete.\n";

# The Second stage bootloader is optional

unless ($SECOND_SIZE == 0) {

print "Writing $s_file ...";

&dump_file($IMAGE_FN, $s_file, $s_offset, $SECOND_SIZE);

print " complete.\n";

}

# The dt is always there

print "Writing $d_file ...";

&dump_file($IMAGE_FN, $d_file, $d_offset, $DT_SIZE);

print " complete.\n";



######################################################################

## Supporting Subroutines

=header_format (from bootimg.h)

struct boot_img_hdr

{

unsigned char magic[BOOT_MAGIC_SIZE];

unsigned kernel_size; /* size in bytes */

unsigned kernel_addr; /* physical load addr */

unsigned ramdisk_size; /* size in bytes */

unsigned ramdisk_addr; /* physical load addr */

unsigned second_size; /* size in bytes */

unsigned second_addr; /* physical load addr */

unsigned tags_addr; /* physical addr for kernel tags */

unsigned page_size; /* flash page size we assume */

unsigned dt size; /* flash device tree size we assume */

unsigned unused; /* future expansion: should be 0 */

unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name *

unsigned char cmdline[BOOT_ARGS_SIZE];

unsigned id[8]; /* timestamp / checksum / sha1 / etc */

};

=cut

sub parse_header {

my ($fn) = @_;

my $buf = undef;

open INF, $fn or die "Could not open $fn: $!\n";

binmode INF;

# Read the Magic

read(INF, $buf, BOOT_MAGIC_SIZE);

unless ($buf eq BOOT_MAGIC) {

die "Android Magic not found in $fn. Giving up.\n";

}

# Read kernel size and address (assume little-endian)

read(INF, $buf, UNSIGNED_SIZE * 2);

my ($k_size, $k_addr) = unpack("VV", $buf);

# Read ramdisk size and address (assume little-endian)

read(INF, $buf, UNSIGNED_SIZE * 2);

my ($r_size, $r_addr) = unpack("VV", $buf);

# Read second size and address (assume little-endian)

read(INF, $buf, UNSIGNED_SIZE * 2);

my ($s_size, $s_addr) = unpack("VV", $buf);



# Ignore tags_addr

read(INF, $buf, UNSIGNED_SIZE);

# get the page size (assume little-endian)

read(INF, $buf, UNSIGNED_SIZE);

my ($p_size) = unpack("V", $buf);

# Read device tree size (assume little-endian)

read(INF, $buf, UNSIGNED_SIZE);

my ($d_size) = unpack("V", $buf);



# Ignore unused

read(INF, $buf, UNSIGNED_SIZE);

# Read the name (board name)

read(INF, $buf, BOOT_NAME_SIZE);

my $name = $buf;

# Read the command line

read(INF, $buf, BOOT_ARGS_SIZE);

my $cmdline = $buf;

# Ignore the id

read(INF, $buf, UNSIGNED_SIZE * 8);

# Close the file

close INF;

# Print important values

printf "Page size: %d (0x%08x)\n", $p_size, $p_size;

printf "Kernel size: %d (0x%08x)\n", $k_size, $k_size;

printf "Ramdisk size: %d (0x%08x)\n", $r_size, $r_size;

printf "Second size: %d (0x%08x)\n", $s_size, $s_size;

printf "DT size: %d (0x%08x)\n", $d_size, $d_size;

printf "Board name: $name\n";

printf "Command line: $cmdline\n";

open(FD,">/tmp/command");

my $position = index($cmdline,"\0");

printf "==================== $position \n";

my $subs = substr($cmdline, 0, $position);

printf "==================== $subs \n";

$subs = $subs."\n";

print FD "$subs";

close FD;

# Save the values

$PAGE_SIZE = $p_size;

$KERNEL_SIZE = $k_size;

$RAMDISK_SIZE = $r_size;

$SECOND_SIZE = $s_size;

$DT_SIZE = $d_size;

}

sub dump_file {

my ($infn, $outfn, $offset, $size) = @_;

my $buf = undef;

open INF, $infn or die "Could not open $infn: $!\n";

open OUTF, ">$outfn" or die "Could not open $outfn: $!\n";

binmode INF;

binmode OUTF;

seek(INF, $offset, 0) or die "Could not seek in $infn: $!\n";

read(INF, $buf, $size) or die "Could not read $infn: $!\n";

print OUTF $buf or die "Could not write $outfn: $!\n";

close INF;

close OUTF;

}

######################################################################

## Configuration Subroutines

sub parse_cmdline {

unless ($#ARGV == 0) {

die "Usage: $SCRIPT boot.img\n";

}

$IMAGE_FN = $ARGV[0];

}

对比一下就会发现,下面的文件中加上了dt.img的相关信息,解压打包后的boot.img升级OK,验证通过。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: