Add debug info for link-time generated thunks.

Add debug info for method call thunks (currently unused) and
Baker read barrier thunks. Refactor debug info generation
for trampolines and record their sizes; change their names
to start with upper-case letters, so that they can be easily
generated as `#fn_name`.

This improved debug info must be generated by `dex2oat -g`,
the debug info generated by `oatdump --symbolize` remains
the same as before, except for the renamed trampolines and
an adjustment for "code delta", i.e. the Thumb mode bit.

Cortex-A53 erratum 843419 workaround thunks are not covered
by this CL.

Test: Manual; run-test --gdb -Xcompiler-option -g 160, pull
      symbols for gdbclient, break in the introspection
      entrypoint, check that gdb knows the new symbols
      (and disassembles them) and `backtrace` works when
      setting $pc to an address in the thunk.
Bug: 36141117
Change-Id: Id224b72cfa7a0628799c7db65e66e24c8517aabf
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 7fa6e14..16e73b0 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -174,31 +174,6 @@
   }
 }
 
-std::vector<MethodDebugInfo> MakeTrampolineInfos(const OatHeader& header) {
-  std::map<const char*, uint32_t> trampolines = {
-    { "interpreterToInterpreterBridge", header.GetInterpreterToInterpreterBridgeOffset() },
-    { "interpreterToCompiledCodeBridge", header.GetInterpreterToCompiledCodeBridgeOffset() },
-    { "jniDlsymLookup", header.GetJniDlsymLookupOffset() },
-    { "quickGenericJniTrampoline", header.GetQuickGenericJniTrampolineOffset() },
-    { "quickImtConflictTrampoline", header.GetQuickImtConflictTrampolineOffset() },
-    { "quickResolutionTrampoline", header.GetQuickResolutionTrampolineOffset() },
-    { "quickToInterpreterBridge", header.GetQuickToInterpreterBridgeOffset() },
-  };
-  std::vector<MethodDebugInfo> result;
-  for (const auto& it : trampolines) {
-    if (it.second != 0) {
-      MethodDebugInfo info = MethodDebugInfo();
-      info.trampoline_name = it.first;
-      info.isa = header.GetInstructionSet();
-      info.is_code_address_text_relative = true;
-      info.code_address = it.second - header.GetExecutableOffset();
-      info.code_size = 0;  // The symbol lasts until the next symbol.
-      result.push_back(std::move(info));
-    }
-  }
-  return result;
-}
-
 // Explicit instantiations
 template void WriteDebugInfo<ElfTypes32>(
     ElfBuilder<ElfTypes32>* builder,
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index 5d68810..6e26ba3 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -58,8 +58,6 @@
     const ArrayRef<mirror::Class*>& types)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-std::vector<MethodDebugInfo> MakeTrampolineInfos(const OatHeader& oat_header);
-
 }  // namespace debug
 }  // namespace art
 
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
index af9f091..abd2699 100644
--- a/compiler/debug/elf_symtab_writer.h
+++ b/compiler/debug/elf_symtab_writer.h
@@ -65,7 +65,7 @@
       continue;  // Add symbol only for the first instance.
     }
     size_t name_offset;
