Add oatdump support for app images

Example usage on host:
oatdumpd --app-oat=art/plus32.odex --app-image=art/plus32.art
--image=art/oats/system@framework@boot.art --instruction-set=arm

TODO: Add to oatdump test.

Bug: 27408512
Bug: 22858531

Change-Id: I320db8b76c780c6eadabcb45ce88f45950741484
diff --git a/compiler/elf_writer.cc b/compiler/elf_writer.cc
index 4219d97..ca0869a 100644
--- a/compiler/elf_writer.cc
+++ b/compiler/elf_writer.cc
@@ -42,7 +42,11 @@
                                      size_t* oat_loaded_size,
                                      size_t* oat_data_offset) {
   std::string error_msg;
-  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, false, false, &error_msg));
+  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file,
+                                                  false,
+                                                  false,
+                                                  /*low_4gb*/false,
+                                                  &error_msg));
   CHECK(elf_file.get() != nullptr) << error_msg;
 
   bool success = elf_file->GetLoadedSize(oat_loaded_size, &error_msg);
@@ -54,7 +58,7 @@
 
 bool ElfWriter::Fixup(File* file, uintptr_t oat_data_begin) {
   std::string error_msg;
-  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, &error_msg));
+  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, /*low_4gb*/false, &error_msg));
   CHECK(elf_file.get() != nullptr) << error_msg;
 
   // Lookup "oatdata" symbol address.
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 7cf774e..449f514 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -64,7 +64,11 @@
   ASSERT_TRUE(file.get() != nullptr);
   {
     std::string error_msg;
-    std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), false, false, &error_msg));
+    std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
+                                              false,
+                                              false,
+                                              /*low_4gb*/false,
+                                              &error_msg));
     CHECK(ef.get() != nullptr) << error_msg;
     EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", false);
     EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", false);
@@ -72,7 +76,11 @@
   }
   {
     std::string error_msg;
-    std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), false, false, &error_msg));
+    std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
+                                              false,
+                                              false,
+                                              /*low_4gb*/false,
+                                              &error_msg));
     CHECK(ef.get() != nullptr) << error_msg;
     EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", true);
     EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", true);
@@ -80,9 +88,13 @@
   }
   {
     std::string error_msg;
-    std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), false, true, &error_msg));
+    std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
+                                              false,
+                                              true,
+                                              /*low_4gb*/false,
+                                              &error_msg));
     CHECK(ef.get() != nullptr) << error_msg;
-    CHECK(ef->Load(false, &error_msg)) << error_msg;
+    CHECK(ef->Load(false, /*low_4gb*/false, &error_msg)) << error_msg;
     EXPECT_EQ(dl_oatdata, ef->FindDynamicSymbolAddress("oatdata"));
     EXPECT_EQ(dl_oatexec, ef->FindDynamicSymbolAddress("oatexec"));
     EXPECT_EQ(dl_oatlastword, ef->FindDynamicSymbolAddress("oatlastword"));
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index d22044a..4b48107 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -230,7 +230,7 @@
     return elf_writer->End();
   }
 
-  void TestDexFileInput(bool verify);
+  void TestDexFileInput(bool verify, bool low_4gb);
   void TestZipFileInput(bool verify);
 
   std::unique_ptr<const InstructionSetFeatures> insn_features_;
@@ -374,8 +374,14 @@
   if (kCompile) {  // OatWriter strips the code, regenerate to compare
     compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
   }
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), nullptr,
-                                                  nullptr, false, nullptr, &error_msg));
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(),
+                                                  tmp.GetFilename(),
+                                                  nullptr,
+                                                  nullptr,
+                                                  false,
+                                                  /*low_4gb*/true,
+                                                  nullptr,
+                                                  &error_msg));
   ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
   const OatHeader& oat_header = oat_file->GetOatHeader();
   ASSERT_TRUE(oat_header.IsValid());
@@ -504,6 +510,7 @@
                                                   nullptr,
                                                   nullptr,
                                                   false,
+                                                  /*low_4gb*/false,
                                                   nullptr,
                                                   &error_msg));
   ASSERT_TRUE(oat_file != nullptr);
@@ -518,7 +525,7 @@
   }
 }
 
-void OatTest::TestDexFileInput(bool verify) {
+void OatTest::TestDexFileInput(bool verify, bool low_4gb) {
   TimingLogger timings("OatTest::DexFileInput", false, false);
 
   std::vector<const char*> input_filenames;
@@ -572,8 +579,13 @@
                                                          nullptr,
                                                          nullptr,
                                                          false,
+                                                         low_4gb,
                                                          nullptr,
                                                          &error_msg));
+  if (low_4gb) {
+    uintptr_t begin = reinterpret_cast<uintptr_t>(opened_oat_file->Begin());
+    EXPECT_EQ(begin, static_cast<uint32_t>(begin));
+  }
   ASSERT_TRUE(opened_oat_file != nullptr);
   ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
   std::unique_ptr<const DexFile> opened_dex_file1 =
@@ -595,11 +607,15 @@
 }
 
 TEST_F(OatTest, DexFileInputCheckOutput) {
-  TestDexFileInput(false);
+  TestDexFileInput(false, /*low_4gb*/false);
+}
+
+TEST_F(OatTest, DexFileInputCheckOutputLow4GB) {
+  TestDexFileInput(false, /*low_4gb*/true);
 }
 
 TEST_F(OatTest, DexFileInputCheckVerifier) {
-  TestDexFileInput(true);
+  TestDexFileInput(true, /*low_4gb*/false);
 }
 
 void OatTest::TestZipFileInput(bool verify) {
@@ -667,6 +683,7 @@
                                                              nullptr,
                                                              nullptr,
                                                              false,
+                                                             /*low_4gb*/false,
                                                              nullptr,
                                                              &error_msg));
       ASSERT_TRUE(opened_oat_file != nullptr);
@@ -714,6 +731,7 @@
                                                              nullptr,
                                                              nullptr,
                                                              false,
+                                                             /*low_4gb*/false,
                                                              nullptr,
                                                              &error_msg));
       ASSERT_TRUE(opened_oat_file != nullptr);
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 3ed5766..2cdd4c7 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -280,6 +280,8 @@
                    bool list_methods,
                    bool dump_header_only,
                    const char* export_dex_location,
+                   const char* app_image,
+                   const char* app_oat,
                    uint32_t addr2instr)
     : dump_raw_mapping_table_(dump_raw_mapping_table),
       dump_raw_gc_map_(dump_raw_gc_map),
@@ -293,6 +295,8 @@
       list_methods_(list_methods),
       dump_header_only_(dump_header_only),
       export_dex_location_(export_dex_location),
+      app_image_(app_image),
+      app_oat_(app_oat),
       addr2instr_(addr2instr),
       class_loader_(nullptr) {}
 
@@ -308,6 +312,8 @@
   const bool list_methods_;
   const bool dump_header_only_;
   const char* const export_dex_location_;
+  const char* const app_image_;
+  const char* const app_oat_;
   uint32_t addr2instr_;
   Handle<mirror::ClassLoader>* class_loader_;
 };
