Refactor ElfDebugReader.

Make the code more flexible, which I will need for
future mini-debug-info work.

Bug: 110133331
Test: ./art/test.py -b -r -t 137
Change-Id: I8b0fe3c43537f546f2ff103bff3c63a59a0f940a
diff --git a/compiler/debug/dwarf/headers.h b/compiler/debug/dwarf/headers.h
index 869a2c2..3cc8ad8 100644
--- a/compiler/debug/dwarf/headers.h
+++ b/compiler/debug/dwarf/headers.h
@@ -90,30 +90,6 @@
   writer.UpdateUint32(fde_header_start, writer.data()->size() - fde_header_start - 4);
 }
 
-// Read singe FDE entry from 'data' (which is advanced).
-template<typename Addr>
-bool ReadFDE(const uint8_t** data, Addr* addr, Addr* size, ArrayRef<const uint8_t>* opcodes) {
-  struct Header {
-    uint32_t length;
-    int32_t cie_pointer;
-    Addr addr;
-    Addr size;
-    uint8_t augmentaion;
-    uint8_t opcodes[];
-  } PACKED(1);
-  const Header* header = reinterpret_cast<const Header*>(*data);
-  const size_t length = 4 + header->length;
-  *data += length;
-  if (header->cie_pointer == -1) {
-    return false;  // Not an FDE entry.
-  }
-  DCHECK_EQ(header->cie_pointer, 0);  // Expects single CIE. Assumes DW_DEBUG_FRAME_FORMAT.
-  *addr = header->addr;
-  *size = header->size;
-  *opcodes = ArrayRef<const uint8_t>(header->opcodes, length - offsetof(Header, opcodes));
-  return true;
-}
-
 // Write compilation unit (CU) to .debug_info section.
 template<typename Vector>
 void WriteDebugInfoCU(uint32_t debug_abbrev_offset,
diff --git a/compiler/debug/elf_debug_reader.h b/compiler/debug/elf_debug_reader.h
index 91b1b3e..1820e7c 100644
--- a/compiler/debug/elf_debug_reader.h
+++ b/compiler/debug/elf_debug_reader.h
@@ -22,6 +22,9 @@
 #include "elf.h"
 #include "xz_utils.h"
 
+#include <map>
+#include <string_view>
+
 namespace art {
 namespace debug {
 
@@ -29,75 +32,141 @@
 //
 // It is the bare minimum needed to read mini-debug-info symbols for unwinding.
 // We use it to merge JIT mini-debug-infos together or to prune them after GC.
-// The consumed ELF file comes from ART JIT.
-template <typename ElfTypes, typename VisitSym, typename VisitFde>
-static void ReadElfSymbols(const uint8_t* elf, VisitSym visit_sym, VisitFde visit_fde) {
+template <typename ElfTypes>
+class ElfDebugReader {
+ public:
   // Note that the input buffer might be misaligned.
   typedef typename ElfTypes::Ehdr ALIGNED(1) Elf_Ehdr;
   typedef typename ElfTypes::Shdr ALIGNED(1) Elf_Shdr;
   typedef typename ElfTypes::Sym ALIGNED(1) Elf_Sym;
   typedef typename ElfTypes::Addr ALIGNED(1) Elf_Addr;
 
-  // Read and check the elf header.
-  const Elf_Ehdr* header = reinterpret_cast<const Elf_Ehdr*>(elf);
-  CHECK(header->checkMagic());
+  // Call Frame Information.
+  struct CFI {
+    uint32_t length;  // Length excluding the size of this field.
+    int32_t cie_pointer;  // Offset in the section or -1 for CIE.
 
-  // Find sections that we are interested in.
-  const Elf_Shdr* sections = reinterpret_cast<const Elf_Shdr*>(elf + header->e_shoff);
-  const Elf_Shdr* strtab = nullptr;
-  const Elf_Shdr* symtab = nullptr;
-  const Elf_Shdr* debug_frame = nullptr;
-  const Elf_Shdr* gnu_debugdata = nullptr;
-  for (size_t i = 1 /* skip null section */; i < header->e_shnum; i++) {
-    const Elf_Shdr* section = sections + i;
-    const char* name = reinterpret_cast<const char*>(
-        elf + sections[header->e_shstrndx].sh_offset + section->sh_name);
-    if (strcmp(name, ".strtab") == 0) {
-      strtab = section;
-    } else if (strcmp(name, ".symtab") == 0) {
-      symtab = section;
-    } else if (strcmp(name, ".debug_frame") == 0) {
-      debug_frame = section;
-    } else if (strcmp(name, ".gnu_debugdata") == 0) {
-      gnu_debugdata = section;
+    const uint8_t* data() const { return reinterpret_cast<const uint8_t*>(this); }
+    size_t size() const { return sizeof(uint32_t) + length; }
+  } PACKED(1);
+
+  // Common Information Entry.
+  struct CIE : public CFI {
+  } PACKED(1);
+
+  // Frame Description Entry.
+  struct FDE : public CFI {
+    Elf_Addr sym_addr;
+    Elf_Addr sym_size;
+  } PACKED(1);
+
+  explicit ElfDebugReader(ArrayRef<const uint8_t> file) : file_(file) {
+    header_ = Read<Elf_Ehdr>(/*offset=*/ 0);
+    CHECK(header_->checkMagic());
+    CHECK_EQ(header_->e_ehsize, sizeof(Elf_Ehdr));
+    CHECK_EQ(header_->e_shentsize, sizeof(Elf_Shdr));
+
+    // Find all ELF sections.
+    sections_ = Read<Elf_Shdr>(header_->e_shoff, header_->e_shnum);
+    for (const Elf_Shdr& section : sections_) {
+      const char* name = Read<char>(sections_[header_->e_shstrndx].sh_offset + section.sh_name);
+      section_map_[std::string_view(name)] = &section;
+    }
+
+    // Decompressed embedded debug symbols, if any.
+    const Elf_Shdr* gnu_debugdata = section_map_[".gnu_debugdata"];
+    if (gnu_debugdata != nullptr) {
+      auto compressed = Read<uint8_t>(gnu_debugdata->sh_offset, gnu_debugdata->sh_size);
+      XzDecompress(compressed, &decompressed_gnu_debugdata_);
+      gnu_debugdata_reader_.reset(new ElfDebugReader(decompressed_gnu_debugdata_));
     }
   }
 
-  // Visit symbols.
-  if (symtab != nullptr && strtab != nullptr) {
-    const Elf_Sym* symbols = reinterpret_cast<const Elf_Sym*>(elf + symtab->sh_offset);
-    DCHECK_EQ(symtab->sh_entsize, sizeof(Elf_Sym));
-    size_t count = symtab->sh_size / sizeof(Elf_Sym);
-    for (size_t i = 1 /* skip null symbol */; i < count; i++) {
-      Elf_Sym symbol = symbols[i];
-      if (symbol.getBinding() != STB_LOCAL) {  // Ignore local symbols (e.g. "$t").
-        const uint8_t* name = elf + strtab->sh_offset + symbol.st_name;
-        visit_sym(symbol, reinterpret_cast<const char*>(name));
+  explicit ElfDebugReader(std::vector<uint8_t>& file)
+      : ElfDebugReader(ArrayRef<const uint8_t>(file)) {
+  }
+
+  const Elf_Ehdr* GetHeader() { return header_; }
+
+  ArrayRef<Elf_Shdr> GetSections() { return sections_; }
+
+  const Elf_Shdr* GetSection(const char* name) { return section_map_[name]; }
+
+  template <typename VisitSym>
+  void VisitFunctionSymbols(VisitSym visit_sym) {
+    const Elf_Shdr* symtab = GetSection(".symtab");
+    const Elf_Shdr* strtab = GetSection(".strtab");
+    const Elf_Shdr* text = GetSection(".text");
+    if (symtab != nullptr && strtab != nullptr) {
+      CHECK_EQ(symtab->sh_entsize, sizeof(Elf_Sym));
+      size_t count = symtab->sh_size / sizeof(Elf_Sym);
+      for (const Elf_Sym& symbol : Read<Elf_Sym>(symtab->sh_offset, count)) {
+        if (symbol.getType() == STT_FUNC && &sections_[symbol.st_shndx] == text) {
+          visit_sym(symbol, Read<char>(strtab->sh_offset + symbol.st_name));
+        }
+      }
+    }
+    if (gnu_debugdata_reader_ != nullptr) {
+      gnu_debugdata_reader_->VisitFunctionSymbols(visit_sym);
+    }
+  }
+
+  template <typename VisitSym>
+  void VisitDynamicSymbols(VisitSym visit_sym) {
+    const Elf_Shdr* dynsym = GetSection(".dynsym");
+    const Elf_Shdr* dynstr = GetSection(".dynstr");
+    if (dynsym != nullptr && dynstr != nullptr) {
+      CHECK_EQ(dynsym->sh_entsize, sizeof(Elf_Sym));
+      size_t count = dynsym->sh_size / sizeof(Elf_Sym);
+      for (const Elf_Sym& symbol : Read<Elf_Sym>(dynsym->sh_offset, count)) {
+        visit_sym(symbol, Read<char>(dynstr->sh_offset + symbol.st_name));
       }
     }
   }
 
-  // Visit CFI (unwind) data.
-  if (debug_frame != nullptr) {
-    const uint8_t* data = elf + debug_frame->sh_offset;
-    const uint8_t* end = data + debug_frame->sh_size;
-    while (data < end) {
-      Elf_Addr addr, size;
-      ArrayRef<const uint8_t> opcodes;
-      if (dwarf::ReadFDE<Elf_Addr>(&data, &addr, &size, &opcodes)) {
-        visit_fde(addr, size, opcodes);
+  template <typename VisitCIE, typename VisitFDE>
+  void VisitDebugFrame(VisitCIE visit_cie, VisitFDE visit_fde) {
+    const Elf_Shdr* debug_frame = GetSection(".debug_frame");
+    if (debug_frame != nullptr) {
+      for (size_t offset = 0; offset < debug_frame->sh_size;) {
+        const CFI* entry = Read<CFI>(debug_frame->sh_offset + offset);
+        DCHECK_LE(entry->size(), debug_frame->sh_size - offset);
+        if (entry->cie_pointer == -1) {
+          visit_cie(Read<CIE>(debug_frame->sh_offset + offset));
+        } else {
+          const FDE* fde = Read<FDE>(debug_frame->sh_offset + offset);
+          visit_fde(fde, Read<CIE>(debug_frame->sh_offset + fde->cie_pointer));
+        }
+        offset += entry->size();
       }
     }
+    if (gnu_debugdata_reader_ != nullptr) {
+      gnu_debugdata_reader_->VisitDebugFrame(visit_cie, visit_fde);
+    }
   }
 
-  // Process embedded compressed ELF file.
-  if (gnu_debugdata != nullptr) {
-    ArrayRef<const uint8_t> compressed(elf + gnu_debugdata->sh_offset, gnu_debugdata->sh_size);
-    std::vector<uint8_t> decompressed;
-    XzDecompress(compressed, &decompressed);
-    ReadElfSymbols<ElfTypes>(decompressed.data(), visit_sym, visit_fde);
+ private:
+  template<typename T>
+  const T* Read(size_t offset) {
+    DCHECK_LE(offset + sizeof(T), file_.size());
+    return reinterpret_cast<const T*>(file_.data() + offset);
   }
-}
+
+  template<typename T>
+  ArrayRef<const T> Read(size_t offset, size_t count) {
+    DCHECK_LE(offset + count * sizeof(T), file_.size());
+    return ArrayRef<const T>(Read<T>(offset), count);
+  }
+
+  ArrayRef<const uint8_t> const file_;
+  const Elf_Ehdr* header_;
+  ArrayRef<const Elf_Shdr> sections_;
+  std::unordered_map<std::string_view, const Elf_Shdr*> section_map_;
+  std::vector<uint8_t> decompressed_gnu_debugdata_;
+  std::unique_ptr<ElfDebugReader> gnu_debugdata_reader_;
+
+  DISALLOW_COPY_AND_ASSIGN(ElfDebugReader);
+};
 
 }  // namespace debug
 }  // namespace art
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index fd638b7..3b7363b 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -203,25 +203,26 @@
   // Verify the ELF file by reading it back using the trivial reader.
   if (kIsDebugBuild) {
     using Elf_Sym = typename ElfTypes::Sym;
-    using Elf_Addr = typename ElfTypes::Addr;
     size_t num_syms = 0;
-    size_t num_cfis = 0;
-    ReadElfSymbols<ElfTypes>(
-        buffer.data(),
-        [&](Elf_Sym sym, const char*) {
-          DCHECK_EQ(sym.st_value, method_info.code_address + CompiledMethod::CodeDelta(isa));
-          DCHECK_EQ(sym.st_size, method_info.code_size);
-          num_syms++;
-        },
-        [&](Elf_Addr addr, Elf_Addr size, ArrayRef<const uint8_t> opcodes) {
-          DCHECK_EQ(addr, method_info.code_address);
-          DCHECK_EQ(size, method_info.code_size);
-          DCHECK_GE(opcodes.size(), method_info.cfi.size());
-          DCHECK_EQ(memcmp(opcodes.data(), method_info.cfi.data(), method_info.cfi.size()), 0);
-          num_cfis++;
-        });
+    size_t num_cies = 0;
+    size_t num_fdes = 0;
+    using Reader = ElfDebugReader<ElfTypes>;
+    Reader reader(buffer);
+    reader.VisitFunctionSymbols([&](Elf_Sym sym, const char*) {
+      DCHECK_EQ(sym.st_value, method_info.code_address + CompiledMethod::CodeDelta(isa));
+      DCHECK_EQ(sym.st_size, method_info.code_size);
+      num_syms++;
+    });
+    reader.VisitDebugFrame([&](const Reader::CIE* cie ATTRIBUTE_UNUSED) {
+      num_cies++;
+    }, [&](const Reader::FDE* fde, const Reader::CIE* cie ATTRIBUTE_UNUSED) {
+      DCHECK_EQ(fde->sym_addr, method_info.code_address);
+      DCHECK_EQ(fde->sym_size, method_info.code_size);
+      num_fdes++;
+    });
     DCHECK_EQ(num_syms, 1u);
-    DCHECK_EQ(num_cfis, 1u);
+    DCHECK_LE(num_cies, 1u);
+    DCHECK_LE(num_fdes, 1u);
   }
   return buffer;
 }
@@ -230,14 +231,13 @@
 std::vector<uint8_t> PackElfFileForJIT(
     InstructionSet isa,
     const InstructionSetFeatures* features,
-    std::vector<const uint8_t*>& added_elf_files,
+    std::vector<ArrayRef<const uint8_t>>& added_elf_files,
     std::vector<const void*>& removed_symbols,
     /*out*/ size_t* num_symbols) {
   using ElfTypes = ElfRuntimeTypes;
   using Elf_Addr = typename ElfTypes::Addr;
   using Elf_Sym = typename ElfTypes::Sym;
   CHECK_EQ(sizeof(Elf_Addr), static_cast<size_t>(GetInstructionSetPointerSize(isa)));
-  const bool is64bit = Is64BitInstructionSet(isa);
   auto is_removed_symbol = [&removed_symbols](Elf_Addr addr) {
     const void* code_ptr = reinterpret_cast<const void*>(addr);
     return std::binary_search(removed_symbols.begin(), removed_symbols.end(), code_ptr);
@@ -259,35 +259,26 @@
     auto* symtab = builder->GetSymTab();
     auto* debug_frame = builder->GetDebugFrame();
     std::deque<Elf_Sym> symbols;
-    std::vector<uint8_t> debug_frame_buffer;
-    WriteCIE(isa, &debug_frame_buffer);
+
+    using Reader = ElfDebugReader<ElfTypes>;
+    std::deque<Reader> readers;
+    for (ArrayRef<const uint8_t> added_elf_file : added_elf_files) {
+      readers.emplace_back(added_elf_file);
+    }
 
     // Write symbols names. All other data is buffered.
     strtab->Start();
     strtab->Write("");  // strtab should start with empty string.
-    for (const uint8_t* added_elf_file : added_elf_files) {
-      ReadElfSymbols<ElfTypes>(
-          added_elf_file,
-          [&](Elf_Sym sym, const char* name) {
-              if (is_removed_symbol(sym.st_value)) {
-                return;
-              }
-              sym.st_name = strtab->Write(name);
-              symbols.push_back(sym);
-              min_address = std::min<uint64_t>(min_address, sym.st_value);
-              max_address = std::max<uint64_t>(max_address, sym.st_value + sym.st_size);
-          },
-          [&](Elf_Addr addr, Elf_Addr size, ArrayRef<const uint8_t> opcodes) {
-              if (is_removed_symbol(addr)) {
-                return;
-              }
-              dwarf::WriteFDE(is64bit,
-                              /* cie_pointer= */ 0,
-                              addr,
-                              size,
-                              opcodes,
-                              &debug_frame_buffer);
-          });
+    for (Reader& reader : readers) {
+      reader.VisitFunctionSymbols([&](Elf_Sym sym, const char* name) {
+          if (is_removed_symbol(sym.st_value)) {
+            return;
+          }
+          sym.st_name = strtab->Write(name);
+          symbols.push_back(sym);
+          min_address = std::min<uint64_t>(min_address, sym.st_value);
+          max_address = std::max<uint64_t>(max_address, sym.st_value + sym.st_size);
+      });
     }
     strtab->End();
 
@@ -305,7 +296,22 @@
 
     // Add the CFI/unwind section.
     debug_frame->Start();
-    debug_frame->WriteFully(debug_frame_buffer.data(), debug_frame_buffer.size());
+    // ART always produces the same CIE, so we copy the first one and ignore the rest.
+    bool copied_cie = false;
+    for (Reader& reader : readers) {
+      reader.VisitDebugFrame([&](const Reader::CIE* cie) {
+        if (!copied_cie) {
+          debug_frame->WriteFully(cie->data(), cie->size());
+          copied_cie = true;
+        }
+      }, [&](const Reader::FDE* fde, const Reader::CIE* cie ATTRIBUTE_UNUSED) {
+        DCHECK(copied_cie);
+        DCHECK_EQ(fde->cie_pointer, 0);
+        if (!is_removed_symbol(fde->sym_addr)) {
+          debug_frame->WriteFully(fde->data(), fde->size());
+        }
+      });
+    }
     debug_frame->End();
 
     builder->End();
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index 3cc38a2..90580b4 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -57,7 +57,7 @@
 std::vector<uint8_t> PackElfFileForJIT(
     InstructionSet isa,
     const InstructionSetFeatures* features,
-    std::vector<const uint8_t*>& added_elf_files,
+    std::vector<ArrayRef<const uint8_t>>& added_elf_files,
     std::vector<const void*>& removed_symbols,
     /*out*/ size_t* num_symbols);
 
diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc
index 99f9387..a69429f 100644
--- a/runtime/jit/debugger_interface.cc
+++ b/runtime/jit/debugger_interface.cc
@@ -301,7 +301,7 @@
     return;  // Nothing to do.
   }
 
-  std::vector<const uint8_t*> added_elf_files;
+  std::vector<ArrayRef<const uint8_t>> added_elf_files;
   std::vector<const void*> removed_symbols;
   auto added_it = g_jit_debug_entries.begin();
   auto removed_it = removed_entries.begin();
@@ -312,7 +312,8 @@
     auto added_begin = added_it;
     while (added_it != g_jit_debug_entries.end() &&
            AlignDown(added_it->first, kGroupSize) == group_ptr) {
-      added_elf_files.push_back((added_it++)->second->symfile_addr_);
+      JITCodeEntry* entry = (added_it++)->second;
+      added_elf_files.emplace_back(entry->symfile_addr_, entry->symfile_size_);
     }
     removed_symbols.clear();
     while (removed_it != removed_entries.end() &&
diff --git a/runtime/jit/debugger_interface.h b/runtime/jit/debugger_interface.h
index 17beb4b..51b7041 100644
--- a/runtime/jit/debugger_interface.h
+++ b/runtime/jit/debugger_interface.h
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include "arch/instruction_set_features.h"
+#include "base/array_ref.h"
 #include "base/locks.h"
 
 namespace art {
@@ -33,7 +34,7 @@
 typedef std::vector<uint8_t> PackElfFileForJITFunction(
     InstructionSet isa,
     const InstructionSetFeatures* features,
-    std::vector<const uint8_t*>& added_elf_files,
+    std::vector<ArrayRef<const uint8_t>>& added_elf_files,
     std::vector<const void*>& removed_symbols,
     /*out*/ size_t* num_symbols);