groovy unpack/pack tasks can run, but still failed unit test

pull/6/head
cfig 9 years ago
parent 460c5b05eb
commit 896d6065f9

@ -32,3 +32,20 @@ task mkbootimg(type: Jar, dependsOn:['build']) {
attributes 'Main-Class': 'cfig.bootimg.mkbootimg'
}
}
task repack(type: Jar, dependsOn:['build']) {
from files(sourceSets.main.output.classesDir)
from configurations.runtime.asFileTree.files.collect { zipTree(it) }
baseName = 'repack'
manifest {
attributes 'Main-Class': 'cfig.bootimg.repack'
}
}
task pack_clear(type: JavaExec, dependsOn:[':pack_ramdisk_and_gz', 'build']) {
classpath = sourceSets.main.runtimeClasspath
main = "cfig.bootimg.repack_with_cmd"
args rootProject.outClearIMg, rootProject.rootWorkDir, rootProject.mkbootimgBin
}

@ -5,7 +5,7 @@ import groovy.transform.ToString
/**
* Created by yu at 09:57 on 2016-06-17
*/
@ToString(includeNames = true, includeFields = true)
@ToString(includeNames = true, includeFields = true, excludes = "toCommandLine, CArgs")
class CArgs {
public String kernel;
public String ramdisk;
@ -24,11 +24,56 @@ class CArgs {
public int tags_offset;
public boolean id;
CArgs() {
public CArgs() {
kernel = "kernel";
ramdisk = "ramdisk.img.gz";
second = "second";
output = "boot.img";
cfg = "bootimg.json";
}
public List<String> toCommandList() {
List<String> ret = new ArrayList<String>();
ret.add("--base");
ret.add("0x" + Integer.toHexString(base));
ret.add("--kernel");
ret.add(kernel);
ret.add("--kernel_offset");
ret.add("0x" + Integer.toHexString(kernel_offset));
if (null != ramdisk) {
ret.add("--ramdisk");
ret.add(ramdisk);
ret.add("--ramdisk_offset");
ret.add("0x" + Integer.toHexString(ramdisk_offset));
}
if (null != second) {
ret.add("--second");
ret.add(second);
ret.add("--second_offset");
ret.add("0x" + Integer.toHexString(second_offset));
}
if (null != board) {
ret.add("--board");
ret.add(board);
}
ret.add("--cmdline");
ret.add(cmdline);
if (null != os_version) {
ret.add("--os_version");
ret.add(os_version);
}
if (null != os_patch_level) {
ret.add("--os_patch_level");
ret.add(os_patch_level);
}
ret.add("--tags_offset");
ret.add("0x" + Integer.toHexString(tags_offset));
if (id) {
ret.add("--id");
}
ret.add("--output");
ret.add(output);
return ret;
}
}

@ -1,11 +1,12 @@
package cfig.bootimg
import groovy.json.JsonSlurper
import groovy.transform.ToString
/**
* Created by yu at 09:58 on 2016-06-17
*/
@ToString(includeNames=true, includeFields=true)
@ToString(includeNames=true, includeFields=true, includeSuper = true)
class CImgInfo extends CArgs {
public int kernel_len;
public int ramdisk_len;
@ -14,7 +15,43 @@ class CImgInfo extends CArgs {
public int ramdisk_pos;
public int second_pos;
public byte[] hash;
public String dump() {
return super.toString() + " ; " + toString();
static CImgInfo fromJson(String outFile, String workDir) {
CImgInfo aArg = new CImgInfo();
//preset info
aArg.kernel = workDir + File.separator + aArg.kernel;
aArg.ramdisk = workDir + File.separator + aArg.ramdisk;
aArg.second = workDir + File.separator + aArg.second;
aArg.cfg = workDir + File.separator + aArg.cfg;
aArg.output = outFile;
JsonSlurper jsonSlurper = new JsonSlurper()
Map result = jsonSlurper.parseText(new File(aArg.cfg).text);
//arg info
aArg.board = result.bootimg.args.board;
aArg.cmdline = result.bootimg.args.cmdline;
aArg.base = Integer.decode(result.bootimg.args.base);
aArg.kernel_offset = Integer.decode(result.bootimg.args.kernel_offset);
aArg.ramdisk_offset = Integer.decode(result.bootimg.args.ramdisk_offset);
aArg.second_offset = Integer.decode(result.bootimg.args.second_offset);
aArg.tags_offset = Integer.decode(result.bootimg.args.tags_offset);
aArg.id = true;
aArg.pagesize = result.bootimg.args.pagesize;
aArg.os_version = result.bootimg.args.os_version;
aArg.os_patch_level = result.bootimg.args.os_patch_level;
//image info
aArg.kernel_len = result.bootimg.img.kernel_len;
aArg.ramdisk_len = result.bootimg.img.ramdisk_len;
aArg.second_len = result.bootimg.img.second_len;
//adjust preset info
if (0 == aArg.ramdisk_len) {
aArg.ramdisk = null;
}
if (0 == aArg.second_len) {
aArg.second = null;
}
return aArg;
}
}

@ -0,0 +1,347 @@
package cfig.bootimg
import java.nio.ByteBuffer;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import java.nio.channels.FileChannel;
import java.nio.ByteOrder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.security.MessageDigest;
/**
* Created by yu at 10:52 on 2016-06-18
*/
class Packer {
CArgs parse_cmdline(String[] inArgs) {
OptionParser parser = new OptionParser();
parser.accepts("kernel", "path to the kernel").withRequiredArg();
parser.accepts("ramdisk", "path to the ramdisk").withRequiredArg();
parser.accepts("second", "path to the 2nd bootloader").withRequiredArg();
parser.accepts("cmdline", "extra arguments to be passed on the kernel command line").withRequiredArg();
parser.accepts("base", "base address").withRequiredArg();
parser.accepts("kernel_offset", "kernel offset").withRequiredArg();
parser.accepts("ramdisk_offset", "ramdisk offset").withRequiredArg();
parser.accepts("second_offset", "2nd bootloader offset").withRequiredArg();
parser.accepts("os_version", "operating system version").withRequiredArg();
parser.accepts("os_patch_level", "operating system patch level").withRequiredArg();
parser.accepts("tags_offset", "tags offset").withRequiredArg();
parser.accepts("board", "board name").withRequiredArg();
parser.accepts("pagesize", "page size").withRequiredArg();
parser.accepts("id", "print the image ID on standard output");
parser.accepts("output", "output file name").withRequiredArg();
OptionSet options = parser.parse(inArgs)
CArgs ret = new CArgs();
ret.kernel = options.valueOf("kernel")
ret.output = options.valueOf("output")
ret.ramdisk = options.valueOf("ramdisk")
ret.second = options.valueOf("second")
if (options.has("board")) {
ret.board = options.valueOf("board")
} else {
ret.board = ""
}
ret.id = options.has("id")
if (options.has("base")) {
ret.base = Integer.decode(options.valueOf("base"))
} else {
ret.base = 0x10000000;
}
if (options.has("kernel_offset")) {
ret.kernel_offset = Integer.decode(options.valueOf("kernel_offset"))
} else {
ret.kernel_offset = 0x00008000;
}
if (options.has("ramdisk_offset")) {
ret.ramdisk_offset = Integer.decode(options.valueOf("ramdisk_offset"))
} else {
ret.ramdisk_offset = 0x01000000
}
ret.os_version = options.valueOf("os_version")
ret.os_patch_level = options.valueOf("os_patch_level")
if (options.has("second_offset")) {
ret.second_offset = Integer.decode(options.valueOf("second_offset"))
} else {
ret.second_offset = 0x00f00000
}
if (options.has("tags_offset")) {
ret.tags_offset = Integer.decode(options.valueOf("tags_offset"))
} else {
ret.tags_offset = 0x00000100
}
if (options.has("pagesize")) {
ret.pagesize = Integer.decode(options.valueOf("pagesize"))
} else {
ret.pagesize = 2048
}
if (options.has("cmdline")) {
ret.cmdline = options.valueOf("cmdline")
} else {
ret.cmdline = ""
}
if (ret.cmdline.length() > 1536) {
println("cmdline length must <= 1536, current is " + ret.cmdline.length());
printUsage(parser);
}
if (null == ret.kernel) {
println("kernel must not be empty");
printUsage(parser);
}
if (null == ret.output) {
println("output file must not be empty");
printUsage(parser);
}
if (ret.board.length() > 16) {
println("board name length must <= 16")
printUsage(parser);
}
return ret;
}
byte[] write_header(CArgs inArgs) {
ByteBuffer bf = ByteBuffer.allocate(1024 * 32);
bf.order(ByteOrder.LITTLE_ENDIAN);
//header start
bf.put("ANDROID!".getBytes())
bf.putInt((int) new File(inArgs.kernel).length());
bf.putInt(inArgs.base + inArgs.kernel_offset)
if (null == inArgs.ramdisk) {
bf.putInt(0)
} else {
bf.putInt((int) new File(inArgs.ramdisk).length());
}
bf.putInt(inArgs.base + inArgs.ramdisk_offset)
if (null == inArgs.second) {
bf.putInt(0)
} else {
bf.putInt((int) new File(inArgs.second).length());
}
bf.putInt(inArgs.base + inArgs.second_offset)
bf.putInt(inArgs.base + inArgs.tags_offset)
bf.putInt(inArgs.pagesize)
bf.putInt(0);
bf.putInt((parse_os_version(inArgs.os_version) << 11) | parse_os_patch_level(inArgs.os_patch_level))
bf.put(inArgs.board.getBytes())
bf.put(new byte[16 - inArgs.board.length()])
bf.put(inArgs.cmdline.substring(0, Math.min(512, inArgs.cmdline.length())).getBytes())
bf.put(new byte[512 - Math.min(512, inArgs.cmdline.length())])
byte[] img_id = hashFile(inArgs.kernel, inArgs.ramdisk, inArgs.second)
bf.put(img_id)
bf.put(new byte[32 - img_id.length])
if (inArgs.cmdline.length() > 512) {
bf.put(inArgs.cmdline.substring(512).getBytes())
bf.put(new byte[1024 + 512 - inArgs.cmdline.length()])
} else {
bf.put(new byte[1024])
}
//padding
pad_file(bf, inArgs.pagesize)
//write
FileOutputStream fos = new FileOutputStream(inArgs.output, false);
fos.write(bf.array(), 0, bf.position())
fos.close();
return img_id;
}
void printUsage(OptionParser p) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write("Usage: mkbootimg <option>\n".getBytes());
p.printHelpOn(out);
System.out.println(out.toString());
out.close();
System.exit(1);
}
void write_padded_file(ByteBuffer inBF, String srcFile, int padding) {
if (null == srcFile) {
return;
}
InputStream is = new FileInputStream(new File(srcFile))
int byteRead;
byte[] dataRead = new byte[128]
while (true) {
byteRead = is.read(dataRead)
if (-1 == byteRead) {
break;
}
inBF.put(dataRead, 0, byteRead);
}
is.close();
pad_file(inBF, padding)
}
void pad_file(ByteBuffer inBF, int padding) {
int pad = (padding - (inBF.position() & (padding - 1))) & (padding - 1);
inBF.put(new byte[pad]);
}
void write_data(CArgs inArgs) {
ByteBuffer bf = ByteBuffer.allocate(1024 * 1024 * 64);
bf.order(ByteOrder.LITTLE_ENDIAN);
write_padded_file(bf, inArgs.kernel, inArgs.pagesize)
write_padded_file(bf, inArgs.ramdisk, inArgs.pagesize)
write_padded_file(bf, inArgs.second, inArgs.pagesize)
//write
FileOutputStream fos = new FileOutputStream(inArgs.output, true);
fos.write(bf.array(), 0, bf.position())
fos.close();
}
int parse_os_patch_level(String x) {
if (null == x) {
return 0;
}
int ret = 0
Pattern pattern = Pattern.compile("^(\\d{4})-(\\d{2})-(\\d{2})")
Matcher matcher = pattern.matcher(x)
if (matcher.find()) {
int y = Integer.decode(matcher.group(1)) - 2000
int m = Integer.decode(matcher.group(2))
// 7 bits allocated for the year, 4 bits for the month
assert y >= 0 && y < 128
assert m > 0 && m <= 12
ret = (y << 4) | m
} else {
throw new IllegalArgumentException("invalid os_patch_level")
}
return ret;
}
int parse_os_version(String x) {
int ret = 0;
if (null != x) {
Pattern pattern = Pattern.compile("^(\\d{1,3})(?:\\.(\\d{1,3})(?:\\.(\\d{1,3}))?)?");
Matcher m = pattern.matcher(x)
if (m.find()) {
int a = Integer.decode(m.group(1))
int b = 0;
int c = 0;
if (m.groupCount() >= 2) {
b = Integer.decode(m.group(2))
}
if (m.groupCount() == 3) {
c = Integer.decode(m.group(3))
}
assert a < 128
assert b < 128
assert c < 128
ret = ((a << 14) | (b << 7) | c)
} else {
throw new IllegalArgumentException("invalid os_version")
}
}
return ret;
}
void test() {
ByteBuffer b2 = ByteBuffer.allocate(1024);
b2.order(ByteOrder.LITTLE_ENDIAN);
b2.putInt(Integer.MAX_VALUE); //4 bytes
println("max: " + Integer.MAX_VALUE)
println("min: " + Integer.MIN_VALUE)
b2.putInt(0x11111111)
b2.putInt(Integer.MIN_VALUE);
b2.putInt(0x11111111)
b2.put("welcome".getBytes())
b2.put(new byte[5]);
b2.putInt(0x11111111)
b2.putInt(0);
b2.putInt(0x11111111)
//b2.put((byte)0);
b2.flip();
FileChannel fc2 = new FileOutputStream(new File("ftest"), false).getChannel();
fc2.write(b2);
fc2.close();
//ByteBuffer bf = ByteBuffer.allocate(1024 * 1024 * 50);
//bf.order(ByteOrder.LITTLE_ENDIAN);
//bf.flip()
//boolean append = false;
//FileChannel fc = new FileOutputStream(new File("f1"), append).getChannel();
//fc.write(bf);
//fc.close();
//
//FileOutputStream stream = new FileOutputStream("f2");
//stream.write(bf.array(), 0, bf.position())
//stream.close();
}
byte[] hashFile(String... inFiles) {
MessageDigest md = MessageDigest.getInstance("SHA1")
for (String item : inFiles) {
ByteBuffer itemBF = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
if (null == item) {
md.update(itemBF.putInt(0).array())
} else {
InputStream is = new FileInputStream(new File(item))
int byteRead;
byte[] dataRead = new byte[128]
while (true) {
byteRead = is.read(dataRead)
if (-1 == byteRead) {
break;
}
md.update(dataRead, 0, byteRead)
}
is.close();
md.update(itemBF.putInt((int) new File(item).length()).array())
}
}
return md.digest();
}
void dumpBytes(byte[] inData) {
StringBuffer sb = new StringBuffer("");
for (int i = 0; i < inData.length; i++) {
sb.append(Integer.toString((inData[i] & 0xff) + 0x100, 16).substring(1));
}
println("0x" + sb.toString());
}
void mkbootimg(CArgs theArgs) {
byte[] img_id = write_header(theArgs)
write_data(theArgs)
if (theArgs.id) {
ByteBuffer bf = ByteBuffer.allocate(32);
bf.order(ByteOrder.LITTLE_ENDIAN);
bf.put(img_id);
bf.put(new byte[32 - img_id.length])
dumpBytes(bf.array());
}
}
}

@ -0,0 +1,182 @@
package cfig.bootimg
import groovy.json.JsonBuilder
import java.nio.ByteBuffer
import java.nio.ByteOrder
/**
* Created by yu at 10:58 on 2016-06-18
*/
class Parser {
int readInt(InputStream is) {
ByteBuffer bf = ByteBuffer.allocate(128);
bf.order(ByteOrder.LITTLE_ENDIAN);
byte[] data4 = new byte[4];
assert 4 == is.read(data4)
bf.clear();
bf.put(data4);
bf.flip();
return bf.getInt();
}
byte[] readBytes(InputStream is, int len) {
byte[] data4 = new byte[len];
assert len == is.read(data4)
return data4;
}
String unparse_os_version(int x) {
int a = x >> 14;
int b = (x - (a << 14)) >> 7;
int c = x & 0x7f;
return String.format("%d.%d.%d", a, b, c);
}
String unparse_os_patch_level(int x) {
int y = x >> 4;
int m = x & 0xf;
y += 2000;
return String.format("%d-%02d-%02d", y, m, 0);
}
int get_header_len(int pagesize) {
int pad = (pagesize - (1632 & (pagesize - 1))) & (pagesize - 1);
return pad + 1632;
}
int get_pad_len(int position, int pagesize) {
return (pagesize - (position & (pagesize - 1))) & (pagesize - 1);
}
String bytes2String(byte[] inData) {
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < inData.length; i++) {
sb.append(Integer.toString((inData[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
void parse_header(String fileName, CImgInfo inImgInfo) {
InputStream is = new FileInputStream(new File(fileName))
assert Arrays.equals(readBytes(is, 8), "ANDROID!".getBytes())
inImgInfo.kernel_len = readInt(is);
inImgInfo.kernel_offset = readInt(is);
inImgInfo.ramdisk_len = readInt(is);
inImgInfo.ramdisk_offset = readInt(is);
inImgInfo.second_len = readInt(is);
inImgInfo.second_offset = readInt(is);
inImgInfo.tags_offset = readInt(is);
inImgInfo.pagesize = readInt(is);
assert 0 == readInt(is) //reserved
int os_and_patch = readInt(is)
if (0 != os_and_patch) { //treated as 'reserved' in this boot image
inImgInfo.os_version = unparse_os_version(os_and_patch >> 11)
inImgInfo.os_patch_level = unparse_os_patch_level(os_and_patch & 0x7ff)
}
inImgInfo.board = new String(readBytes(is, 16), "UTF-8").trim();
inImgInfo.cmdline = new String(readBytes(is, 512), "UTF-8")
inImgInfo.hash = readBytes(is, 32); //hash
inImgInfo.cmdline += new String(readBytes(is, 1024), "UTF-8")
inImgInfo.cmdline = inImgInfo.cmdline.trim();
is.close();
//calc subimg positions
inImgInfo.kernel_pos = get_header_len(inImgInfo.pagesize)
inImgInfo.ramdisk_pos = inImgInfo.kernel_pos + inImgInfo.kernel_len + get_pad_len(inImgInfo.kernel_len, inImgInfo.pagesize)
inImgInfo.second_pos = inImgInfo.ramdisk_pos + inImgInfo.ramdisk_len + get_pad_len(inImgInfo.ramdisk_len, inImgInfo.pagesize)
//adjust args
if (inImgInfo.kernel_offset > 0x10000000) {
inImgInfo.base = 0x10000000;
inImgInfo.kernel_offset -= inImgInfo.base;
inImgInfo.ramdisk_offset -= inImgInfo.base;
inImgInfo.second_offset -= inImgInfo.base;
inImgInfo.tags_offset -= inImgInfo.base;
}
}
void extract_img_header(CImgInfo inImgInfo) {
JsonBuilder jb = new JsonBuilder();
String hashString = bytes2String(inImgInfo.hash);
jb.bootimg {
args {
// kernel inImgInfo.kernel;
// ramdisk inImgInfo.ramdisk;
// second inImgInfo.second;
// output inImgInfo.output;
base "0x" + Integer.toHexString(inImgInfo.base);
kernel_offset "0x" + Integer.toHexString(inImgInfo.kernel_offset);
ramdisk_offset "0x" + Integer.toHexString(inImgInfo.ramdisk_offset);
second_offset "0x" + Integer.toHexString(inImgInfo.second_offset);
tags_offset "0x" + Integer.toHexString(inImgInfo.tags_offset);
pagesize inImgInfo.pagesize;
board inImgInfo.board;
cmdline inImgInfo.cmdline;
os_version inImgInfo.os_version;
os_patch_level inImgInfo.os_patch_level;
id inImgInfo.id;
}
img {
kernel_pos inImgInfo.kernel_pos;
kernel_len inImgInfo.kernel_len;
ramdisk_pos inImgInfo.ramdisk_pos;
ramdisk_len inImgInfo.ramdisk_len;
second_pos inImgInfo.second_pos;
second_len inImgInfo.second_len;
hash hashString;
}
}
FileWriter fw = new FileWriter(inImgInfo.cfg);
fw.write(jb.toPrettyString());
fw.flush();
fw.close();
}
void extract_img_data(String inBootImg, String outImgName, int offset, int length) {
if (0 == length) {
return;
}
RandomAccessFile inRaf = new RandomAccessFile(inBootImg, "r");
RandomAccessFile outRaf = new RandomAccessFile(outImgName, "rw");
inRaf.seek(offset);
byte[] data = new byte[length];
assert length == inRaf.read(data)
outRaf.write(data);
outRaf.close();
inRaf.close();
}
void printUsage() {
println("Usage: abootimg <path_to_boot_image> [work_dir]");
System.exit(1);
}
void abootimg(String[] arg) {
CImgInfo imgInfo = new CImgInfo();
String fileName;
String workDir = "unzip_boot";
if (1 == arg.length) {
fileName = arg[0];
} else if (2 == arg.length) {
fileName = arg[0];
workDir = arg[1];
} else {
printUsage();
}
imgInfo.kernel = workDir + File.separator + imgInfo.kernel;
imgInfo.ramdisk = workDir + File.separator + imgInfo.ramdisk;
imgInfo.second = workDir + File.separator + imgInfo.second;
imgInfo.cfg = workDir + File.separator + imgInfo.cfg;
parse_header(fileName, imgInfo);
new File(workDir).mkdirs();
extract_img_data(fileName, imgInfo.kernel, imgInfo.kernel_pos, imgInfo.kernel_len)
extract_img_data(fileName, imgInfo.ramdisk, imgInfo.ramdisk_pos, imgInfo.ramdisk_len)
extract_img_data(fileName, imgInfo.second, imgInfo.second_pos, imgInfo.second_len)
extract_img_header(imgInfo);
}
}

@ -1,182 +1,8 @@
package cfig.bootimg
import groovy.json.JsonBuilder
import java.nio.ByteBuffer
// http://mvnrepository.com/artifact/net.sf.jopt-simple/jopt-simple
//@Grapes(
// @Grab(group='net.sf.jopt-simple', module='jopt-simple', version='5.0.1')
//)
import java.nio.ByteOrder
int readInt(InputStream is) {
ByteBuffer bf = ByteBuffer.allocate(128);
bf.order(ByteOrder.LITTLE_ENDIAN);
byte[] data4 = new byte[4];
assert 4 == is.read(data4)
bf.clear();
bf.put(data4);
bf.flip();
return bf.getInt();
}
byte[] readBytes(InputStream is, int len) {
byte[] data4 = new byte[len];
assert len == is.read(data4)
return data4;
}
String unparse_os_version(int x) {
int a = x >> 14;
int b = (x - (a << 14)) >> 7;
int c = x & 0x7f;
return String.format("%d.%d.%d", a, b, c);
}
String unparse_os_patch_level(int x) {
int y = x >> 4;
int m = x & 0xf;
y += 2000;
return String.format("%d-%d-%d", y, m, 0);
}
int get_header_len(int pagesize) {
int pad = (pagesize - (1632 & (pagesize - 1))) & (pagesize - 1);
return pad + 1632;
}
int get_pad_len(int position, int pagesize) {
return (pagesize - (position & (pagesize - 1))) & (pagesize - 1);
}
String bytes2String(byte[] inData) {
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < inData.length; i++) {
sb.append(Integer.toString((inData[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
void parse_header(String fileName, CImgInfo inImgInfo) {
InputStream is = new FileInputStream(new File(fileName))
assert Arrays.equals(readBytes(is, 8), "ANDROID!".getBytes())
inImgInfo.kernel_len = readInt(is);
inImgInfo.kernel_offset = readInt(is);
inImgInfo.ramdisk_len = readInt(is);
inImgInfo.ramdisk_offset = readInt(is);
inImgInfo.second_len = readInt(is);
inImgInfo.second_offset = readInt(is);
inImgInfo.tags_offset = readInt(is);
inImgInfo.pagesize = readInt(is);
assert 0 == readInt(is) //reserved
int os_and_patch = readInt(is)
if (0 != os_and_patch) { //treated as 'reserved' in this boot image
inImgInfo.os_version = unparse_os_version(os_and_patch >> 11)
inImgInfo.os_patch_level = unparse_os_patch_level(os_and_patch & 0x7ff)
}
inImgInfo.board = new String(readBytes(is, 16), "UTF-8").trim();
inImgInfo.cmdline = new String(readBytes(is, 512), "UTF-8")
inImgInfo.hash = readBytes(is, 32); //hash
inImgInfo.cmdline += new String(readBytes(is, 1024), "UTF-8")
inImgInfo.cmdline = inImgInfo.cmdline.trim();
is.close();
//calc subimg positions
inImgInfo.kernel_pos = get_header_len(inImgInfo.pagesize)
inImgInfo.ramdisk_pos = inImgInfo.kernel_pos + inImgInfo.kernel_len + get_pad_len(inImgInfo.kernel_len, inImgInfo.pagesize)
inImgInfo.second_pos = inImgInfo.ramdisk_pos + inImgInfo.ramdisk_len + get_pad_len(inImgInfo.ramdisk_len, inImgInfo.pagesize)
//adjust args
if (inImgInfo.kernel_offset > 0x10000000) {
inImgInfo.base = 0x10000000;
inImgInfo.kernel_offset -= inImgInfo.base;
inImgInfo.ramdisk_offset -= inImgInfo.base;
inImgInfo.second_offset -= inImgInfo.base;
inImgInfo.tags_offset -= inImgInfo.base;
}
}
void extract_img_header(CImgInfo inImgInfo) {
JsonBuilder jb = new JsonBuilder();
hashString = bytes2String(inImgInfo.hash);
jb.bootimg {
args {
// kernel inImgInfo.kernel;
// ramdisk inImgInfo.ramdisk;
// second inImgInfo.second;
// output inImgInfo.output;
base "0x" + Integer.toHexString(inImgInfo.base);
kernel_offset "0x" + Integer.toHexString(inImgInfo.kernel_offset);
ramdisk_offset "0x" + Integer.toHexString(inImgInfo.ramdisk_offset);
second_offset "0x" + Integer.toHexString(inImgInfo.second_offset);
tags_offset "0x" + Integer.toHexString(inImgInfo.tags_offset);
pagesize inImgInfo.pagesize;
board inImgInfo.board;
cmdline inImgInfo.cmdline;
os_version inImgInfo.os_version;
os_patch_level inImgInfo.os_patch_level;
id inImgInfo.id;
}
img {
kernel_pos inImgInfo.kernel_pos;
kernel_len inImgInfo.kernel_len;
ramdisk_pos inImgInfo.ramdisk_pos;
ramdisk_len inImgInfo.ramdisk_len;
second_pos inImgInfo.second_pos;
second_len inImgInfo.second_len;
hash hashString;
}
}
FileWriter fw = new FileWriter(inImgInfo.cfg);
fw.write(jb.toPrettyString());
fw.flush();
fw.close();
}
void extract_img_data(String inBootImg, String outImgName, int offset, int length) {
if (0 == length) {
return;
}
RandomAccessFile inRaf = new RandomAccessFile(inBootImg, "r");
RandomAccessFile outRaf = new RandomAccessFile(outImgName, "rw");
inRaf.seek(offset);
byte[] data = new byte[length];
assert length == inRaf.read(data)
outRaf.write(data);
outRaf.close();
inRaf.close();
}
void printUsage() {
println("Usage: abootimg <path_to_boot_image> [work_dir]");
System.exit(1);
}
void abootimg(String[] arg) {
CImgInfo imgInfo = new CImgInfo();
String fileName;
String workDir = "unzip_boot";
if (1 == arg.length) {
fileName = arg[0];
} else if (2 == arg.length) {
fileName = arg[0];
workDir = arg[1];
} else {
printUsage();
}
imgInfo.kernel = workDir + File.separator + imgInfo.kernel;
imgInfo.ramdisk = workDir + File.separator + imgInfo.ramdisk;
imgInfo.second = workDir + File.separator + imgInfo.second;
imgInfo.cfg = workDir + File.separator + imgInfo.cfg;
parse_header(fileName, imgInfo);
new File(workDir).mkdir();
extract_img_data(fileName, imgInfo.kernel, imgInfo.kernel_pos, imgInfo.kernel_len)
extract_img_data(fileName, imgInfo.ramdisk, imgInfo.ramdisk_pos, imgInfo.ramdisk_len)
extract_img_data(fileName, imgInfo.second, imgInfo.second_pos, imgInfo.second_len)
extract_img_header(imgInfo);
}
abootimg(args);
new Parser().abootimg(args);

@ -4,341 +4,6 @@ package cfig.bootimg
//@Grapes(
// @Grab(group='net.sf.jopt-simple', module='jopt-simple', version='5.0.1')
//)
import java.nio.ByteBuffer;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import java.nio.channels.FileChannel;
import java.nio.ByteOrder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.security.MessageDigest;
CArgs parse_cmdline(String[] inArgs) {
OptionParser parser = new OptionParser();
parser.accepts("kernel", "path to the kernel").withRequiredArg();
parser.accepts("ramdisk", "path to the ramdisk").withRequiredArg();
parser.accepts("second", "path to the 2nd bootloader").withRequiredArg();
parser.accepts("cmdline", "extra arguments to be passed on the kernel command line").withRequiredArg();
parser.accepts("base", "base address").withRequiredArg();
parser.accepts("kernel_offset", "kernel offset").withRequiredArg();
parser.accepts("ramdisk_offset", "ramdisk offset").withRequiredArg();
parser.accepts("second_offset", "2nd bootloader offset").withRequiredArg();
parser.accepts("os_version", "operating system version").withRequiredArg();
parser.accepts("os_patch_level", "operating system patch level").withRequiredArg();
parser.accepts("tags_offset", "tags offset").withRequiredArg();
parser.accepts("board", "board name").withRequiredArg();
parser.accepts("pagesize", "page size").withRequiredArg();
parser.accepts("id", "print the image ID on standard output");
parser.accepts("output", "output file name").withRequiredArg();
OptionSet options = parser.parse(inArgs)
CArgs ret = new CArgs();
ret.kernel = options.valueOf("kernel")
ret.output = options.valueOf("output")
ret.ramdisk = options.valueOf("ramdisk")
ret.second = options.valueOf("second")
if (options.has("board")) {
ret.board = options.valueOf("board")
} else {
ret.board = ""
}
ret.id = options.has("id")
if (options.has("base")) {
ret.base = Integer.decode(options.valueOf("base"))
} else {
ret.base = 0x10000000;
}
if (options.has("kernel_offset")) {
ret.kernel_offset = Integer.decode(options.valueOf("kernel_offset"))
} else {
ret.kernel_offset = 0x00008000;
}
if (options.has("ramdisk_offset")) {
ret.ramdisk_offset = Integer.decode(options.valueOf("ramdisk_offset"))
} else {
ret.ramdisk_offset = 0x01000000
}
ret.os_version = options.valueOf("os_version")
ret.os_patch_level = options.valueOf("os_patch_level")
if (options.has("second_offset")) {
ret.second_offset = Integer.decode(options.valueOf("second_offset"))
} else {
ret.second_offset = 0x00f00000
}
if (options.has("tags_offset")) {
ret.tags_offset = Integer.decode(options.valueOf("tags_offset"))
} else {
ret.tags_offset = 0x00000100
}
if (options.has("pagesize")) {
ret.pagesize = Integer.decode(options.valueOf("pagesize"))
} else {
ret.pagesize = 2048
}
if (options.has("cmdline")) {
ret.cmdline = options.valueOf("cmdline")
} else {
ret.cmdline = ""
}
if (ret.cmdline.length() > 1536) {
println("cmdline length must <= 1536, current is " + ret.cmdline.length());
printUsage(parser);
}
if (null == ret.kernel) {
println("kernel must not be empty");
printUsage(parser);
}
if (null == ret.output) {
println("output file must not be empty");
printUsage(parser);
}
if (ret.board.length() > 16) {
println("board name length must <= 16")
printUsage(parser);
}
return ret;
}
byte[] write_header(CArgs inArgs) {
ByteBuffer bf = ByteBuffer.allocate(1024 * 32);
bf.order(ByteOrder.LITTLE_ENDIAN);
//header start
bf.put("ANDROID!".getBytes())
bf.putInt((int) new File(inArgs.kernel).length());
bf.putInt(inArgs.base + inArgs.kernel_offset)
if (null == inArgs.ramdisk) {
bf.putInt(0)
} else {
bf.putInt((int) new File(inArgs.ramdisk).length());
}
bf.putInt(inArgs.base + inArgs.ramdisk_offset)
if (null == inArgs.second) {
bf.putInt(0)
} else {
bf.putInt((int) new File(inArgs.second).length());
}
bf.putInt(inArgs.base + inArgs.second_offset)
bf.putInt(inArgs.base + inArgs.tags_offset)
bf.putInt(inArgs.pagesize)
bf.putInt(0);
bf.putInt((parse_os_version(inArgs.os_version) << 11) | parse_os_patch_level(inArgs.os_patch_level))
bf.put(inArgs.board.getBytes())
bf.put(new byte[16 - inArgs.board.length()])
bf.put(inArgs.cmdline.substring(0, Math.min(512, inArgs.cmdline.length())).getBytes())
bf.put(new byte[512 - Math.min(512, inArgs.cmdline.length())])
byte[] img_id = hashFile(inArgs.kernel, inArgs.ramdisk, inArgs.second)
bf.put(img_id)
bf.put(new byte[32 - img_id.length])
if (inArgs.cmdline.length() > 512) {
bf.put(inArgs.cmdline.substring(512).getBytes())
bf.put(new byte[1024 + 512 - inArgs.cmdline.length()])
} else {
bf.put(new byte[1024])
}
//padding
pad_file(bf, inArgs.pagesize)
//write
FileOutputStream fos = new FileOutputStream(inArgs.output, false);
fos.write(bf.array(), 0, bf.position())
fos.close();
return img_id;
}
void printUsage(OptionParser p) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write("Usage: mkbootimg <option>\n".getBytes());
p.printHelpOn(out);
System.out.println(out.toString());
out.close();
System.exit(1);
}
void write_padded_file(ByteBuffer inBF, String srcFile, int padding) {
if (null == srcFile) {
return;
}
InputStream is = new FileInputStream(new File(srcFile))
int byteRead;
byte[] dataRead = new byte[128]
while (true) {
byteRead = is.read(dataRead)
if (-1 == byteRead) {
break;
}
inBF.put(dataRead, 0, byteRead);
}
is.close();
pad_file(inBF, padding)
}
void pad_file(ByteBuffer inBF, int padding) {
int pad = (padding - (inBF.position() & (padding - 1))) & (padding - 1);
inBF.put(new byte[pad]);
}
void write_data(CArgs inArgs) {
ByteBuffer bf = ByteBuffer.allocate(1024 * 1024 * 64);
bf.order(ByteOrder.LITTLE_ENDIAN);
write_padded_file(bf, inArgs.kernel, inArgs.pagesize)
write_padded_file(bf, inArgs.ramdisk, inArgs.pagesize)
write_padded_file(bf, inArgs.second, inArgs.pagesize)
//write
FileOutputStream fos = new FileOutputStream(inArgs.output, true);
fos.write(bf.array(), 0, bf.position())
fos.close();
}
int parse_os_patch_level(x) {
if (null == x) {
return 0;
}
int ret = 0
Pattern pattern = Pattern.compile("^(\\d{4})-(\\d{2})-(\\d{2})")
Matcher matcher = pattern.matcher(x)
if (matcher.find()) {
int y = Integer.decode(matcher.group(1)) - 2000
int m = Integer.decode(matcher.group(2))
// 7 bits allocated for the year, 4 bits for the month
assert y >= 0 && y < 128
assert m > 0 && m <= 12
ret = (y << 4) | m
} else {
throw new IllegalArgumentException("invalid os_patch_level")
}
return ret;
}
int parse_os_version(x) {
int ret = 0;
if (null != x) {
Pattern pattern = Pattern.compile("^(\\d{1,3})(?:\\.(\\d{1,3})(?:\\.(\\d{1,3}))?)?");
Matcher m = pattern.matcher(x)
if (m.find()) {
int a = Integer.decode(m.group(1))
int b = 0;
int c = 0;
if (m.groupCount() >= 2) {
b = Integer.decode(m.group(2))
}
if (m.groupCount() == 3) {
c = Integer.decode(m.group(3))
}
assert a < 128
assert b < 128
assert c < 128
ret = ((a << 14) | (b << 7) | c)
} else {
throw new IllegalArgumentException("invalid os_version")
}
}
return ret;
}
void test() {
ByteBuffer b2 = ByteBuffer.allocate(1024);
b2.order(ByteOrder.LITTLE_ENDIAN);
b2.putInt(Integer.MAX_VALUE); //4 bytes
println("max: " + Integer.MAX_VALUE)
println("min: " + Integer.MIN_VALUE)
b2.putInt(0x11111111)
b2.putInt(Integer.MIN_VALUE);
b2.putInt(0x11111111)
b2.put("welcome".getBytes())
b2.put(new byte[5]);
b2.putInt(0x11111111)
b2.putInt(0);
b2.putInt(0x11111111)
//b2.put((byte)0);
b2.flip();
FileChannel fc2 = new FileOutputStream(new File("ftest"), false).getChannel();
fc2.write(b2);
fc2.close();
//ByteBuffer bf = ByteBuffer.allocate(1024 * 1024 * 50);
//bf.order(ByteOrder.LITTLE_ENDIAN);
//bf.flip()
//boolean append = false;
//FileChannel fc = new FileOutputStream(new File("f1"), append).getChannel();
//fc.write(bf);
//fc.close();
//
//FileOutputStream stream = new FileOutputStream("f2");
//stream.write(bf.array(), 0, bf.position())
//stream.close();
}
byte[] hashFile(String... inFiles) {
MessageDigest md = MessageDigest.getInstance("SHA1")
for (String item : inFiles) {
ByteBuffer itemBF = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
if (null == item) {
md.update(itemBF.putInt(0).array())
} else {
InputStream is = new FileInputStream(new File(item))
int byteRead;
byte[] dataRead = new byte[128]
while (true) {
byteRead = is.read(dataRead)
if (-1 == byteRead) {
break;
}
md.update(dataRead, 0, byteRead)
}
is.close();
md.update(itemBF.putInt((int) new File(item).length()).array())
}
}
return md.digest();
}
void dumpBytes(byte[] inData) {
StringBuffer sb = new StringBuffer("");
for (int i = 0; i < inData.length; i++) {
sb.append(Integer.toString((inData[i] & 0xff) + 0x100, 16).substring(1));
}
println("0x" + sb.toString());
}
CArgs theArgs = parse_cmdline(args)
byte[] img_id = write_header(theArgs)
write_data(theArgs)
if (theArgs.id) {
ByteBuffer bf = ByteBuffer.allocate(32);
bf.order(ByteOrder.LITTLE_ENDIAN);
bf.put(img_id);
bf.put(new byte[32 - img_id.length])
dumpBytes(bf.array());
}
Packer thePacker = new Packer();
CArgs theArgs = thePacker.parse_cmdline(args)
thePacker.mkbootimg(theArgs);

@ -0,0 +1,11 @@
package cfig.bootimg
if (2 != args.length) {
println("Usage:\n\trepack <out_file> <work_dir>");
println("It will create <out_file> from <work_dir>/kernel, <work_dir>/ramdisk.img.gz and <work_dir>/second");
println("Example:\n\trepack boot.img.clear build/unzip_boot");
System.exit(1);
}
CImgInfo aInfo = CImgInfo.fromJson(args[0], args[1]);
new Packer().mkbootimg(aInfo);

@ -0,0 +1,28 @@
package cfig.bootimg;
void Run(List<String> inCmd, String inWorkdir = null) {
println("CMD:" + inCmd)
if (inWorkdir == null) {
inWorkdir = ".";
}
ProcessBuilder pb = new ProcessBuilder(inCmd)
.directory(new File(inWorkdir))
.redirectErrorStream(true);
Process p = pb.start()
p.inputStream.eachLine {println it}
p.waitFor();
assert 0 == p.exitValue()
}
if (3 != args.length) {
println("Usage:\n\trepack_with_cmd <out_file> <work_dir> <mkbootimg_bin>");
println("It will create <out_file> from <work_dir>/kernel, <work_dir>/ramdisk.img.gz and <work_dir>/second");
println("with the program <mkbootimg_bin>");
println("Example:\n\trepack boot.img.clear build/unzip_boot mkbootimg");
System.exit(1);
}
CImgInfo aInfo = CImgInfo.fromJson(args[0], args[1]);
List<String> cmdArgs = aInfo.toCommandList();
cmdArgs.add(0, args[2]);
Run(cmdArgs);

@ -30,6 +30,7 @@ model {
// global
// ----------------------------------------------------------------------------
def workdir='build/unzip_boot'
project.ext.rootWorkDir = new File(workdir).getAbsolutePath()
def defaultRootDir = workdir + "/root"
String[] bins = [ "sh", "logcat", "logd", "linker", "toolbox", "toybox", "applypatch" ]
String[] libs = [ "libdl.so", "libutils.so", "libc++.so", "libc.so", "libm.so", "libz.so", "libstdc++.so", "libcutils.so", "libselinux.so", "liblog.so", "libpcre.so", "libsysutils.so", "libnl.so", "libbase.so", "libbacktrace.so", "libunwind.so" ]
@ -40,6 +41,8 @@ if (new File("boot.img").exists()) {
} else if (new File("recovery.img").exists()) {
activeImg = "recovery.img";
}
project.ext.outClearIMg = new File(String.format("%s.clear", activeImg)).getAbsolutePath()
project.ext.mkbootimgBin = new File("src/mkbootimg/mkbootimg").getAbsolutePath()
println("Active image target: " + activeImg);
// ----------------------------------------------------------------------------
@ -107,20 +110,16 @@ task pack_ramdisk_and_gz { Task task ->
}
pack_ramdisk_and_gz.dependsOn('mkbootfsExecutable')
task pack_clear(type: Exec, dependsOn: [pack_ramdisk_and_gz]) {
def theCmdLine = getRamdiskConfig(workdir, 'cmdline')
if (gDebug) {
theCmdLine = "androidboot.selinux=disabled " + theCmdLine
}
def theBaseAddr = getBaseAddress(workdir)
workingDir '.'
executable 'src/mkbootimg/mkbootimg'
args = [
'--kernel', workdir + "/kernel",
'--ramdisk', workdir + "/ramdisk.img.gz",
'--cmdline', theCmdLine,
'--base', theBaseAddr,
'--output', activeImg + '.clear']
task pack_clear2(type: JavaExec, dependsOn: ['abootimg:repack', pack_ramdisk_and_gz]) {
main = 'cfig.bootimg.repack'
classpath = files("abootimg/build/libs/repack.jar")
maxHeapSize '512m'
args String.format("%s.clear2", activeImg), workdir
}
String[] getMkbootimgArgs(String inWorkdir, String outFile) {
return "good day".split();
//args = getMkbootimgArgs(workdir, String.format("%s.clear", activeImg))
}
void unGnuzipFile(String compressedFile, String decompressedFile) throws IOException {
@ -179,44 +178,7 @@ void gnuZipFile(String compressedFile, String decompressedFile) throws IOExcepti
}
}
String getBaseAddress(String inWorkdir) {
Long ret;
try {
ret = Long.parseLong(getRamdiskConfig(inWorkdir, "kerneladdr").substring(2), 16) - 0x8000L;
} catch (NumberFormatException e) {
throw new RuntimeException("NumberFormatException: invalid kerneladdr value")
}
return Long.toHexString(ret);
}
String getRamdiskConfig(String inWorkdir, String inKey) {
String ret;
if (!new File(inWorkdir+ "/bootimg.cfg").exists()) {
return "0x0";
}
try {
BufferedReader br = new BufferedReader(new FileReader(inWorkdir + "/bootimg.cfg"));
String item;
while (true) {
item = br.readLine();
if (null == item) {
break;
}
Pattern r = Pattern.compile("(?<=" + inKey + " = ).*");
Matcher m = r.matcher(item);
if (m.find()) {
ret = m.group(0)
}
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Fatal Error");
}
return ret;
}
task pack(type: JavaExec, dependsOn: [pack_clear, 'boot_signer:jar']) {
task pack(type: JavaExec, dependsOn: ['abootimg:pack_clear', 'boot_signer:jar', pack_clear2]) {
main = 'com.android.verity.BootSignature'
classpath = files("boot_signer/build/libs/boot_signer.jar")
maxHeapSize '512m'
@ -355,7 +317,3 @@ if (null == activeImg) {
unpack_bootimg.enabled = false
unpack_ramdisk_gz.enabled = false
}
if (!new File(workdir + "/bootimg.cfg").exists()) {
pack_clear.enabled = false;
}

Loading…
Cancel
Save