@@ -1433,8 +1439,10 @@
 
 class ImageDumper {
  public:
-  ImageDumper(std::ostream* os, gc::space::ImageSpace& image_space,
-              const ImageHeader& image_header, OatDumperOptions* oat_dumper_options)
+  ImageDumper(std::ostream* os,
+              gc::space::ImageSpace& image_space,
+              const ImageHeader& image_header,
+              OatDumperOptions* oat_dumper_options)
       : os_(os),
         vios_(os),
         indent1_(&vios_),
@@ -1532,16 +1540,23 @@
     os << "OAT LOCATION: " << oat_location;
     os << "\n";
     std::string error_msg;
-    const OatFile* oat_file = runtime->GetOatFileManager().FindOpenedOatFileFromOatLocation(
-        oat_location);
+    const OatFile* oat_file = image_space_.GetOatFile();
     if (oat_file == nullptr) {
-      oat_file = OatFile::Open(oat_location, oat_location,
-                               nullptr, nullptr, false, nullptr,
+      oat_file = runtime->GetOatFileManager().FindOpenedOatFileFromOatLocation(oat_location);
+    }
+    if (oat_file == nullptr) {
+      oat_file = OatFile::Open(oat_location,
+                               oat_location,
+                               nullptr,
+                               nullptr,
+                               false,
+                               /*low_4gb*/false,
+                               nullptr,
                                &error_msg);
-      if (oat_file == nullptr) {
-        os << "NOT FOUND: " << error_msg << "\n";
-        return false;
-      }
+    }
+    if (oat_file == nullptr) {
+      os << "OAT FILE NOT FOUND: " << error_msg << "\n";
+      return EXIT_FAILURE;
     }
     os << "\n";
 
@@ -1592,21 +1607,27 @@
       // TODO: Dump fields.
       // Dump methods after.
       const auto& methods_section = image_header_.GetMethodsSection();
-      const size_t pointer_size =
-          InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet());
       DumpArtMethodVisitor visitor(this);
-      methods_section.VisitPackedArtMethods(&visitor, image_space_.Begin(), pointer_size);
+      methods_section.VisitPackedArtMethods(&visitor,
+                                            image_space_.Begin(),
+                                            image_header_.GetPointerSize());
       // Dump the large objects separately.
       heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
       indent_os << "\n";
     }
     os << "STATS:\n" << std::flush;
     std::unique_ptr<File> file(OS::OpenFileForReading(image_filename.c_str()));
-    if (file.get() == nullptr) {
+    size_t data_size = image_header_.GetDataSize();  // stored size in file.
+    if (file == nullptr) {
       LOG(WARNING) << "Failed to find image in " << image_filename;
-    }
-    if (file.get() != nullptr) {
+    } else {
       stats_.file_bytes = file->GetLength();
+      // If the image is compressed, adjust to decompressed size.
+      size_t uncompressed_size = image_header_.GetImageSize() - sizeof(ImageHeader);
+      if (image_header_.GetStorageMode() == ImageHeader::kStorageModeUncompressed) {
+        DCHECK_EQ(uncompressed_size, data_size) << "Sizes should match for uncompressed image";
+      }
+      stats_.file_bytes += uncompressed_size - data_size;
     }
     size_t header_bytes = sizeof(ImageHeader);
     const auto& object_section = image_header_.GetImageSection(ImageHeader::kSectionObjects);
@@ -1653,10 +1674,10 @@
     uint32_t end_intern = intern_section.Offset() + intern_section.Size();
     stats_.alignment_bytes += class_table_section.Offset() - end_intern;
 
-    // Add space between class table and bitmap. Expect the bitmap to be page-aligned.
-    uint32_t end_ctable = class_table_section.Offset() + class_table_section.Size();
+    // Add space between end of image data and bitmap. Expect the bitmap to be page-aligned.
+    const size_t bitmap_offset = sizeof(ImageHeader) + data_size;
     CHECK_ALIGNED(bitmap_section.Offset(), kPageSize);
-    stats_.alignment_bytes += bitmap_section.Offset() - end_ctable;
+    stats_.alignment_bytes += RoundUp(bitmap_offset, kPageSize) - bitmap_offset;
 
     stats_.bitmap_bytes += bitmap_section.Size();
     stats_.art_field_bytes += field_section.Size();
@@ -1680,7 +1701,7 @@
     virtual void Visit(ArtMethod* method) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
       std::ostream& indent_os = image_dumper_->vios_.Stream();
       indent_os << method << " " << " ArtMethod: " << PrettyMethod(method) << "\n";
-      image_dumper_->DumpMethod(method, image_dumper_, indent_os);
+      image_dumper_->DumpMethod(method, indent_os);
       indent_os << "\n";
     }
 
@@ -1773,10 +1794,9 @@
     return image_space_.Contains(object);
   }
 
-  const void* GetQuickOatCodeBegin(ArtMethod* m)
-      SHARED_REQUIRES(Locks::mutator_lock_) {
+  const void* GetQuickOatCodeBegin(ArtMethod* m) SHARED_REQUIRES(Locks::mutator_lock_) {
     const void* quick_code = m->GetEntryPointFromQuickCompiledCodePtrSize(
-        InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet()));
+        image_header_.GetPointerSize());
     if (Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(quick_code)) {
       quick_code = oat_dumper_->GetQuickOatCode(m);
     }
@@ -1835,8 +1855,7 @@
     }
     ScopedIndentation indent1(&state->vios_);
     DumpFields(os, obj, obj_class);
-    const auto image_pointer_size =
-        InstructionSetPointerSize(state->oat_dumper_->GetOatInstructionSet());
+    const size_t image_pointer_size = state->image_header_.GetPointerSize();
     if (obj->IsObjectArray()) {
       auto* obj_array = obj->AsObjectArray<mirror::Object>();
       for (int32_t i = 0, length = obj_array->GetLength(); i < length; i++) {
@@ -1881,7 +1900,9 @@
           ScopedIndentation indent2(&state->vios_);
           auto* resolved_methods = dex_cache->GetResolvedMethods();
           for (size_t i = 0, length = dex_cache->NumResolvedMethods(); i < length; ++i) {
-            auto* elem = mirror::DexCache::GetElementPtrSize(resolved_methods, i, image_pointer_size);
+            auto* elem = mirror::DexCache::GetElementPtrSize(resolved_methods,
+                                                             i,
+                                                             image_pointer_size);
             size_t run = 0;
             for (size_t j = i + 1;
                 j != length && elem == mirror::DexCache::GetElementPtrSize(resolved_methods,
@@ -1943,13 +1964,11 @@
     state->stats_.Update(obj_class->GetDescriptor(&temp), object_bytes);
   }
 
-  void DumpMethod(ArtMethod* method, ImageDumper* state, std::ostream& indent_os)
+  void DumpMethod(ArtMethod* method, std::ostream& indent_os)
       SHARED_REQUIRES(Locks::mutator_lock_) {
     DCHECK(method != nullptr);
-    const auto image_pointer_size =
-        InstructionSetPointerSize(state->oat_dumper_->GetOatInstructionSet());
-    const void* quick_oat_code_begin = state->GetQuickOatCodeBegin(method);
-    const void* quick_oat_code_end = state->GetQuickOatCodeEnd(method);
+    const void* quick_oat_code_begin = GetQuickOatCodeBegin(method);
+    const void* quick_oat_code_end = GetQuickOatCodeEnd(method);
     OatQuickMethodHeader* method_header = reinterpret_cast<OatQuickMethodHeader*>(
         reinterpret_cast<uintptr_t>(quick_oat_code_begin) - sizeof(OatQuickMethodHeader));
     if (method->IsNative()) {
@@ -1958,13 +1977,13 @@
         DCHECK(method_header->GetMappingTable() == nullptr) << PrettyMethod(method);
       }
       bool first_occurrence;
-      uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method);
-      state->ComputeOatSize(quick_oat_code_begin, &first_occurrence);
+      uint32_t quick_oat_code_size = GetQuickOatCodeSize(method);
+      ComputeOatSize(quick_oat_code_begin, &first_occurrence);
       if (first_occurrence) {
-        state->stats_.native_to_managed_code_bytes += quick_oat_code_size;
+        stats_.native_to_managed_code_bytes += quick_oat_code_size;
       }
-      if (quick_oat_code_begin !=
-            method->GetEntryPointFromQuickCompiledCodePtrSize(image_pointer_size)) {
+      if (quick_oat_code_begin != method->GetEntryPointFromQuickCompiledCodePtrSize(
+          image_header_.GetPointerSize())) {
         indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code_begin);
       }
     } else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
@@ -1973,46 +1992,44 @@
     } else {
       const DexFile::CodeItem* code_item = method->GetCodeItem();
       size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
-      state->stats_.dex_instruction_bytes += dex_instruction_bytes;
+      stats_.dex_instruction_bytes += dex_instruction_bytes;
 
       bool first_occurrence;
-      size_t gc_map_bytes = state->ComputeOatSize(
-          method_header->GetNativeGcMap(), &first_occurrence);
+      size_t gc_map_bytes = ComputeOatSize(method_header->GetNativeGcMap(), &first_occurrence);
       if (first_occurrence) {
-        state->stats_.gc_map_bytes += gc_map_bytes;
+        stats_.gc_map_bytes += gc_map_bytes;
       }
 
-      size_t pc_mapping_table_bytes = state->ComputeOatSize(
+      size_t pc_mapping_table_bytes = ComputeOatSize(
           method_header->GetMappingTable(), &first_occurrence);
       if (first_occurrence) {
-        state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
+        stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
       }
 
       size_t vmap_table_bytes = 0u;
       if (!method_header->IsOptimized()) {
         // Method compiled with the optimizing compiler have no vmap table.
-        vmap_table_bytes = state->ComputeOatSize(
-            method_header->GetVmapTable(), &first_occurrence);
+        vmap_table_bytes = ComputeOatSize(method_header->GetVmapTable(), &first_occurrence);
         if (first_occurrence) {
-          state->stats_.vmap_table_bytes += vmap_table_bytes;
+          stats_.vmap_table_bytes += vmap_table_bytes;
         }
       }
 
-      uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method);
-      state->ComputeOatSize(quick_oat_code_begin, &first_occurrence);
+      uint32_t quick_oat_code_size = GetQuickOatCodeSize(method);
+      ComputeOatSize(quick_oat_code_begin, &first_occurrence);
       if (first_occurrence) {
-        state->stats_.managed_code_bytes += quick_oat_code_size;
+        stats_.managed_code_bytes += quick_oat_code_size;
         if (method->IsConstructor()) {
           if (method->IsStatic()) {
-            state->stats_.class_initializer_code_bytes += quick_oat_code_size;
+            stats_.class_initializer_code_bytes += quick_oat_code_size;
           } else if (dex_instruction_bytes > kLargeConstructorDexBytes) {
-            state->stats_.large_initializer_code_bytes += quick_oat_code_size;
+            stats_.large_initializer_code_bytes += quick_oat_code_size;
           }
         } else if (dex_instruction_bytes > kLargeMethodDexBytes) {
-          state->stats_.large_method_code_bytes += quick_oat_code_size;
+          stats_.large_method_code_bytes += quick_oat_code_size;
         }
       }
-      state->stats_.managed_code_bytes_ignoring_deduplication += quick_oat_code_size;
+      stats_.managed_code_bytes_ignoring_deduplication += quick_oat_code_size;
 
       uint32_t method_access_flags = method->GetAccessFlags();
 
@@ -2022,11 +2039,11 @@
                                 method_access_flags);
 
       size_t total_size = dex_instruction_bytes + gc_map_bytes + pc_mapping_table_bytes +
-          vmap_table_bytes + quick_oat_code_size + ArtMethod::Size(image_pointer_size);
+          vmap_table_bytes + quick_oat_code_size + ArtMethod::Size(image_header_.GetPointerSize());
 
       double expansion =
       static_cast<double>(quick_oat_code_size) / static_cast<double>(dex_instruction_bytes);
-      state->stats_.ComputeOutliers(total_size, expansion, method);
+      stats_.ComputeOutliers(total_size, expansion, method);
     }
   }
 
