首页
社区
课程
招聘
[原创]Android ART分析
发表于: 2014-1-11 14:19 56993

[原创]Android ART分析

2014-1-11 14:19
56993

对Android ART的分析,主要包括ART Runtime启动过程以及dex2oat的分析。
由于代码量较多,忽略了很多细节,所以分析过程会存在错误;ART Runtime采用单例模式,启动过程中参数解析实例化会对后续dex2oat的分析有所影响,但是我在分析过程中没有去特别关注每个参数,有兴趣的可以自行分析!另外ART Runtime还可以继续深入分析!

BTW,附件中是分析中使用的javax.obex.jar以及转换后的OAT文件!

Android ART 分析
MindMac
2014/1/11

Google 在 Android4.4 中新增加了 ART(Android Run Time)用来替代之前的 Dalvik,关于 ART
的基本知识可以参考 Google 官方的 Introduction,XDA 的 Android 4.4 KitKat’s ART and App
Compatibility
以及 Android Police 对 ART 的相关介绍。在 Settings->Developer options 中可以选
择运行时环境,默认为 Dalvik,可以选择 ART,改变运行时环境后系统会重新启动,如图 1。

图 1 Android4.4 选择 ART 运行环境

与 Dalvik 不同的是,ART 不再使用 DEX 文件,而是 OAT 格式的文件,所以在重启过程中,系
统会完成从 DEX 到 OAT 格式的转换。本文主要分析 Android4.4 中 DEX 到 OAT 的转换过程。

ZIP 文件结构

在开始分析 Android ART 之前有必要先介绍下 ZIP 文件的格式。关于 ZIP 文件的结构可以
参考 The Great Android Security Hole Of ’08 ? – Part One: ZIP Files,更详细的解释可以参看.ZIP
File Format Specification


一个 ZIP 文件的基本结构如图 2 所示。No.1 到 No.n 分别代表了 ZIP 压缩文件中的文件,
n 表示所有的文件个数(包含文件目录)。新建一个 hello 文件夹,并在其下创建两个名为
helloone.txt 和 hello2.txt 的文件,其中内容分别为 contentone 和 contenttwo,将 hello 文件夹
压缩为 hello.zip 文件,以便后面的分析。

图 2 ZIP 文件基本结构

Local File Header

Local File Header 的结构如表 1 所示。其中 local file header signature 值固定为 0x04034b50。
使用 010Editor 打开,并应用 ZIP 模板,hellotwo.txt 文件的 Local File Header 如图 3 所示,由
于没有 extra 内容,因此 extra field length 为 0 且没有 extra field 字段。另外要注意的是,
hellotwo.txt 的 Local File Header 起始地址是 0x39,也就是 57,如图 4 所示。

表 1 Local File Header 各字段及大小

字段      字节数
local file header signature  4

version needed to extract  2 

general purpose bit flag  2

compression method  2

last mod file time  2

last mod file date  2

crc-32  4

compressed size  4

uncompressed size  4

file name length  2

extra filed length  2

file name  可变长度(size of file name)

extra filed  可变长度(size of file name)

字段             字节数   
central file header signature  4

version made by  2

version needed to extract  2

general purpose bit flag  2

compression method  2

last mod file time  2

last mod file date  2

crc-32  4

compressed size  4

uncompressed size  4

file name length  2

extra field length  2

file comment length  2

disk number start  2

internal file attribute  2

external file attributes  4

relative offset of local header  4

file name  可变长度(size of file name)

extra field   可变长度(size of file name)

file comment  可变长度(size of file name)

字段       字节数
end of central directory signature  4
number of this disk  2
number of the disk with the start of the central directory  2
total number of entries in the central directory on this disk  2
total number of entries in the central directory  2
size of the central directory  4
offset of start of central directory  4
ZIP file comment length  2
ZIP file comment  可变长度
1. if (zygote) {

2. runtime.start("com.android.internal.os.ZygoteInit",

3. startSystemServer ? "start-system-server" : "");

4. }
1. void AndroidRuntime::start(const char* className, const char* options)

