Relocate DWARF using .oat_patches.

The current solution is to hard-code knowledge of DWARF in the linker.
This works for simple use of DWARF, but breaks as soon as I try to do
anything more complex.  Making the linker fully support DWARF would be
non-trivial task and would be essentially rewrite.  Using .oat_patches
is much easier solution.

Relocating .debug_* sections required extending .oat_patches to support
more sections than just .text.  I have encoded each section as
null-terminated section name followed by ULEB128 deltas.

The ULEB128 encoding shrinks .oat_patches for .text by factor of
about 6 with 64-bit compiler, and factor of 3 with 32-bit compiler.

On the other hand, it grows by the extra .oat_patches for DWARF which
were not present before (if debug symbols are included).

Overall, it is still a clear improvement even with the DWARF patches.

Change-Id: I78ffeda0f8a3da03341995a3b5ef15c954e16e9f
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index 9181792..cdb1b9e 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -46,7 +46,9 @@
     constexpr bool is64bit = false;
     dwarf::DebugFrameOpCodeWriter<> initial_opcodes;
     dwarf::WriteEhFrameCIE(is64bit, dwarf::Reg(8), initial_opcodes, &eh_frame_data_);
-    dwarf::WriteEhFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi, &eh_frame_data_);
+    std::vector<uintptr_t> eh_frame_patches;
+    dwarf::WriteEhFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi,
+                           &eh_frame_data_, &eh_frame_patches);
     ReformatCfi(Objdump(false, "-W"), &lines);
     // Pretty-print assembly.
     auto* opts = new DisassemblerOptions(false, actual_asm.data(), true);
diff --git a/compiler/dwarf/debug_info_entry_writer.h b/compiler/dwarf/debug_info_entry_writer.h
index c0350b6..a2c9f5f 100644
--- a/compiler/dwarf/debug_info_entry_writer.h
+++ b/compiler/dwarf/debug_info_entry_writer.h
@@ -17,6 +17,7 @@
 #ifndef ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
 #define ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
 
+#include <cstdint>
 #include <unordered_map>
 
 #include "dwarf.h"
@@ -88,6 +89,7 @@
 
   void WriteAddr(Attribute attrib, uint64_t value) {
     AddAbbrevAttribute(attrib, DW_FORM_addr);
+    patch_locations_.push_back(this->data()->size());
     if (is64bit_) {
       this->PushUint64(value);
     } else {
@@ -168,7 +170,11 @@
     this->PushUint32(address);
   }
 
-  bool is64bit() const { return is64bit_; }
+  bool Is64bit() const { return is64bit_; }
+
+  const std::vector<uintptr_t>& GetPatchLocations() const {
+    return patch_locations_;
+  }
 
   using Writer<Allocator>::data;
 
@@ -240,6 +246,7 @@
   size_t abbrev_code_offset_ = 0;  // Location to patch once we know the code.
   bool inside_entry_ = false;  // Entry ends at first child (if any).
   bool has_children = true;
+  std::vector<uintptr_t> patch_locations_;
 };
 
 }  // namespace dwarf
diff --git a/compiler/dwarf/debug_line_opcode_writer.h b/compiler/dwarf/debug_line_opcode_writer.h
index f34acee..77ed154 100644
--- a/compiler/dwarf/debug_line_opcode_writer.h
+++ b/compiler/dwarf/debug_line_opcode_writer.h
@@ -17,6 +17,8 @@
 #ifndef ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
 #define ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
 
+#include <cstdint>
+
 #include "dwarf.h"
 #include "writer.h"
 
@@ -119,10 +121,12 @@
     if (use_64bit_address_) {
       this->PushUleb128(1 + 8);
       this->PushUint8(DW_LNE_set_address);
+      patch_locations_.push_back(this->data()->size());
       this->PushUint64(absolute_address);
     } else {
       this->PushUleb128(1 + 4);
       this->PushUint8(DW_LNE_set_address);
+      patch_locations_.push_back(this->data()->size());
       this->PushUint32(absolute_address);
     }
     current_address_ = absolute_address;
@@ -204,6 +208,10 @@
     return current_line_;
   }
 
+  const std::vector<uintptr_t>& GetPatchLocations() const {
+    return patch_locations_;
+  }
+
   using Writer<Allocator>::data;
 
   DebugLineOpCodeWriter(bool use64bitAddress,
@@ -233,6 +241,7 @@
   uint64_t current_address_;
   int current_file_;
   int current_line_;
+  std::vector<uintptr_t> patch_locations_;
 
   DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter);
 };
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
index ec18e96..fa12d7e 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -120,19 +120,27 @@
 
   DebugFrameOpCodeWriter<> initial_opcodes;
   WriteEhFrameCIE(is64bit, Reg(is64bit ? 16 : 8), initial_opcodes, &eh_frame_data_);
-  WriteEhFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(), &eh_frame_data_);
+  std::vector<uintptr_t> eh_frame_patches;
+  std::vector<uintptr_t> expected_patches { 28 };  // NOLINT
+  WriteEhFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(),
+                  &eh_frame_data_, &eh_frame_patches);
+
+  EXPECT_EQ(expected_patches, eh_frame_patches);
   CheckObjdumpOutput(is64bit, "-W");
 }
 
-// TODO: objdump seems to have trouble with 64bit CIE length.
-TEST_F(DwarfTest, DISABLED_DebugFrame64) {
+TEST_F(DwarfTest, DebugFrame64) {
   constexpr bool is64bit = true;
   DebugFrameOpCodeWriter<> initial_opcodes;
   WriteEhFrameCIE(is64bit, Reg(16), initial_opcodes, &eh_frame_data_);
   DebugFrameOpCodeWriter<> opcodes;
+  std::vector<uintptr_t> eh_frame_patches;
+  std::vector<uintptr_t> expected_patches { 32 };  // NOLINT
   WriteEhFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
-                  opcodes.data(), &eh_frame_data_);
+                  opcodes.data(), &eh_frame_data_, &eh_frame_patches);
   DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000");
+
+  EXPECT_EQ(expected_patches, eh_frame_patches);
   CheckObjdumpOutput(is64bit, "-W");
 }
 
@@ -184,7 +192,12 @@
   DW_CHECK_NEXT("Entry\tDir\tTime\tSize\tName");
   DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c");
 
-  WriteDebugLineTable(include_directories, files, opcodes, &debug_line_data_);
+  std::vector<uintptr_t> debug_line_patches;
+  std::vector<uintptr_t> expected_patches { 87 };  // NOLINT
+  WriteDebugLineTable(include_directories, files, opcodes,
+                      &debug_line_data_, &debug_line_patches);
+
+  EXPECT_EQ(expected_patches, debug_line_patches);
   CheckObjdumpOutput(is64bit, "-W");
 }
 
@@ -219,7 +232,10 @@
 
   std::vector<std::string> directories;
   std::vector<FileEntry> files { { "file.c", 0, 1000, 2000 } };  // NOLINT
-  WriteDebugLineTable(directories, files, opcodes, &debug_line_data_);
+  std::vector<uintptr_t> debug_line_patches;
+  WriteDebugLineTable(directories, files, opcodes,
+                      &debug_line_data_, &debug_line_patches);
+
   CheckObjdumpOutput(is64bit, "-W -WL");
 }
 
