Tell GDB about Quick ART generated code

This is actually a lot of work.  To do this, we need:
.debug_info
.debug_abbrev
.debug_frame
.debug_str

These are generated into the OAT file by OatWriter and ElfWriterQuick.

Since the Quick ART runtime doesn't use dlopen to load the OAT files,
GDB can't find this information.  Use the alternate GDB JIT interface,
which can be invoked at runtime. To use this interface, an ELF image
needs to be built in memory.  Read the information from the OAT file,
fixup the addresses to point to the real locations, add a symbol table
to hold the .text symbol, and then let GDB know about the information,
which will be read from the runtime address space.

This is quite primitive now, and could be cleaned up considerably.  It
probably needs symbol table entries for the methods, and descriptions of
parameters and return types.

Currently only supported for X86.

This defaults to enabled for debug builds. Added dexoat --gen-gdb-info
and --no-gen-gdb-info flags to override.

Change-Id: I4d18b2370f6dfaa00c8cc1925f10717be3bd1a62
Signed-off-by: Mark Mendell <mark.p.mendell@intel.com>
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 4b416a0..4b823ef 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -98,6 +98,7 @@
   // | .rodata\0               |
   // | .text\0                 |
   // | .shstrtab\0             |
+  // | .debug_frame\0          |
   // +-------------------------+
   // | Elf32_Shdr NULL         |
   // | Elf32_Shdr .dynsym      |
@@ -107,6 +108,9 @@
   // | Elf32_Shdr .rodata      |
   // | Elf32_Shdr .dynamic     |
   // | Elf32_Shdr .shstrtab    |
+  // | Elf32_Shdr .debug_info  |  (Optional)
+  // | Elf32_Shdr .debug_abbrev|  (Optional)
+  // | Elf32_Shdr .debug_frame |  (Optional)
   // +-------------------------+
 
   // phase 1: computing offsets
@@ -259,6 +263,18 @@
   uint32_t shstrtab_shstrtab_offset = shstrtab.size();
   shstrtab += ".shstrtab";
   shstrtab += '\0';
+  uint32_t shstrtab_debug_info_offset = shstrtab.size();
+  shstrtab += ".debug_info";
+  shstrtab += '\0';
+  uint32_t shstrtab_debug_abbrev_offset = shstrtab.size();
+  shstrtab += ".debug_abbrev";
+  shstrtab += '\0';
+  uint32_t shstrtab_debug_str_offset = shstrtab.size();
+  shstrtab += ".debug_str";
+  shstrtab += '\0';
+  uint32_t shstrtab_debug_frame_offset = shstrtab.size();
+  shstrtab += ".debug_frame";
+  shstrtab += '\0';
   uint32_t shstrtab_size = shstrtab.size();
   expected_offset += shstrtab_size;
   if (debug) {
@@ -266,6 +282,52 @@
     LOG(INFO) << "shstrtab_size=" << shstrtab_size << std::hex << " " << shstrtab_size;
   }
 