@@ -2361,26 +2378,75 @@
   DISALLOW_COPY_AND_ASSIGN(ImageDumper);
 };
 
-static int DumpImage(Runtime* runtime, OatDumperOptions* options, std::ostream* os) {
+static int DumpImage(gc::space::ImageSpace* image_space,
+                     OatDumperOptions* options,
+                     std::ostream* os) SHARED_REQUIRES(Locks::mutator_lock_) {
+  const ImageHeader& image_header = image_space->GetImageHeader();
+  if (!image_header.IsValid()) {
+    fprintf(stderr, "Invalid image header %s\n", image_space->GetImageLocation().c_str());
+    return EXIT_FAILURE;
+  }
+  ImageDumper image_dumper(os, *image_space, image_header, options);
+  if (!image_dumper.Dump()) {
+    return EXIT_FAILURE;
+  }
+  return EXIT_SUCCESS;
+}
+
+static int DumpImages(Runtime* runtime, OatDumperOptions* options, std::ostream* os) {
   // Dumping the image, no explicit class loader.
   ScopedNullHandle<mirror::ClassLoader> null_class_loader;
   options->class_loader_ = &null_class_loader;
 
   ScopedObjectAccess soa(Thread::Current());
-  gc::Heap* heap = runtime->GetHeap();
-  std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
-  CHECK(!image_spaces.empty());
-  for (gc::space::ImageSpace* image_space : image_spaces) {
-    const ImageHeader& image_header = image_space->GetImageHeader();
-    if (!image_header.IsValid()) {
-      fprintf(stderr, "Invalid image header %s\n", image_space->GetImageLocation().c_str());
+  if (options->app_image_ != nullptr) {
+    if (options->app_oat_ == nullptr) {
+      LOG(ERROR) << "Can not dump app image without app oat file";
       return EXIT_FAILURE;
     }
-
-    ImageDumper image_dumper(os, *image_space, image_header, options);
-    if (!image_dumper.Dump()) {
+    // We can't know if the app image is 32 bits yet, but it contains pointers into the oat file.
+    // We need to map the oat file in the low 4gb or else the fixup wont be able to fit oat file
+    // pointers into 32 bit pointer sized ArtMethods.
+    std::string error_msg;
+    std::unique_ptr<OatFile> oat_file(OatFile::Open(options->app_oat_,
+                                                    options->app_oat_,
+                                                    nullptr,
+                                                    nullptr,
+                                                    false,
+                                                    /*low_4gb*/true,
+                                                    nullptr,
+                                                    &error_msg));
+    if (oat_file == nullptr) {
+      LOG(ERROR) << "Failed to open oat file " << options->app_oat_ << " with error " << error_msg;
       return EXIT_FAILURE;
     }
+    std::unique_ptr<gc::space::ImageSpace> space(
+        gc::space::ImageSpace::CreateFromAppImage(options->app_image_, oat_file.get(), &error_msg));
+    if (space == nullptr) {
+      LOG(ERROR) << "Failed to open app image " << options->app_image_ << " with error "
+                 << error_msg;
+    }
+    // Open dex files for the image.
+    std::vector<std::unique_ptr<const DexFile>> dex_files;
+    if (!runtime->GetClassLinker()->OpenImageDexFiles(space.get(), &dex_files, &error_msg)) {
+      LOG(ERROR) << "Failed to open app image dex files " << options->app_image_ << " with error "
+                 << error_msg;
+    }
+    // Dump the actual image.
+    int result = DumpImage(space.get(), options, os);
+    if (result != EXIT_SUCCESS) {
+      return result;
+    }
+    // Fall through to dump the boot images.
+  }
+
+  gc::Heap* heap = runtime->GetHeap();
+  CHECK(heap->HasBootImageSpace()) << "No image spaces";
+  for (gc::space::ImageSpace* image_space : heap->GetBootImageSpaces()) {
+    int result = DumpImage(image_space, options, os);
+    if (result != EXIT_SUCCESS) {
+      return result;
+    }
   }
   return EXIT_SUCCESS;
 }
@@ -2436,8 +2502,14 @@
 static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions* options,
                    std::ostream* os) {
   std::string error_msg;
-  OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
-                                    nullptr, &error_msg);
+  OatFile* oat_file = OatFile::Open(oat_filename,
+                                    oat_filename,
+                                    nullptr,
+                                    nullptr,
+                                    false,
+                                    /*low_4gb*/false,
+                                    nullptr,
+                                    &error_msg);
   if (oat_file == nullptr) {
     fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
     return EXIT_FAILURE;
@@ -2452,8 +2524,14 @@
 
 static int SymbolizeOat(const char* oat_filename, std::string& output_name) {
   std::string error_msg;
-  OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
-                                    nullptr, &error_msg);
+  OatFile* oat_file = OatFile::Open(oat_filename,
+                                    oat_filename,
+                                    nullptr,
+                                    nullptr,
+                                    false,
+                                    /*low_4gb*/false,
+                                    nullptr,
+                                    &error_msg);
   if (oat_file == nullptr) {
     fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
     return EXIT_FAILURE;
@@ -2524,6 +2602,10 @@
         *error_msg = "Address conversion failed";
         return kParseError;
       }
+    } else if (option.starts_with("--app-image=")) {
+      app_image_ = option.substr(strlen("--app-image=")).data();
+    } else if (option.starts_with("--app-oat=")) {
+      app_oat_ = option.substr(strlen("--app-oat=")).data();
     } else {
       return kParseUnknownArgument;
     }
@@ -2569,6 +2651,13 @@
         "\n"
         "  --image=<file.art>: specifies an input image location.\n"
         "      Example: --image=/system/framework/boot.art\n"
+        "\n"
+        "  --app-image=<file.art>: specifies an input app image. Must also have a specified\n"
+        " boot image and app oat file.\n"
+        "      Example: --app-image=app.art\n"
+        "\n"
+        "  --app-oat=<file.odex>: specifies an input app oat.\n"
+        "      Example: --app-oat=app.odex\n"
         "\n";
 
     usage += Base::GetUsage();
@@ -2637,6 +2726,8 @@
   bool dump_header_only_ = false;
   uint32_t addr2instr_ = 0;
   const char* export_dex_location_ = nullptr;
+  const char* app_image_ = nullptr;
+  const char* app_oat_ = nullptr;
 };
 
 struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
@@ -2646,7 +2737,7 @@
     // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
     bool absolute_addresses = (args_->oat_filename_ == nullptr);
 
-    oat_dumper_options_ = std::unique_ptr<OatDumperOptions>(new OatDumperOptions(
+    oat_dumper_options_.reset(new OatDumperOptions(
         args_->dump_raw_mapping_table_,
         args_->dump_raw_gc_map_,
         args_->dump_vmap_,
@@ -2659,6 +2750,8 @@
         args_->list_methods_,
         args_->dump_header_only_,
         args_->export_dex_location_,
+        args_->app_image_,
+        args_->app_oat_,
         args_->addr2instr_));
 
     return (args_->boot_image_location_ != nullptr || args_->image_location_ != nullptr) &&
@@ -2691,7 +2784,7 @@
                      args_->os_) == EXIT_SUCCESS;
     }
 
-    return DumpImage(runtime, oat_dumper_options_.get(), args_->os_) == EXIT_SUCCESS;
+    return DumpImages(runtime, oat_dumper_options_.get(), args_->os_) == EXIT_SUCCESS;
   }
 
   std::unique_ptr<OatDumperOptions> oat_dumper_options_;
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index ebe89bb..8541210 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -473,39 +473,40 @@
 }
 
 template <typename Visitor>
