blob: 282b28270760419a3ae22c2b966aeb58f966ab66 [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)) {
130 std::unique_ptr<const DexFile> dex_file(
131 OpenFile(fd.Release(), filename, false, false, error_msg));
132 if (dex_file == nullptr) {
133 return false;
134 }
135 checksums->push_back(dex_file->GetHeader().checksum_);
136 return true;
137 }
138 *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
139 return false;
140}
141
142std::unique_ptr<const DexFile> ArtDexFileLoader::Open(const uint8_t* base,
143 size_t size,
144 const std::string& location,
145 uint32_t location_checksum,
146 const OatDexFile* oat_dex_file,
147 bool verify,
148 bool verify_checksum,
149 std::string* error_msg) const {
150 ScopedTrace trace(std::string("Open dex file from RAM ") + location);
151 return OpenCommon(base,
152 size,
153 location,
154 location_checksum,
155 oat_dex_file,
156 verify,
157 verify_checksum,
158 error_msg,
159 /*container*/ nullptr,
160 /*verify_result*/ nullptr);
161}
162
163std::unique_ptr<const DexFile> ArtDexFileLoader::Open(const std::string& location,
164 uint32_t location_checksum,
165 std::unique_ptr<MemMap> map,
166 bool verify,
167 bool verify_checksum,
168 std::string* error_msg) const {
169 ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location);
170 CHECK(map.get() != nullptr);
171
172 if (map->Size() < sizeof(DexFile::Header)) {
173 *error_msg = StringPrintf(
174 "DexFile: failed to open dex file '%s' that is too short to have a header",
175 location.c_str());
176 return nullptr;
177 }
178
179 std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
180 map->Size(),
181 location,
182 location_checksum,
183 kNoOatDexFile,
184 verify,
185 verify_checksum,
186 error_msg,
187 new MemMapContainer(std::move(map)),
188 /*verify_result*/ nullptr);
189 return dex_file;
190}
191
192bool ArtDexFileLoader::Open(const char* filename,
193 const std::string& location,
194 bool verify,
195 bool verify_checksum,
196 std::string* error_msg,
197 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
198 ScopedTrace trace(std::string("Open dex file ") + std::string(location));
199 DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
200 uint32_t magic;
201 File fd = OpenAndReadMagic(filename, &magic, error_msg);
202 if (fd.Fd() == -1) {
203 DCHECK(!error_msg->empty());
204 return false;
205 }
206 if (IsZipMagic(magic)) {
207 return OpenZip(fd.Release(), location, verify, verify_checksum, error_msg, dex_files);
208 }
209 if (IsMagicValid(magic)) {
210 std::unique_ptr<const DexFile> dex_file(OpenFile(fd.Release(),
211 location,
212 verify,
213 verify_checksum,
214 error_msg));
215 if (dex_file.get() != nullptr) {
216 dex_files->push_back(std::move(dex_file));
217 return true;
218 } else {
219 return false;
220 }
221 }
222 *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
223 return false;
224}
225
226std::unique_ptr<const DexFile> ArtDexFileLoader::OpenDex(int fd,
227 const std::string& location,
228 bool verify,
229 bool verify_checksum,
230 std::string* error_msg) const {
231 ScopedTrace trace("Open dex file " + std::string(location));
232 return OpenFile(fd, location, verify, verify_checksum, error_msg);
233}
234
235bool ArtDexFileLoader::OpenZip(int fd,
236 const std::string& location,
237 bool verify,
238 bool verify_checksum,
239 std::string* error_msg,
240 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
241 ScopedTrace trace("Dex file open Zip " + std::string(location));
242 DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is nullptr";
243 std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
244 if (zip_archive.get() == nullptr) {
245 DCHECK(!error_msg->empty());
246 return false;
247 }
248 return OpenAllDexFilesFromZip(
249 *zip_archive, location, verify, verify_checksum, error_msg, dex_files);
250}
251
252std::unique_ptr<const DexFile> ArtDexFileLoader::OpenFile(int fd,
253 const std::string& location,
254 bool verify,
255 bool verify_checksum,
256 std::string* error_msg) const {
257 ScopedTrace trace(std::string("Open dex file ") + std::string(location));
258 CHECK(!location.empty());
259 std::unique_ptr<MemMap> map;
260 {
261 File delayed_close(fd, /* check_usage */ false);
262 struct stat sbuf;
263 memset(&sbuf, 0, sizeof(sbuf));
264 if (fstat(fd, &sbuf) == -1) {
265 *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location.c_str(),
266 strerror(errno));
267 return nullptr;
268 }
269 if (S_ISDIR(sbuf.st_mode)) {
270 *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str());
271 return nullptr;
272 }
273 size_t length = sbuf.st_size;
274 map.reset(MemMap::MapFile(length,
275 PROT_READ,
276 MAP_PRIVATE,
277 fd,
278 0,
279 /*low_4gb*/false,
280 location.c_str(),
281 error_msg));
282 if (map == nullptr) {
283 DCHECK(!error_msg->empty());
284 return nullptr;
285 }
286 }
287
288 if (map->Size() < sizeof(DexFile::Header)) {
289 *error_msg = StringPrintf(
290 "DexFile: failed to open dex file '%s' that is too short to have a header",
291 location.c_str());
292 return nullptr;
293 }
294
295 const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(map->Begin());
296
297 std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
298 map->Size(),
299 location,
300 dex_header->checksum_,
301 kNoOatDexFile,
302 verify,
303 verify_checksum,
304 error_msg,
305 new MemMapContainer(std::move(map)),
306 /*verify_result*/ nullptr);
307
308 return dex_file;
309}
310
311std::unique_ptr<const DexFile> ArtDexFileLoader::OpenOneDexFileFromZip(
312 const ZipArchive& zip_archive,
313 const char* entry_name,
314 const std::string& location,
315 bool verify,
316 bool verify_checksum,
317 std::string* error_msg,
318 ZipOpenErrorCode* error_code) const {
319 ScopedTrace trace("Dex file open from Zip Archive " + std::string(location));
320 CHECK(!location.empty());
321 std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
322 if (zip_entry == nullptr) {
323 *error_code = ZipOpenErrorCode::kEntryNotFound;
324 return nullptr;
325 }
326 if (zip_entry->GetUncompressedLength() == 0) {
327 *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
328 *error_code = ZipOpenErrorCode::kDexFileError;
329 return nullptr;
330 }
331
332 std::unique_ptr<MemMap> map;
333 if (zip_entry->IsUncompressed()) {
334 if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) {
335 // Do not mmap unaligned ZIP entries because
336 // doing so would fail dex verification which requires 4 byte alignment.
337 LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
338 << "please zipalign to " << alignof(DexFile::Header) << " bytes. "
339 << "Falling back to extracting file.";
340 } else {
341 // Map uncompressed files within zip as file-backed to avoid a dirty copy.
342 map.reset(zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg));
343 if (map == nullptr) {
344 LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
345 << "is your ZIP file corrupted? Falling back to extraction.";
346 // Try again with Extraction which still has a chance of recovery.
347 }
348 }
349 }
350
351 if (map == nullptr) {
352 // Default path for compressed ZIP entries,
353 // and fallback for stored ZIP entries.
354 map.reset(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
355 }
356
357 if (map == nullptr) {
358 *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
359 error_msg->c_str());
360 *error_code = ZipOpenErrorCode::kExtractToMemoryError;
361 return nullptr;
362 }
363 VerifyResult verify_result;
364 std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
365 map->Size(),
366 location,
367 zip_entry->GetCrc32(),
368 kNoOatDexFile,
369 verify,
370 verify_checksum,
371 error_msg,
372 new MemMapContainer(std::move(map)),
373 &verify_result);
374 if (dex_file == nullptr) {
375 if (verify_result == VerifyResult::kVerifyNotAttempted) {
376 *error_code = ZipOpenErrorCode::kDexFileError;
377 } else {
378 *error_code = ZipOpenErrorCode::kVerifyError;
379 }
380 return nullptr;
381 }
382 if (!dex_file->DisableWrite()) {
383 *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
384 *error_code = ZipOpenErrorCode::kMakeReadOnlyError;
385 return nullptr;
386 }
387 CHECK(dex_file->IsReadOnly()) << location;
388 if (verify_result != VerifyResult::kVerifySucceeded) {
389 *error_code = ZipOpenErrorCode::kVerifyError;
390 return nullptr;
391 }
392 *error_code = ZipOpenErrorCode::kNoError;
393 return dex_file;
394}
395
396// Technically we do not have a limitation with respect to the number of dex files that can be in a
397// multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
398// (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
399// seems an excessive number.
400static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
401
402bool ArtDexFileLoader::OpenAllDexFilesFromZip(
403 const ZipArchive& zip_archive,
404 const std::string& location,
405 bool verify,
406 bool verify_checksum,
407 std::string* error_msg,
408 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
409 ScopedTrace trace("Dex file open from Zip " + std::string(location));
410 DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
411 ZipOpenErrorCode error_code;
412 std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive,
413 kClassesDex,
414 location,
415 verify,
416 verify_checksum,
417 error_msg,
418 &error_code));
419 if (dex_file.get() == nullptr) {
420 return false;
421 } else {
422 // Had at least classes.dex.
423 dex_files->push_back(std::move(dex_file));
424
425 // Now try some more.
426
427 // We could try to avoid std::string allocations by working on a char array directly. As we
428 // do not expect a lot of iterations, this seems too involved and brittle.
429
430 for (size_t i = 1; ; ++i) {
431 std::string name = GetMultiDexClassesDexName(i);
432 std::string fake_location = GetMultiDexLocation(i, location.c_str());
433 std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive,
434 name.c_str(),
435 fake_location,
436 verify,
437 verify_checksum,
438 error_msg,
439 &error_code));
440 if (next_dex_file.get() == nullptr) {
441 if (error_code != ZipOpenErrorCode::kEntryNotFound) {
442 LOG(WARNING) << "Zip open failed: " << *error_msg;
443 }
444 break;
445 } else {
446 dex_files->push_back(std::move(next_dex_file));
447 }
448
449 if (i == kWarnOnManyDexFilesThreshold) {
450 LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold
451 << " dex files. Please consider coalescing and shrinking the number to "
452 " avoid runtime overhead.";
453 }
454
455 if (i == std::numeric_limits<size_t>::max()) {
456 LOG(ERROR) << "Overflow in number of dex files!";
457 break;
458 }
459 }
460
461 return true;
462 }
463}
464
465} // namespace art