-    if (info.trampoline_name != nullptr) {
+    if (!info.trampoline_name.empty()) {
       name_offset = strtab->Write(info.trampoline_name);
     } else {
       DCHECK(info.dex_file != nullptr);
diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h
index ed1da2c..5678910 100644
--- a/compiler/debug/method_debug_info.h
+++ b/compiler/debug/method_debug_info.h
@@ -17,6 +17,8 @@
 #ifndef ART_COMPILER_DEBUG_METHOD_DEBUG_INFO_H_
 #define ART_COMPILER_DEBUG_METHOD_DEBUG_INFO_H_
 
+#include <string>
+
 #include "compiled_method.h"
 #include "dex_file.h"
 
@@ -24,7 +26,7 @@
 namespace debug {
 
 struct MethodDebugInfo {
-  const char* trampoline_name;
+  std::string trampoline_name;
   const DexFile* dex_file;  // Native methods (trampolines) do not reference dex file.
   size_t class_def_index;
   uint32_t dex_method_index;
diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc
index 4ca5afe..6e63bf8 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.cc
+++ b/compiler/linker/arm/relative_patcher_arm_base.cc
@@ -18,6 +18,7 @@
 
 #include "base/stl_util.h"
 #include "compiled_method.h"
+#include "debug/method_debug_info.h"
 #include "linker/output_stream.h"
 #include "oat.h"
 #include "oat_quick_method_header.h"
@@ -119,6 +120,25 @@
     return offsets_[pending_offset_ - 1u];
   }
 
+  size_t IndexOfFirstThunkAtOrAfter(uint32_t offset) const {
+    size_t number_of_thunks = NumberOfThunks();
+    for (size_t i = 0; i != number_of_thunks; ++i) {
+      if (GetThunkOffset(i) >= offset) {
+        return i;
+      }
+    }
+    return number_of_thunks;
+  }
+
+  size_t NumberOfThunks() const {
+    return offsets_.size();
+  }
+
+  uint32_t GetThunkOffset(size_t index) const {
+    DCHECK_LT(index, NumberOfThunks());
+    return offsets_[index];
+  }
+
  private:
   std::vector<uint8_t> code_;       // The code of the thunk.
   std::vector<uint32_t> offsets_;   // Offsets at which the thunk needs to be written.
@@ -203,6 +223,48 @@
   return offset;
 }
 
+std::vector<debug::MethodDebugInfo> ArmBaseRelativePatcher::GenerateThunkDebugInfo(
+    uint32_t executable_offset) {
+  // For multi-oat compilation (boot image), `thunks_` records thunks for all oat files.
+  // To return debug info for the current oat file, we must ignore thunks before the
+  // `executable_offset` as they are in the previous oat files and this function must be
+  // called before reserving thunk positions for subsequent oat files.
+  size_t number_of_thunks = 0u;
+  for (auto&& entry : thunks_) {
+    const ThunkData& data = entry.second;
+    number_of_thunks += data.NumberOfThunks() - data.IndexOfFirstThunkAtOrAfter(executable_offset);
+  }
+  std::vector<debug::MethodDebugInfo> result;
+  result.reserve(number_of_thunks);
+  for (auto&& entry : thunks_) {
+    const ThunkKey& key = entry.first;
+    const ThunkData& data = entry.second;
+    size_t start = data.IndexOfFirstThunkAtOrAfter(executable_offset);
+    if (start == data.NumberOfThunks()) {
+      continue;
+    }
+    // Get the base name to use for the first occurrence of the thunk.
+    std::string base_name = GetThunkDebugName(key);
+    for (size_t i = start, num = data.NumberOfThunks(); i != num; ++i) {
+      debug::MethodDebugInfo info = {};
+      if (i == 0u) {
+        info.trampoline_name = base_name;
+      } else {
+        // Add a disambiguating tag for subsequent identical thunks. Since the `thunks_`
+        // keeps records also for thunks in previous oat files, names based on the thunk
+        // index shall be unique across the whole multi-oat output.
+        info.trampoline_name = base_name + "_" + std::to_string(i);
+      }
+      info.isa = instruction_set_;
+      info.is_code_address_text_relative = true;
+      info.code_address = data.GetThunkOffset(i) - executable_offset;
+      info.code_size = data.CodeSize();
+      result.push_back(std::move(info));
+    }
+  }
+  return result;
+}
+
 ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider,
                                                InstructionSet instruction_set)
     : provider_(provider),
diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h
index 5197ce2..4c2be1e 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.h
+++ b/compiler/linker/arm/relative_patcher_arm_base.h
@@ -34,6 +34,7 @@
                         MethodReference method_ref) OVERRIDE;
   uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
   uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
+  std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(uint32_t executable_offset) OVERRIDE;
 
  protected:
   ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider,
@@ -94,6 +95,7 @@
                                            uint32_t target_offset);
 
   virtual std::vector<uint8_t> CompileThunk(const ThunkKey& key) = 0;
