当前位置: 首页 > 工具软件 > fast dex > 使用案例 >

Android dex2oat

袁智明
2023-12-01

Android从4.4开始正式引入了ART虚拟机,并从5.0开始取代了Dalvik成为默认的虚拟机。ART与Dalvik最大的不同就是,在程序安装的时候就将其编译成本地指令集(即所谓的Ahead Of Time,AOT),而不像Dalvik是在运行的时候对经常掉用的函数动态编译的(即所谓的Just In Time,JIT)。但是最终编译的动作,还是通过程序dex2oat来执行的。这个程序本身可带非常多的参数,本文就对一些常用参数的含义做一个简要的解释。

1)--dex-file=<dex-file>

指定要编译的文件路径,这个文件既可以是一个dex文件,也可以是一个内部包含dex文件的apk或jar文件。

对于一次要编译多个文件的情况,每个文件都要用单独的一个--dex-file来指定。

2)--dex-location=<dex-location>

指定要写入最终编译出目标oat文件内部的编译文件的路径。

对于一次要编译多个文件的情况,每一个路径都需要用--dex-location来指定,且出现的次序要和--dex-file指定的次序相同。

3)--zip-fd=<file-descriptor>

指定要编译文件的文件描述符。--zip-fd和--dex-file必须指定一个,但两者也相互冲突,不能同时出现,否则都会报错退出。

4)--zip-location=<zip-location>

说明前面通过--zip-fd参数传入的那个文件描述符号对应的文件的路径。这个参数和--zip-location必须配对使用,也就是说,如果没有--zip-fd但是传入了--zip-location,或者有--zip-fd但是没有传入--zip-location,两种情况dex2oat都会报错退出。

5)--oat-file=<file.oat>

指定编译输出的oat文件的路径。

6)--oat-fd=<number>

指定编译输出的oat文件的文件描述符。--oat-fd和--oat-file必须指定一个,但两者也是冲突的,不能同时出现,否则都会报错退出。

7)--oat-location=<oat-name>

也是指定编译输出的oat文件的路径,不过与--oat-file不同,--oat-location是必须要和--oat-fd配对使用的,两个参数必须同时出现。

通过上面可以看出,dex2oat接受两种方式指定要编译的文件和输出的文件,分别是通过文件路径或通过文件的描述符。如果是通过文件的描述符的话,dex2oat虽然可以对这个文件进行操作,但并不知道这个文件在什么位置,因此还需要通过以location结尾的参数来告之;而如果是通过文件路径的话,则dex2oat会直接打开这个文件,再对其进行操作。

而对于结尾为fd的参数来说,一般都是父进程先将文件打开,获得文件描述符后,在fork出的子进程中掉用execv来运行dex2oat程序,因为子进程是继承父进程所打开的文件描述符的,因此可以直接对其操作。

8)--oat-symbols=<file.oat>

也是指定编译要输出的oat文件,但和--oat-file不同的是,如果使用--oat-symbols,则输出的oat文件会包含所有的符号,而--oat-file则不会。

并且--oat-symbols和--oat-fd是冲突的,只允许出现一个,如果两个同时出现,则会报错退出。

9)--image=<file.art>

指定要编译出的ART镜像文件(Image)所存放的路径。如果指定了这个参数,就说明此次掉用dex2oat是为了编译出系统镜像文件,而不是编译一个普通的应用程序。

这个参数和--oat-fd是冲突的,两者不能同时出现,否则dex2oat会报错退出。

10)--image-classes=<classname-file>

指定要编译处的ART镜像文件(Image)需要包含哪些类的一个列表。

对于Android 5.x和6.x系统来说,这个参数被设置成“/system/etc/preloaded-classes”。

11)--image-classes-zip=<classname-zip-file>

有时候--image-classes指定的那个文件非常的大,需要先用ZIP压缩一下。如果是这样的话,就需要用--image-classes-zip指定那个压缩文件的路径,这时候就会在这个指定的ZIP文件中找--image-classes指定的那个文件读取类列表。

因此,--image-classes-zip一定要和--image-classes一起使用。当然也可以只有--image-classes而没有--image-classes-zip,这时候dex2oat就直接从--image-classes指定的文件中读取类列表。

对于Android 5.x和6.x系统来说,没有用到这个参数。

12)--base=<hex-address>

指定image加载进内存时,被映射到的起始内存地址。

13)--boot-image=<file.art>

指定系统镜像文件存放的路径。如果不指定的话,默认系统将其设置成“/system/framework/boot.art”。但是,实际上镜像文件并不是存放在这个位置,而是在“/system/framework/<image_isa>/boot.art”,比如在arm平台上(非64位),其镜像文件是放在“/system/framework/arm/boot.art”中的。

14)--android-root=<path>

指定Android系统的根路径。如果不指定的话,默认情况下dex2oat会读取当前系统的环境变量ANDROID_ROOT,将其值作为Android系统的根路径,继续编译下去。如果连环境变量ANDROID_ROOT也读不到的话,则报错退出。