@@ -271,7 +287,12 @@
   DW_CHECK_NEXT("DW_AT_high_pc      DW_FORM_addr");
   DW_CHECK("3      DW_TAG_compile_unit    [has children]");
 
-  dwarf::WriteDebugInfoCU(0 /* debug_abbrev_offset */, info, &debug_info_data_);
+  std::vector<uintptr_t> debug_info_patches;
+  std::vector<uintptr_t> expected_patches { 16, 20, 29, 33, 42, 46 };  // NOLINT
+  dwarf::WriteDebugInfoCU(0 /* debug_abbrev_offset */, info,
+                          &debug_info_data_, &debug_info_patches);
+
+  EXPECT_EQ(expected_patches, debug_info_patches);
   CheckObjdumpOutput(is64bit, "-W");
 }
 
diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h
index d866b91..d17d327 100644
--- a/compiler/dwarf/headers.h
+++ b/compiler/dwarf/headers.h
@@ -17,6 +17,8 @@
 #ifndef ART_COMPILER_DWARF_HEADERS_H_
 #define ART_COMPILER_DWARF_HEADERS_H_
 
+#include <cstdint>
+
 #include "debug_frame_opcode_writer.h"
 #include "debug_info_entry_writer.h"
 #include "debug_line_opcode_writer.h"
@@ -26,6 +28,12 @@
 namespace art {
 namespace dwarf {
 
+// Note that all headers start with 32-bit length.
+// DWARF also supports 64-bit lengths, but we never use that.
+// It is intended to support very large debug sections (>4GB),
+// and compilers are expected *not* to use it by default.
+// In particular, it is not related to machine architecture.
+
 // Write common information entry (CIE) to .eh_frame section.
 template<typename Allocator>
 void WriteEhFrameCIE(bool is64bit, Reg return_address_register,
@@ -33,15 +41,8 @@
                      std::vector<uint8_t>* eh_frame) {
   Writer<> writer(eh_frame);
   size_t cie_header_start_ = writer.data()->size();
-  if (is64bit) {
-    // TODO: This is not related to being 64bit.
-    writer.PushUint32(0xffffffff);
-    writer.PushUint64(0);  // Length placeholder.
-    writer.PushUint64(0);  // CIE id.
-  } else {
-    writer.PushUint32(0);  // Length placeholder.
-    writer.PushUint32(0);  // CIE id.
-  }
+  writer.PushUint32(0);  // Length placeholder.
+  writer.PushUint32(0);  // CIE id.
   writer.PushUint8(1);   // Version.
   writer.PushString("zR");
   writer.PushUleb128(DebugFrameOpCodeWriter<Allocator>::kCodeAlignmentFactor);
@@ -55,11 +56,7 @@
   }
   writer.PushData(opcodes.data());
   writer.Pad(is64bit ? 8 : 4);
-  if (is64bit) {
-    writer.UpdateUint64(cie_header_start_ + 4, writer.data()->size() - cie_header_start_ - 12);
-  } else {
-    writer.UpdateUint32(cie_header_start_, writer.data()->size() - cie_header_start_ - 4);
-  }
+  writer.UpdateUint32(cie_header_start_, writer.data()->size() - cie_header_start_ - 4);
 }
 
 // Write frame description entry (FDE) to .eh_frame section.
@@ -67,20 +64,15 @@
 void WriteEhFrameFDE(bool is64bit, size_t cie_offset,
                      uint64_t initial_address, uint64_t address_range,
                      const std::vector<uint8_t, Allocator>* opcodes,
-                     std::vector<uint8_t>* eh_frame) {
+                     std::vector<uint8_t>* eh_frame,
+                     std::vector<uintptr_t>* eh_frame_patches) {
   Writer<> writer(eh_frame);
   size_t fde_header_start = writer.data()->size();
-  if (is64bit) {
-    // TODO: This is not related to being 64bit.
-    writer.PushUint32(0xffffffff);
-    writer.PushUint64(0);  // Length placeholder.
-    uint64_t cie_pointer = writer.data()->size() - cie_offset;
-    writer.PushUint64(cie_pointer);
-  } else {
-    writer.PushUint32(0);  // Length placeholder.
-    uint32_t cie_pointer = writer.data()->size() - cie_offset;
-    writer.PushUint32(cie_pointer);
-  }
+  writer.PushUint32(0);  // Length placeholder.
+  uint32_t cie_pointer = writer.data()->size() - cie_offset;
+  writer.PushUint32(cie_pointer);
+  // Relocate initial_address, but not address_range (it is size).
+  eh_frame_patches->push_back(writer.data()->size());
   if (is64bit) {
     writer.PushUint64(initial_address);
     writer.PushUint64(address_range);
@@ -91,26 +83,28 @@
   writer.PushUleb128(0);  // Augmentation data size.
   writer.PushData(opcodes);
   writer.Pad(is64bit ? 8 : 4);
-  if (is64bit) {
-    writer.UpdateUint64(fde_header_start + 4, writer.data()->size() - fde_header_start - 12);
-  } else {
-    writer.UpdateUint32(fde_header_start, writer.data()->size() - fde_header_start - 4);
-  }
+  writer.UpdateUint32(fde_header_start, writer.data()->size() - fde_header_start - 4);
 }
 
 // Write compilation unit (CU) to .debug_info section.
 template<typename Allocator>
 void WriteDebugInfoCU(uint32_t debug_abbrev_offset,
                       const DebugInfoEntryWriter<Allocator>& entries,
-                      std::vector<uint8_t>* debug_info) {
+                      std::vector<uint8_t>* debug_info,
+                      std::vector<uintptr_t>* debug_info_patches) {
   Writer<> writer(debug_info);
   size_t start = writer.data()->size();
   writer.PushUint32(0);  // Length placeholder.
   writer.PushUint16(3);  // Version.
   writer.PushUint32(debug_abbrev_offset);
-  writer.PushUint8(entries.is64bit() ? 8 : 4);
+  writer.PushUint8(entries.Is64bit() ? 8 : 4);
+  size_t entries_offset = writer.data()->size();
   writer.PushData(entries.data());
   writer.UpdateUint32(start, writer.data()->size() - start - 4);
+  // Copy patch locations and make them relative to .debug_info section.
+  for (uintptr_t patch_location : entries.GetPatchLocations()) {
+    debug_info_patches->push_back(entries_offset + patch_location);
+  }
 }
 
 struct FileEntry {
@@ -125,7 +119,8 @@
 void WriteDebugLineTable(const std::vector<std::string>& include_directories,
                          const std::vector<FileEntry>& files,
                          const DebugLineOpCodeWriter<Allocator>& opcodes,
-                         std::vector<uint8_t>* debug_line) {
+                         std::vector<uint8_t>* debug_line,
+                         std::vector<uintptr_t>* debug_line_patches) {
   Writer<> writer(debug_line);
   size_t header_start = writer.data()->size();
   writer.PushUint32(0);  // Section-length placeholder.
@@ -157,8 +152,13 @@
   }
   writer.PushUint8(0);  // Terminate file list.
   writer.UpdateUint32(header_length_pos, writer.data()->size() - header_length_pos - 4);
-  writer.PushData(opcodes.data()->data(), opcodes.data()->size());
+  size_t opcodes_offset = writer.data()->size();
+  writer.PushData(opcodes.data());
   writer.UpdateUint32(header_start, writer.data()->size() - header_start - 4);
+  // Copy patch locations and make them relative to .debug_line section.
+  for (uintptr_t patch_location : opcodes.GetPatchLocations()) {
+    debug_line_patches->push_back(opcodes_offset + patch_location);
+  }
 }
 
 }  // namespace dwarf
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index 5e8e24b..6df5ea9 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -159,7 +159,7 @@
  * @param debug_line Line number table.
  */
 void WriteDebugSections(const CompilerDriver* compiler,
-                        const OatWriter* oat_writer,
+                        OatWriter* oat_writer,
                         uint32_t text_section_offset,
                         std::vector<uint8_t>* eh_frame,
                         std::vector<uint8_t>* debug_info,
@@ -176,6 +176,7 @@
   }
 
   // Write .eh_frame section.
+  auto* eh_frame_patches = oat_writer->GetAbsolutePatchLocationsFor(".eh_frame");
   size_t cie_offset = eh_frame->size();
   WriteEhFrameCIE(isa, eh_frame);
   for (const OatWriter::DebugInfo& mi : method_infos) {
@@ -183,7 +184,7 @@
     if (opcodes != nullptr) {
       WriteEhFrameFDE(Is64BitInstructionSet(isa), cie_offset,
                       text_section_offset + mi.low_pc_, mi.high_pc_ - mi.low_pc_,
-                      opcodes, eh_frame);
+                      opcodes, eh_frame, eh_frame_patches);
     }
   }
 