2. {

3. ......

4. JniInvocation jni_invocation;

5. jni_invocation.Init(NULL);

6. JNIEnv* env;

7. if (startVm(&mJavaVM, &env) != 0) {

8. return;

9. }

10. ......

11. }
1. bool JniInvocation::Init(const char* library) {

2. #ifdef HAVE_ANDROID_OS

3. char default_library[PROPERTY_VALUE_MAX];

4. property_get("persist.sys.dalvik.vm.lib", default_library, "libdvm.so");

5. #else

6. const char* default_library = "libdvm.so";

7. #endif

8. if (library == NULL) {

9. library = default_library;

10. }

11. handle_ = dlopen(library, RTLD_NOW);

12. ......

13. if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),

14. "JNI_GetDefaultJavaVMInitArgs")) {

15. return false;

16. }

17. if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),

18. "JNI_CreateJavaVM")) {

19. return false;

20. }

21. if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),

22. "JNI_GetCreatedJavaVMs")) {

23. return false;

24. }

25. return true;

26. }
1. extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {

2. const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);

3. ......

4. Runtime::Options options;

5. for (int i = 0; i < args->nOptions; ++i) {

6. JavaVMOption* option = &args->options[i];

7. options.push_back(std::make_pair(std::string(option->optionString),

 option->extraInfo));

8. }

9. bool ignore_unrecognized = args->ignoreUnrecognized;

10. if (!Runtime::Create(options, ignore_unrecognized)) {

11. return JNI_ERR;

12. }

13. Runtime* runtime = Runtime::Current();

14. bool started = runtime->Start();

15. .....

16. return JNI_OK;

17. }
1. bool Runtime::Create(const Options& options, bool ignore_unrecognized) {

2. ......

3. InitLogging(NULL);

4. instance_ = new Runtime;

5. if (!instance_->Init(options, ignore_unrecognized)) {

6. .....

7. return false;

8. }

9. return true;
 
10. }

1. bool Runtime::Start() {

2. ......

3. Thread* self = Thread::Current();

4. self->TransitionFromRunnableToSuspended(kNative);

5. started_ = true;

6. InitNativeMethods();

7. ......

8. if (is_zygote_) {

9. if (!InitZygote()) {

10. return false;

11. }

12. } else {

13. DidForkFromZygote();

14. }

15. StartDaemonThreads();

16. ......

17. return true;
 
18. }
1. void Runtime::InitNativeMethods() {

2. .....

3. JNIEnv* env = self->GetJniEnv();

4. ......

5. RegisterRuntimeNativeMethods(env);

6. ......

7. }
1. public void initAndLoop(){

2. ......

3. Slog.i(TAG, "Waiting for installd to be ready.");

4. installer = new Installer();

5. installer.ping();

6. ......

7. pm = PackageManagerService.main(context, installer,

8. actoryTest != SystemServer.FACTORY_TEST_OFF, onlyCore);

9. ......
 
10. }
1. public static final IPackageManager main(Context context, Installer installer,

2. boolean factoryTest, boolean onlyCore) {

3. PackageManagerService m = new PackageManagerService(context, installer,

4. factoryTest, onlyCore);

5. ServiceManager.addService("package", m);

6. return m;

7. }

1. public PackageManagerService(Context context, Installer installer,

2. boolean factoryTest, boolean onlyCore) {

3. ......

4. mInstaller = installer;

5. ......

6. if (mSharedLibraries.size() > 0) {

7. Iterator<SharedLibraryEntry> libs = mSharedLibraries.values().iterator();

8. while (libs.hasNext()) {

9. String lib = libs.next().path;

10. ......

11. try {

12. if (dalvik.system.DexFile.isDexOptNeeded(lib)) {

13. alreadyDexOpted.add(lib);

14. mInstaller.dexopt(lib, Process.SYSTEM_UID, true);

15. didDexOpt = true;

16. }

18. }

19. }

20. }

21. ......

22. }
1. native public static boolean isDexOptNeeded(String fileName)
 