+  virtual std::string GetThunkDebugName(const ThunkKey& key) = 0;
   virtual uint32_t MaxPositiveDisplacement(const ThunkKey& key) = 0;
   virtual uint32_t MaxNegativeDisplacement(const ThunkKey& key) = 0;
 
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
index 2ac2a1d..704feeb 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -281,7 +281,7 @@
       Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
       CheckValidReg(base_reg.GetCode());
       DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data));
-      DCHECK(BakerReadBarrierWidth::kWide == BakerReadBarrierWidthField::Decode(encoded_data));
+      DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
       UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
       temps.Exclude(ip);
       vixl::aarch32::Label slow_path;
@@ -379,6 +379,44 @@
   return thunk_code;
 }
 
+std::string Thumb2RelativePatcher::GetThunkDebugName(const ThunkKey& key) {
+  switch (key.GetType()) {
+    case ThunkType::kMethodCall:
+      return "MethodCallThunk";
+
+    case ThunkType::kBakerReadBarrier: {
+      uint32_t encoded_data = key.GetCustomValue1();
+      BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
+      std::ostringstream oss;
+      oss << "BakerReadBarrierThunk";
+      switch (kind) {
+        case BakerReadBarrierKind::kField:
+          oss << "Field";
+          if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
+            oss << "Wide";
+          }
+          oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data)
+              << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data);
+          break;
+        case BakerReadBarrierKind::kArray:
+          oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
+          DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data));
+          DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
+          break;
+        case BakerReadBarrierKind::kGcRoot:
+          oss << "GcRoot";
+          if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
+            oss << "Wide";
+          }
+          oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
+          DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data));
+          break;
+      }
+      return oss.str();
+    }
+  }
+}
+
 #undef __
 
 uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) {
diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h
index 183e5e6..68386c0 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.h
+++ b/compiler/linker/arm/relative_patcher_thumb2.h
@@ -84,6 +84,7 @@
 
  protected:
   std::vector<uint8_t> CompileThunk(const ThunkKey& key) OVERRIDE;
+  std::string GetThunkDebugName(const ThunkKey& key) OVERRIDE;
   uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE;
   uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE;
 
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index 4960f4d..270ba3c 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -535,6 +535,35 @@
   return thunk_code;
 }
 
+std::string Arm64RelativePatcher::GetThunkDebugName(const ThunkKey& key) {
+  switch (key.GetType()) {
+    case ThunkType::kMethodCall:
+      return "MethodCallThunk";
+
+    case ThunkType::kBakerReadBarrier: {
+      uint32_t encoded_data = key.GetCustomValue1();
+      BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
+      std::ostringstream oss;
+      oss << "BakerReadBarrierThunk";
+      switch (kind) {
+        case BakerReadBarrierKind::kField:
+          oss << "Field_r" << BakerReadBarrierFirstRegField::Decode(encoded_data)
+              << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data);
+          break;
+        case BakerReadBarrierKind::kArray:
+          oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
+          DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data));
+          break;
+        case BakerReadBarrierKind::kGcRoot:
+          oss << "GcRoot_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
+          DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data));
+          break;
+      }
+      return oss.str();
+    }
+  }
+}
+
 #undef __
 
 uint32_t Arm64RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) {
diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/compiler/linker/arm64/relative_patcher_arm64.h
index b00dd08..8ba5997 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.h
+++ b/compiler/linker/arm64/relative_patcher_arm64.h
@@ -76,6 +76,7 @@
 
  protected:
   std::vector<uint8_t> CompileThunk(const ThunkKey& key) OVERRIDE;
+  std::string GetThunkDebugName(const ThunkKey& key) OVERRIDE;
   uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE;
   uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE;
 
diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/compiler/linker/mips/relative_patcher_mips.cc
index 6c974c3..408ac22 100644
--- a/compiler/linker/mips/relative_patcher_mips.cc
+++ b/compiler/linker/mips/relative_patcher_mips.cc
@@ -17,6 +17,7 @@
 #include "linker/mips/relative_patcher_mips.h"
 
 #include "compiled_method.h"
+#include "debug/method_debug_info.h"
 
 namespace art {
 namespace linker {
@@ -90,5 +91,10 @@
   LOG(FATAL) << "UNIMPLEMENTED";
 }
 
+std::vector<debug::MethodDebugInfo> MipsRelativePatcher::GenerateThunkDebugInfo(
+    uint32_t executable_offset ATTRIBUTE_UNUSED) {
+  return std::vector<debug::MethodDebugInfo>();  // No thunks added.
+}
+
 }  // namespace linker
 }  // namespace art
