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
  • 相關文章
    相關標籤/搜索