@@ -211,7 +212,8 @@
     info.EndTag();  // DW_TAG_subprogram
   }
   info.EndTag();  // DW_TAG_compile_unit
-  WriteDebugInfoCU(debug_abbrev_offset, info, debug_info);
+  auto* debug_info_patches = oat_writer->GetAbsolutePatchLocationsFor(".debug_info");
+  WriteDebugInfoCU(debug_abbrev_offset, info, debug_info, debug_info_patches);
 
   // TODO: in gdb info functions <regexp> - reports Java functions, but
   // source file is <unknown> because .debug_line is formed as one
@@ -353,7 +355,8 @@
   }
   opcodes.AdvancePC(text_section_offset + cunit_high_pc);
   opcodes.EndSequence();
-  WriteDebugLineTable(directories, files, opcodes, debug_line);
+  auto* debug_line_patches = oat_writer->GetAbsolutePatchLocationsFor(".debug_line");
+  WriteDebugLineTable(directories, files, opcodes, debug_line, debug_line_patches);
 }
 
 }  // namespace dwarf
diff --git a/compiler/elf_writer_debug.h b/compiler/elf_writer_debug.h
index 39a99d6..4f1e333 100644
--- a/compiler/elf_writer_debug.h
+++ b/compiler/elf_writer_debug.h
@@ -25,7 +25,7 @@
 namespace dwarf {
 
 void WriteDebugSections(const CompilerDriver* compiler,
-                        const OatWriter* oat_writer,
+                        OatWriter* oat_writer,
                         uint32_t text_section_offset,
                         std::vector<uint8_t>* eh_frame_data,
                         std::vector<uint8_t>* debug_info_data,
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index e9af25f..da1f81a 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -74,6 +74,40 @@
                                          Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
                               OatWriter* oat_writer);
 
+// Encode patch locations in .oat_patches format.
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+          typename Elf_Phdr, typename Elf_Shdr>
+void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn, Elf_Sym, Elf_Ehdr,
+  Elf_Phdr, Elf_Shdr>::EncodeOatPatches(const OatWriter::PatchLocationsMap& sections,
+                                        std::vector<uint8_t>* buffer) {
+  for (const auto& section : sections) {
+    const std::string& name = section.first;
+    std::vector<uintptr_t>* locations = section.second.get();
+    DCHECK(!name.empty());
+    std::sort(locations->begin(), locations->end());
+    // Reserve buffer space - guess 2 bytes per ULEB128.
+    buffer->reserve(buffer->size() + name.size() + locations->size() * 2);
+    // Write null-terminated section name.
+    const uint8_t* name_data = reinterpret_cast<const uint8_t*>(name.c_str());
+    buffer->insert(buffer->end(), name_data, name_data + name.size() + 1);
+    // Write placeholder for data length.
+    size_t length_pos = buffer->size();
+    EncodeUnsignedLeb128(buffer, UINT32_MAX);
+    // Write LEB128 encoded list of advances (deltas between consequtive addresses).
+    size_t data_pos = buffer->size();
+    uintptr_t address = 0;  // relative to start of section.
+    for (uintptr_t location : *locations) {
+      DCHECK_LT(location - address, UINT32_MAX) << "Large gap between patch locations";
+      EncodeUnsignedLeb128(buffer, location - address);
+      address = location;
+    }
+    // Update length.
+    UpdateUnsignedLeb128(buffer->data() + length_pos, buffer->size() - data_pos);
+  }
+  buffer->push_back(0);  // End of sections.
+}
+
 template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
           typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
           typename Elf_Phdr, typename Elf_Shdr>
@@ -115,16 +149,13 @@
     WriteDebugSymbols(compiler_driver_, builder.get(), oat_writer);
   }
 
-  if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {
+  if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation() ||
+      // ElfWriter::Fixup will be called regardless and it needs to be able
+      // to patch debug sections so we have to include patches for them.
+      compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
     ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> oat_patches(
-        ".oat_patches", SHT_OAT_PATCH, 0, NULL, 0, sizeof(uintptr_t), sizeof(uintptr_t));
-    const std::vector<uintptr_t>& locations = oat_writer->GetAbsolutePatchLocations();
-    const uint8_t* begin = reinterpret_cast<const uint8_t*>(&locations[0]);
-    const uint8_t* end = begin + locations.size() * sizeof(locations[0]);
-    oat_patches.GetBuffer()->assign(begin, end);
-    if (debug) {
-      LOG(INFO) << "Prepared .oat_patches for " << locations.size() << " patches.";
-    }
+        ".oat_patches", SHT_OAT_PATCH, 0, NULL, 0, 1, 0);
+    EncodeOatPatches(oat_writer->GetAbsolutePatchLocations(), oat_patches.GetBuffer());
     builder->RegisterRawSection(oat_patches);
   }
 
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index 4990ed0..811beb4 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -19,6 +19,7 @@
 
 #include "elf_utils.h"
 #include "elf_writer.h"
+#include "oat_writer.h"
 
 namespace art {
 
@@ -36,6 +37,9 @@
                      const CompilerDriver& driver)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  static void EncodeOatPatches(const OatWriter::PatchLocationsMap& sections,
+                               std::vector<uint8_t>* buffer);
+
  protected:
   bool Write(OatWriter* oat_writer,
              const std::vector<const DexFile*>& dex_files,
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 8e2d175..3e5ad7b 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -19,6 +19,9 @@
 #include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 #include "common_compiler_test.h"
+#include "elf_file.h"
+#include "elf_file_impl.h"
+#include "elf_writer_quick.h"
 #include "oat.h"
 #include "utils.h"
 
@@ -85,4 +88,73 @@
   }
 }
 
