summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYi-Yo Chiang <yochiang@google.com>2021-05-04 08:42:06 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-05-04 08:42:06 +0000
commit54e2cc4c57e86279b6e07b33d16a3cff7d948a0d (patch)
treeac4fe3efcac5bf598b6061ceaa58ef911d83a84c
parent9e3a1e8d4cec88723c992ce4e1d630bf873387fb (diff)
parentde73fd2a36ba183978c7190eeeaf49879499e4ed (diff)
downloadmkbootimg-54e2cc4c57e86279b6e07b33d16a3cff7d948a0d.tar.gz
Deprecate mkbootimg_args.json by fully support "--format=mkbootimg" am: 07c6063146 am: de73fd2a36
Original change: https://android-review.googlesource.com/c/platform/system/tools/mkbootimg/+/1693414 Change-Id: Ie46068732f1982292e1f2c398561a4f3d8823676
-rwxr-xr-xrepack_bootimg.py58
-rw-r--r--tests/mkbootimg_test.py260
-rwxr-xr-xunpack_bootimg.py187
3 files changed, 195 insertions, 310 deletions
diff --git a/repack_bootimg.py b/repack_bootimg.py
index cd0732a..3e07707 100755
--- a/repack_bootimg.py
+++ b/repack_bootimg.py
@@ -22,8 +22,8 @@ the ramdisk to repack the boot image.
import argparse
import enum
-import json
import os
+import shlex
import shutil
import subprocess
import tempfile
@@ -168,12 +168,7 @@ class BootImage:
self._bootimg_dir = None
self._bootimg_type = None
self._ramdisk = None
- # Potential images to extract from a boot.img. Unlike the ramdisk,
- # the content of the following images will not be changed during the
- # repack process.
- self._intact_image_candidates = ('dtb', 'kernel',
- 'recovery_dtbo', 'second')
- self._repack_intact_image_args = []
+ self._previous_mkbootimg_args = []
self._temp_file_manager = TempFileManager()
self._unpack_bootimg()
@@ -184,18 +179,26 @@ class BootImage:
suffix=os.path.basename(self._bootimg))
# Unpacks the boot.img first.
- subprocess.check_call(
- ['unpack_bootimg', '--boot_img', self._bootimg,
- '--out', self._bootimg_dir], stdout=subprocess.DEVNULL)
- print("=== Unpacked boot image: '{}' ===".format(self._bootimg))
+ unpack_bootimg_cmds = [
+ 'unpack_bootimg',
+ '--boot_img', self._bootimg,
+ '--out', self._bootimg_dir,
+ '--format=mkbootimg',
+ ]
+ result = subprocess.run(unpack_bootimg_cmds, check=True,
+ capture_output=True, encoding='utf-8')
+ ignore_next = False
+ for arg in shlex.split(result.stdout):
+ if ignore_next:
+ ignore_next = False
+ continue
+ # Skips the original ramdisk since we'll pack a new ramdisk.
+ if arg in {'--ramdisk', '--vendor_ramdisk'}:
+ ignore_next = True
+ continue
+ self._previous_mkbootimg_args.append(arg)
- for img_name in self._intact_image_candidates:
- img_file = os.path.join(self._bootimg_dir, img_name)
- if os.path.exists(img_file):
- # Prepares args for repacking those intact images. e.g.,
- # --kernel kernel_image, --dtb dtb_image.
- self._repack_intact_image_args.extend(
- ['--' + img_name, img_file])
+ print("=== Unpacked boot image: '{}' ===".format(self._bootimg))
# From the output dir, checks there is 'ramdisk' or 'vendor_ramdisk'.
ramdisk = os.path.join(self._bootimg_dir, 'ramdisk')
@@ -209,22 +212,6 @@ class BootImage:
else:
raise RuntimeError('Both ramdisk and vendor_ramdisk do not exist.')
- @property
- def _previous_mkbootimg_args(self):
- """Returns the previous mkbootimg args from mkbootimg_args.json file."""
- # Loads the saved mkbootimg_args.json from previous unpack_bootimg.
- command = []
- mkbootimg_config = os.path.join(
- self._bootimg_dir, 'mkbootimg_args.json')
- with open (mkbootimg_config) as config:
- mkbootimg_args = json.load(config)
- for argname, value in mkbootimg_args.items():
- # argname, e.g., 'board', 'header_version', etc., does not have
- # prefix '--', which is required when invoking `mkbootimg.py`.
- # Prepends '--' to make the full args, e.g., --header_version.
- command.extend(['--' + argname, value])
- return command
-
def repack_bootimg(self):
"""Repacks the ramdisk and rebuild the boot.img"""
@@ -237,9 +224,6 @@ class BootImage:
# Uses previous mkbootimg args, e.g., --vendor_cmdline, --dtb_offset.
mkbootimg_cmd.extend(self._previous_mkbootimg_args)
- if self._repack_intact_image_args:
- mkbootimg_cmd.extend(self._repack_intact_image_args)
-
if self._bootimg_type == BootImageType.VENDOR_BOOT_IMAGE:
mkbootimg_cmd.extend(['--vendor_ramdisk', new_ramdisk])
mkbootimg_cmd.extend(['--vendor_boot', self._bootimg])
diff --git a/tests/mkbootimg_test.py b/tests/mkbootimg_test.py
index 110b3cb..ae5cf6b 100644
--- a/tests/mkbootimg_test.py
+++ b/tests/mkbootimg_test.py
@@ -17,7 +17,6 @@
"""Tests mkbootimg and unpack_bootimg."""
import filecmp
-import json
import logging
import os
import random
@@ -35,9 +34,13 @@ BOOT_V3_ARGS_OFFSET = 44
VENDOR_BOOT_ARGS_OFFSET = 28
VENDOR_BOOT_ARGS_SIZE = 2048
-
BOOT_IMAGE_V4_SIGNATURE_SIZE = 4096
+TEST_KERNEL_CMDLINE = (
+ 'printk.devkmsg=on firmware_class.path=/vendor/etc/ init=/init '
+ 'kfence.sample_interval=500 loop.max_part=7 bootconfig'
+)
+
def generate_test_file(pathname, size, seed=None):
"""Generates a gibberish-filled test file and returns its pathname."""
@@ -96,7 +99,7 @@ class MkbootimgTest(unittest.TestCase):
'--header_version', '4',
'--kernel', kernel,
'--ramdisk', ramdisk,
- '--cmdline', 'test-cmdline',
+ '--cmdline', TEST_KERNEL_CMDLINE,
'--os_version', '11.0.0',
'--os_patch_level', '2021-01',
'--gki_signing_algorithm', 'SHA256_RSA2048',
@@ -141,8 +144,8 @@ class MkbootimgTest(unittest.TestCase):
' Partition Name: boot\n'
' Salt: d00df00d\n'
' Digest: '
- '9749bb508f2677426b14ff668d39a163'
- 'e16f0c4cbaf92ec096124e3f199fafac\n'
+ 'cf3755630856f23ab70e501900050fee'
+ 'f30b633b3e82a9085a578617e344f9c7\n'
' Flags: 0\n'
" Prop: foo -> 'bar'\n"
" Prop: gki -> 'nice'\n"
@@ -180,7 +183,7 @@ class MkbootimgTest(unittest.TestCase):
'--header_version', '4',
'--kernel', kernel,
'--ramdisk', ramdisk,
- '--cmdline', 'test-cmdline',
+ '--cmdline', TEST_KERNEL_CMDLINE,
'--os_version', '11.0.0',
'--os_patch_level', '2021-01',
'--gki_signing_avbtool_path', self._avbtool_path,
@@ -218,7 +221,7 @@ class MkbootimgTest(unittest.TestCase):
'--header_version', '4',
'--kernel', kernel,
'--ramdisk', ramdisk,
- '--cmdline', 'test-cmdline',
+ '--cmdline', TEST_KERNEL_CMDLINE,
'--os_version', '11.0.0',
'--os_patch_level', '2021-01',
'--output', boot_img,
@@ -263,6 +266,7 @@ class MkbootimgTest(unittest.TestCase):
'--board_id0', '0xC0FFEE',
'--board_id15', '0x15151515',
'--vendor_ramdisk_fragment', ramdisk2,
+ '--vendor_cmdline', TEST_KERNEL_CMDLINE,
'--vendor_bootconfig', bootconfig,
]
unpack_bootimg_cmds = [
@@ -274,6 +278,7 @@ class MkbootimgTest(unittest.TestCase):
'boot magic: VNDRBOOT',
'vendor boot image header version: 4',
'vendor ramdisk total size: 16384',
+ f'vendor command line args: {TEST_KERNEL_CMDLINE}',
'dtb size: 4096',
'vendor ramdisk table size: 324',
'size: 4096', 'offset: 0', 'type: 0x1', 'name:',
@@ -309,12 +314,12 @@ class MkbootimgTest(unittest.TestCase):
])
self.fail(msg)
- def test_unpack_vendor_boot_image_v4_format_mkbootimg(self):
- """Tests `unpack_bootimg --format=mkbootimg`."""
+ def test_unpack_vendor_boot_image_v4(self):
+ """Tests that mkbootimg(unpack_bootimg(image)) is an identity."""
with tempfile.TemporaryDirectory() as temp_out_dir:
vendor_boot_img = os.path.join(temp_out_dir, 'vendor_boot.img')
- vendor_boot_reconstructed = os.path.join(
- temp_out_dir, 'vendor_boot.reconstructed')
+ vendor_boot_img_reconstructed = os.path.join(
+ temp_out_dir, 'vendor_boot.img.reconstructed')
dtb = generate_test_file(os.path.join(temp_out_dir, 'dtb'), 0x1000)
ramdisk1 = generate_test_file(
os.path.join(temp_out_dir, 'ramdisk1'), 0x121212)
@@ -337,6 +342,7 @@ class MkbootimgTest(unittest.TestCase):
'--board_id0', '0xC0FFEE',
'--board_id15', '0x15151515',
'--vendor_ramdisk_fragment', ramdisk2,
+ '--vendor_cmdline', TEST_KERNEL_CMDLINE,
'--vendor_bootconfig', bootconfig,
]
unpack_bootimg_cmds = [
@@ -350,14 +356,14 @@ class MkbootimgTest(unittest.TestCase):
capture_output=True, encoding='utf-8')
mkbootimg_cmds = [
'mkbootimg',
- '--vendor_boot', vendor_boot_reconstructed,
+ '--vendor_boot', vendor_boot_img_reconstructed,
]
unpack_format_args = shlex.split(result.stdout)
mkbootimg_cmds.extend(unpack_format_args)
subprocess.run(mkbootimg_cmds, check=True)
self.assertTrue(
- filecmp.cmp(vendor_boot_img, vendor_boot_reconstructed),
+ filecmp.cmp(vendor_boot_img, vendor_boot_img_reconstructed),
'reconstructed vendor_boot image differ from the original')
# Also check that -0, --null are as expected.
@@ -368,22 +374,29 @@ class MkbootimgTest(unittest.TestCase):
self.assertEqual('\0'.join(unpack_format_args) + '\0',
unpack_format_null_args)
- def test_unpack_vendor_boot_image_v3_format_mkbootimg(self):
- """Tests `unpack_bootimg --format=mkbootimg`."""
+ def test_unpack_vendor_boot_image_v3(self):
+ """Tests that mkbootimg(unpack_bootimg(image)) is an identity."""
with tempfile.TemporaryDirectory() as temp_out_dir:
vendor_boot_img = os.path.join(temp_out_dir, 'vendor_boot.img')
- vendor_boot_reconstructed = os.path.join(
- temp_out_dir, 'vendor_boot.reconstructed')
+ vendor_boot_img_reconstructed = os.path.join(
+ temp_out_dir, 'vendor_boot.img.reconstructed')
dtb = generate_test_file(os.path.join(temp_out_dir, 'dtb'), 0x1000)
- ramdisk1 = generate_test_file(
- os.path.join(temp_out_dir, 'ramdisk1'), 0x121212)
-
+ ramdisk = generate_test_file(os.path.join(temp_out_dir, 'ramdisk'),
+ 0x121212)
mkbootimg_cmds = [
'mkbootimg',
'--header_version', '3',
'--vendor_boot', vendor_boot_img,
+ '--vendor_ramdisk', ramdisk,
'--dtb', dtb,
- '--vendor_ramdisk', ramdisk1,
+ '--vendor_cmdline', TEST_KERNEL_CMDLINE,
+ '--board', 'product_name',
+ '--base', '0x00000000',
+ '--dtb_offset', '0x01f00000',
+ '--kernel_offset', '0x00008000',
+ '--pagesize', '0x00001000',
+ '--ramdisk_offset', '0x01000000',
+ '--tags_offset', '0x00000100',
]
unpack_bootimg_cmds = [
'unpack_bootimg',
@@ -396,19 +409,21 @@ class MkbootimgTest(unittest.TestCase):
capture_output=True, encoding='utf-8')
mkbootimg_cmds = [
'mkbootimg',
- '--vendor_boot', vendor_boot_reconstructed,
+ '--vendor_boot', vendor_boot_img_reconstructed,
]
mkbootimg_cmds.extend(shlex.split(result.stdout))
subprocess.run(mkbootimg_cmds, check=True)
self.assertTrue(
- filecmp.cmp(vendor_boot_img, vendor_boot_reconstructed),
+ filecmp.cmp(vendor_boot_img, vendor_boot_img_reconstructed),
'reconstructed vendor_boot image differ from the original')
- def test_unpack_boot_image_v3_json_args(self):
- """Tests mkbootimg_args.json when unpacking a boot image version 3."""
+ def test_unpack_boot_image_v3(self):
+ """Tests that mkbootimg(unpack_bootimg(image)) is an identity."""
with tempfile.TemporaryDirectory() as temp_out_dir:
boot_img = os.path.join(temp_out_dir, 'boot.img')
+ boot_img_reconstructed = os.path.join(
+ temp_out_dir, 'boot.img.reconstructed')
kernel = generate_test_file(os.path.join(temp_out_dir, 'kernel'),
0x1000)
ramdisk = generate_test_file(os.path.join(temp_out_dir, 'ramdisk'),
@@ -418,7 +433,7 @@ class MkbootimgTest(unittest.TestCase):
'--header_version', '3',
'--kernel', kernel,
'--ramdisk', ramdisk,
- '--cmdline', 'test-cmdline',
+ '--cmdline', TEST_KERNEL_CMDLINE,
'--os_version', '11.0.0',
'--os_patch_level', '2021-01',
'--output', boot_img,
@@ -427,82 +442,30 @@ class MkbootimgTest(unittest.TestCase):
'unpack_bootimg',
'--boot_img', boot_img,
'--out', os.path.join(temp_out_dir, 'out'),
+ '--format=mkbootimg',
]
- # The expected dict in mkbootimg_args.json.
- expected_mkbootimg_args = {
- 'cmdline': 'test-cmdline',
- 'header_version': '3',
- 'os_patch_level': '2021-01',
- 'os_version': '11.0.0'
- }
subprocess.run(mkbootimg_cmds, check=True)
- subprocess.run(unpack_bootimg_cmds, check=True)
-
- json_file = os.path.join(
- temp_out_dir, 'out', 'mkbootimg_args.json')
- with open(json_file) as json_fd:
- actual_mkbootimg_args = json.load(json_fd)
- self.assertEqual(actual_mkbootimg_args,
- expected_mkbootimg_args)
-
- def test_unpack_vendor_boot_image_v3_json_args(self):
- """Tests mkbootimg_args.json when unpacking a vendor boot image version
- 3.
- """
- with tempfile.TemporaryDirectory() as temp_out_dir:
- vendor_boot_img = os.path.join(temp_out_dir, 'vendor_boot.img')
- dtb = generate_test_file(os.path.join(temp_out_dir, 'dtb'), 0x1000)
- ramdisk = generate_test_file(os.path.join(temp_out_dir, 'ramdisk'),
- 0x1000)
+ result = subprocess.run(unpack_bootimg_cmds, check=True,
+ capture_output=True, encoding='utf-8')
mkbootimg_cmds = [
'mkbootimg',
- '--header_version', '3',
- '--vendor_boot', vendor_boot_img,
- '--vendor_ramdisk', ramdisk,
- '--dtb', dtb,
- '--vendor_cmdline', 'test-vendor_cmdline',
- '--board', 'product_name',
- '--base', '0x00000000',
- '--dtb_offset', '0x01f00000',
- '--kernel_offset', '0x00008000',
- '--pagesize', '0x00001000',
- '--ramdisk_offset', '0x01000000',
- '--tags_offset', '0x00000100',
- ]
- unpack_bootimg_cmds = [
- 'unpack_bootimg',
- '--boot_img', vendor_boot_img,
- '--out', os.path.join(temp_out_dir, 'out'),
+ '--out', boot_img_reconstructed,
]
- # The expected dict in mkbootimg_args.json.
- expected_mkbootimg_args = {
- 'header_version': '3',
- 'vendor_cmdline': 'test-vendor_cmdline',
- 'board': 'product_name',
- 'base': '0x00000000',
- 'dtb_offset': '0x0000000001f00000', # dtb_offset is uint64_t.
- 'kernel_offset': '0x00008000',
- 'pagesize': '0x00001000',
- 'ramdisk_offset': '0x01000000',
- 'tags_offset': '0x00000100',
- }
+ mkbootimg_cmds.extend(shlex.split(result.stdout))
subprocess.run(mkbootimg_cmds, check=True)
- subprocess.run(unpack_bootimg_cmds, check=True)
-
- json_file = os.path.join(
- temp_out_dir, 'out', 'mkbootimg_args.json')
- with open(json_file) as json_fd:
- actual_mkbootimg_args = json.load(json_fd)
- self.assertEqual(actual_mkbootimg_args,
- expected_mkbootimg_args)
+ self.assertTrue(
+ filecmp.cmp(boot_img, boot_img_reconstructed),
+ 'reconstructed boot image differ from the original')
- def test_unpack_boot_image_v2_json_args(self):
- """Tests mkbootimg_args.json when unpacking a boot image v2."""
+ def test_unpack_boot_image_v2(self):
+ """Tests that mkbootimg(unpack_bootimg(image)) is an identity."""
with tempfile.TemporaryDirectory() as temp_out_dir:
# Output image path.
boot_img = os.path.join(temp_out_dir, 'boot.img')
+ boot_img_reconstructed = os.path.join(
+ temp_out_dir, 'boot.img.reconstructed')
# Creates blank images first.
kernel = generate_test_file(
os.path.join(temp_out_dir, 'kernel'), 0x1000)
@@ -543,38 +506,30 @@ class MkbootimgTest(unittest.TestCase):
'unpack_bootimg',
'--boot_img', boot_img,
'--out', os.path.join(temp_out_dir, 'out'),
+ '--format=mkbootimg',
]
- # The expected dict in mkbootimg_args.json.
- expected_mkbootimg_args = {
- 'header_version': '2',
- 'base': '0x00000000',
- 'kernel_offset': '0x00008000',
- 'ramdisk_offset': '0x01000000',
- 'second_offset': '0x40000000',
- 'dtb_offset': '0x0000000001f00000', # dtb_offset is uint64_t.
- 'tags_offset': '0x00000100',
- 'pagesize': '0x00001000',
- 'os_version': '11.0.0',
- 'os_patch_level': '2021-03',
- 'board': 'boot_v2',
- 'cmdline': cmdline + extra_cmdline,
- }
subprocess.run(mkbootimg_cmds, check=True)
- subprocess.run(unpack_bootimg_cmds, check=True)
+ result = subprocess.run(unpack_bootimg_cmds, check=True,
+ capture_output=True, encoding='utf-8')
+ mkbootimg_cmds = [
+ 'mkbootimg',
+ '--out', boot_img_reconstructed,
+ ]
+ mkbootimg_cmds.extend(shlex.split(result.stdout))
- json_file = os.path.join(
- temp_out_dir, 'out', 'mkbootimg_args.json')
- with open(json_file) as json_fd:
- actual_mkbootimg_args = json.load(json_fd)
- self.assertEqual(actual_mkbootimg_args,
- expected_mkbootimg_args)
+ subprocess.run(mkbootimg_cmds, check=True)
+ self.assertTrue(
+ filecmp.cmp(boot_img, boot_img_reconstructed),
+ 'reconstructed boot image differ from the original')
- def test_unpack_boot_image_v1_json_args(self):
- """Tests mkbootimg_args.json when unpacking a boot image v1."""
+ def test_unpack_boot_image_v1(self):
+ """Tests that mkbootimg(unpack_bootimg(image)) is an identity."""
with tempfile.TemporaryDirectory() as temp_out_dir:
# Output image path.
boot_img = os.path.join(temp_out_dir, 'boot.img')
+ boot_img_reconstructed = os.path.join(
+ temp_out_dir, 'boot.img.reconstructed')
# Creates blank images first.
kernel = generate_test_file(
os.path.join(temp_out_dir, 'kernel'), 0x1000)
@@ -607,37 +562,30 @@ class MkbootimgTest(unittest.TestCase):
'unpack_bootimg',
'--boot_img', boot_img,
'--out', os.path.join(temp_out_dir, 'out'),
+ '--format=mkbootimg',
]
- # The expected dict in mkbootimg_args.json.
- expected_mkbootimg_args = {
- 'header_version': '1',
- 'base': '0x00000000',
- 'kernel_offset': '0x00008000',
- 'ramdisk_offset': '0x01000000',
- 'second_offset': '0x00000000',
- 'tags_offset': '0x00000100',
- 'pagesize': '0x00001000',
- 'os_version': '11.0.0',
- 'os_patch_level': '2021-03',
- 'board': 'boot_v1',
- 'cmdline': cmdline + extra_cmdline,
- }
subprocess.run(mkbootimg_cmds, check=True)
- subprocess.run(unpack_bootimg_cmds, check=True)
+ result = subprocess.run(unpack_bootimg_cmds, check=True,
+ capture_output=True, encoding='utf-8')
+ mkbootimg_cmds = [
+ 'mkbootimg',
+ '--out', boot_img_reconstructed,
+ ]
+ mkbootimg_cmds.extend(shlex.split(result.stdout))
- json_file = os.path.join(
- temp_out_dir, 'out', 'mkbootimg_args.json')
- with open(json_file) as json_fd:
- actual_mkbootimg_args = json.load(json_fd)
- self.assertEqual(actual_mkbootimg_args,
- expected_mkbootimg_args)
+ subprocess.run(mkbootimg_cmds, check=True)
+ self.assertTrue(
+ filecmp.cmp(boot_img, boot_img_reconstructed),
+ 'reconstructed boot image differ from the original')
- def test_unpack_boot_image_v0_json_args(self):
- """Tests mkbootimg_args.json when unpacking a boot image v0."""
+ def test_unpack_boot_image_v0(self):
+ """Tests that mkbootimg(unpack_bootimg(image)) is an identity."""
with tempfile.TemporaryDirectory() as temp_out_dir:
# Output image path.
boot_img = os.path.join(temp_out_dir, 'boot.img')
+ boot_img_reconstructed = os.path.join(
+ temp_out_dir, 'boot.img.reconstructed')
# Creates blank images first.
kernel = generate_test_file(
os.path.join(temp_out_dir, 'kernel'), 0x1000)
@@ -672,30 +620,26 @@ class MkbootimgTest(unittest.TestCase):
'--boot_img', boot_img,
'--out', os.path.join(temp_out_dir, 'out'),
]
- # The expected dict in mkbootimg_args.json.
- expected_mkbootimg_args = {
- 'header_version': '0',
- 'base': '0x00000000',
- 'kernel_offset': '0x00008000',
- 'ramdisk_offset': '0x01000000',
- 'second_offset': '0x40000000',
- 'tags_offset': '0x00000100',
- 'pagesize': '0x00001000',
- 'os_version': '11.0.0',
- 'os_patch_level': '2021-03',
- 'board': 'boot_v0',
- 'cmdline': cmdline + extra_cmdline,
- }
+ unpack_bootimg_cmds = [
+ 'unpack_bootimg',
+ '--boot_img', boot_img,
+ '--out', os.path.join(temp_out_dir, 'out'),
+ '--format=mkbootimg',
+ ]
subprocess.run(mkbootimg_cmds, check=True)
- subprocess.run(unpack_bootimg_cmds, check=True)
+ result = subprocess.run(unpack_bootimg_cmds, check=True,
+ capture_output=True, encoding='utf-8')
+ mkbootimg_cmds = [
+ 'mkbootimg',
+ '--out', boot_img_reconstructed,
+ ]
+ mkbootimg_cmds.extend(shlex.split(result.stdout))
- json_file = os.path.join(
- temp_out_dir, 'out', 'mkbootimg_args.json')
- with open(json_file) as json_fd:
- actual_mkbootimg_args = json.load(json_fd)
- self.assertEqual(actual_mkbootimg_args,
- expected_mkbootimg_args)
+ subprocess.run(mkbootimg_cmds, check=True)
+ self.assertTrue(
+ filecmp.cmp(boot_img, boot_img_reconstructed),
+ 'reconstructed boot image differ from the original')
def test_boot_image_v2_cmdline_null_terminator(self):
"""Tests that kernel commandline is null-terminated."""
diff --git a/unpack_bootimg.py b/unpack_bootimg.py
index 0a508a9..2b176e5 100755
--- a/unpack_bootimg.py
+++ b/unpack_bootimg.py
@@ -21,7 +21,6 @@ Extracts the kernel, ramdisk, second bootloader, dtb and recovery dtbo images.
from argparse import ArgumentParser, FileType, RawDescriptionHelpFormatter
from struct import unpack
-import json
import os
import shlex
@@ -29,8 +28,6 @@ BOOT_IMAGE_HEADER_V3_PAGESIZE = 4096
VENDOR_RAMDISK_NAME_SIZE = 32
VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16
-MKBOOTIMG_ARGS_FILE = 'mkbootimg_args.json'
-
def create_out_dir(dir_path):
"""creates a directory 'dir_path' if it does not exist"""
@@ -130,58 +127,52 @@ class BootImageInfoFormatter:
return '\n'.join(lines)
- def _format_json_dict_boot_image_v2_and_below(self):
- """Returns a dict of mkbootimg.py arguments for v0-v2 boot.img."""
- args_dict = {}
-
- args_dict['header_version'] = str(self.header_version)
- # The type of pagesize is uint32_t, using '0xFFFFFFFF' as the
- # output format.
- args_dict['pagesize'] = f'{self.page_size:#010x}'
-
- # Kernel load address is base + kernel_offset in mkbootimg.py.
- # However, we don't know the value of 'base' when unpack a boot.img
- # in this script. So always set 'base' to be zero and 'kernel_offset'
- # to be the kernel load address. Same for 'ramdisk_offset',
- # 'second_offset', etc.
- # The following types are uint32_t, using '0xFFFFFFFF' as the output
- # format.
- args_dict['base'] = f'{0:#010x}'
- args_dict['kernel_offset'] = f'{self.kernel_load_address:#010x}'
- args_dict['ramdisk_offset'] = f'{self.ramdisk_load_address:#010x}'
- args_dict['second_offset'] = f'{self.second_load_address:#010x}'
- args_dict['tags_offset'] = f'{self.tags_load_address:#010x}'
-
- # dtb is added in boot image v2, and is absent in v1 or v0.
- if self.header_version == 2:
- # The type of dtb_offset is uint64_t, using '0xFFFFFFFFEEEEEEEE' as
- # the output format.
- args_dict['dtb_offset'] = f'{self.dtb_load_address:#018x}'
-
- args_dict['os_version'] = self.os_version
- args_dict['os_patch_level'] = self.os_patch_level
- args_dict['cmdline'] = self.cmdline + self.extra_cmdline
- args_dict['board'] = self.product_name
-
- return args_dict
-
- def _format_json_dict_boot_image_v3_and_above(self):
- """Returns a dict of mkbootimg.py arguments for >= v3 boot.img."""
- args_dict = {}
-
- args_dict['header_version'] = str(self.header_version)
- args_dict['os_version'] = self.os_version
- args_dict['os_patch_level'] = self.os_patch_level
- args_dict['cmdline'] = self.cmdline
+ def format_mkbootimg_argument(self):
+ args = []
+ args.extend(['--header_version', str(self.header_version)])
+ args.extend(['--os_version', self.os_version])
+ args.extend(['--os_patch_level', self.os_patch_level])
- return args_dict
+ args.extend(['--kernel', os.path.join(self.image_dir, 'kernel')])
+ args.extend(['--ramdisk', os.path.join(self.image_dir, 'ramdisk')])
- def format_json_dict(self):
- """Returns a dict of arguments to be used in mkbootimg.py later."""
if self.header_version <= 2:
- return self._format_json_dict_boot_image_v2_and_below()
+ if self.second_size > 0:
+ args.extend(['--second',
+ os.path.join(self.image_dir, 'second')])
+ if self.recovery_dtbo_size > 0:
+ args.extend(['--recovery_dtbo',
+ os.path.join(self.image_dir, 'recovery_dtbo')])
+ if self.dtb_size > 0:
+ args.extend(['--dtb', os.path.join(self.image_dir, 'dtb')])
+
+ args.extend(['--pagesize', f'{self.page_size:#010x}'])
+
+ # Kernel load address is base + kernel_offset in mkbootimg.py.
+ # However we don't know the value of 'base' when unpacking a boot
+ # image in this script, so we set 'base' to zero and 'kernel_offset'
+ # to the kernel load address, 'ramdisk_offset' to the ramdisk load
+ # address, ... etc.
+ args.extend(['--base', f'{0:#010x}'])
+ args.extend(['--kernel_offset',
+ f'{self.kernel_load_address:#010x}'])
+ args.extend(['--ramdisk_offset',
+ f'{self.ramdisk_load_address:#010x}'])
+ args.extend(['--second_offset',
+ f'{self.second_load_address:#010x}'])
+ args.extend(['--tags_offset', f'{self.tags_load_address:#010x}'])
+
+ # dtb is added in boot image v2, and is absent in v1 or v0.
+ if self.header_version == 2:
+ # dtb_offset is uint64_t.
+ args.extend(['--dtb_offset', f'{self.dtb_load_address:#018x}'])
+
+ args.extend(['--board', self.product_name])
+ args.extend(['--cmdline', self.cmdline + self.extra_cmdline])
else:
- return self._format_json_dict_boot_image_v3_and_above()
+ args.extend(['--cmdline', self.cmdline])
+
+ return args
def unpack_boot_image(args):
@@ -287,17 +278,12 @@ def unpack_boot_image(args):
image_info_list.append((boot_signature_offset, info.boot_signature_size,
'boot_signature'))
- for image_info in image_info_list:
- extract_image(image_info[0], image_info[1], args.boot_img,
- os.path.join(args.out, image_info[2]))
-
- # Saves the arguments to be reused in mkbootimg.py later.
- mkbootimg_args = info.format_json_dict()
- with open(os.path.join(args.out, MKBOOTIMG_ARGS_FILE), 'w') as f:
- json.dump(mkbootimg_args, f, sort_keys=True, indent=4)
+ create_out_dir(args.out)
+ for offset, size, name in image_info_list:
+ extract_image(offset, size, args.boot_img, os.path.join(args.out, name))
+ info.image_dir = args.out
- # TODO(yochiang): Support --format=mkbootimg
- print(info.format_pretty_text())
+ return info
class VendorBootImageInfoFormatter:
@@ -349,7 +335,7 @@ class VendorBootImageInfoFormatter:
return '\n'.join(lines)
- def format_mkbootimg_argument(self, null=False):
+ def format_mkbootimg_argument(self):
args = []
args.extend(['--header_version', str(self.header_version)])
args.extend(['--pagesize', f'{self.page_size:#010x}'])
@@ -361,12 +347,11 @@ class VendorBootImageInfoFormatter:
args.extend(['--vendor_cmdline', self.cmdline])
args.extend(['--board', self.product_name])
- dtb_path = os.path.join(self.image_dir, 'dtb')
- args.extend(['--dtb', dtb_path])
+ args.extend(['--dtb', os.path.join(self.image_dir, 'dtb')])
if self.header_version > 3:
- bootconfig_path = os.path.join(self.image_dir, 'bootconfig')
- args.extend(['--vendor_bootconfig', bootconfig_path])
+ args.extend(['--vendor_bootconfig',
+ os.path.join(self.image_dir, 'bootconfig')])
for entry in self.vendor_ramdisk_table:
(output_ramdisk_name, _, _, ramdisk_type,
@@ -380,38 +365,10 @@ class VendorBootImageInfoFormatter:
self.image_dir, output_ramdisk_name)
args.extend(['--vendor_ramdisk_fragment', vendor_ramdisk_path])
else:
- vendor_ramdisk_path = os.path.join(self.image_dir, 'vendor_ramdisk')
- args.extend(['--vendor_ramdisk', vendor_ramdisk_path])
-
- if null:
- return '\0'.join(args) + '\0'
- return shlex.join(args)
-
- def format_json_dict(self):
- """Returns a dict of arguments to be used in mkbootimg.py later."""
- args_dict = {}
- args_dict['header_version'] = str(self.header_version)
-
- # Format uint32_t as '0xFFFFFFFF', uint64_t as '0xFFFFFFFFEEEEEEEE'.
- args_dict['pagesize'] = f'{self.page_size:#010x}'
-
- # Kernel load address is base + kernel_offset in mkbootimg.py.
- # However, we don't know the value of 'base' when unpacking a
- # vendor_boot.img in this script. So always set 'base' to be zero and
- # 'kernel_offset' to be the kernel load address. Same for
- # 'ramdisk_offset', 'tags_offset' and 'dtb_offset'.
- args_dict['base'] = f'{0:#010x}'
- args_dict['kernel_offset'] = f'{self.kernel_load_address:#010x}'
- args_dict['ramdisk_offset'] = f'{self.ramdisk_load_address:#010x}'
- args_dict['tags_offset'] = f'{self.tags_load_address:#010x}'
- # The type of dtb_offset is uint64_t.
- args_dict['dtb_offset'] = f'{self.dtb_load_address:#018x}'
+ args.extend(['--vendor_ramdisk',
+ os.path.join(self.image_dir, 'vendor_ramdisk')])
- args_dict['vendor_cmdline'] = self.cmdline
- args_dict['board'] = self.product_name
-
- # TODO(bowgotsai): support for multiple vendor ramdisk (vendor boot v4).
- return args_dict
+ return args
def unpack_vendor_boot_image(args):
@@ -490,9 +447,10 @@ def unpack_vendor_boot_image(args):
) # header + vendor_ramdisk
image_info_list.append((dtb_offset, info.dtb_size, 'dtb'))
- for image_info in image_info_list:
- extract_image(image_info[0], image_info[1], args.boot_img,
- os.path.join(args.out, image_info[2]))
+ create_out_dir(args.out)
+ for offset, size, name in image_info_list:
+ extract_image(offset, size, args.boot_img, os.path.join(args.out, name))
+ info.image_dir = args.out
if info.header_version > 3:
vendor_ramdisk_by_name_dir = os.path.join(
@@ -506,27 +464,27 @@ def unpack_vendor_boot_image(args):
os.remove(dst_pathname)
os.symlink(src_pathname, dst_pathname)
- info.image_dir = args.out
-
- # Saves the arguments to be reused in mkbootimg.py later.
- mkbootimg_args = info.format_json_dict()
- with open(os.path.join(args.out, MKBOOTIMG_ARGS_FILE), 'w') as f:
- json.dump(mkbootimg_args, f, sort_keys=True, indent=4)
-
- if args.format == 'mkbootimg':
- print(info.format_mkbootimg_argument(null=args.null),
- end='' if args.null else None)
- else:
- print(info.format_pretty_text())
+ return info
def unpack_image(args):
boot_magic = unpack('8s', args.boot_img.read(8))[0].decode()
args.boot_img.seek(0)
if boot_magic == 'ANDROID!':
- unpack_boot_image(args)
+ info = unpack_boot_image(args)
elif boot_magic == 'VNDRBOOT':
- unpack_vendor_boot_image(args)
+ info = unpack_vendor_boot_image(args)
+ else:
+ raise ValueError(f'Not an Android boot image, magic: {boot_magic}')
+
+ if args.format == 'mkbootimg':
+ mkbootimg_args = info.format_mkbootimg_argument()
+ if args.null:
+ print('\0'.join(mkbootimg_args) + '\0', end='')
+ else:
+ print(shlex.join(mkbootimg_args))
+ else:
+ print(info.format_pretty_text())
def get_unpack_usage():
@@ -582,7 +540,6 @@ def parse_cmdline():
def main():
"""parse arguments and unpack boot image"""
args = parse_cmdline()
- create_out_dir(args.out)
unpack_image(args)