diff --git a/compiler/linker/mips/relative_patcher_mips.h b/compiler/linker/mips/relative_patcher_mips.h
index d6eda34..5714a7d 100644
--- a/compiler/linker/mips/relative_patcher_mips.h
+++ b/compiler/linker/mips/relative_patcher_mips.h
@@ -44,6 +44,7 @@
   void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
                                    const LinkerPatch& patch,
                                    uint32_t patch_offset) OVERRIDE;
+  std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(uint32_t executable_offset) OVERRIDE;
 
  private:
   bool is_r6;
diff --git a/compiler/linker/mips64/relative_patcher_mips64.cc b/compiler/linker/mips64/relative_patcher_mips64.cc
index d9f4758..2bcd98a 100644
--- a/compiler/linker/mips64/relative_patcher_mips64.cc
+++ b/compiler/linker/mips64/relative_patcher_mips64.cc
@@ -17,6 +17,7 @@
 #include "linker/mips64/relative_patcher_mips64.h"
 
 #include "compiled_method.h"
+#include "debug/method_debug_info.h"
 
 namespace art {
 namespace linker {
@@ -88,5 +89,10 @@
   LOG(FATAL) << "UNIMPLEMENTED";
 }
 
+std::vector<debug::MethodDebugInfo> Mips64RelativePatcher::GenerateThunkDebugInfo(
+    uint32_t executable_offset ATTRIBUTE_UNUSED) {
+  return std::vector<debug::MethodDebugInfo>();  // No thunks added.
+}
+
 }  // namespace linker
 }  // namespace art
diff --git a/compiler/linker/mips64/relative_patcher_mips64.h b/compiler/linker/mips64/relative_patcher_mips64.h
index f478d7f..6d7c638 100644
--- a/compiler/linker/mips64/relative_patcher_mips64.h
+++ b/compiler/linker/mips64/relative_patcher_mips64.h
@@ -42,6 +42,7 @@
   void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
                                    const LinkerPatch& patch,
                                    uint32_t patch_offset) OVERRIDE;
+  std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(uint32_t executable_offset) OVERRIDE;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Mips64RelativePatcher);
diff --git a/compiler/linker/multi_oat_relative_patcher.h b/compiler/linker/multi_oat_relative_patcher.h
index bdc1ee1..1c5c8a0 100644
--- a/compiler/linker/multi_oat_relative_patcher.h
+++ b/compiler/linker/multi_oat_relative_patcher.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
 
 #include "arch/instruction_set.h"
+#include "debug/method_debug_info.h"
 #include "method_reference.h"
 #include "relative_patcher.h"
 #include "safe_map.h"
@@ -119,6 +120,11 @@
     relative_patcher_->PatchBakerReadBarrierBranch(code, patch, patch_offset);
   }
 
+  std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(size_t executable_offset) {
+    executable_offset += adjustment_;
+    return relative_patcher_->GenerateThunkDebugInfo(executable_offset);
+  }
+
   // Wrappers around RelativePatcher for statistics retrieval.
   uint32_t CodeAlignmentSize() const;
   uint32_t RelativeCallThunksSize() const;
diff --git a/compiler/linker/multi_oat_relative_patcher_test.cc b/compiler/linker/multi_oat_relative_patcher_test.cc
index e967901..f89fba6 100644
--- a/compiler/linker/multi_oat_relative_patcher_test.cc
+++ b/compiler/linker/multi_oat_relative_patcher_test.cc
@@ -17,6 +17,7 @@
 #include "multi_oat_relative_patcher.h"
 
 #include "compiled_method.h"
+#include "debug/method_debug_info.h"
 #include "gtest/gtest.h"
 #include "vector_output_stream.h"
 
@@ -102,6 +103,12 @@
       LOG(FATAL) << "UNIMPLEMENTED";
     }
 
