image_test.cc revision 46ee31b67d7ee1bd085fbc240502053caa3cf8fa
1/*
2 * Copyright (C) 2011 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 "image.h"
18
19#include <memory>
20#include <string>
21#include <vector>
22
23#include "android-base/stringprintf.h"
24
25#include "base/unix_file/fd_file.h"
26#include "class_linker-inl.h"
27#include "common_compiler_test.h"
28#include "debug/method_debug_info.h"
29#include "driver/compiler_options.h"
30#include "elf_writer.h"
31#include "elf_writer_quick.h"
32#include "gc/space/image_space.h"
33#include "image_writer.h"
34#include "linker/buffered_output_stream.h"
35#include "linker/file_output_stream.h"
36#include "linker/multi_oat_relative_patcher.h"
37#include "lock_word.h"
38#include "mirror/object-inl.h"
39#include "oat_writer.h"
40#include "scoped_thread_state_change-inl.h"
41#include "signal_catcher.h"
42#include "utils.h"
43
44namespace art {
45
46static const uintptr_t kRequestedImageBase = ART_BASE_ADDRESS;
47
48struct CompilationHelper {
49  std::vector<std::string> dex_file_locations;
50  std::vector<ScratchFile> image_locations;
51  std::vector<std::unique_ptr<const DexFile>> extra_dex_files;
52  std::vector<ScratchFile> image_files;
53  std::vector<ScratchFile> oat_files;
54  std::vector<ScratchFile> vdex_files;
55  std::string image_dir;
56
57  void Compile(CompilerDriver* driver,
58               ImageHeader::StorageMode storage_mode);
59
60  std::vector<size_t> GetImageObjectSectionSizes();
61
62  ~CompilationHelper();
63};
64
65class ImageTest : public CommonCompilerTest {
66 protected:
67  virtual void SetUp() {
68    ReserveImageSpace();
69    CommonCompilerTest::SetUp();
70  }
71
72  void TestWriteRead(ImageHeader::StorageMode storage_mode);
73
74  void Compile(ImageHeader::StorageMode storage_mode,
75               CompilationHelper& out_helper,
76               const std::string& extra_dex = "",
77               const std::string& image_class = "");
78
79  std::unordered_set<std::string>* GetImageClasses() OVERRIDE {
80    return new std::unordered_set<std::string>(image_classes_);
81  }
82
83 private:
84  std::unordered_set<std::string> image_classes_;
85};
86
87CompilationHelper::~CompilationHelper() {
88  for (ScratchFile& image_file : image_files) {
89    image_file.Unlink();
90  }
91  for (ScratchFile& oat_file : oat_files) {
92    oat_file.Unlink();
93  }
94  for (ScratchFile& vdex_file : vdex_files) {
95    vdex_file.Unlink();
96  }
97  const int rmdir_result = rmdir(image_dir.c_str());
98  CHECK_EQ(0, rmdir_result);
99}
100
101std::vector<size_t> CompilationHelper::GetImageObjectSectionSizes() {
102  std::vector<size_t> ret;
103  for (ScratchFile& image_file : image_files) {
104    std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str()));
105    CHECK(file.get() != nullptr);
106    ImageHeader image_header;
107    CHECK_EQ(file->ReadFully(&image_header, sizeof(image_header)), true);
108    CHECK(image_header.IsValid());
109    ret.push_back(image_header.GetImageSize());
110  }
111  return ret;
112}
113
114void CompilationHelper::Compile(CompilerDriver* driver,
115                                ImageHeader::StorageMode storage_mode) {
116  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
117  std::vector<const DexFile*> class_path = class_linker->GetBootClassPath();
118
119  for (const std::unique_ptr<const DexFile>& dex_file : extra_dex_files) {
120    {
121      ScopedObjectAccess soa(Thread::Current());
122      // Inject in boot class path so that the compiler driver can see it.
123      class_linker->AppendToBootClassPath(soa.Self(), *dex_file.get());
124    }
125    class_path.push_back(dex_file.get());
126  }
127
128  // Enable write for dex2dex.
129  for (const DexFile* dex_file : class_path) {
130    dex_file_locations.push_back(dex_file->GetLocation());
131    if (dex_file->IsReadOnly()) {
132      dex_file->EnableWrite();
133    }
134  }
135  {
136    // Create a generic tmp file, to be the base of the .art and .oat temporary files.
137    ScratchFile location;
138    for (int i = 0; i < static_cast<int>(class_path.size()); ++i) {
139      std::string cur_location =
140          android::base::StringPrintf("%s-%d.art", location.GetFilename().c_str(), i);
141      image_locations.push_back(ScratchFile(cur_location));
142    }
143  }
144  std::vector<std::string> image_filenames;
145  for (ScratchFile& file : image_locations) {
146    std::string image_filename(GetSystemImageFilename(file.GetFilename().c_str(), kRuntimeISA));
147    image_filenames.push_back(image_filename);
148    size_t pos = image_filename.rfind('/');
149    CHECK_NE(pos, std::string::npos) << image_filename;
150    if (image_dir.empty()) {
151      image_dir = image_filename.substr(0, pos);
152      int mkdir_result = mkdir(image_dir.c_str(), 0700);
153      CHECK_EQ(0, mkdir_result) << image_dir;
154    }
155    image_files.push_back(ScratchFile(OS::CreateEmptyFile(image_filename.c_str())));
156  }
157
158  std::vector<std::string> oat_filenames;
159  std::vector<std::string> vdex_filenames;
160  for (const std::string& image_filename : image_filenames) {
161    std::string oat_filename = ReplaceFileExtension(image_filename, "oat");
162    oat_files.push_back(ScratchFile(OS::CreateEmptyFile(oat_filename.c_str())));
163    oat_filenames.push_back(oat_filename);
164    std::string vdex_filename = ReplaceFileExtension(image_filename, "vdex");
165    vdex_files.push_back(ScratchFile(OS::CreateEmptyFile(vdex_filename.c_str())));
166    vdex_filenames.push_back(vdex_filename);
167  }
168
169  std::unordered_map<const DexFile*, size_t> dex_file_to_oat_index_map;
170  std::vector<const char*> oat_filename_vector;
171  for (const std::string& file : oat_filenames) {
172    oat_filename_vector.push_back(file.c_str());
173  }
174  std::vector<const char*> image_filename_vector;
175  for (const std::string& file : image_filenames) {
176    image_filename_vector.push_back(file.c_str());
177  }
178  size_t image_idx = 0;
179  for (const DexFile* dex_file : class_path) {
180    dex_file_to_oat_index_map.emplace(dex_file, image_idx);
181    ++image_idx;
182  }
183  // TODO: compile_pic should be a test argument.
184  std::unique_ptr<ImageWriter> writer(new ImageWriter(*driver,
185                                                      kRequestedImageBase,
186                                                      /*compile_pic*/false,
187                                                      /*compile_app_image*/false,
188                                                      storage_mode,
189                                                      oat_filename_vector,
190                                                      dex_file_to_oat_index_map));
191  {
192    {
193      jobject class_loader = nullptr;
194      TimingLogger timings("ImageTest::WriteRead", false, false);
195      TimingLogger::ScopedTiming t("CompileAll", &timings);
196      driver->SetDexFilesForOatFile(class_path);
197      driver->CompileAll(class_loader, class_path, /* verifier_deps */ nullptr, &timings);
198
199      t.NewTiming("WriteElf");
200      SafeMap<std::string, std::string> key_value_store;
201      std::vector<const char*> dex_filename_vector;
202      for (size_t i = 0; i < class_path.size(); ++i) {
203        dex_filename_vector.push_back("");
204      }
205      key_value_store.Put(OatHeader::kBootClassPathKey,
206                          gc::space::ImageSpace::GetMultiImageBootClassPath(
207                              dex_filename_vector,
208                              oat_filename_vector,
209                              image_filename_vector));
210
211      std::vector<std::unique_ptr<ElfWriter>> elf_writers;
212      std::vector<std::unique_ptr<OatWriter>> oat_writers;
213      for (ScratchFile& oat_file : oat_files) {
214        elf_writers.emplace_back(CreateElfWriterQuick(driver->GetInstructionSet(),
215                                                      driver->GetInstructionSetFeatures(),
216                                                      &driver->GetCompilerOptions(),
217                                                      oat_file.GetFile()));
218        elf_writers.back()->Start();
219        oat_writers.emplace_back(new OatWriter(/*compiling_boot_image*/true,
220                                               &timings,
221                                               /*profile_compilation_info*/nullptr));
222      }
223
224      std::vector<OutputStream*> rodata;
225      std::vector<std::unique_ptr<MemMap>> opened_dex_files_map;
226      std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
227      // Now that we have finalized key_value_store_, start writing the oat file.
228      for (size_t i = 0, size = oat_writers.size(); i != size; ++i) {
229        const DexFile* dex_file = class_path[i];
230        rodata.push_back(elf_writers[i]->StartRoData());
231        ArrayRef<const uint8_t> raw_dex_file(
232            reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()),
233            dex_file->GetHeader().file_size_);
234        oat_writers[i]->AddRawDexFileSource(raw_dex_file,
235                                            dex_file->GetLocation().c_str(),
236                                            dex_file->GetLocationChecksum());
237
238        std::unique_ptr<MemMap> cur_opened_dex_files_map;
239        std::vector<std::unique_ptr<const DexFile>> cur_opened_dex_files;
240        bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles(
241            kIsVdexEnabled ? vdex_files[i].GetFile() : oat_files[i].GetFile(),
242            rodata.back(),
243            driver->GetInstructionSet(),
244            driver->GetInstructionSetFeatures(),
245            &key_value_store,
246            /* verify */ false,           // Dex files may be dex-to-dex-ed, don't verify.
247            &cur_opened_dex_files_map,
248            &cur_opened_dex_files);
249        ASSERT_TRUE(dex_files_ok);
250
251        if (cur_opened_dex_files_map != nullptr) {
252          opened_dex_files_map.push_back(std::move(cur_opened_dex_files_map));
253          for (std::unique_ptr<const DexFile>& cur_dex_file : cur_opened_dex_files) {
254            // dex_file_oat_index_map_.emplace(dex_file.get(), i);
255            opened_dex_files.push_back(std::move(cur_dex_file));
256          }
257        } else {
258          ASSERT_TRUE(cur_opened_dex_files.empty());
259        }
260      }
261      bool image_space_ok = writer->PrepareImageAddressSpace();
262      ASSERT_TRUE(image_space_ok);
263
264      if (kIsVdexEnabled) {
265        for (size_t i = 0, size = vdex_files.size(); i != size; ++i) {
266          std::unique_ptr<BufferedOutputStream> vdex_out(
267              MakeUnique<BufferedOutputStream>(
268                  MakeUnique<FileOutputStream>(vdex_files[i].GetFile())));
269          oat_writers[i]->WriteVerifierDeps(vdex_out.get(), nullptr);
270          oat_writers[i]->WriteChecksumsAndVdexHeader(vdex_out.get());
271        }
272      }
273
274      for (size_t i = 0, size = oat_files.size(); i != size; ++i) {
275        linker::MultiOatRelativePatcher patcher(driver->GetInstructionSet(),
276                                                driver->GetInstructionSetFeatures());
277        OatWriter* const oat_writer = oat_writers[i].get();
278        ElfWriter* const elf_writer = elf_writers[i].get();
279        std::vector<const DexFile*> cur_dex_files(1u, class_path[i]);
280        oat_writer->Initialize(driver, writer.get(), cur_dex_files);
281        oat_writer->PrepareLayout(&patcher);
282        size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset();
283        size_t text_size = oat_writer->GetOatSize() - rodata_size;
284        elf_writer->PrepareDynamicSection(rodata_size,
285                                          text_size,
286                                          oat_writer->GetBssSize(),
287                                          oat_writer->GetBssRootsOffset());
288
289        writer->UpdateOatFileLayout(i,
290                                    elf_writer->GetLoadedSize(),
291                                    oat_writer->GetOatDataOffset(),
292                                    oat_writer->GetOatSize());
293
294        bool rodata_ok = oat_writer->WriteRodata(rodata[i]);
295        ASSERT_TRUE(rodata_ok);
296        elf_writer->EndRoData(rodata[i]);
297
298        OutputStream* text = elf_writer->StartText();
299        bool text_ok = oat_writer->WriteCode(text);
300        ASSERT_TRUE(text_ok);
301        elf_writer->EndText(text);
302
303        bool header_ok = oat_writer->WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u);
304        ASSERT_TRUE(header_ok);
305
306        writer->UpdateOatFileHeader(i, oat_writer->GetOatHeader());
307
308        elf_writer->WriteDynamicSection();
309        elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo());
310        elf_writer->WritePatchLocations(oat_writer->GetAbsolutePatchLocations());
311
312        bool success = elf_writer->End();
313        ASSERT_TRUE(success);
314      }
315    }
316
317    bool success_image = writer->Write(kInvalidFd,
318                                       image_filename_vector,
319                                       oat_filename_vector);
320    ASSERT_TRUE(success_image);
321
322    for (size_t i = 0, size = oat_filenames.size(); i != size; ++i) {
323      const char* oat_filename = oat_filenames[i].c_str();
324      std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename));
325      ASSERT_TRUE(oat_file != nullptr);
326      bool success_fixup = ElfWriter::Fixup(oat_file.get(),
327                                            writer->GetOatDataBegin(i));
328      ASSERT_TRUE(success_fixup);
329      ASSERT_EQ(oat_file->FlushCloseOrErase(), 0) << "Could not flush and close oat file "
330                                                  << oat_filename;
331    }
332  }
333}
334
335void ImageTest::Compile(ImageHeader::StorageMode storage_mode,
336                        CompilationHelper& helper,
337                        const std::string& extra_dex,
338                        const std::string& image_class) {
339  if (!image_class.empty()) {
340    image_classes_.insert(image_class);
341  }
342  CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U);
343  // Set inline filter values.
344  compiler_options_->SetInlineDepthLimit(CompilerOptions::kDefaultInlineDepthLimit);
345  compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
346  image_classes_.clear();
347  if (!extra_dex.empty()) {
348    helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str());
349  }
350  helper.Compile(compiler_driver_.get(), storage_mode);
351  if (!image_class.empty()) {
352    // Make sure the class got initialized.
353    ScopedObjectAccess soa(Thread::Current());
354    ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
355    mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str());
356    EXPECT_TRUE(klass != nullptr);
357    EXPECT_TRUE(klass->IsInitialized());
358  }
359}
360
361void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
362  CompilationHelper helper;
363  Compile(storage_mode, /*out*/ helper);
364  std::vector<uint64_t> image_file_sizes;
365  for (ScratchFile& image_file : helper.image_files) {
366    std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str()));
367    ASSERT_TRUE(file.get() != nullptr);
368    ImageHeader image_header;
369    ASSERT_EQ(file->ReadFully(&image_header, sizeof(image_header)), true);
370    ASSERT_TRUE(image_header.IsValid());
371    const auto& bitmap_section = image_header.GetImageSection(ImageHeader::kSectionImageBitmap);
372    ASSERT_GE(bitmap_section.Offset(), sizeof(image_header));
373    ASSERT_NE(0U, bitmap_section.Size());
374
375    gc::Heap* heap = Runtime::Current()->GetHeap();
376    ASSERT_TRUE(heap->HaveContinuousSpaces());
377    gc::space::ContinuousSpace* space = heap->GetNonMovingSpace();
378    ASSERT_FALSE(space->IsImageSpace());
379    ASSERT_TRUE(space != nullptr);
380    ASSERT_TRUE(space->IsMallocSpace());
381    image_file_sizes.push_back(file->GetLength());
382  }
383
384  ASSERT_TRUE(compiler_driver_->GetImageClasses() != nullptr);
385  std::unordered_set<std::string> image_classes(*compiler_driver_->GetImageClasses());
386
387  // Need to delete the compiler since it has worker threads which are attached to runtime.
388  compiler_driver_.reset();
389
390  // Tear down old runtime before making a new one, clearing out misc state.
391
392  // Remove the reservation of the memory for use to load the image.
393  // Need to do this before we reset the runtime.
394  UnreserveImageSpace();
395
396  helper.extra_dex_files.clear();
397  runtime_.reset();
398  java_lang_dex_file_ = nullptr;
399
400  MemMap::Init();
401
402  RuntimeOptions options;
403  std::string image("-Ximage:");
404  image.append(helper.image_locations[0].GetFilename());
405  options.push_back(std::make_pair(image.c_str(), static_cast<void*>(nullptr)));
406  // By default the compiler this creates will not include patch information.
407  options.push_back(std::make_pair("-Xnorelocate", nullptr));
408
409  if (!Runtime::Create(options, false)) {
410    LOG(FATAL) << "Failed to create runtime";
411    return;
412  }
413  runtime_.reset(Runtime::Current());
414  // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
415  // give it away now and then switch to a more managable ScopedObjectAccess.
416  Thread::Current()->TransitionFromRunnableToSuspended(kNative);
417  ScopedObjectAccess soa(Thread::Current());
418  ASSERT_TRUE(runtime_.get() != nullptr);
419  class_linker_ = runtime_->GetClassLinker();
420
421  gc::Heap* heap = Runtime::Current()->GetHeap();
422  ASSERT_TRUE(heap->HasBootImageSpace());
423  ASSERT_TRUE(heap->GetNonMovingSpace()->IsMallocSpace());
424
425  // We loaded the runtime with an explicit image, so it must exist.
426  ASSERT_EQ(heap->GetBootImageSpaces().size(), image_file_sizes.size());
427  for (size_t i = 0; i < helper.dex_file_locations.size(); ++i) {
428    std::unique_ptr<const DexFile> dex(
429        LoadExpectSingleDexFile(helper.dex_file_locations[i].c_str()));
430    ASSERT_TRUE(dex != nullptr);
431    uint64_t image_file_size = image_file_sizes[i];
432    gc::space::ImageSpace* image_space = heap->GetBootImageSpaces()[i];
433    ASSERT_TRUE(image_space != nullptr);
434    if (storage_mode == ImageHeader::kStorageModeUncompressed) {
435      // Uncompressed, image should be smaller than file.
436      ASSERT_LE(image_space->GetImageHeader().GetImageSize(), image_file_size);
437    } else if (image_file_size > 16 * KB) {
438      // Compressed, file should be smaller than image. Not really valid for small images.
439      ASSERT_LE(image_file_size, image_space->GetImageHeader().GetImageSize());
440    }
441
442    image_space->VerifyImageAllocations();
443    uint8_t* image_begin = image_space->Begin();
444    uint8_t* image_end = image_space->End();
445    if (i == 0) {
446      // This check is only valid for image 0.
447      CHECK_EQ(kRequestedImageBase, reinterpret_cast<uintptr_t>(image_begin));
448    }
449    for (size_t j = 0; j < dex->NumClassDefs(); ++j) {
450      const DexFile::ClassDef& class_def = dex->GetClassDef(j);
451      const char* descriptor = dex->GetClassDescriptor(class_def);
452      mirror::Class* klass = class_linker_->FindSystemClass(soa.Self(), descriptor);
453      EXPECT_TRUE(klass != nullptr) << descriptor;
454      if (image_classes.find(descriptor) == image_classes.end()) {
455        EXPECT_TRUE(reinterpret_cast<uint8_t*>(klass) >= image_end ||
456                    reinterpret_cast<uint8_t*>(klass) < image_begin) << descriptor;
457      } else {
458        // Image classes should be located inside the image.
459        EXPECT_LT(image_begin, reinterpret_cast<uint8_t*>(klass)) << descriptor;
460        EXPECT_LT(reinterpret_cast<uint8_t*>(klass), image_end) << descriptor;
461      }
462      EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false)));
463    }
464  }
465}
466
467TEST_F(ImageTest, WriteReadUncompressed) {
468  TestWriteRead(ImageHeader::kStorageModeUncompressed);
469}
470
471TEST_F(ImageTest, WriteReadLZ4) {
472  TestWriteRead(ImageHeader::kStorageModeLZ4);
473}
474
475TEST_F(ImageTest, WriteReadLZ4HC) {
476  TestWriteRead(ImageHeader::kStorageModeLZ4HC);
477}
478
479TEST_F(ImageTest, TestImageLayout) {
480  std::vector<size_t> image_sizes;
481  std::vector<size_t> image_sizes_extra;
482  // Compile multi-image with ImageLayoutA being the last image.
483  {
484    CompilationHelper helper;
485    Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutA", "LMyClass;");
486    image_sizes = helper.GetImageObjectSectionSizes();
487  }
488  TearDown();
489  runtime_.reset();
490  SetUp();
491  // Compile multi-image with ImageLayoutB being the last image.
492  {
493    CompilationHelper helper;
494    Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutB", "LMyClass;");
495    image_sizes_extra = helper.GetImageObjectSectionSizes();
496  }
497  // Make sure that the new stuff in the clinit in ImageLayoutB is in the last image and not in the
498  // first two images.
499  ASSERT_EQ(image_sizes.size(), image_sizes.size());
500  // Sizes of the images should be the same. These sizes are for the whole image unrounded.
501  for (size_t i = 0; i < image_sizes.size() - 1; ++i) {
502    EXPECT_EQ(image_sizes[i], image_sizes_extra[i]);
503  }
504  // Last image should be larger since it has a hash map and a string.
505  EXPECT_LT(image_sizes.back(), image_sizes_extra.back());
506}
507
508TEST_F(ImageTest, ImageHeaderIsValid) {
509    uint32_t image_begin = ART_BASE_ADDRESS;
510    uint32_t image_size_ = 16 * KB;
511    uint32_t image_roots = ART_BASE_ADDRESS + (1 * KB);
512    uint32_t oat_checksum = 0;
513    uint32_t oat_file_begin = ART_BASE_ADDRESS + (4 * KB);  // page aligned
514    uint32_t oat_data_begin = ART_BASE_ADDRESS + (8 * KB);  // page aligned
515    uint32_t oat_data_end = ART_BASE_ADDRESS + (9 * KB);
516    uint32_t oat_file_end = ART_BASE_ADDRESS + (10 * KB);
517    ImageSection sections[ImageHeader::kSectionCount];
518    ImageHeader image_header(image_begin,
519                             image_size_,
520                             sections,
521                             image_roots,
522                             oat_checksum,
523                             oat_file_begin,
524                             oat_data_begin,
525                             oat_data_end,
526                             oat_file_end,
527                             /*boot_image_begin*/0U,
528                             /*boot_image_size*/0U,
529                             /*boot_oat_begin*/0U,
530                             /*boot_oat_size_*/0U,
531                             sizeof(void*),
532                             /*compile_pic*/false,
533                             /*is_pic*/false,
534                             ImageHeader::kDefaultStorageMode,
535                             /*data_size*/0u);
536    ASSERT_TRUE(image_header.IsValid());
537    ASSERT_TRUE(!image_header.IsAppImage());
538
539    char* magic = const_cast<char*>(image_header.GetMagic());
540    strcpy(magic, "");  // bad magic
541    ASSERT_FALSE(image_header.IsValid());
542    strcpy(magic, "art\n000");  // bad version
543    ASSERT_FALSE(image_header.IsValid());
544}
545
546}  // namespace art
547