2. throws FileNotFoundException, IOException;
1. static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename)
{

2. ......

3. ScopedUtfChars filename(env, javaFilename);

4. ......

5. Runtime* runtime = Runtime::Current();

6. ClassLinker* class_linker = runtime->GetClassLinker();

7. const std::vector<const DexFile*>& boot_class_path = class_linker ->

 GetBootClassPath();

8. for (size_t i = 0; i < boot_class_path.size(); i++) {

9. if (boot_class_path[i]->GetLocation() == filename.c_str()) {

10. return JNI_FALSE;

11. }

12. }

13. ......

14. std::string cache_location(GetDalvikCacheFilenameOrDie(filename.c_str()));

15. oat_file.reset(OatFile::Open(cache_location, filename.c_str(), NULL, false));

16. if (oat_file.get() == NULL) {

17. LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location

18. << " does not exist for " << filename.c_str();

19. return JNI_TRUE;

20. }
 
21. ......

22. }

1. public int dexopt(String apkPath, int uid, boolean isPublic) {

2. StringBuilder builder = new StringBuilder("dexopt");

3. builder.append(' ');

4. builder.append(apkPath);

5. builder.append(' ');

6. builder.append(uid);

7. builder.append(isPublic ? " 1" : " 0");

8. return execute(builder.toString());
 
9. }

1. private int execute(String cmd) {

2. String res = transaction(cmd);

3. try {

4. return Integer.parseInt(res);

5. } catch (NumberFormatException ex) {

6. return -1;

7. }

8. }
1. private synchronized String transaction(String cmd) {

2.if (!connect()) {

3. Slog.e(TAG, "connection failed");

4. return "-1";

5. }

6. if (!writeCommand(cmd)) {

7. Slog.e(TAG, "write command failed? reconnect!");

8. if (!connect() || !writeCommand(cmd)) {

9. return "-1";

10. }

11. }

12. ......

13. }
1. int main(const int argc, const char *argv[]) {

2. ......

3. for (;;) {

4. alen = sizeof(addr);

5. s = accept(lsocket, &addr, &alen);

6. ......

7. ALOGI("new connection\n");

8. for (;;) {

9. unsigned short count;

10. if (readx(s, &count, sizeof(count))) {

11. ALOGE("failed to read size\n");

12. break;

13. }

14. if ((count < 1) || (count >= BUFFER_MAX)) {

15. ALOGE("invalid size %d\n", count);

16. break;

17. }

18. if (readx(s, buf, count)) {

19. ALOGE("failed to read command\n");

20. break;

21. }

22. buf[count] = 0;

23. if (execute(s, buf)) break;

24.  }

25. ALOGI("closing connection\n");

26. close(s);

27. }

28.

29. return 0;

30. }

1. static int do_dexopt(char **arg, char reply[REPLY_MAX])

2. {

3.return dexopt(arg[0], atoi(arg[1]), atoi(arg[2])); 

4. }

1. int dexopt(const char *apk_path, uid_t uid, int is_public)

2. {

3. struct utimbuf ut;

4. struct stat apk_stat, dex_stat;

5. char out_path[PKG_PATH_MAX];

6. char dexopt_flags[PROPERTY_VALUE_MAX];

7. char persist_sys_dalvik_vm_lib[PROPERTY_VALUE_MAX];

8. char *end;

9. int res, zip_fd=-1, out_fd=-1;

10. if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {

11. return -1;

12. }

13. property_get("persist.sys.dalvik.vm.lib", persist_sys_dalvik_vm_lib,

"libdvm.so");

14. sprintf(out_path, "%s%s", apk_path, ".odex");

15. if (stat(out_path, &dex_stat) == 0) {

16. return 0;

17. }

18. if (create_cache_path(out_path, apk_path)) {

19. return -1;

20. }

21. ......

22. pid_t pid;

23. pid = fork();

24. if (pid == 0) {

25. ......

26. if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) {

27. run_dexopt(zip_fd, out_fd, apk_path, out_path, dexopt_flags);

28. } else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) {

29. run_dex2oat(zip_fd, out_fd, apk_path, out_path, dexopt_flags);

30. } else {

31. exit(69); /* Unexpected persist.sys.dalvik.vm.lib value */

32. }