+// Run only on host since we do unaligned memory accesses.
+#ifndef HAVE_ANDROID_OS
+
+static void PatchSection(const std::vector<uintptr_t>& patch_locations,
+                         std::vector<uint8_t>* section, int32_t delta) {
+  for (uintptr_t location : patch_locations) {
+    *reinterpret_cast<int32_t*>(section->data() + location) += delta;
+  }
+}
+
+TEST_F(ElfWriterTest, EncodeDecodeOatPatches) {
+  std::vector<uint8_t> oat_patches;  // Encoded patches.
+
+  // Encode patch locations for a few sections.
+  OatWriter::PatchLocationsMap sections;
+  std::vector<uintptr_t> patches0 { 0, 4, 8, 15, 128, 200 };  // NOLINT
+  sections.emplace(".section0", std::unique_ptr<std::vector<uintptr_t>>(
+      new std::vector<uintptr_t> { patches0 }));
+  std::vector<uintptr_t> patches1 { 8, 127 };  // NOLINT
+  sections.emplace(".section1", std::unique_ptr<std::vector<uintptr_t>>(
+      new std::vector<uintptr_t> { patches1 }));
+  std::vector<uintptr_t> patches2 { };  // NOLINT
+  sections.emplace(".section2", std::unique_ptr<std::vector<uintptr_t>>(
+      new std::vector<uintptr_t> { patches2 }));
+  ElfWriterQuick32::EncodeOatPatches(sections, &oat_patches);
+
+  // Create buffers to be patched.
+  std::vector<uint8_t> initial_data(256);
+  for (size_t i = 0; i < initial_data.size(); i++) {
+    initial_data[i] = i;
+  }
+  std::vector<uint8_t> section0_expected = initial_data;
+  std::vector<uint8_t> section1_expected = initial_data;
+  std::vector<uint8_t> section2_expected = initial_data;
+  std::vector<uint8_t> section0_actual = initial_data;
+  std::vector<uint8_t> section1_actual = initial_data;
+  std::vector<uint8_t> section2_actual = initial_data;
+
+  // Patch manually.
+  constexpr int32_t delta = 0x11235813;
+  PatchSection(patches0, &section0_expected, delta);
+  PatchSection(patches1, &section1_expected, delta);
+  PatchSection(patches2, &section2_expected, delta);
+
+  // Decode and apply patch locations.
+  bool section0_successful = ElfFileImpl32::ApplyOatPatches(
+      oat_patches.data(), oat_patches.data() + oat_patches.size(),
+      ".section0", delta,
+      section0_actual.data(), section0_actual.data() + section0_actual.size());
+  EXPECT_TRUE(section0_successful);
+  EXPECT_EQ(section0_expected, section0_actual);
+
+  bool section1_successful = ElfFileImpl32::ApplyOatPatches(
+      oat_patches.data(), oat_patches.data() + oat_patches.size(),
+      ".section1", delta,
+      section1_actual.data(), section1_actual.data() + section1_actual.size());
+  EXPECT_TRUE(section1_successful);
+  EXPECT_EQ(section1_expected, section1_actual);
+
+  bool section2_successful = ElfFileImpl32::ApplyOatPatches(
+      oat_patches.data(), oat_patches.data() + oat_patches.size(),
+      ".section2", delta,
+      section2_actual.data(), section2_actual.data() + section2_actual.size());
+  EXPECT_TRUE(section2_successful);
+  EXPECT_EQ(section2_expected, section2_actual);
+}
+
+#endif
+
 }  // namespace art
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 5b4cc54..62c9836 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -350,7 +350,8 @@
  public:
   InitCodeMethodVisitor(OatWriter* writer, size_t offset)
     : OatDexMethodVisitor(writer, offset) {
-    writer_->absolute_patch_locations_.reserve(
+    text_absolute_patch_locations_ = writer->GetAbsolutePatchLocationsFor(".text");
+    text_absolute_patch_locations_->reserve(
         writer_->compiler_driver_->GetNonRelativeLinkerPatchCount());
   }
 
@@ -442,7 +443,7 @@
           uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset();
           for (const LinkerPatch& patch : compiled_method->GetPatches()) {
             if (!patch.IsPcRelative()) {
-              writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
+              text_absolute_patch_locations_->push_back(base_loc + patch.LiteralOffset());
             }
           }
         }
@@ -532,6 +533,9 @@
   // Deduplication is already done on a pointer basis by the compiler driver,
   // so we can simply compare the pointers to find out if things are duplicated.
   SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_;
+
+  // Patch locations for the .text section.
+  std::vector<uintptr_t>* text_absolute_patch_locations_;
 };
 
 template <typename DataAccess>
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 51bc9b4..cc2b39a 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 #include <cstddef>
+#include <map>
 #include <memory>
 
 #include "linker/relative_patcher.h"  // For linker::RelativePatcherTargetProvider.