+    std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(
+        uint32_t executable_offset ATTRIBUTE_UNUSED) {
+      LOG(FATAL) << "UNIMPLEMENTED";
+      UNREACHABLE();
+    }
+
     uint32_t last_reserve_offset_ = 0u;
     MethodReference last_reserve_method_ = kNullMethodRef;
     uint32_t next_reserve_adjustment_ = 0u;
diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc
index ee49453..dc15bb0 100644
--- a/compiler/linker/relative_patcher.cc
+++ b/compiler/linker/relative_patcher.cc
@@ -16,6 +16,7 @@
 
 #include "linker/relative_patcher.h"
 
+#include "debug/method_debug_info.h"
 #ifdef ART_ENABLE_CODEGEN_arm
 #include "linker/arm/relative_patcher_thumb2.h"
 #endif
@@ -81,6 +82,11 @@
       LOG(FATAL) << "Unexpected baker read barrier branch patch.";
     }
 
+    std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(
+        uint32_t executable_offset ATTRIBUTE_UNUSED) OVERRIDE {
+      return std::vector<debug::MethodDebugInfo>();  // No thunks added.
+    }
+
    private:
     DISALLOW_COPY_AND_ASSIGN(RelativePatcherNone);
   };
diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h
index 38c8228..53a0966 100644
--- a/compiler/linker/relative_patcher.h
+++ b/compiler/linker/relative_patcher.h
@@ -31,6 +31,10 @@
 class LinkerPatch;
 class OutputStream;
 
