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

(cherry picked from commit bcb6a72569a1401b36a3ad3b6aa4d13e29966cf0)

Change-Id: I9d1aa7eaa16795e5fbabc6974d245849e16b1d03
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 d51a1f7..52beb15 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1462,6 +1462,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,
@@ -1528,29 +1586,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 1084253..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;
     }