@@ -81,6 +82,8 @@
 //
 class OatWriter {
  public:
+  typedef std::map<std::string, std::unique_ptr<std::vector<uintptr_t>>> PatchLocationsMap;
+
   OatWriter(const std::vector<const DexFile*>& dex_files,
             uint32_t image_file_location_oat_checksum,
             uintptr_t image_file_location_oat_begin,
@@ -102,10 +105,19 @@
     return bss_size_;
   }
 
-  const std::vector<uintptr_t>& GetAbsolutePatchLocations() const {
+  const PatchLocationsMap& GetAbsolutePatchLocations() const {
     return absolute_patch_locations_;
   }
 
+  std::vector<uintptr_t>* GetAbsolutePatchLocationsFor(const char* section_name) {
+    auto it = absolute_patch_locations_.emplace(
+        std::string(section_name), std::unique_ptr<std::vector<uintptr_t>>());
+    if (it.second) {  // Inserted new item.
+      it.first->second.reset(new std::vector<uintptr_t>());
+    }
+    return it.first->second.get();
+  }
+
   void SetOatDataOffset(size_t oat_data_offset) {
     oat_data_offset_ = oat_data_offset;
   }
@@ -330,8 +342,9 @@
 
   std::unique_ptr<linker::RelativePatcher> relative_patcher_;
 
-  // The locations of absolute patches relative to the start of the executable section.
-  std::vector<uintptr_t> absolute_patch_locations_;
+  // The locations of absolute patches relative to the start of section.
+  // The map's key is the ELF's section name (including the dot).
+  PatchLocationsMap absolute_patch_locations_;
 
   // Map method reference to assigned offset.
   // Wrap the map in a class implementing linker::RelativePatcherTargetProvider.
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 74c9c38..4dc0967 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -625,35 +625,6 @@
   return true;
 }
 
-template <typename ElfFileImpl, typename ptr_t>
-bool PatchOat::CheckOatFile(ElfFileImpl* oat_file) {
-  auto patches_sec = oat_file->FindSectionByName(".oat_patches");
-  if (patches_sec->sh_type != SHT_OAT_PATCH) {
-    return false;
-  }
-  ptr_t* patches = reinterpret_cast<ptr_t*>(oat_file->Begin() + patches_sec->sh_offset);
-  ptr_t* patches_end = patches + (patches_sec->sh_size / sizeof(ptr_t));
-  auto oat_data_sec = oat_file->FindSectionByName(".rodata");
-  auto oat_text_sec = oat_file->FindSectionByName(".text");
-  if (oat_data_sec == nullptr) {
-    return false;
-  }
-  if (oat_text_sec == nullptr) {
-    return false;
-  }
-  if (oat_text_sec->sh_offset <= oat_data_sec->sh_offset) {
-    return false;
-  }
-
-  for (; patches < patches_end; patches++) {
-    if (oat_text_sec->sh_size <= *patches) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
 template <typename ElfFileImpl>
 bool PatchOat::PatchOatHeader(ElfFileImpl* oat_file) {
   auto rodata_sec = oat_file->FindSectionByName(".rodata");
@@ -679,7 +650,7 @@
 template <typename ElfFileImpl>
 bool PatchOat::PatchElf(ElfFileImpl* oat_file) {
   TimingLogger::ScopedTiming t("Fixup Elf Text Section", timings_);
-  if (!PatchTextSection<ElfFileImpl>(oat_file)) {
+  if (!oat_file->ApplyOatPatchesTo(".text", delta_)) {
     return false;
   }
 
@@ -731,51 +702,6 @@
   return true;
 }
 
-template <typename ElfFileImpl>
-bool PatchOat::PatchTextSection(ElfFileImpl* oat_file) {
-  auto patches_sec = oat_file->FindSectionByName(".oat_patches");
-  if (patches_sec == nullptr) {
-    LOG(ERROR) << ".oat_patches section not found. Aborting patch";
-    return false;
-  }
-  if (patches_sec->sh_type != SHT_OAT_PATCH) {
-    LOG(ERROR) << "Unexpected type of .oat_patches";
-    return false;
-  }
-
-  switch (patches_sec->sh_entsize) {
-    case sizeof(uint32_t):
-      return PatchTextSection<ElfFileImpl, uint32_t>(oat_file);
-    case sizeof(uint64_t):
-      return PatchTextSection<ElfFileImpl, uint64_t>(oat_file);
-    default:
-      LOG(ERROR) << ".oat_patches Entsize of " << patches_sec->sh_entsize << "bits "
-                 << "is not valid";
-      return false;
-  }
-}
-
-template <typename ElfFileImpl, typename patch_loc_t>
-bool PatchOat::PatchTextSection(ElfFileImpl* oat_file) {
-  bool oat_file_valid = CheckOatFile<ElfFileImpl, patch_loc_t>(oat_file);
-  CHECK(oat_file_valid) << "Oat file invalid";
-  auto patches_sec = oat_file->FindSectionByName(".oat_patches");
-  patch_loc_t* patches = reinterpret_cast<patch_loc_t*>(oat_file->Begin() + patches_sec->sh_offset);
-  patch_loc_t* patches_end = patches + (patches_sec->sh_size / sizeof(patch_loc_t));
-  auto oat_text_sec = oat_file->FindSectionByName(".text");
-  CHECK(oat_text_sec != nullptr);
-  uint8_t* to_patch = oat_file->Begin() + oat_text_sec->sh_offset;
-  uintptr_t to_patch_end = reinterpret_cast<uintptr_t>(to_patch) + oat_text_sec->sh_size;
-
-  for (; patches < patches_end; patches++) {
-    CHECK_LT(*patches, oat_text_sec->sh_size) << "Bad Patch";
-    uint32_t* patch_loc = reinterpret_cast<uint32_t*>(to_patch + *patches);
-    CHECK_LT(reinterpret_cast<uintptr_t>(patch_loc), to_patch_end);
-    *patch_loc += delta_;
-  }
-  return true;
-}
-
 static int orig_argc;
 static char** orig_argv;
 
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 418650a..86f9118 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -109,11 +109,6 @@
   template <typename ElfFileImpl>
   bool PatchElf(ElfFileImpl* oat_file);
   template <typename ElfFileImpl>
-  bool PatchTextSection(ElfFileImpl* oat_file);
-  // Templatized version to actually do the patching with the right sized offsets.
-  template <typename ElfFileImpl, typename patch_loc_t> bool PatchTextSection(ElfFileImpl* oat_file);
-  template <typename ElfFileImpl, typename patch_loc_t> bool CheckOatFile(ElfFileImpl* oat_filec);
-  template <typename ElfFileImpl>
   bool PatchOatHeader(ElfFileImpl* oat_file);
 
   bool PatchImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 411ec43..f2b013f 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -25,7 +25,6 @@
 #include "base/stringprintf.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
-#include "dwarf.h"
 #include "elf_file_impl.h"
 #include "elf_utils.h"
 #include "leb128.h"
@@ -1587,487 +1586,6 @@
   return nullptr;
 }
 
-struct PACKED(1) FDE32 {
-  uint32_t raw_length_;
-  uint32_t GetLength() {
-    return raw_length_ + sizeof(raw_length_);
-  }
-  uint32_t CIE_pointer;
-  uint32_t initial_location;
-  uint32_t address_range;
-  uint8_t instructions[0];
-};
-
-static FDE32* NextFDE(FDE32* frame) {
-  uint8_t* fde_bytes = reinterpret_cast<uint8_t*>(frame);
-  fde_bytes += frame->GetLength();
-  return reinterpret_cast<FDE32*>(fde_bytes);
-}
-
-static bool IsFDE(FDE32* frame) {
-  return frame->CIE_pointer != 0;
-}
-
-struct PACKED(1) FDE64 {
-  uint32_t raw_length_;
-  uint64_t extended_length_;
-  uint64_t GetLength() {
-    return extended_length_ + sizeof(raw_length_) + sizeof(extended_length_);
-  }
-  uint64_t CIE_pointer;
-  uint64_t initial_location;
-  uint64_t address_range;
-  uint8_t instructions[0];
-};
-
-static FDE64* NextFDE(FDE64* frame) {
-  uint8_t* fde_bytes = reinterpret_cast<uint8_t*>(frame);
-  fde_bytes += frame->GetLength();
-  return reinterpret_cast<FDE64*>(fde_bytes);
-}
-
-static bool IsFDE(FDE64* frame) {
-  return frame->CIE_pointer != 0;
-}
-
-template <typename Elf_SOff>
-static bool FixupEHFrame(Elf_SOff base_address_delta, uint8_t* eh_frame, size_t eh_frame_size) {
-  // TODO: Check the spec whether this is really data-dependent, or whether it's clear from the
-  //       ELF file whether we should expect 32-bit or 64-bit.
-  if (*(reinterpret_cast<uint32_t*>(eh_frame)) == 0xffffffff) {
-    FDE64* last_frame = reinterpret_cast<FDE64*>(eh_frame + eh_frame_size);
-    FDE64* frame = NextFDE(reinterpret_cast<FDE64*>(eh_frame));
-    for (; frame < last_frame; frame = NextFDE(frame)) {
-      if (!IsFDE(frame)) {
-        return false;
-      }
-      frame->initial_location += base_address_delta;
-    }
-    return true;
-  } else {
-    CHECK(IsInt<32>(base_address_delta));
-    FDE32* last_frame = reinterpret_cast<FDE32*>(eh_frame + eh_frame_size);
-    FDE32* frame = NextFDE(reinterpret_cast<FDE32*>(eh_frame));
-    for (; frame < last_frame; frame = NextFDE(frame)) {
-      if (!IsFDE(frame)) {
-        return false;
-      }
-      frame->initial_location += base_address_delta;
-    }
-    return true;
-  }
-}
-
-static uint8_t* NextLeb128(uint8_t* current) {
-  DecodeUnsignedLeb128(const_cast<const uint8_t**>(&current));
-  return current;
-}
-
-struct PACKED(1) DebugLineHeader {
-  uint32_t unit_length_;  // TODO 32-bit specific size
-  uint16_t version_;
-  uint32_t header_length_;  // TODO 32-bit specific size
-  uint8_t minimum_instruction_lenght_;
-  uint8_t default_is_stmt_;
-  int8_t line_base_;
-  uint8_t line_range_;
-  uint8_t opcode_base_;
-  uint8_t remaining_[0];
-
-  bool IsStandardOpcode(const uint8_t* op) const {
-    return *op != 0 && *op < opcode_base_;
-  }
-
-  bool IsExtendedOpcode(const uint8_t* op) const {
-    return *op == 0;
-  }
-
-  const uint8_t* GetStandardOpcodeLengths() const {
-    return remaining_;
-  }
-
-  uint8_t* GetNextOpcode(uint8_t* op) const {
-    if (IsExtendedOpcode(op)) {
-      uint8_t* length_field = op + 1;
-      uint32_t length = DecodeUnsignedLeb128(const_cast<const uint8_t**>(&length_field));
-      return length_field + length;
-    } else if (!IsStandardOpcode(op)) {
-      return op + 1;
-    } else if (*op == dwarf::DW_LNS_fixed_advance_pc) {
-      return op + 1 + sizeof(uint16_t);
-    } else {
-      uint8_t num_args = GetStandardOpcodeLengths()[*op - 1];
-      op += 1;
-      for (int i = 0; i < num_args; i++) {
-        op = NextLeb128(op);
-      }
-      return op;
-    }
-  }
-
-  uint8_t* GetDebugLineData() const {
-    const uint8_t* hdr_start =
-        reinterpret_cast<const uint8_t*>(&header_length_) + sizeof(header_length_);
-    return const_cast<uint8_t*>(hdr_start + header_length_);
-  }
-};
-
-class DebugLineInstructionIterator FINAL {
- public:
-  static DebugLineInstructionIterator* Create(DebugLineHeader* header, size_t section_size) {
-    std::unique_ptr<DebugLineInstructionIterator> line_iter(
-        new DebugLineInstructionIterator(header, section_size));
-    if (line_iter.get() == nullptr) {
-      return nullptr;
-    } else {
-      return line_iter.release();
-    }
-  }
-
-  ~DebugLineInstructionIterator() {}
-
-  bool Next() {
-    if (current_instruction_ == nullptr) {
-      return false;
-    }
-    current_instruction_ = header_->GetNextOpcode(current_instruction_);
-    if (current_instruction_ >= last_instruction_) {
-      current_instruction_ = nullptr;
-      return false;
-    } else {
-      return true;
-    }
-  }
-
-  uint8_t* GetInstruction() const {
-    return current_instruction_;
-  }
-
-  bool IsExtendedOpcode() const {
-    return header_->IsExtendedOpcode(current_instruction_);
-  }
-
-  uint8_t GetOpcode() {
-    if (!IsExtendedOpcode()) {
-      return *current_instruction_;
-    } else {
-      uint8_t* len_ptr = current_instruction_ + 1;
-      return *NextLeb128(len_ptr);
-    }
-  }
-
-  uint8_t* GetArguments() {
-    if (!IsExtendedOpcode()) {
-      return current_instruction_ + 1;
-    } else {
-      uint8_t* len_ptr = current_instruction_ + 1;
-      return NextLeb128(len_ptr) + 1;
-    }
-  }
-
- private:
-  DebugLineInstructionIterator(DebugLineHeader* header, size_t size)
-    : header_(header), last_instruction_(reinterpret_cast<uint8_t*>(header) + size),
-      current_instruction_(header->GetDebugLineData()) {}
-
-  DebugLineHeader* const header_;
-  uint8_t* const last_instruction_;
-  uint8_t* current_instruction_;
-};
-
-template <typename Elf_SOff>
-static bool FixupDebugLine(Elf_SOff base_offset_delta, DebugLineInstructionIterator* iter) {
-  CHECK(IsInt<32>(base_offset_delta));
-  for (; iter->GetInstruction(); iter->Next()) {
-    if (iter->IsExtendedOpcode() && iter->GetOpcode() == dwarf::DW_LNE_set_address) {
-      *reinterpret_cast<uint32_t*>(iter->GetArguments()) += base_offset_delta;
-    }
-  }
-  return true;
-}
-
-struct PACKED(1) DebugInfoHeader {
-  uint32_t unit_length;  // TODO 32-bit specific size
-  uint16_t version;
-  uint32_t debug_abbrev_offset;  // TODO 32-bit specific size
-  uint8_t  address_size;
-};
-
-// Returns -1 if it is variable length, which we will just disallow for now.
-static int32_t FormLength(uint32_t att) {
-  switch (att) {
-    case dwarf::DW_FORM_data1:
-    case dwarf::DW_FORM_flag:
-    case dwarf::DW_FORM_flag_present:
-    case dwarf::DW_FORM_ref1:
-      return 1;
-
-    case dwarf::DW_FORM_data2:
-    case dwarf::DW_FORM_ref2:
-      return 2;
-
-    case dwarf::DW_FORM_addr:        // TODO 32-bit only
-    case dwarf::DW_FORM_ref_addr:    // TODO 32-bit only
-    case dwarf::DW_FORM_sec_offset:  // TODO 32-bit only
-    case dwarf::DW_FORM_strp:        // TODO 32-bit only
-    case dwarf::DW_FORM_data4:
-    case dwarf::DW_FORM_ref4:
-      return 4;
-
-    case dwarf::DW_FORM_data8:
-    case dwarf::DW_FORM_ref8:
-    case dwarf::DW_FORM_ref_sig8:
-      return 8;
-
-    case dwarf::DW_FORM_block:
-    case dwarf::DW_FORM_block1:
-    case dwarf::DW_FORM_block2:
-    case dwarf::DW_FORM_block4:
-    case dwarf::DW_FORM_exprloc:
-    case dwarf::DW_FORM_indirect:
-    case dwarf::DW_FORM_ref_udata:
-    case dwarf::DW_FORM_sdata:
-    case dwarf::DW_FORM_string:
-    case dwarf::DW_FORM_udata:
-    default:
-      return -1;
-  }
-}
-
-class DebugTag FINAL {
- public:
-  ~DebugTag() {}
-  // Creates a new tag and moves data pointer up to the start of the next one.
-  // nullptr means error.
-  static DebugTag* Create(const uint8_t** data_pointer) {
-    const uint8_t* data = *data_pointer;
-    uint32_t index = DecodeUnsignedLeb128(&data);
-    std::unique_ptr<DebugTag> tag(new DebugTag(index));
-    tag->size_ = static_cast<uint32_t>(
-        reinterpret_cast<uintptr_t>(data) - reinterpret_cast<uintptr_t>(*data_pointer));
-    // skip the abbrev
-    tag->tag_ = DecodeUnsignedLeb128(&data);
-    tag->has_child_ = (*data == 0);
-    data++;
-    while (true) {
-      uint32_t attr = DecodeUnsignedLeb128(&data);
-      uint32_t form = DecodeUnsignedLeb128(&data);
-      if (attr == 0 && form == 0) {
-        break;
-      } else if (attr == 0 || form == 0) {
-        // Bad abbrev.
-        return nullptr;
-      }
-      int32_t size = FormLength(form);
-      if (size == -1) {
-        return nullptr;
-      }
-      tag->AddAttribute(attr, static_cast<uint32_t>(size));
-    }
-    *data_pointer = data;
-    return tag.release();
-  }
-
-  uint32_t GetSize() const {
-    return size_;
-  }
-
-  bool HasChild() const {
-    return has_child_;
-  }
-
-  uint32_t GetTagNumber() const {
-    return tag_;
-  }
-
-  uint32_t GetIndex() const {
-    return index_;
-  }
-
-  // Gets the offset of a particular attribute in this tag structure.
-  // Interpretation of the data is left to the consumer. 0 is returned if the
-  // tag does not contain the attribute.
-  uint32_t GetOffsetOf(uint32_t dwarf_attribute) const {
-    auto it = off_map_.find(dwarf_attribute);
-    if (it == off_map_.end()) {
-      return 0;
-    } else {
-      return it->second;
-    }
-  }
-
-  // Gets the size of attribute
-  uint32_t GetAttrSize(uint32_t dwarf_attribute) const {
-    auto it = size_map_.find(dwarf_attribute);
-    if (it == size_map_.end()) {
-      return 0;
-    } else {
-      return it->second;
-    }
-  }
-
- private:
-  explicit DebugTag(uint32_t index) : index_(index), size_(0), tag_(0), has_child_(false) {}
-  void AddAttribute(uint32_t type, uint32_t attr_size) {
-    off_map_.insert(std::pair<uint32_t, uint32_t>(type, size_));
-    size_map_.insert(std::pair<uint32_t, uint32_t>(type, attr_size));
-    size_ += attr_size;
-  }
-
-  const uint32_t index_;
-  std::map<uint32_t, uint32_t> off_map_;
-  std::map<uint32_t, uint32_t> size_map_;
-  uint32_t size_;
-  uint32_t tag_;
-  bool has_child_;
-};
-
-class DebugAbbrev {
- public:
-  ~DebugAbbrev() {}
-  static DebugAbbrev* Create(const uint8_t* dbg_abbrev, size_t dbg_abbrev_size) {
-    std::unique_ptr<DebugAbbrev> abbrev(new DebugAbbrev(dbg_abbrev, dbg_abbrev + dbg_abbrev_size));
-    if (!abbrev->ReadAtOffset(0)) {
-      return nullptr;
-    }
-    return abbrev.release();
-  }
-
-  bool ReadAtOffset(uint32_t abbrev_offset) {
-    tags_.clear();
-    tag_list_.clear();
-    const uint8_t* dbg_abbrev = begin_ + abbrev_offset;
-    while (dbg_abbrev < end_ && *dbg_abbrev != 0) {
-      std::unique_ptr<DebugTag> tag(DebugTag::Create(&dbg_abbrev));
-      if (tag.get() == nullptr) {
-        return false;
-      } else {
-        tags_.insert(std::pair<uint32_t, uint32_t>(tag->GetIndex(), tag_list_.size()));
-        tag_list_.push_back(std::move(tag));
-      }
-    }
-    return true;
-  }
-
-  DebugTag* ReadTag(const uint8_t* entry) {
-    uint32_t tag_num = DecodeUnsignedLeb128(&entry);
-    auto it = tags_.find(tag_num);
-    if (it == tags_.end()) {
-      return nullptr;
-    } else {
-      CHECK_GT(tag_list_.size(), it->second);
-      return tag_list_.at(it->second).get();
-    }
-  }
-
- private:
-  DebugAbbrev(const uint8_t* begin, const uint8_t* end) : begin_(begin), end_(end) {}
-  const uint8_t* const begin_;
-  const uint8_t* const end_;
-  std::map<uint32_t, uint32_t> tags_;
-  std::vector<std::unique_ptr<DebugTag>> tag_list_;
-};
-
-class DebugInfoIterator {
- public:
-  static DebugInfoIterator* Create(DebugInfoHeader* header, size_t frame_size,
-                                   DebugAbbrev* abbrev) {
-    std::unique_ptr<DebugInfoIterator> iter(new DebugInfoIterator(header, frame_size, abbrev));
-    if (iter->GetCurrentTag() == nullptr) {
-      return nullptr;
-    } else {
-      return iter.release();
-    }
-  }
-  ~DebugInfoIterator() {}
-
-  // Moves to the next DIE. Returns false if at last entry.
-  // TODO Handle variable length attributes.
-  bool next() {
-    if (current_entry_ == nullptr || current_tag_ == nullptr) {
-      return false;
-    }
-    bool reread_abbrev = false;
-    current_entry_ += current_tag_->GetSize();
-    if (reinterpret_cast<DebugInfoHeader*>(current_entry_) >= next_cu_) {
-      current_cu_ = next_cu_;
-      next_cu_ = GetNextCu(current_cu_);
-      current_entry_ = reinterpret_cast<uint8_t*>(current_cu_) + sizeof(DebugInfoHeader);
-      reread_abbrev = true;
-    }
-    if (current_entry_ >= last_entry_) {
-      current_entry_ = nullptr;
-      return false;
-    }
-    if (reread_abbrev) {
-      abbrev_->ReadAtOffset(current_cu_->debug_abbrev_offset);
-    }
-    current_tag_ = abbrev_->ReadTag(current_entry_);
-    if (current_tag_ == nullptr) {
-      current_entry_ = nullptr;
-      return false;
-    } else {
-      return true;
-    }
-  }
-
-  const DebugTag* GetCurrentTag() {
-    return const_cast<DebugTag*>(current_tag_);
-  }
-  uint8_t* GetPointerToField(uint8_t dwarf_field) {
-    if (current_tag_ == nullptr || current_entry_ == nullptr || current_entry_ >= last_entry_) {
-      return nullptr;
-    }
-    uint32_t off = current_tag_->GetOffsetOf(dwarf_field);
-    if (off == 0) {
-      // tag does not have that field.
-      return nullptr;
-    } else {
-      DCHECK_LT(off, current_tag_->GetSize());
-      return current_entry_ + off;
-    }
-  }
-
- private:
-  static DebugInfoHeader* GetNextCu(DebugInfoHeader* hdr) {
-    uint8_t* hdr_byte = reinterpret_cast<uint8_t*>(hdr);
-    return reinterpret_cast<DebugInfoHeader*>(hdr_byte + sizeof(uint32_t) + hdr->unit_length);
-  }
-
-  DebugInfoIterator(DebugInfoHeader* header, size_t frame_size, DebugAbbrev* abbrev)
-      : abbrev_(abbrev),
-        current_cu_(header),
-        next_cu_(GetNextCu(header)),
-        last_entry_(reinterpret_cast<uint8_t*>(header) + frame_size),
-        current_entry_(reinterpret_cast<uint8_t*>(header) + sizeof(DebugInfoHeader)),
-        current_tag_(abbrev_->ReadTag(current_entry_)) {}
-  DebugAbbrev* const abbrev_;
-  DebugInfoHeader* current_cu_;
-  DebugInfoHeader* next_cu_;
-  uint8_t* const last_entry_;
-  uint8_t* current_entry_;
-  DebugTag* current_tag_;
-};
-
-template <typename Elf_SOff>
-static bool FixupDebugInfo(Elf_SOff base_address_delta, DebugInfoIterator* iter) {
-  CHECK(IsInt<32>(base_address_delta));
-  do {
-    if (iter->GetCurrentTag()->GetAttrSize(dwarf::DW_AT_low_pc) != sizeof(int32_t) ||
-        iter->GetCurrentTag()->GetAttrSize(dwarf::DW_AT_high_pc) != sizeof(int32_t)) {
-      LOG(ERROR) << "DWARF information with 64 bit pointers is not supported yet.";
-      return false;
-    }
-    uint32_t* PC_low = reinterpret_cast<uint32_t*>(iter->GetPointerToField(dwarf::DW_AT_low_pc));
-    uint32_t* PC_high = reinterpret_cast<uint32_t*>(iter->GetPointerToField(dwarf::DW_AT_high_pc));
-    if (PC_low != nullptr && PC_high != nullptr) {
-      *PC_low  += base_address_delta;
-      *PC_high += base_address_delta;
-    }
-  } while (iter->next());
-  return true;
-}
-
 template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
           typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
           typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
@@ -2076,9 +1594,7 @@
     ::FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta) {
   const Elf_Shdr* debug_info = FindSectionByName(".debug_info");
   const Elf_Shdr* debug_abbrev = FindSectionByName(".debug_abbrev");
-  const Elf_Shdr* eh_frame = FindSectionByName(".eh_frame");
   const Elf_Shdr* debug_str = FindSectionByName(".debug_str");
-  const Elf_Shdr* debug_line = FindSectionByName(".debug_line");
   const Elf_Shdr* strtab_sec = FindSectionByName(".strtab");
   const Elf_Shdr* symtab_sec = FindSectionByName(".symtab");
 
@@ -2090,37 +1606,82 @@
   if (base_address_delta == 0) {
     return true;
   }
-  if (eh_frame != nullptr &&
-      !FixupEHFrame(base_address_delta, Begin() + eh_frame->sh_offset, eh_frame->sh_size)) {
+  if (!ApplyOatPatchesTo(".eh_frame", base_address_delta)) {
     return false;
   }
+  if (!ApplyOatPatchesTo(".debug_info", base_address_delta)) {
+    return false;
+  }
+  if (!ApplyOatPatchesTo(".debug_line", base_address_delta)) {
+    return false;
+  }
+  return true;
+}
 
-  std::unique_ptr<DebugAbbrev> abbrev(DebugAbbrev::Create(Begin() + debug_abbrev->sh_offset,
-                                                          debug_abbrev->sh_size));
-  if (abbrev.get() == nullptr) {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::ApplyOatPatchesTo(const char* target_section_name,
+                        typename std::make_signed<Elf_Off>::type delta) {
+  auto patches_section = FindSectionByName(".oat_patches");
+  if (patches_section == nullptr) {
+    LOG(ERROR) << ".oat_patches section not found.";
     return false;
   }
-  DebugInfoHeader* info_header =
-      reinterpret_cast<DebugInfoHeader*>(Begin() + debug_info->sh_offset);
-  std::unique_ptr<DebugInfoIterator> info_iter(DebugInfoIterator::Create(info_header,
-                                                                         debug_info->sh_size,
-                                                                         abbrev.get()));
-  if (info_iter.get() == nullptr) {
+  if (patches_section->sh_type != SHT_OAT_PATCH) {
+    LOG(ERROR) << "Unexpected type of .oat_patches.";
     return false;
   }
-  if (debug_line != nullptr) {
-    DebugLineHeader* line_header =
-        reinterpret_cast<DebugLineHeader*>(Begin() + debug_line->sh_offset);
-    std::unique_ptr<DebugLineInstructionIterator> line_iter(
-        DebugLineInstructionIterator::Create(line_header, debug_line->sh_size));
-    if (line_iter.get() == nullptr) {
-      return false;
-    }
-    if (!FixupDebugLine(base_address_delta, line_iter.get())) {
-      return false;
-    }
+  auto target_section = FindSectionByName(target_section_name);
+  if (target_section == nullptr) {
+    LOG(ERROR) << target_section_name << " section not found.";
+    return false;
   }
-  return FixupDebugInfo(base_address_delta, info_iter.get());
+  if (!ApplyOatPatches(
+      Begin() + patches_section->sh_offset,
+      Begin() + patches_section->sh_offset + patches_section->sh_size,
+      target_section_name, delta,
+      Begin() + target_section->sh_offset,
+      Begin() + target_section->sh_offset + target_section->sh_size)) {
+    LOG(ERROR) << target_section_name << " section not found in .oat_patches.";
+  }
+  return true;
+}
+
+// Apply .oat_patches to given section.
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+          typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+          typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+    Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+    ::ApplyOatPatches(const uint8_t* patches, const uint8_t* patches_end,
+                      const char* target_section_name,
+                      typename std::make_signed<Elf_Off>::type delta,
+                      uint8_t* to_patch, const uint8_t* to_patch_end) {
+  // Read null-terminated section name.
+  const char* section_name;
+  while ((section_name = reinterpret_cast<const char*>(patches))[0] != '\0') {
+    patches += strlen(section_name) + 1;
+    uint32_t length = DecodeUnsignedLeb128(&patches);
+    const uint8_t* next_section = patches + length;
+    // Is it the section we want to patch?
+    if (strcmp(section_name, target_section_name) == 0) {
+      // Read LEB128 encoded list of advances.
+      while (patches < next_section) {
+        DCHECK_LT(patches, patches_end) << "Unexpected end of .oat_patches.";
+        to_patch += DecodeUnsignedLeb128(&patches);
+        DCHECK_LT(to_patch, to_patch_end) << "Patch past the end of " << section_name;
+        // TODO: 32-bit vs 64-bit.  What is the right type to use here?
+        auto* patch_loc = reinterpret_cast<typename std::make_signed<Elf_Off>::type*>(to_patch);
+        *patch_loc += delta;
+      }
+      return true;
+    }
+    patches = next_section;
+  }
+  return false;
 }
 
 template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h
index 16d3857..383dc41 100644
--- a/runtime/elf_file_impl.h
+++ b/runtime/elf_file_impl.h
@@ -110,6 +110,12 @@
   bool FixupSymbols(Elf_Addr base_address, bool dynamic);
   bool FixupRelocations(Elf_Addr base_address);
   bool FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta);
+  bool ApplyOatPatchesTo(const char* target_section_name,
+                         typename std::make_signed<Elf_Off>::type base_address_delta);
+  static bool ApplyOatPatches(const uint8_t* patches, const uint8_t* patches_end,
+                              const char* target_section_name,
+                              typename std::make_signed<Elf_Off>::type delta,
+                              uint8_t* to_patch, const uint8_t* to_patch_end);
 
   bool Strip(std::string* error_msg);