+namespace debug {
+struct MethodDebugInfo;
+}  // namespace debug
+
 namespace linker {
 
 /**
@@ -114,6 +118,9 @@
                                            const LinkerPatch& patch,
                                            uint32_t patch_offset) = 0;
 
+  virtual std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(
+      uint32_t executable_offset) = 0;
+
  protected:
   RelativePatcher()
       : size_code_alignment_(0u),
diff --git a/compiler/linker/x86/relative_patcher_x86_base.cc b/compiler/linker/x86/relative_patcher_x86_base.cc
index bf3a648..6a9690d 100644
--- a/compiler/linker/x86/relative_patcher_x86_base.cc
+++ b/compiler/linker/x86/relative_patcher_x86_base.cc
@@ -16,6 +16,8 @@
 
 #include "linker/x86/relative_patcher_x86_base.h"
 
+#include "debug/method_debug_info.h"
+
 namespace art {
 namespace linker {
 
@@ -34,6 +36,11 @@
   return offset;  // No thunks added; no limit on relative call distance.
 }
 
+std::vector<debug::MethodDebugInfo> X86BaseRelativePatcher::GenerateThunkDebugInfo(
+    uint32_t executable_offset ATTRIBUTE_UNUSED) {
+  return std::vector<debug::MethodDebugInfo>();  // No thunks added.
+}
+
 void X86BaseRelativePatcher::PatchCall(std::vector<uint8_t>* code,
                                        uint32_t literal_offset,
                                        uint32_t patch_offset,
diff --git a/compiler/linker/x86/relative_patcher_x86_base.h b/compiler/linker/x86/relative_patcher_x86_base.h
index ca83a72..6097345 100644
--- a/compiler/linker/x86/relative_patcher_x86_base.h
+++ b/compiler/linker/x86/relative_patcher_x86_base.h
@@ -33,6 +33,7 @@
                  uint32_t literal_offset,
                  uint32_t patch_offset,
                  uint32_t target_offset) OVERRIDE;
+  std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(uint32_t executable_offset) OVERRIDE;
 
  protected:
   X86BaseRelativePatcher() { }
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index cc8c6df..7ae3866 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -867,16 +867,19 @@
 class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
  public:
   InitCodeMethodVisitor(OatWriter* writer, size_t offset)
-      : OatDexMethodVisitor(writer, offset),
-        debuggable_(writer->GetCompilerDriver()->GetCompilerOptions().GetDebuggable()) {
-    writer_->absolute_patch_locations_.reserve(
-        writer_->compiler_driver_->GetNonRelativeLinkerPatchCount());
-  }
+      : InitCodeMethodVisitor(writer, offset, writer->GetCompilerDriver()->GetCompilerOptions()) {}
 
   bool EndClass() OVERRIDE {
     OatDexMethodVisitor::EndClass();
     if (oat_class_index_ == writer_->oat_classes_.size()) {
-      offset_ = writer_->relative_patcher_->ReserveSpaceEnd(offset_);
+      offset_ = relative_patcher_->ReserveSpaceEnd(offset_);
+      if (generate_debug_info_) {
+        std::vector<debug::MethodDebugInfo> thunk_infos =
+            relative_patcher_->GenerateThunkDebugInfo(executable_offset_);
+        writer_->method_info_.insert(writer_->method_info_.end(),
+                                     std::make_move_iterator(thunk_infos.begin()),
+                                     std::make_move_iterator(thunk_infos.end()));
+      }
     }
     return true;
   }
@@ -898,7 +901,7 @@
       bool deduped = true;
       MethodReference method_ref(dex_file_, it.GetMemberIndex());
       if (debuggable_) {
-        quick_code_offset = writer_->relative_patcher_->GetOffset(method_ref);
+        quick_code_offset = relative_patcher_->GetOffset(method_ref);
         if (quick_code_offset != 0u) {
           // Duplicate methods, we want the same code for both of them so that the oat writer puts
           // the same code in both ArtMethods so that we do not get different oat code at runtime.
@@ -916,14 +919,14 @@
       }
 
       if (code_size != 0) {
-        if (writer_->relative_patcher_->GetOffset(method_ref) != 0u) {
+        if (relative_patcher_->GetOffset(method_ref) != 0u) {
           // TODO: Should this be a hard failure?
           LOG(WARNING) << "Multiple definitions of "
               << method_ref.dex_file->PrettyMethod(method_ref.dex_method_index)
-              << " offsets " << writer_->relative_patcher_->GetOffset(method_ref)
+              << " offsets " << relative_patcher_->GetOffset(method_ref)
               << " " << quick_code_offset;
         } else {
-          writer_->relative_patcher_->SetOffset(method_ref, quick_code_offset);
+          relative_patcher_->SetOffset(method_ref, quick_code_offset);
         }
       }
 
@@ -977,13 +980,12 @@
         }
       }
 
-      const CompilerOptions& compiler_options = writer_->compiler_driver_->GetCompilerOptions();
       // Exclude quickened dex methods (code_size == 0) since they have no native code.
-      if (compiler_options.GenerateAnyDebugInfo() && code_size != 0) {
+      if (generate_debug_info_ && code_size != 0) {
         bool has_code_info = method_header->IsOptimized();
         // Record debug information for this function if we are doing that.
-        debug::MethodDebugInfo info = debug::MethodDebugInfo();
-        info.trampoline_name = nullptr;
+        debug::MethodDebugInfo info = {};
+        DCHECK(info.trampoline_name.empty());
         info.dex_file = dex_file_;
         info.class_def_index = class_def_index_;
         info.dex_method_index = it.GetMemberIndex();
@@ -991,10 +993,10 @@
         info.code_item = it.GetMethodCodeItem();
         info.isa = compiled_method->GetInstructionSet();
         info.deduped = deduped;
-        info.is_native_debuggable = compiler_options.GetNativeDebuggable();
+        info.is_native_debuggable = native_debuggable_;
         info.is_optimized = method_header->IsOptimized();
         info.is_code_address_text_relative = true;
-        info.code_address = code_offset - writer_->oat_header_->GetExecutableOffset();
+        info.code_address = code_offset - executable_offset_;
         info.code_size = code_size;
         info.frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
         info.code_info = has_code_info ? compiled_method->GetVmapTable().data() : nullptr;
@@ -1012,6 +1014,17 @@
   }
 
  private:
+  InitCodeMethodVisitor(OatWriter* writer, size_t offset, const CompilerOptions& compiler_options)
+      : OatDexMethodVisitor(writer, offset),
+        relative_patcher_(writer->relative_patcher_),
+        executable_offset_(writer->oat_header_->GetExecutableOffset()),
+        debuggable_(compiler_options.GetDebuggable()),
+        native_debuggable_(compiler_options.GetNativeDebuggable()),
+        generate_debug_info_(compiler_options.GenerateAnyDebugInfo()) {
+    writer->absolute_patch_locations_.reserve(
+        writer->GetCompilerDriver()->GetNonRelativeLinkerPatchCount());
+  }
+
   struct CodeOffsetsKeyComparator {
     bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
       // Code is deduplicated by CompilerDriver, compare only data pointers.
@@ -1035,7 +1048,7 @@
   uint32_t NewQuickCodeOffset(CompiledMethod* compiled_method,
                               const ClassDataItemIterator& it,
                               uint32_t thumb_offset) {
-    offset_ = writer_->relative_patcher_->ReserveSpace(
+    offset_ = relative_patcher_->ReserveSpace(
         offset_, compiled_method, MethodReference(dex_file_, it.GetMemberIndex()));
     offset_ += CodeAlignmentSize(offset_, *compiled_method);
     DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader),
@@ -1047,8 +1060,12 @@
   // so we can simply compare the pointers to find out if things are duplicated.
   SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_;
 
-  // Cache of compiler's --debuggable option.
+  // Cache writer_'s members and compiler options.
+  linker::MultiOatRelativePatcher* relative_patcher_;
+  uint32_t executable_offset_;
   const bool debuggable_;
+  const bool native_debuggable_;
+  const bool generate_debug_info_;
 };
 
 class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor {
@@ -1935,19 +1952,33 @@
 size_t OatWriter::InitOatCode(size_t offset) {
   // calculate the offsets within OatHeader to executable code
   size_t old_offset = offset;
-  size_t adjusted_offset = offset;
   // required to be on a new page boundary
   offset = RoundUp(offset, kPageSize);
   oat_header_->SetExecutableOffset(offset);
   size_executable_offset_alignment_ = offset - old_offset;
+  // TODO: Remove unused trampoline offsets from the OatHeader (requires oat version change).
+  oat_header_->SetInterpreterToInterpreterBridgeOffset(0);
+  oat_header_->SetInterpreterToCompiledCodeBridgeOffset(0);
   if (compiler_driver_->GetCompilerOptions().IsBootImage()) {
     InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
+    const bool generate_debug_info = compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo();
+    size_t adjusted_offset = offset;
 
-    #define DO_TRAMPOLINE(field, fn_name) \
-      offset = CompiledCode::AlignCode(offset, instruction_set); \
-      adjusted_offset = offset + CompiledCode::CodeDelta(instruction_set); \
-      oat_header_->Set ## fn_name ## Offset(adjusted_offset); \
-      (field) = compiler_driver_->Create ## fn_name(); \
+    #define DO_TRAMPOLINE(field, fn_name)                                   \
+      offset = CompiledCode::AlignCode(offset, instruction_set);            \
+      adjusted_offset = offset + CompiledCode::CodeDelta(instruction_set);  \
+      oat_header_->Set ## fn_name ## Offset(adjusted_offset);               \
+      (field) = compiler_driver_->Create ## fn_name();                      \
+      if (generate_debug_info) {                                            \
+        debug::MethodDebugInfo info = {};                                   \
+        info.trampoline_name = #fn_name;                                    \
+        info.isa = instruction_set;                                         \
+        info.is_code_address_text_relative = true;                          \
+        /* Use the code offset rather than the `adjusted_offset`. */        \
+        info.code_address = offset - oat_header_->GetExecutableOffset();    \
+        info.code_size = (field)->size();                                   \
+        method_info_.push_back(std::move(info));                            \
+      }                                                                     \
       offset += (field)->size();
 
     DO_TRAMPOLINE(jni_dlsym_lookup_, JniDlsymLookup);