+  // Create debug informatin, if we have it.
+  bool generateDebugInformation = compiler_driver_->GetCallFrameInformation() != nullptr;
+  std::vector<uint8_t> dbg_info;
+  std::vector<uint8_t> dbg_abbrev;
+  std::vector<uint8_t> dbg_str;
+  if (generateDebugInformation) {
+    FillInCFIInformation(oat_writer, &dbg_info, &dbg_abbrev, &dbg_str);
+  }
+
+  uint32_t shdbg_info_alignment = 1;
+  uint32_t shdbg_info_offset = expected_offset;
+  uint32_t shdbg_info_size = dbg_info.size();
+  expected_offset += shdbg_info_size;
+  if (debug) {
+    LOG(INFO) << "shdbg_info_offset=" << shdbg_info_offset << std::hex << " " << shdbg_info_offset;
+    LOG(INFO) << "shdbg_info_size=" << shdbg_info_size << std::hex << " " << shdbg_info_size;
+  }
+
+  uint32_t shdbg_abbrev_alignment = 1;
+  uint32_t shdbg_abbrev_offset = expected_offset;
+  uint32_t shdbg_abbrev_size = dbg_abbrev.size();
+  expected_offset += shdbg_abbrev_size;
+  if (debug) {
+    LOG(INFO) << "shdbg_abbrev_offset=" << shdbg_abbrev_offset << std::hex << " " << shdbg_abbrev_offset;
+    LOG(INFO) << "shdbg_abbrev_size=" << shdbg_abbrev_size << std::hex << " " << shdbg_abbrev_size;
+  }
+
+  uint32_t shdbg_frm_alignment = 4;
+  uint32_t shdbg_frm_offset = expected_offset = RoundUp(expected_offset, shdbg_frm_alignment);
+  uint32_t shdbg_frm_size =
+    generateDebugInformation ? compiler_driver_->GetCallFrameInformation()->size() : 0;
+  expected_offset += shdbg_frm_size;
+  if (debug) {
+    LOG(INFO) << "shdbg_frm_offset=" << shdbg_frm_offset << std::hex << " " << shdbg_frm_offset;
+    LOG(INFO) << "shdbg_frm_size=" << shdbg_frm_size << std::hex << " " << shdbg_frm_size;
+  }
+
+  uint32_t shdbg_str_alignment = 1;
+  uint32_t shdbg_str_offset = expected_offset;
+  uint32_t shdbg_str_size = dbg_str.size();
+  expected_offset += shdbg_str_size;
+  if (debug) {
+    LOG(INFO) << "shdbg_str_offset=" << shdbg_str_offset << std::hex << " " << shdbg_str_offset;
+    LOG(INFO) << "shdbg_str_size=" << shdbg_str_size << std::hex << " " << shdbg_str_size;
+  }
+
   // section headers (after all sections)
   uint32_t shdr_alignment = sizeof(Elf32_Word);
   uint32_t shdr_offset = expected_offset = RoundUp(expected_offset, shdr_alignment);
@@ -277,7 +339,11 @@
   const uint8_t SH_TEXT     = 5;
   const uint8_t SH_DYNAMIC  = 6;
   const uint8_t SH_SHSTRTAB = 7;
