blob: dee736ecff368d341c8096efb1355aaeeb552724 [file] [log] [blame]
David Sehr013fd802018-01-11 22:55:24 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "art_dex_file_loader.h"
18
19#include <sys/mman.h> // For the PROT_* and MAP_* constants.
20#include <sys/stat.h>
21
22#include "android-base/stringprintf.h"
23
24#include "base/file_magic.h"
25#include "base/stl_util.h"
26#include "base/systrace.h"
27#include "base/unix_file/fd_file.h"
28#include "compact_dex_file.h"
29#include "dex_file.h"
30#include "dex_file_verifier.h"
31#include "standard_dex_file.h"
32#include "zip_archive.h"
33
34namespace art {
35
36namespace {
37
38class MemMapContainer : public DexFileContainer {
39 public:
40 explicit MemMapContainer(std::unique_ptr<MemMap>&& mem_map) : mem_map_(std::move(mem_map)) { }
41 virtual ~MemMapContainer() OVERRIDE { }
42
43 int GetPermissions() OVERRIDE {
44 if (mem_map_.get() == nullptr) {
45 return 0;
46 } else {
47 return mem_map_->GetProtect();
48 }
49 }
50
51 bool IsReadOnly() OVERRIDE {
52 return GetPermissions() == PROT_READ;
53 }
54
55 bool EnableWrite() OVERRIDE {
56 CHECK(IsReadOnly());
57 if (mem_map_.get() == nullptr) {
58 return false;
59 } else {
60 return mem_map_->Protect(PROT_READ | PROT_WRITE);
61 }
62 }
63
64 bool DisableWrite() OVERRIDE {
65 CHECK(!IsReadOnly());
66 if (mem_map_.get() == nullptr) {
67 return false;
68 } else {
69 return mem_map_->Protect(PROT_READ);
70 }
71 }
72
73 private:
74 std::unique_ptr<MemMap> mem_map_;
75 DISALLOW_COPY_AND_ASSIGN(MemMapContainer);
76};
77
78} // namespace
79
80using android::base::StringPrintf;
81
82static constexpr OatDexFile* kNoOatDexFile = nullptr;
83
84
85bool ArtDexFileLoader::GetMultiDexChecksums(const char* filename,
86 std::vector<uint32_t>* checksums,
87 std::string* error_msg,
88 int zip_fd) const {
89 CHECK(checksums != nullptr);
90 uint32_t magic;
91
92 File fd;
93 if (zip_fd != -1) {
94 if (ReadMagicAndReset(zip_fd, &magic, error_msg)) {
95 fd = File(zip_fd, false /* check_usage */);
96 }
97 } else {
98 fd = OpenAndReadMagic(filename, &magic, error_msg);
99 }
100 if (fd.Fd() == -1) {
101 DCHECK(!error_msg->empty());
102 return false;
103 }
104 if (IsZipMagic(magic)) {
105 std::unique_ptr<ZipArchive> zip_archive(
106 ZipArchive::OpenFromFd(fd.Release(), filename, error_msg));
107 if (zip_archive.get() == nullptr) {
108 *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename,
109 error_msg->c_str());
110 return false;
111 }
112
113 uint32_t i = 0;
114 std::string zip_entry_name = GetMultiDexClassesDexName(i++);
115 std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg));
116 if (zip_entry.get() == nullptr) {
117 *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename,
118 zip_entry_name.c_str(), error_msg->c_str());
119 return false;
120 }
121
122 do {
123 checksums->push_back(zip_entry->GetCrc32());
124 zip_entry_name = GetMultiDexClassesDexName(i++);
125 zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg));
126 } while (zip_entry.get() != nullptr);
127 return true;
128 }
129 if (IsMagicValid(magic)) {
David Brazdil2b9c35b2018-01-12 15:44:43 +0000130 std::unique_ptr<const DexFile> dex_file(OpenFile(fd.Release(),
131 filename,
132 /* verify */ false,
133 /* verify_checksum */ false,
134 /* mmap_shared */ false,
135 error_msg));
David Sehr013fd802018-01-11 22:55:24 -0800136 if (dex_file == nullptr) {
137 return false;
138 }
139 checksums->push_back(dex_file->GetHeader().checksum_);
140 return true;
141 }
142 *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
143 return false;
144}
145
146std::unique_ptr<const DexFile> ArtDexFileLoader::Open(const uint8_t* base,
147 size_t size,
148 const std::string& location,
149 uint32_t location_checksum,
150 const OatDexFile* oat_dex_file,
151 bool verify,
152 bool verify_checksum,
153 std::string* error_msg) const {
154 ScopedTrace trace(std::string("Open dex file from RAM ") + location);
155 return OpenCommon(base,
156 size,
157 location,
158 location_checksum,
159 oat_dex_file,
160 verify,
161 verify_checksum,
162 error_msg,
163 /*container*/ nullptr,
164 /*verify_result*/ nullptr);
165}
166
167std::unique_ptr<const DexFile> ArtDexFileLoader::Open(const std::string& location,
168 uint32_t location_checksum,
169 std::unique_ptr<MemMap> map,
170 bool verify,
171 bool verify_checksum,
172 std::string* error_msg) const {
173 ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location);
174 CHECK(map.get() != nullptr);
175
176 if (map->Size() < sizeof(DexFile::Header)) {
177 *error_msg = StringPrintf(
178 "DexFile: failed to open dex file '%s' that is too short to have a header",
179 location.c_str());
180 return nullptr;
181 }
182
183 std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
184 map->Size(),
185 location,
186 location_checksum,
187 kNoOatDexFile,
188 verify,
189 verify_checksum,
190 error_msg,
191 new MemMapContainer(std::move(map)),
192 /*verify_result*/ nullptr);
193 return dex_file;
194}
195
196bool ArtDexFileLoader::Open(const char* filename,
197 const std::string& location,
198 bool verify,
199 bool verify_checksum,
200 std::string* error_msg,
201 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
202 ScopedTrace trace(std::string("Open dex file ") + std::string(location));
203 DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
204 uint32_t magic;
205 File fd = OpenAndReadMagic(filename, &magic, error_msg);
206 if (fd.Fd() == -1) {
207 DCHECK(!error_msg->empty());
208 return false;
209 }
210 if (IsZipMagic(magic)) {
211 return OpenZip(fd.Release(), location, verify, verify_checksum, error_msg, dex_files);
212 }
213 if (IsMagicValid(magic)) {
214 std::unique_ptr<const DexFile> dex_file(OpenFile(fd.Release(),
215 location,
216 verify,
217 verify_checksum,
David Brazdil2b9c35b2018-01-12 15:44:43 +0000218 /* mmap_shared */ false,
David Sehr013fd802018-01-11 22:55:24 -0800219 error_msg));
220 if (dex_file.get() != nullptr) {
221 dex_files->push_back(std::move(dex_file));
222 return true;
223 } else {
224 return false;
225 }
226 }
227 *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
228 return false;
229}
230
231std::unique_ptr<const DexFile> ArtDexFileLoader::OpenDex(int fd,
232 const std::string& location,
233 bool verify,
234 bool verify_checksum,
David Brazdil2b9c35b2018-01-12 15:44:43 +0000235 bool mmap_shared,
David Sehr013fd802018-01-11 22:55:24 -0800236 std::string* error_msg) const {
237 ScopedTrace trace("Open dex file " + std::string(location));
David Brazdil2b9c35b2018-01-12 15:44:43 +0000238 return OpenFile(fd, location, verify, verify_checksum, mmap_shared, error_msg);
David Sehr013fd802018-01-11 22:55:24 -0800239}
240
241bool ArtDexFileLoader::OpenZip(int fd,
242 const std::string& location,
243 bool verify,
244 bool verify_checksum,
245 std::string* error_msg,
246 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
247 ScopedTrace trace("Dex file open Zip " + std::string(location));
248 DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is nullptr";
249 std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
250 if (zip_archive.get() == nullptr) {
251 DCHECK(!error_msg->empty());
252 return false;
253 }
254 return OpenAllDexFilesFromZip(
255 *zip_archive, location, verify, verify_checksum, error_msg, dex_files);
256}
257
258std::unique_ptr<const DexFile> ArtDexFileLoader::OpenFile(int fd,
259 const std::string& location,
260 bool verify,
261 bool verify_checksum,
David Brazdil2b9c35b2018-01-12 15:44:43 +0000262 bool mmap_shared,
David Sehr013fd802018-01-11 22:55:24 -0800263 std::string* error_msg) const {
264 ScopedTrace trace(std::string("Open dex file ") + std::string(location));
265 CHECK(!location.empty());
266 std::unique_ptr<MemMap> map;
267 {
268 File delayed_close(fd, /* check_usage */ false);
269 struct stat sbuf;
270 memset(&sbuf, 0, sizeof(sbuf));
271 if (fstat(fd, &sbuf) == -1) {
272 *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location.c_str(),
273 strerror(errno));
274 return nullptr;
275 }
276 if (S_ISDIR(sbuf.st_mode)) {
277 *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str());
278 return nullptr;
279 }
280 size_t length = sbuf.st_size;
281 map.reset(MemMap::MapFile(length,
282 PROT_READ,
David Brazdil2b9c35b2018-01-12 15:44:43 +0000283 mmap_shared ? MAP_SHARED : MAP_PRIVATE,
David Sehr013fd802018-01-11 22:55:24 -0800284 fd,
285 0,
286 /*low_4gb*/false,
287 location.c_str(),
288 error_msg));
289 if (map == nullptr) {
290 DCHECK(!error_msg->empty());
291 return nullptr;
292 }
293 }
294
295 if (map->Size() < sizeof(DexFile::Header)) {
296 *error_msg = StringPrintf(
297 "DexFile: failed to open dex file '%s' that is too short to have a header",
298 location.c_str());
299 return nullptr;
300 }
301
302 const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(map->Begin());
303
304 std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
305 map->Size(),
306 location,
307 dex_header->checksum_,
308 kNoOatDexFile,
309 verify,
310 verify_checksum,
311 error_msg,
312 new MemMapContainer(std::move(map)),
313 /*verify_result*/ nullptr);
314
315 return dex_file;
316}
317
318std::unique_ptr<const DexFile> ArtDexFileLoader::OpenOneDexFileFromZip(
319 const ZipArchive& zip_archive,
320 const char* entry_name,
321 const std::string& location,
322 bool verify,
323 bool verify_checksum,
324 std::string* error_msg,
325 ZipOpenErrorCode* error_code) const {
326 ScopedTrace trace("Dex file open from Zip Archive " + std::string(location));
327 CHECK(!location.empty());
328 std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
329 if (zip_entry == nullptr) {
330 *error_code = ZipOpenErrorCode::kEntryNotFound;
331 return nullptr;
332 }
333 if (zip_entry->GetUncompressedLength() == 0) {
334 *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
335 *error_code = ZipOpenErrorCode::kDexFileError;
336 return nullptr;
337 }
338
339 std::unique_ptr<MemMap> map;
340 if (zip_entry->IsUncompressed()) {
341 if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) {
342 // Do not mmap unaligned ZIP entries because
343 // doing so would fail dex verification which requires 4 byte alignment.
344 LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
345 << "please zipalign to " << alignof(DexFile::Header) << " bytes. "
346 << "Falling back to extracting file.";
347 } else {
348 // Map uncompressed files within zip as file-backed to avoid a dirty copy.
349 map.reset(zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg));
350 if (map == nullptr) {
351 LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
352 << "is your ZIP file corrupted? Falling back to extraction.";
353 // Try again with Extraction which still has a chance of recovery.
354 }
355 }
356 }
357
358 if (map == nullptr) {
359 // Default path for compressed ZIP entries,
360 // and fallback for stored ZIP entries.
361 map.reset(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
362 }
363
364 if (map == nullptr) {
365 *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
366 error_msg->c_str());
367 *error_code = ZipOpenErrorCode::kExtractToMemoryError;
368 return nullptr;
369 }
370 VerifyResult verify_result;
371 std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
372 map->Size(),
373 location,
374 zip_entry->GetCrc32(),
375 kNoOatDexFile,
376 verify,
377 verify_checksum,
378 error_msg,
379 new MemMapContainer(std::move(map)),
380 &verify_result);
381 if (dex_file == nullptr) {
382 if (verify_result == VerifyResult::kVerifyNotAttempted) {
383 *error_code = ZipOpenErrorCode::kDexFileError;
384 } else {
385 *error_code = ZipOpenErrorCode::kVerifyError;
386 }
387 return nullptr;
388 }
389 if (!dex_file->DisableWrite()) {
390 *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
391 *error_code = ZipOpenErrorCode::kMakeReadOnlyError;
392 return nullptr;
393 }
394 CHECK(dex_file->IsReadOnly()) << location;
395 if (verify_result != VerifyResult::kVerifySucceeded) {
396 *error_code = ZipOpenErrorCode::kVerifyError;
397 return nullptr;
398 }
399 *error_code = ZipOpenErrorCode::kNoError;
400 return dex_file;
401}
402
403// Technically we do not have a limitation with respect to the number of dex files that can be in a
404// multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
405// (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
406// seems an excessive number.
407static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
408
409bool ArtDexFileLoader::OpenAllDexFilesFromZip(
410 const ZipArchive& zip_archive,
411 const std::string& location,
412 bool verify,
413 bool verify_checksum,
414 std::string* error_msg,
415 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
416 ScopedTrace trace("Dex file open from Zip " + std::string(location));
417 DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
418 ZipOpenErrorCode error_code;
419 std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive,
420 kClassesDex,
421 location,
422 verify,
423 verify_checksum,
424 error_msg,
425 &error_code));
426 if (dex_file.get() == nullptr) {
427 return false;
428 } else {
429 // Had at least classes.dex.
430 dex_files->push_back(std::move(dex_file));
431
432 // Now try some more.
433
434 // We could try to avoid std::string allocations by working on a char array directly. As we
435 // do not expect a lot of iterations, this seems too involved and brittle.
436
437 for (size_t i = 1; ; ++i) {
438 std::string name = GetMultiDexClassesDexName(i);
439 std::string fake_location = GetMultiDexLocation(i, location.c_str());
440 std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive,
441 name.c_str(),
442 fake_location,
443 verify,
444 verify_checksum,
445 error_msg,
446 &error_code));
447 if (next_dex_file.get() == nullptr) {
448 if (error_code != ZipOpenErrorCode::kEntryNotFound) {
449 LOG(WARNING) << "Zip open failed: " << *error_msg;
450 }
451 break;
452 } else {
453 dex_files->push_back(std::move(next_dex_file));
454 }
455
456 if (i == kWarnOnManyDexFilesThreshold) {
457 LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold
458 << " dex files. Please consider coalescing and shrinking the number to "
459 " avoid runtime overhead.";
460 }
461
462 if (i == std::numeric_limits<size_t>::max()) {
463 LOG(ERROR) << "Overflow in number of dex files!";
464 break;
465 }
466 }
467
468 return true;
469 }
470}
471
472} // namespace art