@@ -1958,8 +1989,6 @@
 
     #undef DO_TRAMPOLINE
   } else {
-    oat_header_->SetInterpreterToInterpreterBridgeOffset(0);
-    oat_header_->SetInterpreterToCompiledCodeBridgeOffset(0);
     oat_header_->SetJniDlsymLookupOffset(0);
     oat_header_->SetQuickGenericJniTrampolineOffset(0);
     oat_header_->SetQuickImtConflictTrampolineOffset(0);
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 7f2045f..ef0ce52 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -231,10 +231,6 @@
 
   ~OatWriter();
 
-  void AddMethodDebugInfos(const std::vector<debug::MethodDebugInfo>& infos) {
-    method_info_.insert(method_info_.end(), infos.begin(), infos.end());
-  }
-
   ArrayRef<const debug::MethodDebugInfo> GetMethodDebugInfo() const {
     return ArrayRef<const debug::MethodDebugInfo>(method_info_);
   }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 399cd98..73b0fa2 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -1269,8 +1269,8 @@
   if (compiler_options.GetGenerateDebugInfo()) {
     const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code);
     const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode());
-    debug::MethodDebugInfo info = debug::MethodDebugInfo();
-    info.trampoline_name = nullptr;
+    debug::MethodDebugInfo info = {};
+    DCHECK(info.trampoline_name.empty());
     info.dex_file = dex_file;
     info.class_def_index = class_def_idx;
     info.dex_method_index = method_idx;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 4ecb043..d8caf42 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -2119,8 +2119,6 @@
         std::unique_ptr<ElfWriter>& elf_writer = elf_writers_[i];
         std::unique_ptr<OatWriter>& oat_writer = oat_writers_[i];
 