-  const uint8_t SH_NUM      = 8;
+  const uint8_t SH_DBG_INFO = 8;
+  const uint8_t SH_DBG_ABRV = 9;
+  const uint8_t SH_DBG_FRM  = 10;
+  const uint8_t SH_DBG_STR  = 11;
+  const uint8_t SH_NUM      = generateDebugInformation ? 12 : 8;
   uint32_t shdr_size = sizeof(Elf32_Shdr) * SH_NUM;
   expected_offset += shdr_size;
   if (debug) {
@@ -554,6 +620,52 @@
   section_headers[SH_SHSTRTAB].sh_addralign = shstrtab_alignment;
   section_headers[SH_SHSTRTAB].sh_entsize   = 0;
 
+  if (generateDebugInformation) {
+    section_headers[SH_DBG_INFO].sh_name      = shstrtab_debug_info_offset;
+    section_headers[SH_DBG_INFO].sh_type      = SHT_PROGBITS;
+    section_headers[SH_DBG_INFO].sh_flags     = 0;
+    section_headers[SH_DBG_INFO].sh_addr      = 0;
+    section_headers[SH_DBG_INFO].sh_offset    = shdbg_info_offset;
+    section_headers[SH_DBG_INFO].sh_size      = shdbg_info_size;
+    section_headers[SH_DBG_INFO].sh_link      = 0;
+    section_headers[SH_DBG_INFO].sh_info      = 0;
+    section_headers[SH_DBG_INFO].sh_addralign = shdbg_info_alignment;
+    section_headers[SH_DBG_INFO].sh_entsize   = 0;
+
+    section_headers[SH_DBG_ABRV].sh_name      = shstrtab_debug_abbrev_offset;
+    section_headers[SH_DBG_ABRV].sh_type      = SHT_PROGBITS;
+    section_headers[SH_DBG_ABRV].sh_flags     = 0;
+    section_headers[SH_DBG_ABRV].sh_addr      = 0;
+    section_headers[SH_DBG_ABRV].sh_offset    = shdbg_abbrev_offset;
+    section_headers[SH_DBG_ABRV].sh_size      = shdbg_abbrev_size;
+    section_headers[SH_DBG_ABRV].sh_link      = 0;
+    section_headers[SH_DBG_ABRV].sh_info      = 0;
+    section_headers[SH_DBG_ABRV].sh_addralign = shdbg_abbrev_alignment;
+    section_headers[SH_DBG_ABRV].sh_entsize   = 0;
+
+    section_headers[SH_DBG_FRM].sh_name      = shstrtab_debug_frame_offset;
+    section_headers[SH_DBG_FRM].sh_type      = SHT_PROGBITS;
+    section_headers[SH_DBG_FRM].sh_flags     = 0;
+    section_headers[SH_DBG_FRM].sh_addr      = 0;
+    section_headers[SH_DBG_FRM].sh_offset    = shdbg_frm_offset;
+    section_headers[SH_DBG_FRM].sh_size      = shdbg_frm_size;
+    section_headers[SH_DBG_FRM].sh_link      = 0;
+    section_headers[SH_DBG_FRM].sh_info      = 0;
+    section_headers[SH_DBG_FRM].sh_addralign = shdbg_frm_alignment;
+    section_headers[SH_DBG_FRM].sh_entsize   = 0;
+
+    section_headers[SH_DBG_STR].sh_name      = shstrtab_debug_str_offset;
+    section_headers[SH_DBG_STR].sh_type      = SHT_PROGBITS;
+    section_headers[SH_DBG_STR].sh_flags     = 0;
+    section_headers[SH_DBG_STR].sh_addr      = 0;
+    section_headers[SH_DBG_STR].sh_offset    = shdbg_str_offset;
+    section_headers[SH_DBG_STR].sh_size      = shdbg_str_size;
+    section_headers[SH_DBG_STR].sh_link      = 0;
+    section_headers[SH_DBG_STR].sh_info      = 0;
+    section_headers[SH_DBG_STR].sh_addralign = shdbg_str_alignment;
+    section_headers[SH_DBG_STR].sh_entsize   = 0;
+  }
+
   // phase 3: writing file
 
   // Elf32_Ehdr
@@ -646,8 +758,62 @@
     return false;
   }
 
+  if (generateDebugInformation) {
+    // .debug_info
+    DCHECK_LE(shstrtab_offset + shstrtab_size, shdbg_info_offset);
+    if (static_cast<off_t>(shdbg_info_offset) != lseek(elf_file_->Fd(), shdbg_info_offset, SEEK_SET)) {
+      PLOG(ERROR) << "Failed to seek to .shdbg_info offset " << shdbg_info_offset
+                  << " for " << elf_file_->GetPath();
+      return false;
+    }
+    if (!elf_file_->WriteFully(&dbg_info[0], shdbg_info_size)) {
+      PLOG(ERROR) << "Failed to write .debug_info for " << elf_file_->GetPath();
+      return false;
+    }
+
+    // .debug_abbrev
+    DCHECK_LE(shdbg_info_offset + shdbg_info_size, shdbg_abbrev_offset);
+    if (static_cast<off_t>(shdbg_abbrev_offset) != lseek(elf_file_->Fd(), shdbg_abbrev_offset, SEEK_SET)) {
+      PLOG(ERROR) << "Failed to seek to .shdbg_abbrev offset " << shdbg_abbrev_offset
+                  << " for " << elf_file_->GetPath();
+      return false;
+    }
+    if (!elf_file_->WriteFully(&dbg_abbrev[0], shdbg_abbrev_size)) {
+      PLOG(ERROR) << "Failed to write .debug_abbrev for " << elf_file_->GetPath();
+      return false;
+    }
+
+    // .debug_frame
+    DCHECK_LE(shdbg_abbrev_offset + shdbg_abbrev_size, shdbg_frm_offset);
+    if (static_cast<off_t>(shdbg_frm_offset) != lseek(elf_file_->Fd(), shdbg_frm_offset, SEEK_SET)) {
+      PLOG(ERROR) << "Failed to seek to .shdbg_frm offset " << shdbg_frm_offset
+                  << " for " << elf_file_->GetPath();
+      return false;
+    }
+    if (!elf_file_->WriteFully(&((*compiler_driver_->GetCallFrameInformation())[0]), shdbg_frm_size)) {
+      PLOG(ERROR) << "Failed to write .debug_frame for " << elf_file_->GetPath();
+      return false;
+    }
+
+    // .debug_str
+    DCHECK_LE(shdbg_frm_offset + shdbg_frm_size, shdbg_str_offset);
+    if (static_cast<off_t>(shdbg_str_offset) != lseek(elf_file_->Fd(), shdbg_str_offset, SEEK_SET)) {
+      PLOG(ERROR) << "Failed to seek to .shdbg_str offset " << shdbg_str_offset
+                  << " for " << elf_file_->GetPath();
+      return false;
+    }
+    if (!elf_file_->WriteFully(&dbg_str[0], shdbg_str_size)) {
+      PLOG(ERROR) << "Failed to write .debug_frame for " << elf_file_->GetPath();
+      return false;
+    }
+  }
+
   // section headers (after all sections)
