blob: fafd69889d8e973862a705fedc5ae144a79ad63c [file] [log] [blame]
Mathieu Chartier79c87da2017-10-10 11:54:29 -07001/*
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 "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"
David Sehr9e734c72018-01-04 17:56:19 -080028#include "compact_dex_file.h"
Mathieu Chartier79c87da2017-10-10 11:54:29 -070029#include "dex_file.h"
30#include "dex_file_verifier.h"
Mathieu Chartier292567e2017-10-12 13:24:38 -070031#include "standard_dex_file.h"
Mathieu Chartier79c87da2017-10-10 11:54:29 -070032#include "zip_archive.h"
33
34namespace art {
35
David Sehr733bd4d2017-10-26 10:39:15 -070036namespace {
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
Mathieu Chartier79c87da2017-10-10 11:54:29 -070080using android::base::StringPrintf;
81
82static constexpr OatDexFile* kNoOatDexFile = nullptr;
83
84
Mathieu Chartiercf76bf82017-09-25 16:22:36 -070085bool DexFileLoader::IsMagicValid(uint32_t magic) {
86 return IsMagicValid(reinterpret_cast<uint8_t*>(&magic));
Mathieu Chartier79c87da2017-10-10 11:54:29 -070087}
88
Mathieu Chartiercf76bf82017-09-25 16:22:36 -070089bool DexFileLoader::IsMagicValid(const uint8_t* magic) {
90 return StandardDexFile::IsMagicValid(magic) ||
91 CompactDexFile::IsMagicValid(magic);
92}
93
94bool DexFileLoader::IsVersionAndMagicValid(const uint8_t* magic) {
95 if (StandardDexFile::IsMagicValid(magic)) {
96 return StandardDexFile::IsVersionValid(magic);
97 }
98 if (CompactDexFile::IsMagicValid(magic)) {
99 return CompactDexFile::IsVersionValid(magic);
100 }
101 return false;
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700102}
103
104bool DexFileLoader::GetMultiDexChecksums(const char* filename,
105 std::vector<uint32_t>* checksums,
Shubham Ajmerac12bf4c2017-10-24 16:59:42 -0700106 std::string* error_msg,
107 int zip_fd) {
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700108 CHECK(checksums != nullptr);
109 uint32_t magic;
110
Shubham Ajmerac12bf4c2017-10-24 16:59:42 -0700111 File fd;
112 if (zip_fd != -1) {
113 if (ReadMagicAndReset(zip_fd, &magic, error_msg)) {
114 fd = File(zip_fd, false /* check_usage */);
115 }
116 } else {
117 fd = OpenAndReadMagic(filename, &magic, error_msg);
118 }
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700119 if (fd.Fd() == -1) {
120 DCHECK(!error_msg->empty());
121 return false;
122 }
123 if (IsZipMagic(magic)) {
124 std::unique_ptr<ZipArchive> zip_archive(
125 ZipArchive::OpenFromFd(fd.Release(), filename, error_msg));
126 if (zip_archive.get() == nullptr) {
127 *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename,
128 error_msg->c_str());
129 return false;
130 }
131
132 uint32_t i = 0;
133 std::string zip_entry_name = GetMultiDexClassesDexName(i++);
134 std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg));
135 if (zip_entry.get() == nullptr) {
136 *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename,
137 zip_entry_name.c_str(), error_msg->c_str());
138 return false;
139 }
140
141 do {
142 checksums->push_back(zip_entry->GetCrc32());
143 zip_entry_name = GetMultiDexClassesDexName(i++);
144 zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg));
145 } while (zip_entry.get() != nullptr);
146 return true;
147 }
Mathieu Chartiercf76bf82017-09-25 16:22:36 -0700148 if (IsMagicValid(magic)) {
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700149 std::unique_ptr<const DexFile> dex_file(
150 OpenFile(fd.Release(), filename, false, false, error_msg));
151 if (dex_file == nullptr) {
152 return false;
153 }
154 checksums->push_back(dex_file->GetHeader().checksum_);
155 return true;
156 }
157 *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
158 return false;
159}
160
161bool DexFileLoader::IsMultiDexLocation(const char* location) {
162 return strrchr(location, kMultiDexSeparator) != nullptr;
163}
164
165std::string DexFileLoader::GetMultiDexClassesDexName(size_t index) {
166 return (index == 0) ? "classes.dex" : StringPrintf("classes%zu.dex", index + 1);
167}
168
169std::string DexFileLoader::GetMultiDexLocation(size_t index, const char* dex_location) {
170 return (index == 0)
171 ? dex_location
172 : StringPrintf("%s%cclasses%zu.dex", dex_location, kMultiDexSeparator, index + 1);
173}
174
175std::string DexFileLoader::GetDexCanonicalLocation(const char* dex_location) {
176 CHECK_NE(dex_location, static_cast<const char*>(nullptr));
177 std::string base_location = GetBaseLocation(dex_location);
178 const char* suffix = dex_location + base_location.size();
179 DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator);
180 UniqueCPtr<const char[]> path(realpath(base_location.c_str(), nullptr));
181 if (path != nullptr && path.get() != base_location) {
182 return std::string(path.get()) + suffix;
183 } else if (suffix[0] == 0) {
184 return base_location;
185 } else {
186 return dex_location;
187 }
188}
189
190std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base,
191 size_t size,
192 const std::string& location,
193 uint32_t location_checksum,
194 const OatDexFile* oat_dex_file,
195 bool verify,
196 bool verify_checksum,
197 std::string* error_msg) {
198 ScopedTrace trace(std::string("Open dex file from RAM ") + location);
199 return OpenCommon(base,
200 size,
201 location,
202 location_checksum,
203 oat_dex_file,
204 verify,
205 verify_checksum,
David Sehr733bd4d2017-10-26 10:39:15 -0700206 error_msg,
207 /*container*/ nullptr,
208 /*verify_result*/ nullptr);
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700209}
210
211std::unique_ptr<const DexFile> DexFileLoader::Open(const std::string& location,
212 uint32_t location_checksum,
213 std::unique_ptr<MemMap> map,
214 bool verify,
215 bool verify_checksum,
216 std::string* error_msg) {
217 ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location);
218 CHECK(map.get() != nullptr);
219
220 if (map->Size() < sizeof(DexFile::Header)) {
221 *error_msg = StringPrintf(
222 "DexFile: failed to open dex file '%s' that is too short to have a header",
223 location.c_str());
224 return nullptr;
225 }
226
227 std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
228 map->Size(),
229 location,
230 location_checksum,
231 kNoOatDexFile,
232 verify,
233 verify_checksum,
David Sehr733bd4d2017-10-26 10:39:15 -0700234 error_msg,
235 new MemMapContainer(std::move(map)),
236 /*verify_result*/ nullptr);
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700237 return dex_file;
238}
239
240bool DexFileLoader::Open(const char* filename,
241 const std::string& location,
Nicolas Geoffray095c6c92017-10-19 13:59:55 +0100242 bool verify,
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700243 bool verify_checksum,
244 std::string* error_msg,
245 std::vector<std::unique_ptr<const DexFile>>* dex_files) {
246 ScopedTrace trace(std::string("Open dex file ") + std::string(location));
247 DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
248 uint32_t magic;
249 File fd = OpenAndReadMagic(filename, &magic, error_msg);
250 if (fd.Fd() == -1) {
251 DCHECK(!error_msg->empty());
252 return false;
253 }
254 if (IsZipMagic(magic)) {
Nicolas Geoffray095c6c92017-10-19 13:59:55 +0100255 return OpenZip(fd.Release(), location, verify, verify_checksum, error_msg, dex_files);
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700256 }
Mathieu Chartiercf76bf82017-09-25 16:22:36 -0700257 if (IsMagicValid(magic)) {
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700258 std::unique_ptr<const DexFile> dex_file(OpenFile(fd.Release(),
259 location,
Nicolas Geoffray095c6c92017-10-19 13:59:55 +0100260 verify,
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700261 verify_checksum,
262 error_msg));
263 if (dex_file.get() != nullptr) {
264 dex_files->push_back(std::move(dex_file));
265 return true;
266 } else {
267 return false;
268 }
269 }
270 *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
271 return false;
272}
273
274std::unique_ptr<const DexFile> DexFileLoader::OpenDex(int fd,
275 const std::string& location,
Nicolas Geoffray095c6c92017-10-19 13:59:55 +0100276 bool verify,
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700277 bool verify_checksum,
278 std::string* error_msg) {
279 ScopedTrace trace("Open dex file " + std::string(location));
Nicolas Geoffray095c6c92017-10-19 13:59:55 +0100280 return OpenFile(fd, location, verify, verify_checksum, error_msg);
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700281}
282
283bool DexFileLoader::OpenZip(int fd,
284 const std::string& location,
Nicolas Geoffray095c6c92017-10-19 13:59:55 +0100285 bool verify,
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700286 bool verify_checksum,
287 std::string* error_msg,
288 std::vector<std::unique_ptr<const DexFile>>* dex_files) {
289 ScopedTrace trace("Dex file open Zip " + std::string(location));
290 DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is nullptr";
291 std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
292 if (zip_archive.get() == nullptr) {
293 DCHECK(!error_msg->empty());
294 return false;
295 }
Nicolas Geoffray095c6c92017-10-19 13:59:55 +0100296 return OpenAllDexFilesFromZip(
297 *zip_archive, location, verify, verify_checksum, error_msg, dex_files);
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700298}
299
300std::unique_ptr<const DexFile> DexFileLoader::OpenFile(int fd,
301 const std::string& location,
302 bool verify,
303 bool verify_checksum,
304 std::string* error_msg) {
305 ScopedTrace trace(std::string("Open dex file ") + std::string(location));
306 CHECK(!location.empty());
307 std::unique_ptr<MemMap> map;
308 {
309 File delayed_close(fd, /* check_usage */ false);
310 struct stat sbuf;
311 memset(&sbuf, 0, sizeof(sbuf));
312 if (fstat(fd, &sbuf) == -1) {
313 *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location.c_str(),
314 strerror(errno));
315 return nullptr;
316 }
317 if (S_ISDIR(sbuf.st_mode)) {
318 *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str());
319 return nullptr;
320 }
321 size_t length = sbuf.st_size;
322 map.reset(MemMap::MapFile(length,
323 PROT_READ,
324 MAP_PRIVATE,
325 fd,
326 0,
327 /*low_4gb*/false,
328 location.c_str(),
329 error_msg));
330 if (map == nullptr) {
331 DCHECK(!error_msg->empty());
332 return nullptr;
333 }
334 }
335
336 if (map->Size() < sizeof(DexFile::Header)) {
337 *error_msg = StringPrintf(
338 "DexFile: failed to open dex file '%s' that is too short to have a header",
339 location.c_str());
340 return nullptr;
341 }
342
343 const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(map->Begin());
344
345 std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
346 map->Size(),
347 location,
348 dex_header->checksum_,
349 kNoOatDexFile,
350 verify,
351 verify_checksum,
David Sehr733bd4d2017-10-26 10:39:15 -0700352 error_msg,
353 new MemMapContainer(std::move(map)),
354 /*verify_result*/ nullptr);
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700355
356 return dex_file;
357}
358
359std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip(
360 const ZipArchive& zip_archive,
361 const char* entry_name,
362 const std::string& location,
Nicolas Geoffray095c6c92017-10-19 13:59:55 +0100363 bool verify,
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700364 bool verify_checksum,
365 std::string* error_msg,
366 ZipOpenErrorCode* error_code) {
367 ScopedTrace trace("Dex file open from Zip Archive " + std::string(location));
368 CHECK(!location.empty());
369 std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
370 if (zip_entry == nullptr) {
371 *error_code = ZipOpenErrorCode::kEntryNotFound;
372 return nullptr;
373 }
374 if (zip_entry->GetUncompressedLength() == 0) {
375 *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
376 *error_code = ZipOpenErrorCode::kDexFileError;
377 return nullptr;
378 }
379
380 std::unique_ptr<MemMap> map;
381 if (zip_entry->IsUncompressed()) {
Andreas Gampee166e672017-12-19 18:59:29 +0000382 if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) {
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700383 // Do not mmap unaligned ZIP entries because
384 // doing so would fail dex verification which requires 4 byte alignment.
385 LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
386 << "please zipalign to " << alignof(DexFile::Header) << " bytes. "
387 << "Falling back to extracting file.";
388 } else {
389 // Map uncompressed files within zip as file-backed to avoid a dirty copy.
390 map.reset(zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg));
391 if (map == nullptr) {
392 LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
393 << "is your ZIP file corrupted? Falling back to extraction.";
394 // Try again with Extraction which still has a chance of recovery.
395 }
396 }
397 }
398
399 if (map == nullptr) {
400 // Default path for compressed ZIP entries,
401 // and fallback for stored ZIP entries.
402 map.reset(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
403 }
404
405 if (map == nullptr) {
406 *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
407 error_msg->c_str());
408 *error_code = ZipOpenErrorCode::kExtractToMemoryError;
409 return nullptr;
410 }
411 VerifyResult verify_result;
412 std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
413 map->Size(),
414 location,
415 zip_entry->GetCrc32(),
416 kNoOatDexFile,
Nicolas Geoffray095c6c92017-10-19 13:59:55 +0100417 verify,
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700418 verify_checksum,
419 error_msg,
David Sehr733bd4d2017-10-26 10:39:15 -0700420 new MemMapContainer(std::move(map)),
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700421 &verify_result);
422 if (dex_file == nullptr) {
423 if (verify_result == VerifyResult::kVerifyNotAttempted) {
424 *error_code = ZipOpenErrorCode::kDexFileError;
425 } else {
426 *error_code = ZipOpenErrorCode::kVerifyError;
427 }
428 return nullptr;
429 }
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700430 if (!dex_file->DisableWrite()) {
431 *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
432 *error_code = ZipOpenErrorCode::kMakeReadOnlyError;
433 return nullptr;
434 }
435 CHECK(dex_file->IsReadOnly()) << location;
436 if (verify_result != VerifyResult::kVerifySucceeded) {
437 *error_code = ZipOpenErrorCode::kVerifyError;
438 return nullptr;
439 }
440 *error_code = ZipOpenErrorCode::kNoError;
441 return dex_file;
442}
443
444// Technically we do not have a limitation with respect to the number of dex files that can be in a
445// multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
446// (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
447// seems an excessive number.
448static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
449
450bool DexFileLoader::OpenAllDexFilesFromZip(const ZipArchive& zip_archive,
Nicolas Geoffray095c6c92017-10-19 13:59:55 +0100451 const std::string& location,
452 bool verify,
453 bool verify_checksum,
454 std::string* error_msg,
455 std::vector<std::unique_ptr<const DexFile>>* dex_files) {
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700456 ScopedTrace trace("Dex file open from Zip " + std::string(location));
457 DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
458 ZipOpenErrorCode error_code;
459 std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive,
460 kClassesDex,
461 location,
Nicolas Geoffray095c6c92017-10-19 13:59:55 +0100462 verify,
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700463 verify_checksum,
464 error_msg,
465 &error_code));
466 if (dex_file.get() == nullptr) {
467 return false;
468 } else {
469 // Had at least classes.dex.
470 dex_files->push_back(std::move(dex_file));
471
472 // Now try some more.
473
474 // We could try to avoid std::string allocations by working on a char array directly. As we
475 // do not expect a lot of iterations, this seems too involved and brittle.
476
477 for (size_t i = 1; ; ++i) {
478 std::string name = GetMultiDexClassesDexName(i);
479 std::string fake_location = GetMultiDexLocation(i, location.c_str());
480 std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive,
481 name.c_str(),
482 fake_location,
Nicolas Geoffray095c6c92017-10-19 13:59:55 +0100483 verify,
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700484 verify_checksum,
485 error_msg,
486 &error_code));
487 if (next_dex_file.get() == nullptr) {
488 if (error_code != ZipOpenErrorCode::kEntryNotFound) {
489 LOG(WARNING) << "Zip open failed: " << *error_msg;
490 }
491 break;
492 } else {
493 dex_files->push_back(std::move(next_dex_file));
494 }
495
496 if (i == kWarnOnManyDexFilesThreshold) {
497 LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold
498 << " dex files. Please consider coalescing and shrinking the number to "
499 " avoid runtime overhead.";
500 }
501
502 if (i == std::numeric_limits<size_t>::max()) {
503 LOG(ERROR) << "Overflow in number of dex files!";
504 break;
505 }
506 }
507
508 return true;
509 }
510}
511
512std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base,
513 size_t size,
514 const std::string& location,
515 uint32_t location_checksum,
516 const OatDexFile* oat_dex_file,
517 bool verify,
518 bool verify_checksum,
519 std::string* error_msg,
David Sehr733bd4d2017-10-26 10:39:15 -0700520 DexFileContainer* container,
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700521 VerifyResult* verify_result) {
522 if (verify_result != nullptr) {
523 *verify_result = VerifyResult::kVerifyNotAttempted;
524 }
525 std::unique_ptr<DexFile> dex_file;
Mathieu Chartier292567e2017-10-12 13:24:38 -0700526 if (StandardDexFile::IsMagicValid(base)) {
David Sehr733bd4d2017-10-26 10:39:15 -0700527 dex_file.reset(
528 new StandardDexFile(base, size, location, location_checksum, oat_dex_file, container));
Mathieu Chartier603ccab2017-10-20 14:34:28 -0700529 } else if (CompactDexFile::IsMagicValid(base)) {
David Sehr733bd4d2017-10-26 10:39:15 -0700530 dex_file.reset(
531 new CompactDexFile(base, size, location, location_checksum, oat_dex_file, container));
Mathieu Chartier79c87da2017-10-10 11:54:29 -0700532 }
533 if (dex_file == nullptr) {
534 *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
535 error_msg->c_str());
536 return nullptr;
537 }
538 if (!dex_file->Init(error_msg)) {
539 dex_file.reset();
540 return nullptr;
541 }
542 if (verify && !DexFileVerifier::Verify(dex_file.get(),
543 dex_file->Begin(),
544 dex_file->Size(),
545 location.c_str(),
546 verify_checksum,
547 error_msg)) {
548 if (verify_result != nullptr) {
549 *verify_result = VerifyResult::kVerifyFailed;
550 }
551 return nullptr;
552 }
553 if (verify_result != nullptr) {
554 *verify_result = VerifyResult::kVerifySucceeded;
555 }
556 return dex_file;
557}
558
559} // namespace art