-        oat_writer->AddMethodDebugInfos(debug::MakeTrampolineInfos(oat_writer->GetOatHeader()));
-
         // We need to mirror the layout of the ELF file in the compressed debug-info.
         // Therefore PrepareDebugInfo() relies on the SetLoadedSectionSizes() call further above.
         elf_writer->PrepareDebugInfo(oat_writer->GetMethodDebugInfo());
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 4161067..13f7211 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -178,10 +178,32 @@
                                     oat_file_->BssRootsOffset());
     builder_->WriteDynamicSection();
 
+    const OatHeader& oat_header = oat_file_->GetOatHeader();
+    #define DO_TRAMPOLINE(fn_name)                                                \
+      if (oat_header.Get ## fn_name ## Offset() != 0) {                           \
+        debug::MethodDebugInfo info = {};                                         \
+        info.trampoline_name = #fn_name;                                          \
+        info.isa = oat_header.GetInstructionSet();                                \
+        info.is_code_address_text_relative = true;                                \
+        size_t code_offset = oat_header.Get ## fn_name ## Offset();               \
+        code_offset -= CompiledCode::CodeDelta(oat_header.GetInstructionSet());   \
+        info.code_address = code_offset - oat_header.GetExecutableOffset();       \
+        info.code_size = 0;  /* The symbol lasts until the next symbol. */        \
+        method_debug_infos_.push_back(std::move(info));                           \
+      }
+    DO_TRAMPOLINE(InterpreterToInterpreterBridge)
+    DO_TRAMPOLINE(InterpreterToCompiledCodeBridge)
+    DO_TRAMPOLINE(JniDlsymLookup);
+    DO_TRAMPOLINE(QuickGenericJniTrampoline);
+    DO_TRAMPOLINE(QuickImtConflictTrampoline);
+    DO_TRAMPOLINE(QuickResolutionTrampoline);
+    DO_TRAMPOLINE(QuickToInterpreterBridge);
+    #undef DO_TRAMPOLINE
+
     Walk();
-    for (const auto& trampoline : debug::MakeTrampolineInfos(oat_file_->GetOatHeader())) {
-      method_debug_infos_.push_back(trampoline);
-    }
+
+    // TODO: Try to symbolize link-time thunks?
+    // This would require disassembling all methods to find branches outside the method code.
 
     debug::WriteDebugInfo(builder_.get(),
                           ArrayRef<const debug::MethodDebugInfo>(method_debug_infos_),
@@ -282,8 +304,8 @@
     // Clear Thumb2 bit.
     const void* code_address = EntryPointToCodePointer(reinterpret_cast<void*>(entry_point));
 
-    debug::MethodDebugInfo info = debug::MethodDebugInfo();
-    info.trampoline_name = nullptr;
+    debug::MethodDebugInfo info = {};
+    DCHECK(info.trampoline_name.empty());
     info.dex_file = &dex_file;
     info.class_def_index = class_def_index;
     info.dex_method_index = dex_method_index;