篇文章中我们介绍了 Android 4.4 新开发的运行时 ART 项目,其中的一个重要模快是 dex2oat,简单讲就是使用 LLVM 把 dex 文件编译成 oat 文件(Optimized ART?)。下面我们详细研究一下 dex2oat 的功能,以及他是如何被调用的。
一、dex2oat 简介
dex2oat 顾名思义 dex file to oat file,就是在新旧两种运行时文件的转换。他是一个可执行 ELF 文件,adb 连接手机以后也可以直接调用。dex2oat 的源码只有一个文件 /art/dex2oat/dex2oat.cc。dex2oat 有很多参数,并且实用方法打印到 logcat里面,看起来非常不便,我们直接看源码 Usage()。我挑了几个比较重要的参数看了一下,先介绍一下,调用过程中可能会用到。
dex2oat 用法
--dex-file=: specifies a .dex file to compile.
: 需要转换的 dex 文件,也可以是 apk, jar,dex2oat 会找到里面的 classes.dex 进行转换。
--oat-file=: specifies the oat output destination via a filename.
: 指定输出 oat 的文件名。
--boot-image=: provide the image file for the boot class path.
: 系统运行时工具类在 ART 下编译后的文件,他的例子是指向/system/framework/boot.art
。但其实 boot.art 不在这个目录下,而是在/data/dalvik-cache/system@[email protected]
。后面还会详细介绍这个 boot 文件。
--compiler-backend=(Quick|QuickGBC|Portable): select compiler backend"
: dex2oat 好像只处理了 Quick 和 Portable 两种编译 backend,暂时还不理解有什么区别,待以后继续研究。
其他参数大多都能从他的描述中知道用途,等以后用到的时候再详细看。
oat 文件在哪里?
我们知道选择 ART 作为运行时后,需要重启手机。然后系统会使用 dex2oat 把所有 App 编译成 oat 文件。那么 oat 都存在哪里?我怎么找不到?通过查看代码和开机的 log,发现 oat 文件原来在 /data/dalvik-cache/*@classes.dex
。竟然和 odex 文件名字一样!检验一下文件格式:
$ file system@
[email protected]@classes.dex system@
[email protected]@classes.dex: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (GNU/Linux), dynamically linked, stripped
|
$
file
system
@
app
@
Email
.apk
@
classes
.dex
system
@
app
@
Email
.apk
@
classes
.dex
:
ELF
32
-
bit
LSB
shared
object
,
ARM
,
EABI5
version
1
(
GNU
/
Linux
)
,
dynamically
linked
,
stripped
|
果然是 ELF 文件。
二、调用过程分析
PackageManagerService 是负责 apk 包管理的服务,包括安装,检查,删除,更新等等所有与 apk 相关的服务。我在源码中研究了一下 dex2oat 详细的调用过程,在下面介绍过程的时候我打算从 PackageManagerService 入手,经过一系列的调用过程才真正执行 dex2oat。
PackageManagerService (PMS)
我们知道 PMS 服务启动之后会监控某些目录(/data/data/ 等)。如果目录多了某个文件,PMS 会调用一些方法对文件进行处理。扫描监控目录的方法是:scanDirLI(),在 PackageManagerService 的构造方法中调用,详见代码 /frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
:
// Find base frameworks (resource packages without code). mFrameworkInstallObserver = new AppDirObserver( frameworkDir.getPath(), OBSERVER_EVENTS, true, false); mFrameworkInstallObserver.startWatching(); scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode | SCAN_NO_DEX, 0); // Collected privileged system packages. File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app"); mPrivilegedInstallObserver = new AppDirObserver( privilegedAppDir.getPath(), OBSERVER_EVENTS, true, true); mPrivilegedInstallObserver.startWatching(); scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0); // Collect ordinary system packages. File systemAppDir = new File(Environment.getRootDirectory(), "app"); mSystemInstallObserver = new AppDirObserver( systemAppDir.getPath(), OBSERVER_EVENTS, true, false); mSystemInstallObserver.startWatching(); scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); // Collect all vendor packages. File vendorAppDir = new File("/vendor/app"); mVendorInstallObserver = new AppDirObserver( vendorAppDir.getPath(), OBSERVER_EVENTS, true, false); mVendorInstallObserver.startWatching(); scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
|
// Find base frameworks (resource packages without code).
mFrameworkInstallObserver
=
new
AppDirObserver
(
frameworkDir
.
getPath
(
)
,
OBSERVER_EVENTS
,
true
,
false
)
;
mFrameworkInstallObserver
.
startWatching
(
)
;
scanDirLI
(
frameworkDir
,
PackageParser
.
PARSE_IS_SYSTEM
|
PackageParser
.
PARSE_IS_SYSTEM_DIR
,
scanMode
|
SCAN_NO_DEX
,
0
)
;
// Collected privileged system packages.
File
privilegedAppDir
=
new
File
(
Environment
.
getRootDirectory
(
)
,
"priv-app"
)
;
mPrivilegedInstallObserver
=
new
AppDirObserver
(
privilegedAppDir
.
getPath
(
)
,
OBSERVER_EVENTS
,
true
,
true
)
;
mPrivilegedInstallObserver
.
startWatching
(
)
;
scanDirLI
(
privilegedAppDir
,
PackageParser
.
PARSE_IS_SYSTEM
|
PackageParser
.
PARSE_IS_SYSTEM_DIR
|
PackageParser
.
PARSE_IS_PRIVILEGED
,
scanMode
,
0
)
;
// Collect ordinary system packages.
File
systemAppDir
=
new
File
(
Environment
.
getRootDirectory
(
)
,
"app"
)
;
mSystemInstallObserver
=
new
AppDirObserver
(
systemAppDir
.
getPath
(
)
,
OBSERVER_EVENTS
,
true
,
false
)
;
mSystemInstallObserver
.
startWatching
(
)
;
scanDirLI
(
systemAppDir
,
PackageParser
.
PARSE_IS_SYSTEM
|
PackageParser
.
PARSE_IS_SYSTEM_DIR
,
scanMode
,
0
)
;
// Collect all vendor packages.
File
vendorAppDir
=
new
File
(
"/vendor/app"
)
;
mVendorInstallObserver
=
new
AppDirObserver
(
vendorAppDir
.
getPath
(
)
,
OBSERVER_EVENTS
,
true
,
false
)
;
mVendorInstallObserver
.
startWatching
(
)
;
scanDirLI
(
vendorAppDir
,
PackageParser
.
PARSE_IS_SYSTEM
|
PackageParser
.
PARSE_IS_SYSTEM_DIR
,
scanMode
,
0
)
;
|
从源码中可以看到,有四个地方调用 scanDirLI()。再调用之前有一个 AppDirObserver 类引起了我的注意,看一下是怎么对目录进行监控的。依然是在 PackageManagerService 中找到了 AppDirObserver 继承于 FileObserver。FileObserver 有一个 native 方法 startWatching 找到了他的实现 /frameworks/base/core/java/android/os/FileObserver.java
:
const char* path = env->GetStringUTFChars(pathString, NULL); res = inotify_add_watch(fd, path, mask); env->ReleaseStringUTFChars(pathString, path);
|
const
char
*
path
=
env
->
GetStringUTFChars
(
pathString
,
NULL
)
;
res
=
inotify_add_watch
(
fd
,
path
,
mask
)
;
env
->
ReleaseStringUTFChars
(
pathString
,
path
)
;
|
man 一下 inotify_add_watch:
INOTIFY_ADD_WATCH(2) Linux Programmer's Manual INOTIFY_ADD_WATCH(2) NAME inotify_add_watch - add a watch to an initialized inotify instance SYNOPSIS #include <sys/inotify.h> int inotify_add_watch(int fd, const char *pathname, uint32_t mask); DESCRIPTION inotify_add_watch() adds a new watch, or modifies an existing watch, for the file whose location is specified in pathname; the caller must have read permission for this file. The fd argument is a file descrip‐ tor referring to the inotify instance whose watch list is to be modi‐ fied. The events to be monitored for pathname are specified in the mask bit-mask argument. See inotify(7) for a description of the bits that can be set in mask. ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
INOTIFY_ADD_WATCH
(
2
)
Linux
Programmer'
s
Manual
INOTIFY_ADD_WATCH
(
2
)
NAME
inotify_add_watch
-
add
a
watch
to
an
initialized
inotify
instance
SYNOPSIS
#include <sys/inotify.h>
int
inotify_add_watch
(
int
fd
,
const
char
*
pathname
,
uint32_t
mask
)
;
DESCRIPTION
inotify_add_watch
(
)
adds
a
new
watch
,
or
modifies
an
existing
watch
,
for
the
file
whose
location
is
specified
in
pathname
;
the
caller
must
have
read
permission
for
this
file
.
The
fd
argument
is
a
file
descrip‐
tor
referring
to
the
inotify
instance
whose
watch
list
is
to
be
modi‐
fied
.
The
events
to
be
monitored
for
pathname
are
specified
in
the
mask
bit
-
mask
argument
.
See
inotify
(
7
)
for
a
description
of
the
bits
that
can
be
set
in
mask
.
.
.
.
|
原来 inotify_add_watch() 是监控目录的。
PackageManagerService.scanDirLI()
回到正题,我们从构造方法中找到了 scanDirLI(),现在来看一下 scanDirLI() 都做了什么:
for (i=0; i<files.length; i++) { File file = new File(dir, files[i]); if (!isPackageFilename(files[i])) { // Ignore entries which are not apk's continue; } PackageParser.Package pkg = scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
3458
3459
3460
3461
3462
3463
3464
3465
|
for
(
i
=
0
;
i
<
files
.
length
;
i
++
)
{
File
file
=
new
File
(
dir
,
files
[
i
]
)
;
if
(
!
isPackageFilename
(
files
[
i
]
)
)
{
// Ignore entries which are not apk's
continue
;
}
PackageParser
.
Package
pkg
=
scanPackageLI
(
file
,
flags
|
PackageParser
.
PARSE_MUST_BE_APK
,
scanMode
,
currentTime
,
null
)
;
|
scanDirLI() 便利所有目录下的 apk 文件并调用 scanPackageLI(),我们继续。
PackageManagerService.scanPackageLI()
scanPackageLI() 先对文件的设置进行读取,还包含其他处理。我们跳过直接看
// Note that we invoke the following method only if we are about to unpack an application PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user);
|
// Note that we invoke the following method only if we are about to unpack an application
PackageParser
.
Package
scannedPkg
=
scanPackageLI
(
pkg
,
parseFlags
,
scanMode
|
SCAN_UPDATE_SIGNATURE
,
currentTime
,
user
)
;
|
又调用了另外一个重载的 scanPackageLI()。在这个 scanPackageLI() 里面又进行了一系列的包的解析,都与我们的目标无关,直接跳到这里:
if ((scanMode&SCAN_NO_DEX) == 0) { if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) { mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; return null; } }
4610
4611
4612
4613
4614
4615
4616
|
if
(
(
scanMode
&
SCAN_NO_DEX
)
==
0
)
{
if
(
performDexOptLI
(
pkg
,
forceDex
,
(
scanMode
&
SCAN_DEFER_DEX
)
!=
0
,
false
)
==
DEX_OPT_FAILED
)
{
mLastScanError
=
PackageManager
.
INSTALL_FAILED_DEXOPT
;
return
null
;
}
}
|
在这里又调用 performDexOptLI() 来处理进行 dex 优化,参数就是这个 package。这里已经感觉到有点快到 dex2oat 的调用点了。我们继续看 performDexOptLI()。
PackageManagerService.performDexOptLI()
performDexOptLI 先检查 dex 是否需要优化,然后再有一些判断,忽略他,看到这里:
if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { if (!forceDex && defer) { if (mDeferredDexOpt == null) { mDeferredDexOpt = new HashSet<PackageParser.Package>(); } mDeferredDexOpt.add(pkg); return DEX_OPT_DEFERRED; } else { Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName); final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo. uid); ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg)); pkg.mDidDexOpt = true; performed = true; } }
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
|
if
(
forceDex
||
dalvik
.
system
.
DexFile
.
isDexOptNeeded
(
path
)
)
{
if
(
!
forceDex
&&
defer
)
{
if
(
mDeferredDexOpt
==
null
)
{
mDeferredDexOpt
=
new
HashSet
<
PackageParser
.
Package
>
(
)
;
}
mDeferredDexOpt
.
add
(
pkg
)
;
return
DEX_OPT_DEFERRED
;
}
else
{
Log
.
i
(
TAG
,
"Running dexopt on: "
+
pkg
.
applicationInfo
.
packageName
)
;
final
int
sharedGid
=
UserHandle
.
getSharedAppGid
(
pkg
.
applicationInfo
.
uid
)
;
ret
=
mInstaller
.
dexopt
(
path
,
sharedGid
,
!
isForwardLocked
(
pkg
)
)
;
pkg
.
mDidDexOpt
=
true
;
performed
=
true
;
}
}
|
最后 mInstaller.dexopt 调用的优化 dex 的函数,Google 在这里把 dex2oat 和 dex2odex 统称为 dexopt。mInstaller 是 Installer 类。下面我们详细了解一下 Installer 的运行机制。
Installer
从 Installer 的代码中我们可以看到 connect(), disconnect(), readBytes(), writeCommand() 等方法。在 connect() 方法中可以看到,Installer 就是与 installd daemon 进行通信的辅助类。代码在这里:
mSocket = new LocalSocket(); LocalSocketAddress address = new LocalSocketAddress("installd", LocalSocketAddress.Namespace.RESERVED); mSocket.connect(address); mIn = mSocket.getInputStream(); mOut = mSocket.getOutputStream();
49
50
51
52
53
54
55
56
57
|
mSocket
=
new
LocalSocket
(
)
;
LocalSocketAddress
address
=
new
LocalSocketAddress
(
"installd"
,
LocalSocketAddress
.
Namespace
.
RESERVED
)
;
mSocket
.
connect
(
address
)
;
mIn
=
mSocket
.
getInputStream
(
)
;
mOut
=
mSocket
.
getOutputStream
(
)
;
|
了解了这个之后,我们找一下 dexopt 看看 Installer 向 installd 发送了什么信息:
public int dexopt(String apkPath, int uid, boolean isPublic) { StringBuilder builder = new StringBuilder("dexopt"); builder.append(' '); builder.append(apkPath); builder.append(' '); builder.append(uid); builder.append(isPublic ? " 1" : " 0"); return execute(builder.toString()); }
204
205
206
207
208
209
210
211
212
|
public
int
dexopt
(
String
apkPath
,
int
uid
,
boolean
isPublic
)
{
StringBuilder
builder
=
new
StringBuilder
(
"dexopt"
)
;
builder
.
append
(
' '
)
;
builder
.
append
(
apkPath
)
;
builder
.
append
(
' '
)
;
builder
.
append
(
uid
)
;
builder
.
append
(
isPublic
?
" 1"
:
" 0"
)
;
return
execute
(
builder
.
toString
(
)
)
;
}
|
其实发送了 dexopt apkpath uid 1|0
给 installd。好了,Installer 分析结束,我们转战 installd。
installd.c
installd 是 socket 通信的 server,会 accept 连接,并读取数据。从下面的两个死循环就能看出来:
for (;;) { alen = sizeof(addr); s = accept(lsocket, &addr, &alen); if (s < 0) { ALOGE("Accept failed: %s\n", strerror(errno)); continue; } fcntl(s, F_SETFD, FD_CLOEXEC); ALOGI("new connection\n"); for (;;) { unsigned short count; if (readx(s, &count, sizeof(count))) {
554
555
556
557
558
559
560
561
562
563
564
565
566
|
for
(
;
;
)
{
alen
=
sizeof
(
addr
)
;
s
=
accept
(
lsocket
,
&
addr
,
&
alen
)
;
if
(
s
<
0
)
{
ALOGE
(
"Accept failed: %s\n"
,
strerror
(
errno
)
)
;
continue
;
}
fcntl
(
s
,
F_SETFD
,
FD_CLOEXEC
)
;
ALOGI
(
"new connection\n"
)
;
for
(
;
;
)
{
unsigned
short
count
;
if
(
readx
(
s
,
&
count
,
sizeof
(
count
)
)
)
{
|
之后会调用 execute 函数:
if (execute(s, buf)) break;
|
if
(
execute
(
s
,
buf
)
)
break
;
|
execute() 函数先把 cmd 分割然后通过 cmds 表查询函数名称:
struct cmdinfo { const char *name; unsigned numargs; int (*func)(char **arg, char reply[REPLY_MAX]); }; struct cmdinfo cmds[] = { { "ping", 0, do_ping }, { "install", 4, do_install }, { "dexopt", 3, do_dexopt }, { "movedex", 2, do_move_dex }, { "rmdex", 1, do_rm_dex }, { "remove", 2, do_remove }, { "rename", 2, do_rename }, { "fixuid", 3, do_fixuid }, { "freecache", 1, do_free_cache }, { "rmcache", 2, do_rm_cache }, { "getsize", 6, do_get_size }, { "rmuserdata", 2, do_rm_user_data }, { "movefiles", 0, do_movefiles }, { "linklib", 3, do_linklib }, { "mkuserdata", 3, do_mk_user_data }, { "rmuser", 1, do_rm_user }, };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
struct
cmdinfo
{
const
char
*
name
;
unsigned
numargs
;
int
(
*
func
)
(
char
*
*
arg
,
char
reply
[
REPLY_MAX
]
)
;
}
;
struct
cmdinfo
cmds
[
]
=
{
{
"ping"
,
0
,
do
_ping
}
,
{
"install"
,
4
,
do
_install
}
,
{
"dexopt"
,
3
,
do
_dexopt
}
,
{
"movedex"
,
2
,
do_move
_dex
}
,
{
"rmdex"
,
1
,
do_rm
_dex
}
,
{
"remove"
,
2
,
do
_remove
}
,
{
"rename"
,
2
,
do
_rename
}
,
{
"fixuid"
,
3
,
do
_fixuid
}
,
{
"freecache"
,
1
,
do_free
_cache
}
,
{
"rmcache"
,
2
,
do_rm
_cache
}
,
{
"getsize"
,
6
,
do_get
_size
}
,
{
"rmuserdata"
,
2
,
do_rm_user
_data
}
,
{
"movefiles"
,
0
,
do
_movefiles
}
,
{
"linklib"
,
3
,
do
_linklib
}
,
{
"mkuserdata"
,
3
,
do_mk_user
_data
}
,
{
"rmuser"
,
1
,
do_rm
_user
}
,
}
;
|
dexopt 对应的是 do_dexopt() 函数,do_dexopt() 又调用了 commands.c 里面的 dexopt() 函数。感觉离我们的目标不远了,继续看 commands.c。
commands.c
在 commands.c 中的 dexopt() 函数,我们看到此函数从 property 中获得 dalvik.vm.dexopt-flags 和 persist.sys.dalvik.vm.lib:
/* platform-specific flags affecting optimization and verification */ property_get("dalvik.vm.dexopt-flags", dexopt_flags, ""); ALOGV("dalvik.vm.dexopt_flags=%s\n", dexopt_flags); /* The command to run depend ones the value of persist.sys.dalvik.vm.lib */ property_get("persist.sys.dalvik.vm.lib", persist_sys_dalvik_vm_lib, "libdvm.so");
|
/* platform-specific flags affecting optimization and verification */
property_get
(
"dalvik.vm.dexopt-flags"
,
dexopt_flags
,
""
)
;
ALOGV
(
"dalvik.vm.dexopt_flags=%s\n"
,
dexopt_flags
)
;
/* The command to run depend ones the value of persist.sys.dalvik.vm.lib */
property_get
(
"persist.sys.dalvik.vm.lib"
,
persist_sys_dalvik_vm_lib
,
"libdvm.so"
)
;
|
dalvik.vm.dexopt-flags 暂时不知道是什么意思,persist.sys.dalvik.vm.lib 就是当前系统使用的运行时默认是 Dalvik VM,如果选择了 ART,应该就是 libart.so 了。
之后函数通过检查是否有 odex 文件来判断是否已经进行过 dexopt。然后进行一系列检查 chmod 和 chown。最后函数 fork 了一个子进程处理 dexopt:
pid_t pid; pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ if (setgid(uid) != 0) { ALOGE("setgid(%d) failed in installd during dexopt\n", uid); exit(64); } if (setuid(uid) != 0) { ALOGE("setuid(%d) failed in installd during dexopt\n", uid); exit(65); } // drop capabilities struct __user_cap_header_struct capheader; struct __user_cap_data_struct capdata[2]; memset(&capheader, 0, sizeof(capheader)); memset(&capdata, 0, sizeof(capdata)); capheader.version = _LINUX_CAPABILITY_VERSION_3; if (capset(&capheader, &capdata[0]) < 0) { ALOGE("capset failed: %s\n", strerror(errno)); exit(66); } if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) { ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno)); exit(67); } if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) { run_dexopt(zip_fd, out_fd, apk_path, out_path, dexopt_flags); } else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) { run_dex2oat(zip_fd, out_fd, apk_path, out_path, dexopt_flags); } else { exit(69); /* Unexpected persist.sys.dalvik.vm.lib value */ } exit(68); /* only get here on exec failure */ } else { res = wait_dexopt(pid, apk_path); if (res != 0) { ALOGE("dexopt in='%s' out='%s' res=%d\n", apk_path, out_path, res); goto fail; } }
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
|
pid_t
pid
;
pid
=
fork
(
)
;
if
(
pid
==
0
)
{
/* child -- drop privileges before continuing */
if
(
setgid
(
uid
)
!=
0
)
{
ALOGE
(
"setgid(%d) failed in installd during dexopt\n"
,
uid
)
;
exit
(
64
)
;
}
if
(
setuid
(
uid
)
!=
0
)
{
ALOGE
(
"setuid(%d) failed in installd during dexopt\n"
,
uid
)
;
exit
(
65
)
;
}
// drop capabilities
struct
__user_cap_header_struct
capheader
;
struct
__user_cap_data_struct
capdata
[
2
]
;
memset
(
&
capheader
,
0
,
sizeof
(
capheader
)
)
;
memset
(
&
capdata
,
0
,
sizeof
(
capdata
)
)
;
capheader
.
version
=
_LINUX_CAPABILITY_VERSION_3
;
if
(
capset
(
&
capheader
,
&
capdata
[
0
]
)
<
0
)
{
ALOGE
(
"capset failed: %s\n"
,
strerror
(
errno
)
)
;
exit
(
66
)
;
}
if
(
flock
(
out_fd
,
LOCK_EX
|
LOCK_NB
)
!=
0
)
{
ALOGE
(
"flock(%s) failed: %s\n"
,
out_path
,
strerror
(
errno
)
)
;
exit
(
67
)
;
}
if
(
strncmp
(
persist_sys_dalvik_vm_lib
,
"libdvm"
,
6
)
==
0
)
{
run_dexopt
(
zip_fd
,
out_fd
,
apk_path
,
out_path
,
dexopt_flags
)
;
}
else
if
(
strncmp
(
persist_sys_dalvik_vm_lib
,
"libart"
,
6
)
==
0
)
{
run_dex2oat
(
zip_fd
,
out_fd
,
apk_path
,
out_path
,
dexopt_flags
)
;
}
else
{
exit
(
69
)
;
/* Unexpected persist.sys.dalvik.vm.lib value */
}
exit
(
68
)
;
/* only get here on exec failure */
}
else
{
res
=
wait_dexopt
(
pid
,
apk_path
)
;
if
(
res
!=
0
)
{
ALOGE
(
"dexopt in='%s' out='%s' res=%d\n"
,
apk_path
,
out_path
,
res
)
;
goto
fail
;
}
}
|
开始先 drop capabilities(关于 Linux capabilities: man capabilities)。最后比较 persist_sys_dalvik_vm_lib,如果是 libart 则调用 run_dex2oat()。Good,终于有头绪了,再看 run_dex2oat():
static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name, const char* output_file_name, const char* dexopt_flags) { static const char* DEX2OAT_BIN = "/system/bin/dex2oat"; static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN]; char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX]; char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN]; char oat_location_arg[strlen("--oat-name=") + PKG_PATH_MAX]; sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd); sprintf(zip_location_arg, "--zip-location=%s", input_file_name); sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd); sprintf(oat_location_arg, "--oat-location=%s", output_file_name); ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name); execl(DEX2OAT_BIN, DEX2OAT_BIN, zip_fd_arg, zip_location_arg, oat_fd_arg, oat_location_arg, (char*) NULL); ALOGE("execl(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno)); }
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
|
static
void
run_dex2oat
(
int
zip_fd
,
int
oat_fd
,
const
char
*
input_file_name
,
const
char
*
output_file_name
,
const
char
*
dexopt_flags
)
{
static
const
char
*
DEX2OAT_BIN
=
"/system/bin/dex2oat"
;
static
const
int
MAX_INT_LEN
=
12
;
// '-'+10dig+'\0' -OR- 0x+8dig
char
zip_fd_arg
[
strlen
(
"--zip-fd="
)
+
MAX_INT_LEN
]
;
char
zip_location_arg
[
strlen
(
"--zip-location="
)
+
PKG_PATH_MAX
]
;
char
oat_fd_arg
[
strlen
(
"--oat-fd="
)
+
MAX_INT_LEN
]
;
char
oat_location_arg
[
strlen
(
"--oat-name="
)
+
PKG_PATH_MAX
]
;
sprintf
(
zip_fd_arg
,
"--zip-fd=%d"
,
zip_fd
)
;
sprintf
(
zip_location_arg
,
"--zip-location=%s"
,
input_file_name
)
;
sprintf
(
oat_fd_arg
,
"--oat-fd=%d"
,
oat_fd
)
;
sprintf
(
oat_location_arg
,
"--oat-location=%s"
,
output_file_name
)
;
ALOGV
(
"Running %s in=%s out=%s\n"
,
DEX2OAT_BIN
,
input_file_name
,
output_file_name
)
;
execl
(
DEX2OAT_BIN
,
DEX2OAT_BIN
,
zip_fd_arg
,
zip_location_arg
,
oat_fd_arg
,
oat_location_arg
,
(
char
*
)
NULL
)
;
ALOGE
(
"execl(%s) failed: %s\n"
,
DEX2OAT_BIN
,
strerror
(
errno
)
)
;
}
|
终于找到了,这就是调用 dex2oat 的地方,写的非常清楚。–zip-fd 是 apk 的 fd,–oat-fd 是输出 oat 的 fd。最终使用 execl() 执行 /system/bin/dex2oat 程序对 apk 进行编译。
三、总结
dex2oat 对所有 apk 进行编译并保存在 dalvik-cache 目录里。PMS 会持续扫描安装目录,如果有新的 App 安装则马上调用 dex2oat 进行编译。
最后,之前我们提到的 boot.art 我在 logcat 中找到了他的来源:
/system/bin/dex2oat --image=/data/dalvik-cache/system@
[email protected] --runtime-arg -Xms64m --runtime-arg -Xmx64m --dex-file=/system/framework/core-libart.jar --dex-file=/system/framework/conscrypt.jar --dex-file=/system/framework/okhttp.jar --dex-file=/system/framework/core-junit.jar --dex-file=/system/framework/bouncycastle.jar --dex-file=/system/framework/ext.jar --dex-file=/system/framework/framework.jar --dex-file=/system/framework/framework2.jar --dex-file=/system/framework/telephony-common.jar --dex-file=/system/framework/voip-common.jar --dex-file=/system/framework/mms-common.jar --dex-file=/system/framework/android.policy.jar --dex-file=/system/framework/services.jar --dex-file=/system/framework/apache-xml.jar --dex-file=/system/framework/webviewchromium.jar --oat-file=/data/dalvik-cache/system@
[email protected] --base=0x60000000 --image-classes-zip=/system/framework/framework.jar --image-classes=preloaded-classes
|
/
system
/
bin
/
dex2oat
--
image
=
/
data
/
dalvik
-
cache
/
system
@
framework
@
boot
.art
--
runtime
-
arg
-
Xms64m
--
runtime
-
arg
-
Xmx64m
--
dex
-
file
=
/
system
/
framework
/
core
-
libart
.jar
--
dex
-
file
=
/
system
/
framework
/
conscrypt
.jar
--
dex
-
file
=
/
system
/
framework
/
okhttp
.jar
--
dex
-
file
=
/
system
/
framework
/
core
-
junit
.jar
--
dex
-
file
=
/
system
/
framework
/
bouncycastle
.jar
--
dex
-
file
=
/
system
/
framework
/
ext
.jar
--
dex
-
file
=
/
system
/
framework
/
framework
.jar
--
dex
-
file
=
/
system
/
framework
/
framework2
.jar
--
dex
-
file
=
/
system
/
framework
/
telephony
-
common
.jar
--
dex
-
file
=
/
system
/
framework
/
voip
-
common
.jar
--
dex
-
file
=
/
system
/
framework
/
mms
-
common
.jar
--
dex
-
file
=
/
system
/
framework
/
android
.policy
.jar
--
dex
-
file
=
/
system
/
framework
/
services
.jar
--
dex
-
file
=
/
system
/
framework
/
apache
-
xml
.jar
--
dex
-
file
=
/
system
/
framework
/
webviewchromium
.jar
--
oat
-
file
=
/
data
/
dalvik
-
cache
/
system
@
framework
@
boot
.oat
--
base
=
0x60000000
--
image
-
classes
-
zip
=
/
system
/
framework
/
framework
.jar
--
image
-
classes
=
preloaded
-
classes
|
似乎是把所有 framework 的 jar 编译成 boot.art,在 dex2oat 的时候使用。还是有很多不理解的地方,相信在以后的分析中应该有更深入的理解。
附:相关代码链接
- PackageManagerService constructor: http://androidxref.com/4.4_r1/xref/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java#1062
- scanDirLI(): http://androidxref.com/4.4_r1/xref/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java#3445
- scanPackageLI(): http://androidxref.com/4.4_r1/xref/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java#4103
- performDexOptLI(): http://androidxref.com/4.4_r1/xref/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java#3860
- Installer.dexopt(): http://androidxref.com/4.4_r1/xref/frameworks/base/services/java/com/android/server/pm/Installer.java#204
- do_dexopt(): http://androidxref.com/4.4_r1/xref/frameworks/native/cmds/installd/installd.c#37
- dexopt(): http://androidxref.com/4.4_r1/xref/frameworks/native/cmds/installd/commands.c#653
- run_dex2oat(): http://androidxref.com/4.4_r1/xref/frameworks/native/cmds/installd/commands.c#run_dex2oat
This entry was posted in Android, Technology and tagged Android, ART Runtime, dex2oat on January 10, 2014
by Mingshen Sun.