33. exit(68); /* only get here on exec failure */

34. }

35. ......
 
36. }

1. static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,
 
2. const char* output_file_name, const char* dexopt_flags)

3. {

4. static const char* DEX2OAT_BIN = "/system/bin/dex2oat";

5. static const int MAX_INT_LEN = 12;

6. char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN];

7. char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX];

8. char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN];

9. char oat_location_arg[strlen("--oat-name=") + PKG_PATH_MAX];

10. 

11. sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);

12. sprintf(zip_location_arg, "--zip-location=%s", input_file_name);

13. sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);

14. sprintf(oat_location_arg, "--oat-location=%s", output_file_name);

15. execl(DEX2OAT_BIN, DEX2OAT_BIN,

16. zip_fd_arg, zip_location_arg,

17. oat_fd_arg, oat_location_arg,

18. (char*) NULL);
 
19. }

1. int main(int argc, char** argv) {
 
2. return art::dex2oat(argc, argv);

3. }


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 6
支持
分享
最新回复 (13)
雪    币: 13246
活跃值: (4296)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
我的NEXUS5开了ART感觉也没多大变化
而且兼容也还有问题。。。
楼主是用IDA调戏的?
2014-1-11 16:02
0
雪    币: 293
活跃值: (225)
能力值: (RANK:250 )
在线值:
发帖
回帖
粉丝
3
网上有相关的测评报告,确实有兼容性问题,可以看下这个http://www.xda-developers.com/android/android-4-4-kitkats-art-and-app-compatibility/,另外有一个兼容性列表http://www.androidruntime.com/list

分析的源代码,不需要IDA!
2014-1-11 17:20
0
雪    币: 13246
活跃值: (4296)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4

但列表毕竟是极少数
需要个人提交的
还以为楼主真机调试了
2014-1-11 18:42
0
雪    币: 28
活跃值: (100)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
第一次看见art的分析,nexus7fhd开ART变化确实不大,不过4.4更新到4.4.2之后,确实感觉流畅度高了一些,兼容性差是肯定的
2014-1-11 19:41
0
雪    币: 6
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
android4.4.2 还是有些不稳定~
2014-1-12 22:54
0
雪    币: 53
活跃值: (280)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
Posix.getenv native实现在:
libcore\luni\src\main\native\libcore_io_Posix.cpp
static jstring Posix_getenv(JNIEnv* env, jobject, jstring javaName) {
    ScopedUtfChars name(env, javaName);
    if (name.c_str() == NULL) {
        return NULL;
    }
    return env->NewStringUTF(getenv(name.c_str()));
}
注册在该文件底部:
void register_libcore_io_Posix(JNIEnv* env) {
    jniRegisterNativeMethods(env, "libcore/io/Posix", gMethods, NELEM(gMethods));
}
2014-1-14 10:59
0
雪    币: 293
活跃值: (225)
能力值: (RANK:250 )
在线值:
发帖
回帖
粉丝
8
Good! 多谢!
2014-1-14 21:37
0
雪    币: 105
活跃值: (211)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
9
很好很强大
2015-2-27 17:43
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
哈哈 ,我也是正式会员了、。。
2015-3-2 20:20
0
雪    币: 244
活跃值: (25)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
11
正在学习这部分,谢谢楼主分享
2015-3-3 11:40
0
雪    币: 168
活跃值: (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
很棒的帖子,谢谢楼主
2015-3-12 17:02
0
雪    币: 57
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
不明觉厉
2015-11-18 15:58
0
雪    币: 2700
活跃值: (63)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
nice
2020-7-9 07:22
0
游客
登录 | 注册 方可回帖
返回
//