-inline void ArtMethod::UpdateObjectsForImageRelocation(const Visitor& visitor) {
+inline void ArtMethod::UpdateObjectsForImageRelocation(const Visitor& visitor,
+                                                       size_t pointer_size) {
   mirror::Class* old_class = GetDeclaringClassUnchecked<kWithoutReadBarrier>();
   mirror::Class* new_class = visitor(old_class);
   if (old_class != new_class) {
     SetDeclaringClass(new_class);
   }
-  ArtMethod** old_methods = GetDexCacheResolvedMethods(sizeof(void*));
+  ArtMethod** old_methods = GetDexCacheResolvedMethods(pointer_size);
   ArtMethod** new_methods = visitor(old_methods);
   if (old_methods != new_methods) {
-    SetDexCacheResolvedMethods(new_methods, sizeof(void*));
+    SetDexCacheResolvedMethods(new_methods, pointer_size);
   }
-  GcRoot<mirror::Class>* old_types = GetDexCacheResolvedTypes(sizeof(void*));
+  GcRoot<mirror::Class>* old_types = GetDexCacheResolvedTypes(pointer_size);
   GcRoot<mirror::Class>* new_types = visitor(old_types);
   if (old_types != new_types) {
-    SetDexCacheResolvedTypes(new_types, sizeof(void*));
+    SetDexCacheResolvedTypes(new_types, pointer_size);
   }
 }
 
 template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor) {
+inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor, size_t pointer_size) {
   if (IsNative<kReadBarrierOption>()) {
-    const void* old_native_code = GetEntryPointFromJni();
+    const void* old_native_code = GetEntryPointFromJniPtrSize(pointer_size);
     const void* new_native_code = visitor(old_native_code);
     if (old_native_code != new_native_code) {
-      SetEntryPointFromJni(new_native_code);
+      SetEntryPointFromJniPtrSize(new_native_code, pointer_size);
     }
   } else {
-    DCHECK(GetEntryPointFromJni() == nullptr);
+    DCHECK(GetEntryPointFromJniPtrSize(pointer_size) == nullptr);
   }
-  const void* old_code = GetEntryPointFromQuickCompiledCode();
+  const void* old_code = GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
   const void* new_code = visitor(old_code);
   if (old_code != new_code) {
-    SetEntryPointFromQuickCompiledCode(new_code);
+    SetEntryPointFromQuickCompiledCodePtrSize(new_code, pointer_size);
   }
 }
 
diff --git a/runtime/art_method.h b/runtime/art_method.h
index ec00a7b..5ca362c 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -490,12 +490,12 @@
   // Update heap objects and non-entrypoint pointers by the passed in visitor for image relocation.
   // Does not use read barrier.
   template <typename Visitor>
-  ALWAYS_INLINE void UpdateObjectsForImageRelocation(const Visitor& visitor)
+  ALWAYS_INLINE void UpdateObjectsForImageRelocation(const Visitor& visitor, size_t pointer_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Update entry points by passing them through the visitor.
   template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
-  ALWAYS_INLINE void UpdateEntrypoints(const Visitor& visitor);
+  ALWAYS_INLINE void UpdateEntrypoints(const Visitor& visitor, size_t pointer_size);
 
  protected:
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0689589..6a2b702 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1489,6 +1489,64 @@
   const bool forward_strings_;
 };
 
+static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file,
+                                                     const char* location,
+                                                     std::string* error_msg)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
+  DCHECK(error_msg != nullptr);
+  std::unique_ptr<const DexFile> dex_file;
+  const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(location, nullptr);
+  if (oat_dex_file == nullptr) {
+    *error_msg = StringPrintf("Failed finding oat dex file for %s %s",
+                              oat_file->GetLocation().c_str(),
+                              location);
+    return std::unique_ptr<const DexFile>();
+  }
+  std::string inner_error_msg;
+  dex_file = oat_dex_file->OpenDexFile(&inner_error_msg);
+  if (dex_file == nullptr) {
+    *error_msg = StringPrintf("Failed to open dex file %s from within oat file %s error '%s'",
+                              location,
+                              oat_file->GetLocation().c_str(),
+                              inner_error_msg.c_str());
+    return std::unique_ptr<const DexFile>();
+  }
+
+  if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) {
+    *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x",
+                              location,
+                              dex_file->GetLocationChecksum(),
+                              oat_dex_file->GetDexFileLocationChecksum());
+    return std::unique_ptr<const DexFile>();
+  }
+  return dex_file;
+}
+
+bool ClassLinker::OpenImageDexFiles(gc::space::ImageSpace* space,
+                                    std::vector<std::unique_ptr<const DexFile>>* out_dex_files,
+                                    std::string* error_msg) {
+  ScopedAssertNoThreadSuspension nts(Thread::Current(), __FUNCTION__);
+  const ImageHeader& header = space->GetImageHeader();
+  mirror::Object* dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches);
+  DCHECK(dex_caches_object != nullptr);
+  mirror::ObjectArray<mirror::DexCache>* dex_caches =
+      dex_caches_object->AsObjectArray<mirror::DexCache>();
+  const OatFile* oat_file = space->GetOatFile();
+  for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+    mirror::DexCache* dex_cache = dex_caches->Get(i);
+    std::string dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
+    std::unique_ptr<const DexFile> dex_file = OpenOatDexFile(oat_file,
+                                                             dex_file_location.c_str(),
+                                                             error_msg);
+    if (dex_file == nullptr) {
+      return false;
+    }
+    dex_cache->SetDexFile(dex_file.get());
+    out_dex_files->push_back(std::move(dex_file));
+  }
+  return true;
+}
+
 bool ClassLinker::AddImageSpace(
     gc::space::ImageSpace* space,
     Handle<mirror::ClassLoader> class_loader,
@@ -1555,29 +1613,10 @@
       dex_location_path = dex_location_path.substr(0, pos + 1);  // Keep trailing '/'
       dex_file_location = dex_location_path + dex_file_location;
     }