-  DCHECK_LE(shstrtab_offset + shstrtab_size, shdr_offset);
+  if (generateDebugInformation) {
+    DCHECK_LE(shdbg_str_offset + shdbg_str_size, shdr_offset);
+  } else {
+    DCHECK_LE(shstrtab_offset + shstrtab_size, shdr_offset);
+  }
   if (static_cast<off_t>(shdr_offset) != lseek(elf_file_->Fd(), shdr_offset, SEEK_SET)) {
     PLOG(ERROR) << "Failed to seek to ELF section headers offset " << shdr_offset
                 << " for " << elf_file_->GetPath();
@@ -660,6 +826,164 @@
 
   VLOG(compiler) << "ELF file written successfully: " << elf_file_->GetPath();
   return true;
+}  // NOLINT(readability/fn_size)
+
+static void UpdateWord(std::vector<uint8_t>*buf, int offset, int data) {
+  (*buf)[offset+0] = data;
+  (*buf)[offset+1] = data >> 8;
+  (*buf)[offset+2] = data >> 16;
+  (*buf)[offset+3] = data >> 24;
+}
+
+static void PushWord(std::vector<uint8_t>*buf, int data) {
+  buf->push_back(data & 0xff);
+  buf->push_back((data >> 8) & 0xff);
+  buf->push_back((data >> 16) & 0xff);
+  buf->push_back((data >> 24) & 0xff);
+}
+
+static void PushHalf(std::vector<uint8_t>*buf, int data) {
+  buf->push_back(data & 0xff);
+  buf->push_back((data >> 8) & 0xff);
+}
+
+// DWARF constants needed to generate CFI information.
+enum {
+  // Tag encodings.
+  DW_TAG_compile_unit = 0x11,
+  DW_TAG_subprogram = 0X2e,
+
+  // Attribute encodings.
+  DW_AT_name = 0x03,
+  DW_AT_low_pc = 0x11,
+  DW_AT_high_pc = 0x12,
+  DW_AT_language = 0x13,
+
+  // Constant encoding.
+  DW_CHILDREN_no = 0x00,
+  DW_CHILDREN_yes = 0x01,
+
+  // Attribute form encodings.
+  DW_FORM_addr = 0x01,
+  DW_FORM_data1 = 0x0b,
+  DW_FORM_strp = 0x0e,
+
+  // Language encoding.
+  DW_LANG_Java = 0x000b
+};
+
+void ElfWriterQuick::FillInCFIInformation(OatWriter* oat_writer,
+                                          std::vector<uint8_t>* dbg_info,
+                                          std::vector<uint8_t>* dbg_abbrev,
+                                          std::vector<uint8_t>* dbg_str) {
+  // Create the debug_abbrev section with boilerplate information.
+  // We only care about low_pc and high_pc right now for the compilation
+  // unit and methods.
+
+  // Tag 1: Compilation unit: DW_TAG_compile_unit.
+  dbg_abbrev->push_back(1);
+  dbg_abbrev->push_back(DW_TAG_compile_unit);
+
+  // There are children (the methods).
+  dbg_abbrev->push_back(DW_CHILDREN_yes);
+
+  // DW_LANG_Java DW_FORM_data1.
+  dbg_abbrev->push_back(DW_AT_language);
+  dbg_abbrev->push_back(DW_FORM_data1);
+
+  // DW_AT_low_pc DW_FORM_addr.
+  dbg_abbrev->push_back(DW_AT_low_pc);
+  dbg_abbrev->push_back(DW_FORM_addr);
+
+  // DW_AT_high_pc DW_FORM_addr.
+  dbg_abbrev->push_back(DW_AT_high_pc);
+  dbg_abbrev->push_back(DW_FORM_addr);
+
+  // End of DW_TAG_compile_unit.
+  PushHalf(dbg_abbrev, 0);
+
+  // Tag 2: Compilation unit: DW_TAG_subprogram.
+  dbg_abbrev->push_back(2);
+  dbg_abbrev->push_back(DW_TAG_subprogram);
+
+  // There are no children.
+  dbg_abbrev->push_back(DW_CHILDREN_no);
+
+  // Name of the method.
+  dbg_abbrev->push_back(DW_AT_name);
+  dbg_abbrev->push_back(DW_FORM_strp);
+
+  // DW_AT_low_pc DW_FORM_addr.
+  dbg_abbrev->push_back(DW_AT_low_pc);
+  dbg_abbrev->push_back(DW_FORM_addr);
+
+  // DW_AT_high_pc DW_FORM_addr.
+  dbg_abbrev->push_back(DW_AT_high_pc);
+  dbg_abbrev->push_back(DW_FORM_addr);
+
+  // End of DW_TAG_subprogram.
+  PushHalf(dbg_abbrev, 0);
+
+  // Start the debug_info section with the header information
+  // 'unit_length' will be filled in later.
+  PushWord(dbg_info, 0);
+
+  // 'version' - 3.
+  PushHalf(dbg_info, 3);
+
+  // Offset into .debug_abbrev section (always 0).
+  PushWord(dbg_info, 0);
+
+  // Address size: 4.
+  dbg_info->push_back(4);
+
+  // Start the description for the compilation unit.
+  // This uses tag 1.
+  dbg_info->push_back(1);
+
+  // The language is Java.
+  dbg_info->push_back(DW_LANG_Java);
+
+  // Leave space for low_pc and high_pc.
+  int low_pc_offset = dbg_info->size();
+  PushWord(dbg_info, 0);
+  PushWord(dbg_info, 0);
+
+  // Walk through the information in the method table, and enter into dbg_info.
+  const std::vector<OatWriter::DebugInfo>& dbg = oat_writer->GetCFIMethodInfo();
+  uint32_t low_pc = 0xFFFFFFFFU;
+  uint32_t high_pc = 0;
+
+  for (uint32_t i = 0; i < dbg.size(); i++) {
+    const OatWriter::DebugInfo& info = dbg[i];
+    if (info.low_pc_ < low_pc) {
+      low_pc = info.low_pc_;
+    }
+    if (info.high_pc_ > high_pc) {
+      high_pc = info.high_pc_;
+    }
+
+    // Start a new TAG: subroutine (2).
+    dbg_info->push_back(2);
+
+    // Enter the name into the string table (and NUL terminate).
+    uint32_t str_offset = dbg_str->size();
+    dbg_str->insert(dbg_str->end(), info.method_name_.begin(), info.method_name_.end());
+    dbg_str->push_back('\0');
+
+    // Enter name, low_pc, high_pc.
+    PushWord(dbg_info, str_offset);
+    PushWord(dbg_info, info.low_pc_);
+    PushWord(dbg_info, info.high_pc_);
+  }
+
+  // One byte terminator
+  dbg_info->push_back(0);
+
+  // We have now walked all the methods.  Fill in lengths and low/high PCs.
+  UpdateWord(dbg_info, 0, dbg_info->size() - 4);
+  UpdateWord(dbg_info, low_pc_offset, low_pc);
+  UpdateWord(dbg_info, low_pc_offset + 4, high_pc);
 }
 
 }  // namespace art