Android Art分析2

dex2oat

/system/bin/dex2oat 对应的源码文件位于/art/dex2oat/dex2oat.cc。main 函数代码如下:

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


直接调用 art 命名空间下的 dex2oat 函数,该函数有点长,去除注释差不多有 500 行,但是
有很大以部分代码都是在解析参数,我们只关注前面提到的几个参数,所以下面贴的代码有
所省略,而且鉴于代码略长,会分成几部分来分析。

参数解析

与参数解析相关的代码如下,大家可以参考下源码,因为函数开头声明了很多变量,为
了避免篇幅过长,此处略去不表。基本后面所有遇到的变量都是在函数开头声明的,而且这
些变量的值最终都是从传递过来的参数中获取的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
1. static int dex2oat(int argc, char** argv) {
 
2. ......
 
3.  #if defined(ART_USE_PORTABLE_COMPILER)
 
4. CompilerBackend compiler_backend = kPortable;
 
5.  #else
 
6. CompilerBackend compiler_backend = kQuick;
  
7.  #endif
 
8.  #if defined(__arm__)
 
9. InstructionSet instruction_set = kThumb2;
  
10.  #elif defined(__i386__)
 
11. InstructionSet instruction_set = kX86;
 
12.  #elif defined(__mips__)
 
13. InstructionSet instruction_set = kMips;
 
14.  #else
 
15.  #error "Unsupported architecture"
 
16.  #endif
 
17. ......
 
18.  for  (int i = 0; i < argc; i++) {
 
19. const StringPiece option(argv[i]);
 
20. ......
 
21.  else  if  (option.starts_with( "--zip-fd=" )) {
 
22. const char* zip_fd_str = option.substr(strlen( "--zip-fd=" )).data();
 
23.  if  (!ParseInt(zip_fd_str, &zip_fd)) {
 
24. Usage( "Failed to parse --zip-fd argument '%s' as an integer" , zip_fd_str);
 
25. }
 
26. }  else  if  (option.starts_with( "--zip-location=" )) {
 
27. zip_location = option.substr(strlen( "--zip-location=" )).data();
 
28. }
 
29. ......
 
30. }  else  if  (option.starts_with( "--oat-fd=" )) {
 
31. const char* oat_fd_str = option.substr(strlen( "--oat-fd=" )).data();
 
32.  if  (!ParseInt(oat_fd_str, &oat_fd)) {
 
33. Usage( "Failed to parse --oat-fd argument '%s' as an integer" , oat_fd_str);
 
34. }
 
35. }
 
36. ......
 
37. }  else  if  (option.starts_with( "--oat-location=" )) {
 
38. oat_location = option.substr(strlen( "--oat-location=" )).data();
 
39. }
 
40. ......
 
41. }
 
42.  if  (oat_filename.empty() && oat_fd == -1) {
 
43. Usage( "Output must be supplied with either --oat-file or --oat-fd" );
 
44. }
 
45.  if  (!oat_filename.empty() && oat_fd != -1) {
 
46. Usage( "--oat-file should not be used with --oat-fd" );
 
47. }
 
48. ......
 
49.  if  (oat_fd != -1 && !image_filename.empty()) {
 
50. Usage( "--oat-fd should not be used with --image" );
 
51. }
 
52.  if  (host_prefix.get() == NULL) {
 
53. const char* android_product_out = getenv( "ANDROID_PRODUCT_OUT" );
 
54.  if  (android_product_out != NULL) {
 
55. host_prefix.reset(new std::string(android_product_out));
 
56. }
 
57. }
 
58.  if  (android_root.empty()) {
 
59. const char* android_root_env_var = getenv( "ANDROID_ROOT" );
 
60.  if  (android_root_env_var == NULL) {
 
61. Usage( "--android-root unspecified and ANDROID_ROOT not set" );
 
62. }
 
63. android_root += android_root_env_var;
 
64. }
 
65. bool image = (!image_filename.empty());
 
66.  if  (!image && boot_image_filename.empty()) {
 
67.  if  (host_prefix.get() == NULL) {
 
68. boot_image_filename += GetAndroidRoot();
 
69. }  else  {
 
70. boot_image_filename += *host_prefix.get();
 
71. boot_image_filename +=  "/system" ;
 
72. }
 
73. boot_image_filename +=  "/framework/boot.art" ;
 
74. }
 