-    const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file_location.c_str(),
-                                                                      nullptr);
-    if (oat_dex_file == nullptr) {
-      *error_msg = StringPrintf("Failed finding oat dex file for %s %s",
-                                oat_file->GetLocation().c_str(),
-                                dex_file_location.c_str());
-      return false;
-    }
-    std::string inner_error_msg;
-    std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&inner_error_msg);
+    std::unique_ptr<const DexFile> dex_file = OpenOatDexFile(oat_file,
+                                                             dex_file_location.c_str(),
+                                                             error_msg);
     if (dex_file == nullptr) {
-      *error_msg = StringPrintf("Failed to open dex file %s from within oat file %s error '%s'",
-                                dex_file_location.c_str(),
-                                oat_file->GetLocation().c_str(),
-                                inner_error_msg.c_str());
-      return false;
-    }
-
-    if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) {
-      *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x",
-                                dex_file_location.c_str(),
-                                dex_file->GetLocationChecksum(),
-                                oat_dex_file->GetDexFileLocationChecksum());
       return false;
     }
 
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 492a228..36ed820 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -149,6 +149,12 @@
       REQUIRES(!dex_lock_)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  bool OpenImageDexFiles(gc::space::ImageSpace* space,
+                         std::vector<std::unique_ptr<const DexFile>>* out_dex_files,
+                         std::string* error_msg)
+      REQUIRES(!dex_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
   // Finds a class by its descriptor, loading it if necessary.
   // If class_loader is null, searches boot_class_path_.
   mirror::Class* FindClass(Thread* self,
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 52da28b..3b4b88d 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -57,9 +57,12 @@
 }
 
 template <typename ElfTypes>
-ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(
-    File* file, bool writable, bool program_header_only,
-    std::string* error_msg, uint8_t* requested_base) {
+ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(File* file,
+                                                   bool writable,
+                                                   bool program_header_only,
+                                                   bool low_4gb,
+                                                   std::string* error_msg,
+                                                   uint8_t* requested_base) {
   std::unique_ptr<ElfFileImpl<ElfTypes>> elf_file(new ElfFileImpl<ElfTypes>
       (file, writable, program_header_only, requested_base));
   int prot;
@@ -71,26 +74,29 @@
     prot = PROT_READ;
     flags = MAP_PRIVATE;
   }
-  if (!elf_file->Setup(prot, flags, error_msg)) {
+  if (!elf_file->Setup(prot, flags, low_4gb, error_msg)) {
     return nullptr;
   }
   return elf_file.release();
 }
 
 template <typename ElfTypes>
-ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(
-    File* file, int prot, int flags, std::string* error_msg) {
+ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(File* file,
+                                                   int prot,
+                                                   int flags,
+                                                   bool low_4gb,
+                                                   std::string* error_msg) {
   std::unique_ptr<ElfFileImpl<ElfTypes>> elf_file(new ElfFileImpl<ElfTypes>
       (file, (prot & PROT_WRITE) == PROT_WRITE, /*program_header_only*/false,
       /*requested_base*/nullptr));
-  if (!elf_file->Setup(prot, flags, error_msg)) {
+  if (!elf_file->Setup(prot, flags, low_4gb, error_msg)) {
     return nullptr;
   }
   return elf_file.release();
 }
 
 template <typename ElfTypes>
-bool ElfFileImpl<ElfTypes>::Setup(int prot, int flags, std::string* error_msg) {
+bool ElfFileImpl<ElfTypes>::Setup(int prot, int flags, bool low_4gb, std::string* error_msg) {
   int64_t temp_file_length = file_->GetLength();
   if (temp_file_length < 0) {
     errno = -temp_file_length;
@@ -114,7 +120,7 @@
                                 flags,
                                 file_->Fd(),
                                 0,
-                                /*low4_gb*/false,
+                                low_4gb,
                                 file_->GetPath().c_str(),
                                 error_msg),
                 error_msg)) {
@@ -133,7 +139,7 @@
                                 flags,
                                 file_->Fd(),
                                 0,
-                                /*low4_gb*/false,
+                                low_4gb,
                                 file_->GetPath().c_str(),
                                 error_msg),
                 error_msg)) {
@@ -147,7 +153,7 @@
                                 flags,
                                 file_->Fd(),
                                 0,
-                                /*low4_gb*/false,
+                                low_4gb,
                                 file_->GetPath().c_str(),
                                 error_msg),
                 error_msg)) {
@@ -1058,7 +1064,7 @@
 }
 
 template <typename ElfTypes>
-bool ElfFileImpl<ElfTypes>::Load(bool executable, std::string* error_msg) {
+bool ElfFileImpl<ElfTypes>::Load(bool executable, bool low_4gb, std::string* error_msg) {
   CHECK(program_header_only_) << file_->GetPath();
 
   if (executable) {
@@ -1124,7 +1130,10 @@
       }
       std::unique_ptr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
                                                            reserve_base_override,
-                                                           loaded_size, PROT_NONE, false, false,
+                                                           loaded_size,
+                                                           PROT_NONE,
+                                                           low_4gb,
+                                                           false,
                                                            error_msg));
       if (reserve.get() == nullptr) {
         *error_msg = StringPrintf("Failed to allocate %s: %s",
@@ -1656,7 +1665,11 @@
   CHECK_NE(elf32_.get() == nullptr, elf64_.get() == nullptr);
 }
 
-ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only, std::string* error_msg,
+ElfFile* ElfFile::Open(File* file,
+                       bool writable,
+                       bool program_header_only,
+                       bool low_4gb,
+                       std::string* error_msg,
                        uint8_t* requested_base) {
   if (file->GetLength() < EI_NIDENT) {
     *error_msg = StringPrintf("File %s is too short to be a valid ELF file",
@@ -1668,7 +1681,7 @@
                                               MAP_PRIVATE,
                                               file->Fd(),
                                               0,
-                                              /*low4_gb*/false,
+                                              low_4gb,
                                               file->GetPath().c_str(),
                                               error_msg));
   if (map == nullptr && map->Size() != EI_NIDENT) {
@@ -1676,14 +1689,22 @@
   }
   uint8_t* header = map->Begin();
   if (header[EI_CLASS] == ELFCLASS64) {
-    ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file, writable, program_header_only,
-                                                       error_msg, requested_base);
+    ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file,
+                                                       writable,
+                                                       program_header_only,
+                                                       low_4gb,
+                                                       error_msg,
+                                                       requested_base);
     if (elf_file_impl == nullptr)
       return nullptr;
     return new ElfFile(elf_file_impl);
   } else if (header[EI_CLASS] == ELFCLASS32) {
-    ElfFileImpl32* elf_file_impl = ElfFileImpl32::Open(file, writable, program_header_only,
-                                                       error_msg, requested_base);
+    ElfFileImpl32* elf_file_impl = ElfFileImpl32::Open(file,
+                                                       writable,
+                                                       program_header_only,
+                                                       low_4gb,
+                                                       error_msg,
+                                                       requested_base);
     if (elf_file_impl == nullptr) {
       return nullptr;
     }
@@ -1698,6 +1719,8 @@
 }
 
 ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg) {
+  // low_4gb support not required for this path.
+  constexpr bool low_4gb = false;
   if (file->GetLength() < EI_NIDENT) {
     *error_msg = StringPrintf("File %s is too short to be a valid ELF file",
                               file->GetPath().c_str());
@@ -1708,7 +1731,7 @@
                                               MAP_PRIVATE,
                                               file->Fd(),
                                               0,
-                                              /*low4_gb*/false,
+                                              low_4gb,
                                               file->GetPath().c_str(),
                                               error_msg));
   if (map == nullptr && map->Size() != EI_NIDENT) {
@@ -1716,13 +1739,21 @@
   }
   uint8_t* header = map->Begin();
   if (header[EI_CLASS] == ELFCLASS64) {
-    ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file, mmap_prot, mmap_flags, error_msg);
+    ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file,
+                                                       mmap_prot,
+                                                       mmap_flags,
+                                                       low_4gb,
+                                                       error_msg);
     if (elf_file_impl == nullptr) {
       return nullptr;
     }
     return new ElfFile(elf_file_impl);
   } else if (header[EI_CLASS] == ELFCLASS32) {
-    ElfFileImpl32* elf_file_impl = ElfFileImpl32::Open(file, mmap_prot, mmap_flags, error_msg);
+    ElfFileImpl32* elf_file_impl = ElfFileImpl32::Open(file,
+                                                       mmap_prot,
+                                                       mmap_flags,
+                                                       low_4gb,
+                                                       error_msg);
     if (elf_file_impl == nullptr) {
       return nullptr;
     }
@@ -1744,8 +1775,8 @@
     return elf32_->func(__VA_ARGS__); \
   }
 