一般情况下,Android设备上,环境变量ANDROID_ROOT被设置成了“/system”。

15)--instruction-set=(arm|arm64|mips|mips64|x86|x86_64)

指定要用什么指令集来编译dex文件。目前共支持六种指令集,三种平台(arm、mips和x86),每种平台有分为32位和64位两种。

16)--instruction-set-features=...,

对于一个处理器来说,除了要知道其能执行代码的指令集之外,还需要知道它的一些特别的属性,从而可以在编译中的代码中使用到。

对于支持arm指令集的处理器来说,有“smp”(对称多处理器,也就是多核)、“div”(硬件除法器)和“lpae”(大内存模式)三个。

17)--compile-pic

用PIC(Position Independent Code,位置无关代码)模式来编译。

18)--compiler-backend=(Quick|Optimizing)

指定编译器的后端使用哪种模式,可选的是所谓快模式(Quick)或优化模式(Optimizing)。如果没有特别指定的话,编译镜像使用快模式,而编译一般的应用程序则使用优化模式。

什么是编译器的后段呢?其实这是LLVM引入的概念。所有的程序先用前端翻译成中间表示层,然后进行优化,最后用后端将优化过的中间表示层代码编译成平台相关的代码。Android虽然没有直接用LLVM编译器(以前也确实用过,但从6.0开始就废弃掉了),但是借鉴了这种编译器的设计结构。

值得一提的是,如果使用优化模式,则一定是用PIC模式进行编译。

19)--compiler-filter=(verify-none|interpret-only|verify-at-runtime|space|balanced|speed|everything|time)

指定一些编译选项,默认是speed,以速度优先。

20)--huge-method-max=<method-instruction-count>

告诉dex2oat,当发现一个函数内包含的指令数目超过多少时,被当作巨大函数来处理。如果不指定,默认的值是10000。

21)--large-method-max=<method-instruction-count>

告诉dex2oat,当发现一个函数内包含的指令数目超过多少时,被当作大函数来处理。如果不指定,默认的值是600。

22)--small-method-max=<method-instruction-count>

告诉dex2oat,当发现一个函数内包含的指令数目超过多少时,被当作小函数来处理。如果不指定,默认的值是60。

23)--tiny-method-max=<method-instruction-count>

告诉dex2oat,当发现一个函数内包含的指令数目超过多少时,被当作微型函数来处理。如果不指定,默认的值是20。

24)--num-dex-methods=<method-count>

告诉dex2oat,当发现一个dex文件内部包含的方法数少于多少时,将被当作小dex文件来处理。

具体的来说,如果一个dex文件内部的方法数小于这个指定值的话,且--compiler-filter编译过滤器不是被设置成verify-none或interpret-only的话,将编译过滤器强制设置成speed。

25)-j<number>

指定要用多少个线程对文件进行编译。默认情况下,如果不指定的话,dex2oat会用当前系统的处理器能同时执行的最大线程数来编译。

26)--include-patch-information

通知dex2oat要把patch的信息也写入编译出的oat文件中。

27)--no-include-patch-information

通知dex2oat不要把patch的信息也写入编译出的oat文件中。

28)-g或--generate-debug-info

通知dex2oat,要在编译出的oat文件中包含调试的信息。

29)--no-generate-debug-info

通知dex2oat,不要在编译出的oat文件中包含调试的信息。

30)--debuggable

告知dex2oat,编译出来的oat文件要支持调试。当指定了这个参数之后,默认就会将调试信息写入oat文件(既默认带上了--generate-debug-info参数)。

31)--runtime-arg <argument>

告诉ART运行时(Runtime)的参数,例如初始堆大小和最大堆大小之类的,且对每一个不通的参数都要在前面加上--runtime-arg。

顺便提一句,dex2oat在编译的时候会在内部创建一个ART运行时。

32)--swap-file=<file-name>

指定编译所需要的交换文件的路径。

33)--swap-fd=<file-descriptor>

指定编译所需要的交换文件的文件描述符。

这个交换文件是用来临时存放编译过程中所产生的一些数据的,并不是指定了这个交换文件dex2oat就一定会使用,还必须要满足一定条件。

如果是编译镜像文件的话,则一定不会用交换文件。如果要编译的所有dex文件的数目小于2的话,也不会使用。最后,如果要编译的所有dex文件的总大小小于20MB的话,也不会用(这么说基本上都不会使用了)。

最后,举一个例子,在Android 5.1上,当安装一个新的应用程序的时候,都会执行下面的命令对其进行编译:

/system/bin/dex2oat --zip-fd=6 --zip-location=/data/app/<Package Name>-1/base.apk --oat-fd=7 --oat-location=/data/dalvik-cache/arm/data@app@<Package Name>-1@base.apk@classes.dex --instruction-set=arm --instruction-set-features=div --runtime-arg -Xms64m --runtime-arg -Xmx512m --swap-fd=8

 类似资料:

相关阅读

相关文章

相关问答