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/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