-bool ElfFile::Load(bool executable, std::string* error_msg) {
-  DELEGATE_TO_IMPL(Load, executable, error_msg);
+bool ElfFile::Load(bool executable, bool low_4gb, std::string* error_msg) {
+  DELEGATE_TO_IMPL(Load, executable, low_4gb, error_msg);
 }
 
 const uint8_t* ElfFile::FindDynamicSymbolAddress(const std::string& symbol_name) const {
@@ -1810,7 +1841,7 @@
 }
 
 bool ElfFile::Strip(File* file, std::string* error_msg) {
-  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, error_msg));
+  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, /*low_4gb*/false, error_msg));
   if (elf_file.get() == nullptr) {
     return false;
   }
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 1188c97..b5229b5 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -38,15 +38,22 @@
 // ELFObjectFile.
 class ElfFile {
  public:
-  static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg,
+  static ElfFile* Open(File* file,
+                       bool writable,
+                       bool program_header_only,
+                       bool low_4gb,
+                       std::string* error_msg,
                        uint8_t* requested_base = nullptr);  // TODO: move arg to before error_msg.
   // Open with specific mmap flags, Always maps in the whole file, not just the
   // program header sections.
-  static ElfFile* Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg);
+  static ElfFile* Open(File* file,
+                       int mmap_prot,
+                       int mmap_flags,
+                       std::string* error_msg);
   ~ElfFile();
 
   // Load segments into memory based on PT_LOAD program headers
-  bool Load(bool executable, std::string* error_msg);
+  bool Load(bool executable, bool low_4gb, std::string* error_msg);
 
   const uint8_t* FindDynamicSymbolAddress(const std::string& symbol_name) const;
 
diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h
index 2af31dc..1cdbedc 100644
--- a/runtime/elf_file_impl.h
+++ b/runtime/elf_file_impl.h
@@ -48,9 +48,17 @@
   using Elf_Phdr = typename ElfTypes::Phdr;
   using Elf_Dyn = typename ElfTypes::Dyn;
 
-  static ElfFileImpl* Open(File* file, bool writable, bool program_header_only,
-                           std::string* error_msg, uint8_t* requested_base = nullptr);
-  static ElfFileImpl* Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg);
+  static ElfFileImpl* Open(File* file,
+                           bool writable,
+                           bool program_header_only,
+                           bool low_4gb,
+                           std::string* error_msg,
+                           uint8_t* requested_base = nullptr);
+  static ElfFileImpl* Open(File* file,
+                           int mmap_prot,
+                           int mmap_flags,
+                           bool low_4gb,
+                           std::string* error_msg);
   ~ElfFileImpl();
 
   const File& GetFile() const {
@@ -111,7 +119,7 @@
 
   // Load segments into memory based on PT_LOAD program headers.
   // executable is true at run time, false at compile time.
-  bool Load(bool executable, std::string* error_msg);
+  bool Load(bool executable, bool low_4gb, std::string* error_msg);
 
   bool Fixup(Elf_Addr base_address);
   bool FixupDynamic(Elf_Addr base_address);
@@ -129,7 +137,7 @@
  private:
   ElfFileImpl(File* file, bool writable, bool program_header_only, uint8_t* requested_base);
 
-  bool Setup(int prot, int flags, std::string* error_msg);
+  bool Setup(int prot, int flags, bool low_4gb, std::string* error_msg);
 
   bool SetMap(MemMap* map, std::string* error_msg);
 
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index bea1dcc..895d3d3 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -617,7 +617,7 @@
   }
 
   // Returns the delta between the dest from the source.
-  off_t Delta() const {
+  uintptr_t Delta() const {
     return dest_ - source_;
   }
 
@@ -639,6 +639,13 @@
   const uintptr_t length_;
 };
 
+std::ostream& operator<<(std::ostream& os, const RelocationRange& reloc) {
+  return os << "(" << reinterpret_cast<const void*>(reloc.Source()) << "-"
+            << reinterpret_cast<const void*>(reloc.Source() + reloc.Length()) << ")->("
+            << reinterpret_cast<const void*>(reloc.Dest()) << "-"
+            << reinterpret_cast<const void*>(reloc.Dest() + reloc.Length()) << ")";
+}
+
 class FixupVisitor : public ValueObject {
  public:
   FixupVisitor(const RelocationRange& boot_image,
@@ -670,7 +677,7 @@
   ALWAYS_INLINE const void* ForwardCode(const void* src) const {
     const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src);
     if (boot_oat_.InSource(uint_src)) {
-     return reinterpret_cast<const void*>(boot_oat_.ToDest(uint_src));
+      return reinterpret_cast<const void*>(boot_oat_.ToDest(uint_src));
     }
     if (app_oat_.InSource(uint_src)) {
       return reinterpret_cast<const void*>(app_oat_.ToDest(uint_src));
@@ -687,13 +694,6 @@
   const RelocationRange app_oat_;
 };
 
-std::ostream& operator<<(std::ostream& os, const RelocationRange& reloc) {
-  return os << "(" << reinterpret_cast<const void*>(reloc.Source()) << "-"
-            << reinterpret_cast<const void*>(reloc.Source() + reloc.Length()) << ")->("
-            << reinterpret_cast<const void*>(reloc.Dest()) << "-"
-            << reinterpret_cast<const void*>(reloc.Dest() + reloc.Length()) << ")";
-}
-
 // Adapt for mirror::Class::FixupNativePointers.
 class FixupObjectAdapter : public FixupVisitor {
  public:
@@ -756,8 +756,10 @@
  public:
   template<typename... Args>
   explicit FixupObjectVisitor(gc::accounting::ContinuousSpaceBitmap* pointer_array_visited,
+                              const size_t pointer_size,
                               Args... args)
       : FixupVisitor(args...),
+        pointer_size_(pointer_size),
         pointer_array_visited_(pointer_array_visited) {}
 
   // Fix up separately since we also need to fix up method entrypoints.
@@ -791,7 +793,7 @@
     if (array != nullptr &&
         visitor.IsInAppImage(array) &&
         !pointer_array_visited_->Test(array)) {
-      array->Fixup<kVerifyNone, kWithoutReadBarrier>(array, sizeof(void*), visitor);
+      array->Fixup<kVerifyNone, kWithoutReadBarrier>(array, pointer_size_, visitor);
       pointer_array_visited_->Set(array);
     }
   }
@@ -813,7 +815,7 @@
     if (obj->IsClass<kVerifyNone, kWithoutReadBarrier>()) {
       mirror::Class* klass = obj->AsClass<kVerifyNone, kWithoutReadBarrier>();
       FixupObjectAdapter visitor(boot_image_, boot_oat_, app_image_, app_oat_);
-      klass->FixupNativePointers<kVerifyNone, kWithoutReadBarrier>(klass, sizeof(void*), visitor);
+      klass->FixupNativePointers<kVerifyNone, kWithoutReadBarrier>(klass, pointer_size_, visitor);
       // Deal with the pointer arrays. Use the helper function since multiple classes can reference
       // the same arrays.
       VisitPointerArray(klass->GetVTable<kVerifyNone, kWithoutReadBarrier>(), visitor);
@@ -832,6 +834,7 @@
   }
 
  private:
+  const size_t pointer_size_;
   gc::accounting::ContinuousSpaceBitmap* const pointer_array_visited_;
 };
 
@@ -850,7 +853,8 @@
 
 class ForwardCodeAdapter {
  public:
-  ALWAYS_INLINE ForwardCodeAdapter(const FixupVisitor* visitor) : visitor_(visitor) {}
+  ALWAYS_INLINE ForwardCodeAdapter(const FixupVisitor* visitor)
+      : visitor_(visitor) {}
 
   template <typename T>
   ALWAYS_INLINE T* operator()(T* src) const {
@@ -864,19 +868,21 @@
 class FixupArtMethodVisitor : public FixupVisitor, public ArtMethodVisitor {
  public:
   template<typename... Args>
-  explicit FixupArtMethodVisitor(bool fixup_heap_objects, Args... args)
+  explicit FixupArtMethodVisitor(bool fixup_heap_objects, size_t pointer_size, Args... args)
       : FixupVisitor(args...),
-        fixup_heap_objects_(fixup_heap_objects) {}
+        fixup_heap_objects_(fixup_heap_objects),
+        pointer_size_(pointer_size) {}
 
   virtual void Visit(ArtMethod* method) NO_THREAD_SAFETY_ANALYSIS {
     if (fixup_heap_objects_) {
-      method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this));
+      method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this), pointer_size_);
     }
-    method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this));
+    method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this), pointer_size_);
   }
 
  private:
   const bool fixup_heap_objects_;
