System.img是如何打包的
★★★ 友情鏈接 : 個人博客導(dǎo)讀首頁—點擊此處 ★★★
當(dāng)敲擊make命令時,會找到第一個目標(biāo)droid(在build/core/main.mk中),droid依賴droid_targets,droid_targets依賴droidcore和dist_files,droidcore的依賴關(guān)系如下:
.PHONY: droidcore droidcore: files \systemimage \$(INSTALLED_BOOTIMAGE_TARGET) \$(INSTALLED_RECOVERYIMAGE_TARGET) \$(INSTALLED_VBMETAIMAGE_TARGET) \$(INSTALLED_USERDATAIMAGE_TARGET) \$(INSTALLED_CACHEIMAGE_TARGET) \$(INSTALLED_BPTIMAGE_TARGET) \$(INSTALLED_VENDORIMAGE_TARGET) \$(INSTALLED_PRODUCTIMAGE_TARGET) \$(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \$(INSTALLED_FILES_FILE) \$(INSTALLED_FILES_FILE_VENDOR) \$(INSTALLED_FILES_FILE_PRODUCT) \$(INSTALLED_FILES_FILE_SYSTEMOTHER) \soong_docs切到build/core/Makefile中,我們看到systemimage又依賴INSTALLED_SYSTEMIMAGE,而INSTALLED_SYSTEMIMAGE又依賴BUILT_SYSTEMIMAGE,BUILT_SYSTEMIMAGE其實就是system.img
systemimage: $(INSTALLED_SYSTEMIMAGE) $(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH)BUILT_SYSTEMIMAGE := $(systemimage_intermediates)/system.img我們再來看看BUILT_SYSTEMIMAGE的依賴關(guān)系:
$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE) $(BUILD_IMAGE_SRCS)$(call build-systemimage-target,$@)BUILT_SYSTEMIMAGE依賴的一些文件生成后,會調(diào)用makefile中的build-systemimage-target函數(shù),我們接著去看build-systemimage-target的函數(shù)原型:
define build-systemimage-target@echo "Target system fs image: $(1)"$(call create-system-vendor-symlink)@mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt$(call generate-userimage-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt, \skip_fsck=true)$(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \./build/tools/releasetools/build_image.py \$(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \|| ( echo "Out of space? the tree size of $(TARGET_OUT) is (MB): " 1>&2 ;\du -sm $(TARGET_OUT) 1>&2;\if [ "$(INTERNAL_USERIMAGES_EXT_VARIANT)" == "ext4" ]; then \maxsize=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE); \if [ "$(BOARD_HAS_EXT4_RESERVED_BLOCKS)" == "true" ]; then \maxsize=$$((maxsize - 4096 * 4096)); \fi; \echo "The max is $$(( maxsize / 1048576 )) MB." 1>&2 ;\else \echo "The max is $$(( $(BOARD_SYSTEMIMAGE_PARTITION_SIZE) / 1048576 )) MB." 1>&2 ;\fi; \mkdir -p $(DIST_DIR); cp $(INSTALLED_FILES_FILE) $(DIST_DIR)/installed-files-rescued.txt; \exit 1 ) endef看起來挺復(fù)雜吧,其實都是些shell語句,仔細(xì)看還是能看懂的。看不懂也沒關(guān)系,我們只關(guān)心其中的關(guān)鍵語句 ./build/tools/releasetools/build_image.py,其后跟隨了一堆的參數(shù),我們不必關(guān)心這些參數(shù),只要 明白,這里調(diào)用了build_image.py
打開build_image.py,main函數(shù)如下,其實并不復(fù)雜,這里隨后調(diào)用了BuildImage()
繼續(xù)查看BuildImage(),原型如下:
def BuildImage(in_dir, prop_dict, out_file, target_out=None):"""Build an image to out_file from in_dir with property prop_dict.Args:in_dir: path of input directory.prop_dict: property dictionary.out_file: path of the output image file.target_out: path of the product out directory to read device specific FS config files.Returns:True iff the image is built successfully."""# system_root_image=true: build a system.img that combines the contents of# /system and the ramdisk, and can be mounted at the root of the file system.origin_in = in_dirfs_config = prop_dict.get("fs_config")base_fs_file = Noneif (prop_dict.get("system_root_image") == "true"and prop_dict["mount_point"] == "system"):in_dir = tempfile.mkdtemp()# Change the mount point to "/"prop_dict["mount_point"] = "/"if fs_config:# We need to merge the fs_config files of system and ramdisk.fd, merged_fs_config = tempfile.mkstemp(prefix="root_fs_config",suffix=".txt")os.close(fd)with open(merged_fs_config, "w") as fw:if "ramdisk_fs_config" in prop_dict:with open(prop_dict["ramdisk_fs_config"]) as fr:fw.writelines(fr.readlines())with open(fs_config) as fr:fw.writelines(fr.readlines())fs_config = merged_fs_configbuild_command = []fs_type = prop_dict.get("fs_type", "")run_fsck = Falsefs_spans_partition = Trueif fs_type.startswith("squash"):fs_spans_partition = Falseis_verity_partition = "verity_block_device" in prop_dictverity_supported = prop_dict.get("verity") == "true"verity_fec_supported = prop_dict.get("verity_fec") == "true"# Adjust the partition size to make room for the hashes if this is to be# verified.if verity_supported and is_verity_partition:partition_size = int(prop_dict.get("partition_size"))(adjusted_size, verity_size) = AdjustPartitionSizeForVerity(partition_size,verity_fec_supported)if not adjusted_size:return Falseprop_dict["partition_size"] = str(adjusted_size)prop_dict["original_partition_size"] = str(partition_size)prop_dict["verity_size"] = str(verity_size)# Adjust partition size for AVB hash footer or AVB hashtree footer.avb_footer_type = ''if prop_dict.get("avb_hash_enable") == "true":avb_footer_type = 'hash'elif prop_dict.get("avb_hashtree_enable") == "true":avb_footer_type = 'hashtree'if avb_footer_type:avbtool = prop_dict["avb_avbtool"]partition_size = prop_dict["partition_size"]# avb_add_hash_footer_args or avb_add_hashtree_footer_args.additional_args = prop_dict["avb_add_" + avb_footer_type + "_footer_args"]max_image_size = AVBCalcMaxImageSize(avbtool, avb_footer_type, partition_size,additional_args)if max_image_size == 0:return Falseprop_dict["partition_size"] = str(max_image_size)prop_dict["original_partition_size"] = partition_sizeif fs_type.startswith("ext"):build_command = [prop_dict["ext_mkuserimg"]]if "extfs_sparse_flag" in prop_dict:build_command.append(prop_dict["extfs_sparse_flag"])run_fsck = Truebuild_command.extend([in_dir, out_file, fs_type,prop_dict["mount_point"]])build_command.append(prop_dict["partition_size"])if "journal_size" in prop_dict:build_command.extend(["-j", prop_dict["journal_size"]])if "timestamp" in prop_dict:build_command.extend(["-T", str(prop_dict["timestamp"])])if fs_config:build_command.extend(["-C", fs_config])if target_out:build_command.extend(["-D", target_out])if "block_list" in prop_dict:build_command.extend(["-B", prop_dict["block_list"]])if "base_fs_file" in prop_dict:base_fs_file = ConvertBlockMapToBaseFs(prop_dict["base_fs_file"])if base_fs_file is None:return Falsebuild_command.extend(["-d", base_fs_file])build_command.extend(["-L", prop_dict["mount_point"]])if "extfs_inode_count" in prop_dict:build_command.extend(["-i", prop_dict["extfs_inode_count"]])if "flash_erase_block_size" in prop_dict:build_command.extend(["-e", prop_dict["flash_erase_block_size"]])if "flash_logical_block_size" in prop_dict:build_command.extend(["-o", prop_dict["flash_logical_block_size"]])# Specify UUID and hash_seed if using mke2fs.if prop_dict["ext_mkuserimg"] == "mkuserimg_mke2fs.sh":if "uuid" in prop_dict:build_command.extend(["-U", prop_dict["uuid"]])if "hash_seed" in prop_dict:build_command.extend(["-S", prop_dict["hash_seed"]])if "selinux_fc" in prop_dict:build_command.append(prop_dict["selinux_fc"])elif fs_type.startswith("squash"):build_command = ["mksquashfsimage.sh"]build_command.extend([in_dir, out_file])if "squashfs_sparse_flag" in prop_dict:build_command.extend([prop_dict["squashfs_sparse_flag"]])build_command.extend(["-m", prop_dict["mount_point"]])if target_out:build_command.extend(["-d", target_out])if fs_config:build_command.extend(["-C", fs_config])if "selinux_fc" in prop_dict:build_command.extend(["-c", prop_dict["selinux_fc"]])if "block_list" in prop_dict:build_command.extend(["-B", prop_dict["block_list"]])if "squashfs_compressor" in prop_dict:build_command.extend(["-z", prop_dict["squashfs_compressor"]])if "squashfs_compressor_opt" in prop_dict:build_command.extend(["-zo", prop_dict["squashfs_compressor_opt"]])if "squashfs_block_size" in prop_dict:build_command.extend(["-b", prop_dict["squashfs_block_size"]])if "squashfs_disable_4k_align" in prop_dict and prop_dict.get("squashfs_disable_4k_align") == "true":build_command.extend(["-a"])elif fs_type.startswith("f2fs"):build_command = ["mkf2fsuserimg.sh"]build_command.extend([out_file, prop_dict["partition_size"]])else:print("Error: unknown filesystem type '%s'" % (fs_type))return Falseif in_dir != origin_in:# Construct a staging directory of the root file system.ramdisk_dir = prop_dict.get("ramdisk_dir")if ramdisk_dir:shutil.rmtree(in_dir)shutil.copytree(ramdisk_dir, in_dir, symlinks=True)staging_system = os.path.join(in_dir, "system")shutil.rmtree(staging_system, ignore_errors=True)shutil.copytree(origin_in, staging_system, symlinks=True)has_reserved_blocks = prop_dict.get("has_ext4_reserved_blocks") == "true"ext4fs_output = Nonetry:if fs_type.startswith("ext4"):(ext4fs_output, exit_code) = RunCommand(build_command)else:(_, exit_code) = RunCommand(build_command)finally:if in_dir != origin_in:# Clean up temporary directories and files.shutil.rmtree(in_dir, ignore_errors=True)if fs_config:os.remove(fs_config)if base_fs_file is not None:os.remove(base_fs_file)if exit_code != 0:return False# Bug: 21522719, 22023465# There are some reserved blocks on ext4 FS (lesser of 4096 blocks and 2%).# We need to deduct those blocks from the available space, since they are# not writable even with root privilege. It only affects devices using# file-based OTA and a kernel version of 3.10 or greater (currently just# sprout).# Separately, check if there's enough headroom space available. This is useful for# devices with low disk space that have system image variation between builds.if (has_reserved_blocks or "partition_headroom" in prop_dict) and fs_type.startswith("ext4"):assert ext4fs_output is not Noneext4fs_stats = re.compile(r'Created filesystem with .* (?P<used_blocks>[0-9]+)/'r'(?P<total_blocks>[0-9]+) blocks')m = ext4fs_stats.match(ext4fs_output.strip().split('\n')[-1])used_blocks = int(m.groupdict().get('used_blocks'))total_blocks = int(m.groupdict().get('total_blocks'))reserved_blocks = 0headroom_blocks = 0adjusted_blocks = total_blocksif has_reserved_blocks:reserved_blocks = min(4096, int(total_blocks * 0.02))adjusted_blocks -= reserved_blocksif "partition_headroom" in prop_dict:headroom_blocks = int(prop_dict.get('partition_headroom')) / BLOCK_SIZEadjusted_blocks -= headroom_blocksif used_blocks > adjusted_blocks:mount_point = prop_dict.get("mount_point")print("Error: Not enough room on %s (total: %d blocks, used: %d blocks, ""reserved: %d blocks, headroom: %d blocks, available: %d blocks)" % (mount_point, total_blocks, used_blocks, reserved_blocks,headroom_blocks, adjusted_blocks))return Falseif not fs_spans_partition:mount_point = prop_dict.get("mount_point")partition_size = int(prop_dict.get("partition_size"))image_size = GetSimgSize(out_file)if image_size > partition_size:print("Error: %s image size of %d is larger than partition size of ""%d" % (mount_point, image_size, partition_size))return Falseif verity_supported and is_verity_partition:ZeroPadSimg(out_file, partition_size - image_size)# create the verified image if this is to be verifiedif verity_supported and is_verity_partition:if not MakeVerityEnabledImage(out_file, verity_fec_supported, prop_dict):return False# Add AVB HASH or HASHTREE footer (metadata).if avb_footer_type:avbtool = prop_dict["avb_avbtool"]original_partition_size = prop_dict["original_partition_size"]partition_name = prop_dict["partition_name"]# key_path and algorithm are only available when chain partition is used.key_path = prop_dict.get("avb_key_path")algorithm = prop_dict.get("avb_algorithm")salt = prop_dict.get("avb_salt")# avb_add_hash_footer_args or avb_add_hashtree_footer_argsadditional_args = prop_dict["avb_add_" + avb_footer_type + "_footer_args"]if not AVBAddFooter(out_file, avbtool, avb_footer_type, original_partition_size,partition_name, key_path, algorithm, salt, additional_args):return Falseif run_fsck and prop_dict.get("skip_fsck") != "true":success, unsparse_image = UnsparseImage(out_file, replace=False)if not success:return False# Run e2fsck on the inflated image filee2fsck_command = ["e2fsck", "-f", "-n", unsparse_image](_, exit_code) = RunCommand(e2fsck_command)os.remove(unsparse_image)return exit_code == 0這里面有一堆的參數(shù),其實都非常有深意后,我們在學(xué)習(xí)階段可以不必考慮,當(dāng)我們遇到相關(guān)問題時,那時可能就會對照這些參數(shù),了解其功能和作用。 而我們這里重點查看的,如下:
if prop_dict["ext_mkuserimg"] == "mkuserimg_mke2fs.sh":if "uuid" in prop_dict:build_command.extend(["-U", prop_dict["uuid"]])if "hash_seed" in prop_dict:build_command.extend(["-S", prop_dict["hash_seed"]])這里是執(zhí)行了mkuserimg_mke2fs.sh腳本,我們再去看看這個腳本吧
路徑:system/extras/ext4_utils/mkuserimg_mke2fs.sh
同樣,打開該文件后發(fā)現(xiàn)又是一堆的參數(shù)命令,看也看不懂,我們依然只是關(guān)心其核心的部分,截取如下:
這里做了兩件事:
(1)mke2fs打包生成了system.img
(2)e2fsdroid制作了system.map
mke2fs對應(yīng)的源碼:external/e2fsprogs/misc/mke2fs.c
e2fsdroid對應(yīng)的源碼 : external/e2fsprogs/contrib/android/e2fsdroid.c
深思:
其實呢,在正常的android編譯,并不會生成system.map,因為最后一個參數(shù)$OUTPUT_FILE為空. 在android的設(shè)計中system.map是在制作OTA的target-files文件時生成的,然后在制作block差分別時候,會用到system.map,一個block一個block的去求diff。
在制作OTA的target-files時,會重新打包system.img,其流程:
add_img_to_target_files直接調(diào)用了build_image.py中的CreateImage()函數(shù),與正常的打包system.img參數(shù)稍微不同,這里增加了一個block_list="system.map"參數(shù),所以其打包結(jié)束后,生成了system.map
Q & A:
1、system.img是怎樣制作的?
答:使用mke2fs工具制作的
2、system.map是做什么用的?先有的system.img,還是先用的system.map?
答:system.map是制作OTA的target-files文件時,重新打包system.img后,根據(jù)system.img做出的system.map,制作差分別時候,會用到system.map文件
不足之處,歡迎指正!
總結(jié)
以上是生活随笔為你收集整理的System.img是如何打包的的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [crypto]-52-python3中
- 下一篇: 剖析boot.img的制作流程