75. std::string boot_image_option;
 
76.  if  (!boot_image_filename.empty()) {
 
77. boot_image_option +=  "-Ximage:" ;
 
78. boot_image_option += boot_image_filename;
 
79. }
 
80. ......
 
81.  if  (dex_locations.empty()) {
 
82.  for  (size_t i = 0; i < dex_filenames.size(); i++) {
 
83. dex_locations.push_back(dex_filenames[i]);
 
84. }
 
85. }  else  if  (dex_locations.size() != dex_filenames.size()) {
 
86. Usage( "--dex-location arguments do not match --dex-file arguments" );
 
87. }
 
88. ......
  
89. }


3-7 行根据 ART_USE_PORTABLE_COMPILER 宏的值来确定 ART Compiler Driver 的工作方式,关
于 ART Compiler Driver 后续会涉及到;8-16 行根据设备的平台架构来确定指令集,包括 ARM,
i386 以及 Mips;21-25 行解析--zip-fd 参数并转换为整型值赋值给 zip_fd;26-39 行分别解析
获得 zip_location,oat_fd 以及 oat_location。获得参数信息后,会对一些参数进行判断,例如
45-46 行会判断--oat-file 以及--oat-fd 参数是否同时被赋值,对于一些不正确的参数组合,会
调用 Usage 函数打印 dex2oat 的使用方法并退出。

之后会根据一些参数情况进行其他参数的赋值操作,52 行判断 host_prefix 是否为空,
由于没有使用--host-prefix 参数,因此进入 if 分支语句,获取 ANDROID_PRODUCT_OUT 环境变
量并重新赋值给 host_prefix 变量,ANDROID_PRODUCT_OUT 环境变量应该是在 PC 机上进行
Android 源码编译时设置的,一般为<ANDROID BASEDIR>/out/target/product/generic/,所以在
Android 手机设备上不存在此环境变量,host_prefix 值还是为 NULL,这个没有编译调试,分
析结论不一定正确。不过可以编写 Android 程序,在 Java 层通过 System.getenv (“ANDROID
_PRODUCT_OUT”)来测试,会发现没有这个环境变量(System.getenv()可以获取所有的环境变
量)!至于 System. getenv(String)函数是否与上述的 getenv 函数最终调用的是同一个函数,大
家可以跟踪源码,System.getenv 调用过程是:System.getenv -> Libcore.os.getenv-> Posix.getenv,
Posix.getenv(libcore/luni/src/main/java/libcore/io/Posix.java)调用的是 Native 函数,而这个 Native
函数的注册暂时没有找到(囧)。

58-64 行获取 ANDROID_ROOT 环境变量,该值为/system,也就是 Android 系统下的 system
目录路径;65 行由于 image_filename 为空,因此布尔值 image 为 false,则会进入 66 行的 if
分支,66 行由于 host_prefix 为 NULL, boot_image_filename 值为“/system”(GetAndroidRoot
函数源码在/art/runtime/utils.cc 文件中,仍然调用 getenv(“ANDROID_ROOT”),不过会增加
/system 目录是否存在的判断),执行 73 行,最终 boot_iamge_filename 值为“/system/framework
/boot.art”,但是在 Nexus4 上/system/framework 目录下并不存在 boot.art 文件,反而在/data
/dalvik-cache 目录下存在 [email][email protected]@boot.art[/email] 以及 [email][email protected]@boot.oat[/email] 文件
(Android4.4 系统应该都是这样);76 行,经过上述操作,boot_image_filename 不为空,因此进
入 if 分支语句,boot_image_option 最终值为“-Ximage:/system/framework/boot.art”;81 行
dex_locations 为空,进入 if 分支,由于 dex_filenames 仍然为空,所以不会执行 for 循环。