+  const size_t pointer_size_;
 };
 
 class FixupArtFieldVisitor : public FixupVisitor, public ArtFieldVisitor {
@@ -912,6 +918,7 @@
   uint32_t boot_image_end = 0;
   uint32_t boot_oat_begin = 0;
   uint32_t boot_oat_end = 0;
+  const size_t pointer_size = image_header.GetPointerSize();
   gc::Heap* const heap = Runtime::Current()->GetHeap();
   heap->GetBootImagesSize(&boot_image_begin, &boot_image_end, &boot_oat_begin, &boot_oat_end);
   CHECK_NE(boot_image_begin, boot_image_end)
@@ -974,6 +981,7 @@
                                                       target_base,
                                                       image_header.GetImageSize()));
     FixupObjectVisitor fixup_object_visitor(visited_bitmap.get(),
+                                            pointer_size,
                                             boot_image,
                                             boot_oat,
                                             app_image,
@@ -1023,10 +1031,10 @@
           dex_cache->SetResolvedMethods(new_methods);
         }
         for (size_t j = 0, num = dex_cache->NumResolvedMethods(); j != num; ++j) {
-          ArtMethod* orig = mirror::DexCache::GetElementPtrSize(new_methods, j, sizeof(void*));
+          ArtMethod* orig = mirror::DexCache::GetElementPtrSize(new_methods, j, pointer_size);
           ArtMethod* copy = fixup_adapter.ForwardObject(orig);
           if (orig != copy) {
-            mirror::DexCache::SetElementPtrSize(new_methods, j, copy, sizeof(void*));
+            mirror::DexCache::SetElementPtrSize(new_methods, j, copy, pointer_size);
           }
         }
       }
@@ -1037,10 +1045,10 @@
           dex_cache->SetResolvedFields(new_fields);
         }
         for (size_t j = 0, num = dex_cache->NumResolvedFields(); j != num; ++j) {
-          ArtField* orig = mirror::DexCache::GetElementPtrSize(new_fields, j, sizeof(void*));
+          ArtField* orig = mirror::DexCache::GetElementPtrSize(new_fields, j, pointer_size);
           ArtField* copy = fixup_adapter.ForwardObject(orig);
           if (orig != copy) {
-            mirror::DexCache::SetElementPtrSize(new_fields, j, copy, sizeof(void*));
+            mirror::DexCache::SetElementPtrSize(new_fields, j, copy, pointer_size);
           }
         }
       }
@@ -1049,11 +1057,16 @@
   {
     // Only touches objects in the app image, no need for mutator lock.
     TimingLogger::ScopedTiming timing("Fixup methods", &logger);
-    FixupArtMethodVisitor method_visitor(fixup_image, boot_image, boot_oat, app_image, app_oat);
+    FixupArtMethodVisitor method_visitor(fixup_image,
+                                         pointer_size,
+                                         boot_image,
+                                         boot_oat,
+                                         app_image,
+                                         app_oat);
     image_header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
         &method_visitor,
         target_base,
-        sizeof(void*));
+        pointer_size);
   }
   if (fixup_image) {
     {
@@ -1381,6 +1394,7 @@
                                     image_header.GetOatDataBegin(),
                                     image_header.GetOatFileBegin(),
                                     !Runtime::Current()->IsAotCompiler(),
+                                    /*low_4gb*/false,
                                     nullptr,
                                     error_msg);
   if (oat_file == nullptr) {
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 7155c79..033ea56 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -89,6 +89,7 @@
                                   uint8_t* oat_file_begin,
                                   bool writable,
                                   bool executable,
+                                  bool low_4gb,
                                   const char* abs_dex_location,
                                   std::string* error_msg);
 
@@ -102,6 +103,7 @@
                     uint8_t* oat_file_begin,
                     bool writable,
                     bool executable,
+                    bool low_4gb,
                     std::string* error_msg) = 0;
 
   bool ComputeFields(uint8_t* requested_base,
@@ -133,6 +135,7 @@
                                       uint8_t* oat_file_begin,
                                       bool writable,
                                       bool executable,
+                                      bool low_4gb,
                                       const char* abs_dex_location,
                                       std::string* error_msg) {
   std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(location, executable));
@@ -140,6 +143,7 @@
                  oat_file_begin,
                  writable,
                  executable,
+                 low_4gb,
                  error_msg)) {
     return nullptr;
   }
@@ -147,7 +151,6 @@
   if (!ret->ComputeFields(requested_base, elf_filename, error_msg)) {
     return nullptr;
   }
-
   ret->PreSetup(elf_filename);
 
   if (!ret->Setup(abs_dex_location, error_msg)) {
@@ -532,6 +535,7 @@
             uint8_t* oat_file_begin,
             bool writable,
             bool executable,
+            bool low_4gb,
             std::string* error_msg) OVERRIDE;
 
   // Ask the linker where it mmaped the file and notify our mmap wrapper of the regions.
@@ -558,6 +562,7 @@
                          uint8_t* oat_file_begin,
                          bool writable,
                          bool executable,
+                         bool low_4gb,
                          std::string* error_msg) {
   // Use dlopen only when flagged to do so, and when it's OK to load things executable.
   // TODO: Also try when not executable? The issue here could be re-mapping as writable (as
@@ -567,6 +572,10 @@
     *error_msg = "DlOpen is disabled.";
     return false;
   }
+  if (low_4gb) {
+    *error_msg = "DlOpen does not support low 4gb loading.";
+    return false;
+  }
   if (writable) {
     *error_msg = "DlOpen does not support writable loading.";
     return false;
@@ -702,6 +711,7 @@
                                  uint8_t* oat_file_begin,  // Override base if not null
                                  bool writable,
                                  bool executable,
+                                 bool low_4gb,
                                  const char* abs_dex_location,
                                  std::string* error_msg);
 
@@ -723,6 +733,7 @@
             uint8_t* oat_file_begin,  // Override where the file is loaded to if not null
             bool writable,
             bool executable,
+            bool low_4gb,
             std::string* error_msg) OVERRIDE;
 
   void PreSetup(const std::string& elf_filename ATTRIBUTE_UNUSED) OVERRIDE {
@@ -733,6 +744,7 @@
                    uint8_t* oat_file_begin,  // Override where the file is loaded to if not null
                    bool writable,
                    bool executable,
+                   bool low_4gb,
                    std::string* error_msg);
 
  private:
