细说dex2oat(1)
dex2oat的命令行参数
首先我们先看一下dex2oat都支持一些什么样的命令行参数:
通用类
- -j<线程数>:编译时使用多少个线程。缺省值为默认的CPU核数。例:-j8
输入输出的文件类
- --dex-file=
:待编译的.dex, .jar或者.apk文件 - --dex-location=
:dex文件的路径 - --zip-fd=
:包含classes.dex文件的zip的文件描述符 - --zip-location=
:zip文件路径 - --oat-file=<输出的oat文件名>:输出的oat文件名
- --oat-fd=<输出文件描述符>:输出文件描述符
- --oat-location=<输出的oat文件的路径>:输出的oat文件的路径
- --oat-symbols=
:指定输出完整符号的oat路径 - --image=
:指定输出的image文件名,只有在编译boot.art时才需要,编译普通应用的时候不需要 - --image-classes=<预编译文件列表>:指定preloaded-classes的路径,例:--image=frameworks/base/preloaded-classes
- --base=
:指定boot image的基地址,例:--base=0x50000000 - --boot-image=
:指定boot class的文件。例:--boot-image=/system/framework/boot.art,默认值为:$ANDROID_ROOT/system/framework/boot.art - --android-root=<路径>:portable linking所用的库的路径。例:--android-root=out/host/linux-x86,默认值:$ANDROID_ROOT
指令集类
- --instruction-set=(arm|arm64|mips|mips64|x86|x86_64):指定编译的指令集。例:--instruction-set=x86,默认:arm
- --instruction-set-variant=<子架构名>:某一大的架构下更细致的分类,例:--instruction-set-variant=cortex-a53
- --instruction-set-features=<指令集参数>。例:--instruction-set-features=div,默认:default
编译器优化选项类
- --compile-pic:Force indirect use of code, methods, and classes. 默认:disabled
- --compiler-filter=(verify-none| interpret-only| space |balanced |speed |everything |time):选择compiler filter。例:--compiler-filter=everything。默认值:speed
- --huge-method-max=<方法指令数>:巨型方法的指令数,用于编译器调优。例:--huge-method-max=10000,默认值:10000
- --large-method-max=<方法指令数>:大型方法的指令数,用于编译器调优。例:--large-method-max=600,默认值:600
- --small-method-max=<方法指令数>:小型方法的指令数,用于编译器调优。例:--small-method-max=60,默认值:60
- --tiny-method-max=<方法指令数>:微型方法的指令数,用于编译器调优。例:--tiny-method-max=20,默认值:20
- --num-dex-methods=<方法数>:小型dex文件的方法上限,用于编译器调优。如果发现是个小型的dex文件,而编译器的filter不是interpret-only或者verify-none的话,就用speed filter。例:--num-dex-method=900,默认值:900
- --inline-depth-limit=<深度限制>:编译器调优用,只建议开发和实验用。例:--inline-depth-limit=5,默认值:5
- --inline-max-code-units=<方法数>:inline调优用,实验用。例:--inline-max-code-units=100,默认值:100
- --dump-timing: 显示时间都花到哪儿去了。
重定位信息类
- --include-patch-information:编译时包含patch信息,可以在不重编的情况下重定位。
- --no-include-patch-information:不包含patch信息。
调试信息类
- -g:与--generate-debug-info相同
- --generate-debug-info:生成所有可用的调试信息。可以通过标准的strip命令或者objcopy命令来压缩掉无用信息。
- --no-generate-debug-info:不生成调试信息
运行参数类
- --runtime-arg <参数>:指定运行时参数,如:初始堆大小,最大堆大小,详细输出等。每次只能传一个参数。例:--runtime-arg -Xms256m
- --profile-file=
:指定profile信息,供编译器例用
编译选项类
- --print-pass-names: 打印pass name信息
- --disable-passes=
:禁止某些pass项,例:--disable-passes=UseCount,BBOptimizations - --print-pass-options:打印当前配置的pass信息
- --pass-options=Pass1Name:Pass1OptionName:Pass1Option#,Pass2Name:Pass2OptionName:Pass2Option#:指定pass信息。
临时文件类
- --swap-file=<交换文件名>:指定交换文件,例:--swap-file=/data/tmp/swap.001
- --swap-fd=<文件描述符>:指定交换文件的描述符
Makefile中指定的编译参数
上面我们学习了dex2oat的参数的简介,下面我们学以致用,看看在真实的环境中它们是如何被使用的。
运行时的Xms和Xmx参数
我们直接看build时,dex2oat的参数是如何被传进去的,在build/core/dex_preopt_libart.mk中:
91# For a single jar or APK
92
93# $(1): the input .jar or .apk file
94# $(2): the output .odex file
95define dex2oat-one-file
96$(hide) rm -f $(2)
97$(hide) mkdir -p $(dir $(2))
98$(hide) $(DEX2OAT) \
99 --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \
首先指定两个运行时参数,这两个参数是在前面定义的:
44DEX2OAT_XMS := $(call get-product-default-property,dalvik.vm.dex2oat-Xms)
45DEX2OAT_XMX := $(call get-product-default-property,dalvik.vm.dex2oat-Xmx)
也就是说,这两个值取自属性dalvik.vm.dex2oat-Xms和vm.dex2oat-Xmx。这两个属性是哪里来的呢,是在/build/target/product/runtime_libart.mk中,编译的时候指定进来的:
48PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
49 dalvik.vm.image-dex2oat-Xms=64m \
50 dalvik.vm.image-dex2oat-Xmx=64m \
51 dalvik.vm.dex2oat-Xms=64m \
52 dalvik.vm.dex2oat-Xmx=512m \
53 ro.dalvik.vm.native.bridge=0 \
这样,这两个值分别是64m和512m。
boot-image
我们回到dex_preopt_libart.mk中继续看:
100 --boot-image=$(PRIVATE_DEX_PREOPT_IMAGE_LOCATION) \
查这个PRIVATE_DEX_PREOPT_IMAGE_LOCATION,定义于/build/core/setup_one_odex.mk中:
30$(my_built_odex): PRIVATE_DEX_PREOPT_IMAGE_LOCATION := $(my_dex_preopt_image_location)
然后再向上追my_dex_preopt_image_location
22ifdef LOCAL_DEX_PREOPT_IMAGE_LOCATION
23my_dex_preopt_image_location := $(LOCAL_DEX_PREOPT_IMAGE_LOCATION)
24else
25my_dex_preopt_image_location := $($(my_2nd_arch_prefix)DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)
26endif
LOCAL_DEX_PREOPT_IMAGE_LOCATION没有定义,继续顺藤摸瓜。
23$(my_2nd_arch_prefix)DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION := $(DEXPREOPT_BOOT_JAR_DIR_FULL_PATH)/boot.art
再继续追DEXPREOPT_BOOT_JAR_DIR_FULL_PATH:
17DEXPREOPT_BOOT_JAR_DIR_FULL_PATH := $(DEXPREOPT_PRODUCT_DIR_FULL_PATH)/$(DEXPREOPT_BOOT_JAR_DIR)
先看后面的DEXPREOPT_BOOT_JAR_DIR,原来就是system/framework
16DEXPREOPT_BOOT_JAR_DIR := system/framework
再看前面的DEXPREOPT_PRODUCT_DIR_FULL_PATH,是out下的dex_bootjars
14DEXPREOPT_PRODUCT_DIR_FULL_PATH := $(PRODUCT_OUT)/dex_bootjars
最后--boot-image
的值为out/dex_bootjars/system/framework/boot.art
dex文件和oat文件的路径
这个就不多说了
101 --dex-file=$(1) \
102 --dex-location=$(PRIVATE_DEX_LOCATION) \
103 --oat-file=$(2) \
104 --android-root=$(PRODUCT_OUT)/system \
指令集相关
105 --instruction-set=$($(PRIVATE_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH) \
追查:
604DEX2OAT_TARGET_ARCH := $(TARGET_ARCH)
这个板级驱动已经配好了。
106 --instruction-set-variant=$($(PRIVATE_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT) \
107 --instruction-set-features=$($(PRIVATE_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
在同样的位置定义:
604DEX2OAT_TARGET_ARCH := $(TARGET_ARCH)
605ifeq ($(TARGET_CPU_VARIANT),)
606ifeq ($(TARGET_ARCH_VARIANT),)
607DEX2OAT_TARGET_CPU_VARIANT := default
608else
609DEX2OAT_TARGET_CPU_VARIANT := $(TARGET_ARCH_VARIANT)
610endif
611else
612DEX2OAT_TARGET_CPU_VARIANT := $(TARGET_CPU_VARIANT)
613endif
614DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES := default
615
其他参数
108 --include-patch-information --runtime-arg -Xnorelocate --no-generate-debug-info \
109 --abort-on-hard-verifier-error \
110 $(PRIVATE_DEX_PREOPT_FLAGS)
111endef
include-patch-information兼runtime-arg -Xnorelocate,生成重定位的信息,因为后面还要走patchoat呢。
no-generate-debug-info,不生成调试信息
dex2oat的selinux权限配置
大家留神啊,现在已经是selinux的时代了,dex2oat也需要配置相关的权限:
# dex2oat
type dex2oat, domain;
type dex2oat_exec, exec_type, file_type;
allow dex2oat dalvikcache_data_file:file write;
# Read symlinks in /data/dalvik-cache
allow dex2oat dalvikcache_data_file:lnk_file read;
allow dex2oat installd:fd use;
# Read already open asec_apk_file file descriptors passed by installd.
# Also allow reading unlabeled files, to allow for upgrading forward
# locked APKs.
allow dex2oat asec_apk_file:file read;
allow dex2oat unlabeled:file read;
allow dex2oat oemfs:file read;
allow dex2oat apk_tmp_file:file read;
这还不算,在installd的权限配置/external/sepolicy/installd.te中,明确设置了
71# Run dex2oat in its own sandbox.
72domain_auto_trans(installd, dex2oat_exec, dex2oat)
细说dex2oat(2)
生成boot.art和boot.oat
前面讲的参数中,有一项是--boot-image。我们先看一下这个boot-image是如何编出来的,正好是一个完整的dex2oat的例子.
我们看下在MediaTek MT6753平台下,是如何生成的。
MT6753是64位Cortex-A53的架构,所以boot.art也是64位和32位两套。
64位的boot.art
先看64位的吧:
out/host/linux-x86/bin/dex2oat
--runtime-arg -Xms64m
--runtime-arg -Xmx64m
初始堆大小和最大堆大小。
--image-classes=frameworks/base/preloaded-classes
预加载类的路径
--dex-file=out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/javalib.jar --dex-file=out/target/common/obj/JAVA_LIBRARIES/conscrypt_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/okhttp_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/core-junit_intermediates/javalib.jar --dex-file=out/target/common/obj/JAVA_LIBRARIES/bouncycastle_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/voip-common_intermediates/javalib.jar --dex-file=out/target/common/obj/JAVA_LIBRARIES/ims-common_intermediates/javalib.jar --dex-file=out/target/common/obj/JAVA_LIBRARIES/apache-xml_intermediates/javalib.jar --dex-file=out/target/common/obj/JAVA_LIBRARIES/org.apache.http.legacy.boot_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/mediatek-common_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/mediatek-framework_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/mediatek-telephony-common_intermediates/javalib.jar
以上是一大堆的dex文件
--dex-location=/system/framework/core-libart.jar
--dex-location=/system/framework/conscrypt.jar
--dex-location=/system/framework/okhttp.jar
--dex-location=/system/framework/core-junit.jar
--dex-location=/system/framework/bouncycastle.jar
--dex-location=/system/framework/ext.jar
--dex-location=/system/framework/framework.jar
--dex-location=/system/framework/telephony-common.jar
--dex-location=/system/framework/voip-common.jar
--dex-location=/system/framework/ims-common.jar
--dex-location=/system/framework/apache-xml.jar
--dex-location=/system/framework/org.apache.http.legacy.boot.jar
--dex-location=/system/framework/mediatek-common.jar
--dex-location=/system/framework/mediatek-framework.jar
--dex-location=/system/framework/mediatek-telephony-common.jar
以上是上面那一大堆dex对应的jar文件路径
--oat-symbols=out/target/product/6753_65t_m/symbols/system/framework/arm64/boot.oat
符号表的位置
--oat-file=out/target/product/6753_65t_m/dex_bootjars/system/framework/arm64/boot.oat
--oat-location=/system/framework/arm64/boot.oat
--image=out/target/product/6753_65t_m/dex_bootjars/system/framework/arm64/boot.art
输出文件有两个:一个是boot.oat,一个是boot.art。
--base=0x70000000
基地址0x70000000
--instruction-set=arm64
--instruction-set-variant=cortex-a53
--instruction-set-features=default
对于指令架构,除了arm64,更细的是cortex-a53
--android-root=out/target/product/6753_65t_m/system
--include-patch-information
--runtime-arg -Xnorelocate
--no-generate-debug-info
最后这几个前面都说过了。
32位的boot.art
out/host/linux-x86/bin/dex2oat
--runtime-arg -Xms64m
--runtime-arg -Xmx64m
--image-classes=frameworks/base/preloaded-classes
--dex-file=out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/conscrypt_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/okhttp_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/core-junit_intermediates/javalib.jar --dex-file=out/target/common/obj/JAVA_LIBRARIES/bouncycastle_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/voip-common_intermediates/javalib.jar --dex-file=out/target/common/obj/JAVA_LIBRARIES/ims-common_intermediates/javalib.jar --dex-file=out/target/common/obj/JAVA_LIBRARIES/apache-xml_intermediates/javalib.jar --dex-file=out/target/common/obj/JAVA_LIBRARIES/org.apache.http.legacy.boot_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/mediatek-common_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/mediatek-framework_intermediates/javalib.jar
--dex-file=out/target/common/obj/JAVA_LIBRARIES/mediatek-telephony-common_intermediates/javalib.jar
--dex-location=/system/framework/core-libart.jar
--dex-location=/system/framework/conscrypt.jar
--dex-location=/system/framework/okhttp.jar
--dex-location=/system/framework/core-junit.jar
--dex-location=/system/framework/bouncycastle.jar
--dex-location=/system/framework/ext.jar
--dex-location=/system/framework/framework.jar
--dex-location=/system/framework/telephony-common.jar
--dex-location=/system/framework/voip-common.jar
--dex-location=/system/framework/ims-common.jar
--dex-location=/system/framework/apache-xml.jar
--dex-location=/system/framework/org.apache.http.legacy.boot.jar
--dex-location=/system/framework/mediatek-common.jar
--dex-location=/system/framework/mediatek-framework.jar
--dex-location=/system/framework/mediatek-telephony-common.jar
上面都是java和dex,所以跟64位没有什么区别。
--oat-symbols=out/target/product/6753_65t_m/symbols/system/framework/arm/boot.oat
--oat-file=out/target/product/6753_65t_m/dex_bootjars/system/framework/arm/boot.oat
--oat-location=/system/framework/arm/boot.oat
--image=out/target/product/6753_65t_m/dex_bootjars/system/framework/arm/boot.art
输出文件从arm64目录换到了arm目录
--base=0x70000000
基地址没变,反正patchoat的时候也还要改。
--instruction-set=arm
--instruction-set-variant=cortex-a53
--instruction-set-features=default
指令集从arm64变成了arm,其它的参数都不变
--android-root=out/target/product/6753_65t_m/system
--include-patch-information
--runtime-arg -Xnorelocate
--no-generate-debug-info
dex2oat编译应用的例子
out/host/linux-x86/bin/dex2oat
--runtime-arg -Xms64m
--runtime-arg -Xmx512m
这两个值在前面分析Android.oat.mak时已经分析过了,这里验证了我们的分析是正确的。
--boot-image=out/target/product/6753_65t_m/dex_bootjars/system/framework/boot.art
这个boot.art就是上一节讲的命令刚刚生成的。
--dex-file=out/target/product/6753_65t_m/obj/APPS/MusicFX_intermediates/oat/arm64/package.odex.input
--dex-location=/system/app/MusicFX/MusicFX.apk
输入的dex文件
--oat-file=out/target/product/6753_65t_m/obj/APPS/MusicFX_intermediates/oat/arm64/package.odex
输出到odex文件,虽然名字叫odex,但是实际上是个oat。普通的应用就不像上节讲的输出boot.oat和boot.art的时候那样输出那么多了,只有一个odex文件,符号表和image都不用。
--android-root=out/target/product/6753_65t_m/system
--instruction-set=arm64
--instruction-set-variant=cortex-a53
--instruction-set-features=default
--include-patch-information
--runtime-arg -Xnorelocate
--no-generate-debug-info
--abort-on-hard-verifier-error
细说dex2oat(3)
dex2oat是如何在makefile中落地的
在Android的mk系统中,调用dex2oat中有几处,但是真正被调用来生成目标系统上的oat的是下面这个,位于/build/core/dex_preopt_libart.mk中:
# For a single jar or APK
# $(1): the input .jar or .apk file
# $(2): the output .odex file
define dex2oat-one-file
$(hide) rm -f $(2)
$(hide) mkdir -p $(dir $(2))
$(hide) $(DEX2OAT) \
--runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \
--boot-image=$(PRIVATE_DEX_PREOPT_IMAGE_LOCATION) \
--dex-file=$(1) \
--dex-location=$(PRIVATE_DEX_LOCATION) \
--oat-file=$(2) \
--android-root=$(PRODUCT_OUT)/system \
--instruction-set=$($(PRIVATE_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH) \
--instruction-set-variant=$($(PRIVATE_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT) \
--instruction-set-features=$($(PRIVATE_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
--include-patch-information --runtime-arg -Xnorelocate --no-generate-debug-info \
--abort-on-hard-verifier-error \
$(PRIVATE_DEX_PREOPT_FLAGS)
endef
这个dex2oat-one-file,是被dexpreopt-one-file调用,位于/build/core/dex_preopt.mk中:
include $(BUILD_SYSTEM)/dex_preopt_libart.mk
# Define dexpreopt-one-file based on current default runtime.
# $(1): the input .jar or .apk file
# $(2): the output .odex file
define dexpreopt-one-file
$(call dex2oat-one-file,$(1),$(2))
endef
以下根据生成的类型不同,包、库、预先编译好的应用有不同的路径。
Build package中的odex生成
/build/core/package_internal.mk
###############################
## Rule to build the odex file
ifdef LOCAL_DEX_PREOPT
$(built_odex): PRIVATE_DEX_FILE := $(built_dex)
# Use pattern rule - we may have multiple built odex files.
$(built_odex) : $(dir $(LOCAL_BUILT_MODULE))% : $(built_dex)
$(hide) mkdir -p $(dir $@) && rm -f $@
$(add-dex-to-package)
$(hide) mv $@ [email protected]
$(call dexpreopt-one-file,[email protected],$@)
$(hide) rm [email protected]
endif
Build java library中的odex生成
/build/core/java_library.mk
$(built_odex): PRIVATE_MODULE := $(LOCAL_MODULE)
# Use pattern rule - we may have multiple built odex files.
$(built_odex) : $(dir $(LOCAL_BUILT_MODULE))% : $(common_javalib.jar)
@echo "Dexpreopt Jar: $(PRIVATE_MODULE) ($@)"
$(call dexpreopt-one-file,$<,$@)
Prebuild的odex生成
###############################
## Rule to build the odex file
ifdef LOCAL_DEX_PREOPT
$(built_odex) : $(my_prebuilt_src_file)
$(call dexpreopt-one-file,$<,$@)
endif
install odex
我们再来看看,odex是如何被install到system分区下面的。
# Use pattern rule - we may have multiple installed odex files.
# Ugly syntax - See the definition get-odex-file-path.
$(installed_odex) : $(dir $(LOCAL_INSTALLED_MODULE))%$(notdir $(word 1,$(installed_odex))) \
: $(dir $(LOCAL_BUILT_MODULE))%$(notdir $(word 1,$(built_odex))) \
| $(ACP)
@echo "Install: $@"
$(copy-file-to-target)
endif
我们看一个实际的例子:
Install: out/target/product/6753_64_m/system/app/Note/oat/arm64/Note.odex
mkdir -p out/target/product/6753_64_m/system/app/Note/oat/arm64/
out/host/linux-x86/bin/acp -fp out/target/product/6753_64_m/obj/APPS/Note_intermediates/oat/arm64/package.odex out/target/product/6753_64_m/system/app/Note/oat/arm64/Note.odex
Install: out/target/product/6753_64_m/system/priv-app/Settings/oat/arm64/Settings.odex
mkdir -p out/target/product/6753_64_m/system/priv-app/Settings/oat/arm64/
out/host/linux-x86/bin/acp -fp out/target/product/6753_64_m/obj/APPS/Settings_intermediates/oat/arm64/package.odex out/target/product/6753_64_m/system/priv-app/Settings/oat/arm64/Settings.odex
definitions.mk中预定义的一些函数
除了编译之外,我们得先看一些预先定义好的函数。它们定义于/build/core/definitions.mk中。
add-dex-to-package
将class文件压缩成classes*.dex
define add-dex-to-package
$(hide) zip -qj $@ $(dir $(PRIVATE_DEX_FILE))classes*.dex
endef
copy-file-to-target
将依赖文件复制到目标文件
define copy-file-to-target
@mkdir -p $(dir $@)
$(hide) $(ACP) -fp $< $@
endef