经过上述分析,总结一些变量的值:
● 
  • host_prefix: NULL
  • android_root: /system
  • boot_image_filename: /system/framework/boot.art
  • boot_image_option: -Ximage:/system/framework/boot.art
  • dex_locations: 空


    创建 OAT 文件指针

    在完成参数解析判断后,会创建一个指向 oat_location 的文件指针,暂时没有真正的写
    入 OAT 格式的文件数据。代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    1. UniquePtr<File> oat_file;
     
    2. bool create_file = !oat_unstripped.empty();
     
    3.  if  (create_file) {
     
    4. oat_file.reset(OS::CreateEmptyFile(oat_unstripped.c_str()));
     
    5.  if  (oat_location.empty()) {
     
    6. oat_location = oat_filename;
     
    7. }
     
    8. }  else  {
     
    9. oat_file.reset(new File(oat_fd, oat_location));
     
    10. oat_file->DisableAutoClose();
     
    11. }
     
    12.  if  (oat_file.get() == NULL) {
     
    13. PLOG(ERROR) <<  "Failed to create oat file: "  << oat_location;
     
    14.  return  EXIT_FAILURE;
     
    15. }
     
    16.  if  (create_file && fchmod(oat_file->Fd(), 0644) != 0) {
     
    17. PLOG(ERROR) <<  "Failed to make oat file world readable: "  << oat_location;
     
    18.  return  EXIT_FAILURE;
     
    19. }


    第 1 行声明 File 指针变量 oat_file;第 2 行因为 oat_unstripped 为空,故 create_file 布尔值为 flase;
    第 3 行判断 create_file 值,进入 else 分支;9 行根据 oat_fd 以及 oat_location 创建 File 实例并
    赋值给 oat_file;第 10 行调用 DisableAutoClose 函数禁止文件自动关闭;12-18 行会进行一些
    判断操作。上述代码仅仅是创建了一个 File 实例,还没有真正写入任何数据。

    dex2oat 准备工作

    接着会完成一些 DEX 到 OAT 文件格式的转换工作,代码如下,对于部分分支语句由于
    在本次分析中不会执行已忽略。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    1. ......
     
    2. Runtime::Options options;
     
    3. options.push_back(std::make_pair( "compiler" , reinterpret_cast<void*>(NULL)));
     
    4. std::vector<const DexFile*> boot_class_path;
     
    5.  if  (boot_image_option.empty()) {
      
    6. ......
     
    7. }  else  {
     
    8. options.push_back(std::make_pair(boot_image_option.c_str(),
      
      reinterpret_cast<void*>(NULL)));
     
    9. }
     
    10. ......


    第 2 行声明 Runtime::Options 类型的变量 options,而 Runtime::Options 实际上是一个包含
    pair(http://www.cplusplus.com/reference/utility/pair/)的 vector,定义在/art/runtime/runtime.h 头
    文件中,如下,pair 中的两个数据一个是字符串,另一个为指针,类似于 HashMap 之类的结
    构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    1. class Runtime {
     
    2. public:
     
    3. typedef std::vector<std::pair<std::string, const void*> > Options;
     
    4. ......
     
    5. }


    第 3 行在 options 变量中添加一个 pair 数据;第 4 行声明包含 DexFile 的 vector 变量 boot_class_
    path,类 DexFile 的定义在/art/runtime/dex_file.h 头文件中,其实就是 Dalvik 上关于 dex 文件
    结构的定义;第 5 行由于 boot_image_option 不为空,因此执行 else 分支语句,跳到第 8 行,
    继续在 options 中增加一个 pair 数据,boot_image_option 值为-Ximage:/system/framework
    /boot.art。所以最终 options 中包含“compiler”和“-Ximage:/system/framework/boot.art”两
    项。

    提取 classes.dex 文件

    在完成一系列的准备工作后,就要开始进入比较繁重的 OAT 文件创建和转换工作了。
    部分代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    1. Dex2Oat* p_dex2oat;
     
    2.  if  (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set,
     
    thread_count)) {
     
    3. LOG(ERROR) <<  "Failed to create dex2oat" ;
     
    4.  return  EXIT_FAILURE;
     
    5. }
     
    6. UniquePtr<Dex2Oat> dex2oat(p_dex2oat);
     
    7. Thread* self = Thread::Current();
     
    8. self->TransitionFromRunnableToSuspended(kNative);
     
    9. WellKnownClasses::Init(self->GetJniEnv());
     
    10. ......
     
    11. std::vector<const DexFile*> dex_files;
     
    12.  if  (boot_image_option.empty()) {
     
    13. dex_files = Runtime::Current()->GetClassLinker()->GetBootClassPath();
     
    14. }  else  {
     
    15.  if  (dex_filenames.empty()) {
     
    16. UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(zip_fd));
     
    17.  if  (zip_archive.get() == NULL) {
     
    18. LOG(ERROR) <<  "Failed to open zip from file descriptor for "  << zip_location;
     
    19.  return  EXIT_FAILURE;
     
    20. }
     
    21. const DexFile* dex_file = DexFile::Open(*zip_archive.get(), zip_location);
     
    22.  if  (dex_file == NULL) {
     
    23. ......
     
    24.  return  EXIT_FAILURE;
     
    25. }
     
    26. dex_files.push_back(dex_file);
     
    27. }  else  {
     
    28. ......
     
    29. }
     
    30.  for  (const auto& dex_file : dex_files) {
     
    31.  if  (!dex_file->EnableWrite()) {
     
    32. ......
     
    33. }
     
    34. }
     
    35. }


    第 1 行声明指向 Dex2Oat 的指针,类 Dex2Oat 定义在/art/dex2oat/dex2oat.cc 文件中;第 2 行
    调用 Dex2Oat 的静态方法 Create,其中 options 参数已经在“dex2oat 准备工作”部分分析了,
    compiler_backend 在没有指定--compiler-backend 参数的情况下,会根据 ART_USE_PORTABLE
    _COMPILER 宏定义的情况取值,若该宏定义了则为 kPortable,否则为 kQuick;instruction_set
    根据源码编译指定的目标平台会使用相应的指令集,目前大部分 Android 设备使用的 ARM,
    因此 instruction_set 值为 kThumb2。thread_count 值在默认情况下通过 sysconf
    (_SC_NPROCESSORS_CONF)获取,也就是 CPU 的个数。Dex2Oat::Create 函数代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    1. static bool Create(Dex2Oat** p_dex2oat,Runtime::Options& options,
     
    2. CompilerBackend compiler_backend, InstructionSet instruction_set,
     
    3. size_t thread_count)
     
    4. SHARED_TRYLOCK_FUNCTION( true , Locks::mutator_lock_) {
     
    5.  if  (!CreateRuntime(options, instruction_set)) {
     
    6. *p_dex2oat = NULL;
     
    7.  return  false ;
     
    8. }
     
    9. *p_dex2oat = new Dex2Oat(Runtime::Current(), compiler_backend, instruction_set,
     
    thread_count);
     
    10.  return  true ;
      
    11. }


    第 5 行调用 CreateRuntime 函数获取 Runtime(/art/runtime/runtime.cc)类的实例,并进行相关的
    设置,Runtime 使用单例模式,所以获取 Runtime 实例之前会先获取锁,如第 4 行所示,关
    于 Runtime 类实例的获取比较简单,不再详述,是典型的单例设计模式;第 9 行创建 Dex2Oat
    类的实例,Dex2Oat 的构造函数比较简单,只是一些类成员的赋值操作,如下代码所示,除
    了传入的一些参数外,还记录了实例化的时间保存在 start_ns_成员变量中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    1. explicit Dex2Oat(Runtime* runtime,
     
    2. CompilerBackend compiler_backend,
     
    3. InstructionSet instruction_set,
     
    4. size_t thread_count)
     
    5. : compiler_backend_(compiler_backend),
     
    6. instruction_set_(instruction_set),
     
    7. runtime_(runtime),
     
    8. thread_count_(thread_count),
     
    9. start_ns_(NanoTime()) {
      
    10. }


    继续回到 dex2oat 函数,第 6 行将 p_dex2oat 重新赋值给 dex2oat 变量;第 7 行调用
    Thread::Current 得到当前线程,Thread 类定义在/art/runtime/thread.h 头文件中;第 8 行将线程
    状态从 Runnable 切换到 Suspend,释放掉之前在调用 Dex2Oat::Create 中获取的锁,
    TransitionFromRunnableToSuspend 函数定义在/art/runtime/thread-inl.h 头文件中,是个内联函
    数;第 9 行调用 WellKnownClasses::Init 函数完成一些 JNI 类、函数以及字段的初始化操作,
    主要是从 JNI 运行环境中查找一些类、函数等信息并返回,self->GetJniEnv()可以获得线程关
    联的 JNI 环境,Init 函数代码位于/art/runtime/well_known_classes.cc,可自行进行分析;11 行
    声明名为 dex_files 的 vector 变量;12 行由于 boot_image_option 非空,因此进入 else 分支;
    15 行 dex_filenames 为空,进入 if 分支,16 行调用 ZipArchive::OpenFromFd 函数创建 ZipArchive
    类实例,主要完成 zip 文件到内存结构的映射。个人觉得此处 zip 文件的操作对后续的分析
    有一定的影响,因此会在此展开分析 ZipArchive 类(/art/runtime/zip_archive.h 和/art/runtime/
    zip_archive.cc)。OpenFromFd 的函数代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    1. ZipArchive* ZipArchive::OpenFromFd(int fd) {
     
    2. ......
     
    3. UniquePtr<ZipArchive> zip_archive(new ZipArchive(fd));
     
    4. ......
     
    5.  if  (!zip_archive->MapCentralDirectory()) {
     
    6. zip_archive->Close();
     
    7.  return  NULL;
     
    8. }
     
    9.  if  (!zip_archive->Parse()) {
     
    10. zip_archive->Close();
     
    11.  return  NULL;
     
    12. }
     
    13.  return  zip_archive.release();
      
    14. }


    第 3 行初始化 ZipArchive 实例并赋值给 zip_archive 变量,ZipArchive 类的构造函数比较简单,
    如下所示,只是简单的成员变量初始化:

    1
    1. explicit ZipArchive(int fd) : fd_(fd), num_entries_(0), dir_offset_(0) {}


    第 5 行调用 MapCentralDirectory 完成 Central Directory Header 的查找以及将所有 Central
    Directory Header 内容映射到内存(关于 ZIP 文件的格式可以参考本文开头关于 ZIP 文件结构
    介绍),该函数部分代码如下(为避免篇幅过长,已删除各种与文件合法性检查相关的代码):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    1. bool ZipArchive::MapCentralDirectory() {
     
    2. off64_t file_length = lseek64(fd_, 0, SEEK_END);
     
    3. ......
     
    4. size_t read_amount = kMaxEOCDSearch;
     
    5.  if  (file_length < off64_t(read_amount)) {
     
    6. read_amount = file_length;
     
    7. }
     
    8. UniquePtr<uint8_t[]> scan_buf(new uint8_t[read_amount]);
     
    9. ......
     
    10.  if  (lseek64(fd_, 0, SEEK_SET) != 0) {
     
    11.  return  false ;
     
    12. }
     
    13. ssize_t actual = TEMP_FAILURE_RETRY( read (fd_, scan_buf.get(), sizeof(int32_t)));
     
    14. ......
     
    15. unsigned int header = Le32ToHost(scan_buf.get());
     
    16.  if  (header != kLFHSignature) {
     
    17.  return  false ;
     
    18. }
     
    19. off64_t search_start = file_length - read_amount;
     
    20.  if  (lseek64(fd_, search_start, SEEK_SET) != search_start) {
     
    21.  return  false ;
     
    22. }
     
    23. actual = TEMP_FAILURE_RETRY( read (fd_, scan_buf.get(), read_amount));
     
    24. ......
     
    25. int i;
     
    26.  for  (i = read_amount - kEOCDLen; i >= 0; i--) {
     
    27.  if  (scan_buf.get()[i] == 0x50 && Le32ToHost(&(scan_buf.get())[i]) ==
     
      kEOCDSignature) {
     
    28.  break ;
     
    29. }
     
    30. }
     
    31. ......
     
    32. off64_t eocd_offset = search_start + i;
     
    33. const byte* eocd_ptr = scan_buf.get() + i;
     
    34. DCHECK(eocd_offset < file_length);
     
    35. uint16_t disk_number = Le16ToHost(eocd_ptr + kEOCDDiskNumber);
     
    36. uint16_t disk_with_central_dir = Le16ToHost(eocd_ptr + kEOCDDiskNumberForCD);
     
    37. uint16_t num_entries = Le16ToHost(eocd_ptr + kEOCDNumEntries);
     
    38. uint16_t total_num_entries = Le16ToHost(eocd_ptr + kEOCDTotalNumEntries);
     
    39. uint32_t dir_size = Le32ToHost(eocd_ptr + kEOCDSize);
     
    40. uint32_t dir_offset = Le32ToHost(eocd_ptr + kEOCDFileOffset);
     
    41. uint16_t comment_size = Le16ToHost(eocd_ptr + kEOCDCommentSize);
     
    42. ......
     
    43. dir_map_.reset(MemMap::MapFile(dir_size, PROT_READ, MAP_SHARED, fd_,
     
    dir_offset));
     
    44. ......
     
    45. num_entries_ = num_entries;
     
    46. dir_offset_ = dir_offset;
     
    47.  return  true ;
      
    48. }


    第 1 行通过 lseek64 函数将文件描述符定位到 ZIP 文件结尾处得到文件的长度;第 3 行将
    kMaxEOCDSearch 赋值给 read_amount,kMaxEOCDSearch = (kMaxCommentLen + kEOCDLen) =
    65535 + 22,也就是 End of Central Directory 的最大长度,因为 Zip file comment 最大长度为 64KB,
    故 kMaxEOCDSearch 最大长度为 65535+22;第 5 行判断若整个 ZIP 文件的长度小于
    read_amount,则将 read_amount 重新设置为 file_length 的值;第 8 行声明 scan_buf 指针用来
    存储读取的文件内容;第 10 行将文件描述符定位到 ZIP 文件的开头,因为在获取文件长度
    时将其定位到了文件结尾处,现在要重新进行定位;13 行从文件开始处连续读取
    sizeof(int32_t)字节的内容到 scan_buf 中;15-18 行判断文件开头 4 个字节是否为 0x04034b50,
    也就是 Local File Header 的 Signature,若不是说明该 ZIP 文件格式不正确;19 行计算
    search_start 值,也就是后续 End of Central Directory 搜索的起始地址(相对于文件开头);20 行
    将文件描述符定位到 search_start 处;23 行读取 search_start 之后的所有内容到 scan_buf;26-30
    行开始搜索 End of Central Directory 的位置,搜索结果可以参考图 11,图中 End of Central
    Directory Record 没有包含 Zip file comment。注意为了方便描述,Zip file comment 长度设定为
    10Bytes,另外一点要注意的是在 for 循环中比较 Signature 时是从 scan_buf 的末尾往前扫的;
    32 行给 eocd_offset 赋值,从图 11 中可知 End of Central Directory Record 的偏移量为 search_start
    + 65535 - 10,也就是 search_start + i 的值;33 行获得指向 EOCD 起始地址的指针;35-41 行
    完成 EOCD 中字段值的获取,可以参考 ZIP 文件结构中的介绍,其中 dir_size 表示所有 Central

    图 11 End of Central Directory 搜索示意图

    Directory Header 的大小,dir_offset 表示第一个 Central Directory Header 相对文件开始处的偏移
    地址;43 行将所有 Central Directory Header 的内容映射到内存;45-46 行给相应的成员变量
    赋值,保存解析到的 Central Directory Header 的个数及偏移量。

    继续回到 OpenFromFd 函数,第 9 行调用 Parse 函数将 Central Directory Header 保存到名
    为 dir_entries_的 SafeMap 类(/art/runtime/safe_map.h)中,其中 Key 为 StringPiece 实例(StringPiece
    类定义在/art/runtime /base/stringpiece.h 头文件中),Value 为对应 Central Directory Header 映射
    到内存中的起始地址。Parse 函数比较简单,不再赘述。

    分析完 ZipArchive::OpenFromFd 后,继续回到 dex2oat 中,21 行调用 DexFile::Open 函数,
    该函数定义在/art/runtime/dex_file.cc 源文件中,注意有两个 DexFile::Open 函数,一个是 const
  • 相关文章
    相关标签/搜索