@@ -748,11 +760,17 @@
                                     uint8_t* oat_file_begin,  // Override base if not null
                                     bool writable,
                                     bool executable,
+                                    bool low_4gb,
                                     const char* abs_dex_location,
                                     std::string* error_msg) {
   ScopedTrace trace("Open elf file " + location);
   std::unique_ptr<ElfOatFile> oat_file(new ElfOatFile(location, executable));
-  bool success = oat_file->ElfFileOpen(file, oat_file_begin, writable, executable, error_msg);
+  bool success = oat_file->ElfFileOpen(file,
+                                       oat_file_begin,
+                                       writable,
+                                       low_4gb,
+                                       executable,
+                                       error_msg);
   if (!success) {
     CHECK(!error_msg->empty());
     return nullptr;
@@ -792,6 +810,7 @@
                       uint8_t* oat_file_begin,  // Override where the file is loaded to if not null
                       bool writable,
                       bool executable,
+                      bool low_4gb,
                       std::string* error_msg) {
   ScopedTrace trace(__PRETTY_FUNCTION__);
   std::unique_ptr<File> file(OS::OpenFileForReading(elf_filename.c_str()));
@@ -803,6 +822,7 @@
                                  oat_file_begin,
                                  writable,
                                  executable,
+                                 low_4gb,
                                  error_msg);
 }
 
@@ -810,19 +830,21 @@
                              uint8_t* oat_file_begin,
                              bool writable,
                              bool executable,
+                             bool low_4gb,
                              std::string* error_msg) {
   ScopedTrace trace(__PRETTY_FUNCTION__);
   // TODO: rename requested_base to oat_data_begin
   elf_file_.reset(ElfFile::Open(file,
                                 writable,
                                 /*program_header_only*/true,
+                                low_4gb,
                                 error_msg,
                                 oat_file_begin));
   if (elf_file_ == nullptr) {
     DCHECK(!error_msg->empty());
     return false;
   }
-  bool loaded = elf_file_->Load(executable, error_msg);
+  bool loaded = elf_file_->Load(executable, low_4gb, error_msg);
   DCHECK(loaded || !error_msg->empty());
   return loaded;
 }
@@ -870,6 +892,7 @@
                        uint8_t* requested_base,
                        uint8_t* oat_file_begin,
                        bool executable,
+                       bool low_4gb,
                        const char* abs_dex_location,
                        std::string* error_msg) {
   ScopedTrace trace("Open oat file " + location);
@@ -885,15 +908,15 @@
                                                                  oat_file_begin,
                                                                  false,
                                                                  executable,
+                                                                 low_4gb,
                                                                  abs_dex_location,
                                                                  error_msg);
   if (with_dlopen != nullptr) {
     return with_dlopen;
   }
   if (kPrintDlOpenErrorMessage) {
-    LOG(ERROR) << "Failed to dlopen: " << *error_msg;
+    LOG(ERROR) << "Failed to dlopen: " << filename << " with error " << *error_msg;
   }
-
   // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons:
   //
   // On target, dlopen may fail when compiling due to selinux restrictions on installd.
@@ -913,6 +936,7 @@
                                                                 oat_file_begin,
                                                                 false,
                                                                 executable,
+                                                                low_4gb,
                                                                 abs_dex_location,
                                                                 error_msg);
   return with_internal;
@@ -929,6 +953,7 @@
                                  nullptr,
                                  true,
                                  false,
+                                 /*low_4gb*/false,
                                  abs_dex_location,
                                  error_msg);
 }
@@ -944,6 +969,7 @@
                                  nullptr,
                                  false,
                                  false,
+                                 /*low_4gb*/false,
                                  abs_dex_location,
                                  error_msg);
 }
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 1084253a..7af77ae 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -64,6 +64,7 @@
                        uint8_t* requested_base,
                        uint8_t* oat_file_begin,
                        bool executable,
+                       bool low_4gb,
                        const char* abs_dex_location,
                        std::string* error_msg);
 
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 90712c6..cbc0ec6 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -815,8 +815,13 @@
       const std::string& odex_file_name = *OdexFileName();
       std::string error_msg;
       cached_odex_file_.reset(OatFile::Open(odex_file_name.c_str(),
-            odex_file_name.c_str(), nullptr, nullptr, load_executable_,
-            dex_location_.c_str(), &error_msg));
+                                            odex_file_name.c_str(),
+                                            nullptr,
+                                            nullptr,
+                                            load_executable_,
+                                            /*low_4gb*/false,
+                                            dex_location_.c_str(),
+                                            &error_msg));
       if (cached_odex_file_.get() == nullptr) {
         VLOG(oat) << "OatFileAssistant test for existing pre-compiled oat file "
           << odex_file_name << ": " << error_msg;
@@ -846,8 +851,13 @@
       const std::string& oat_file_name = *OatFileName();
       std::string error_msg;
       cached_oat_file_.reset(OatFile::Open(oat_file_name.c_str(),
-            oat_file_name.c_str(), nullptr, nullptr, load_executable_,
-            dex_location_.c_str(), &error_msg));
+                                           oat_file_name.c_str(),
+                                           nullptr,
+                                           nullptr,
+                                           load_executable_,
+                                           /*low_4gb*/false,
+                                           dex_location_.c_str(),
+                                           &error_msg));
       if (cached_oat_file_.get() == nullptr) {
         VLOG(oat) << "OatFileAssistant test for existing oat file "
           << oat_file_name << ": " << error_msg;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 4541468..046d8ae 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -218,9 +218,14 @@
 
     // Verify the odex file was generated as expected and really is
     // unrelocated.
-    std::unique_ptr<OatFile> odex_file(OatFile::Open(
-        odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
-        false, dex_location.c_str(), &error_msg));
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+                                                     odex_location.c_str(),
+                                                     nullptr,
+                                                     nullptr,
+                                                     false,
+                                                     /*low_4gb*/false,
+                                                     dex_location.c_str(),
+                                                     &error_msg));
     ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
 
     const std::vector<gc::space::ImageSpace*> image_spaces =
@@ -252,9 +257,14 @@
     setenv("ANDROID_DATA", android_data_.c_str(), 1);
 
     // Verify the odex file was generated as expected.
-    std::unique_ptr<OatFile> odex_file(OatFile::Open(
-        odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
-        false, dex_location.c_str(), &error_msg));
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+                                                     odex_location.c_str(),
+                                                     nullptr,
+                                                     nullptr,
+                                                     false,
+                                                     /*low_4gb*/false,
+                                                     dex_location.c_str(),
+                                                     &error_msg));
     ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
     EXPECT_TRUE(odex_file->IsPic());
   }
@@ -269,9 +279,14 @@
     ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
 
     // Verify the odex file was generated as expected.
-    std::unique_ptr<OatFile> odex_file(OatFile::Open(
-        odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
-        false, dex_location.c_str(), &error_msg));
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+                                                     odex_location.c_str(),
+                                                     nullptr,
+                                                     nullptr,
+                                                     false,
+                                                     /*low_4gb*/false,
+                                                     dex_location.c_str(),
+                                                     &error_msg));
     ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
     EXPECT_TRUE(odex_file->IsExtractOnly());
     EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0u);
@@ -290,9 +305,14 @@
     ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
 
     // Verify the odex file was generated as expected.
-    std::unique_ptr<OatFile> odex_file(OatFile::Open(
-        odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
-        false, dex_location.c_str(), &error_msg));
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+                                                     odex_location.c_str(),
+                                                     nullptr,
+                                                     nullptr,
+                                                     false,
+                                                     /*low_4gb*/false,
+                                                     dex_location.c_str(),
+                                                     &error_msg));
     printf("error %s", error_msg.c_str());
     ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
     EXPECT_TRUE(odex_file->IsProfileGuideCompiled());
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e95f2c5..0dfb0cf 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -810,7 +810,11 @@
       return false;
     }
     std::string error_msg;
-    std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(), false, false, &error_msg));
+    std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(),
+                                                    false,
+                                                    false,
+                                                    /*low_4gb*/false,
+                                                    &error_msg));
     if (elf_file.get() == nullptr) {
       return false;
     }