[DWARF parser] Add basic support for DWZ DWARF multifile extensions.
authorAlexey Samsonov <vonosmas@gmail.com>
Tue, 19 May 2015 20:29:28 +0000 (20:29 +0000)
committerAlexey Samsonov <vonosmas@gmail.com>
Tue, 19 May 2015 20:29:28 +0000 (20:29 +0000)
This change implements basic support for DWARF alternate sections
proposal: http://www.dwarfstd.org/ShowIssue.php?issue=120604.1&type=open

LLVM tools now understand new forms: DW_FORM_GNU_ref_alt and
DW_FORM_GNU_strp_alt, which are used as references to .debug_info and
.debug_str sections respectively, stored in a separate file, and
possibly shared between different executables / shared objects.

llvm-dwarfdump and llvm-symbolizer don't yet know how to access this
alternate debug file (usually pointed by .gnu_debugaltlink section),
but they can at lease properly parse and dump regular files, which
refer to it.

This change should fix crashes of llvm-dwarfdump and llvm-symbolizer on
files produced by running "dwz" tool. Such files are already installed
on some modern Linux distributions.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@237721 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/DebugInfo/DWARF/DWARFFormValue.h
include/llvm/Support/Dwarf.h
lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp
lib/DebugInfo/DWARF/DWARFFormValue.cpp
lib/Support/Dwarf.cpp
test/DebugInfo/Inputs/dwarfdump-test-dwz.elf-x86-64 [new file with mode: 0755]
test/DebugInfo/Inputs/dwarfdump-test.cc
test/DebugInfo/Inputs/dwarfdump-test.elf-x86-64.dwz [new file with mode: 0644]
test/DebugInfo/dwarfdump-dwz.test [new file with mode: 0644]

index 02f468189d2614cba7937841fcdcd25918873bc8..7ddcc0d81d594c8b4fb558ba093ce25b405b1704 100644 (file)
@@ -87,6 +87,8 @@ public:
 
   static ArrayRef<uint8_t> getFixedFormSizes(uint8_t AddrSize,
                                              uint16_t Version);
+private:
+  void dumpString(raw_ostream &OS, const DWARFUnit *U) const;
 };
 
 }
index c294a72a04b2492d7c36d87b7e465a5822e4dce7..c3d94d19f919118aca9dc37a7fa8dd918762d8a5 100644 (file)
@@ -285,7 +285,11 @@ enum Form : uint16_t {
 
   // Extensions for Fission proposal
   DW_FORM_GNU_addr_index = 0x1f01,
-  DW_FORM_GNU_str_index = 0x1f02
+  DW_FORM_GNU_str_index = 0x1f02,
+
+  // Alternate debug sections proposal (output of "dwz" tool).
+  DW_FORM_GNU_ref_alt = 0x1f20,
+  DW_FORM_GNU_strp_alt = 0x1f21
 };
 
 enum LocationAtom {
index e963b7ca6e68caf4fe1bb34dd9186ffb461ff54d..5abbde4ac0fe19e4bd226c785244fc73c9a911a6 100644 (file)
@@ -161,14 +161,15 @@ void DWARFDebugInfoEntryMinimal::dumpAttribute(raw_ostream &OS,
   // We have dumped the attribute raw value. For some attributes
   // having both the raw value and the pretty-printed value is
   // interesting. These attributes are handled below.
-  if ((attr == DW_AT_specification || attr == DW_AT_abstract_origin) &&
-      // The signature references aren't handled.
-      formValue.getForm() != DW_FORM_ref_sig8) {
-    uint32_t Ref = formValue.getAsReference(u).getValue();
-    DWARFDebugInfoEntryMinimal DIE;
-    if (const DWARFUnit *RefU = findUnitAndExtractFast(DIE, u, &Ref))
-      if (const char *Ref = DIE.getName(RefU, DINameKind::LinkageName))
-        OS << " \"" << Ref << '\"';
+  if (attr == DW_AT_specification || attr == DW_AT_abstract_origin) {
+    Optional<uint64_t> Ref = formValue.getAsReference(u);
+    if (Ref.hasValue()) {
+      uint32_t RefOffset = Ref.getValue();
+      DWARFDebugInfoEntryMinimal DIE;
+      if (const DWARFUnit *RefU = findUnitAndExtractFast(DIE, u, &RefOffset))
+        if (const char *Name = DIE.getName(RefU, DINameKind::LinkageName))
+          OS << " \"" << Name << '\"';
+    }
   } else if (attr == DW_AT_APPLE_property_attribute) {
     if (Optional<uint64_t> OptVal = formValue.getAsUnsignedConstant())
       dumpApplePropertyAttribute(OS, *OptVal);
index 6946f8334240031f68ffec47233d12b24f933606..75ca7622139db256437adf2d2d8f505d8856e376 100644 (file)
@@ -113,14 +113,17 @@ bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const {
   if (Form < ArrayRef<FormClass>(DWARF4FormClasses).size() &&
       DWARF4FormClasses[Form] == FC)
     return true;
-  // Check DW_FORM_ref_sig8 from DWARF4.
-  if (Form == DW_FORM_ref_sig8)
+  // Check more forms from DWARF4 and DWARF5 proposals.
+  switch (Form) {
+  case DW_FORM_ref_sig8:
+  case DW_FORM_GNU_ref_alt:
     return (FC == FC_Reference);
-  // Check for some DWARF5 forms.
-  if (Form == DW_FORM_GNU_addr_index)
+  case DW_FORM_GNU_addr_index:
     return (FC == FC_Address);
-  if (Form == DW_FORM_GNU_str_index)
+  case DW_FORM_GNU_str_index:
+  case DW_FORM_GNU_strp_alt:
     return (FC == FC_String);
+  }
   // In DWARF3 DW_FORM_data4 and DW_FORM_data8 served also as a section offset.
   // Don't check for DWARF version here, as some producers may still do this
   // by mistake.
@@ -199,15 +202,6 @@ bool DWARFFormValue::extractValue(DataExtractor data, uint32_t *offset_ptr,
     case DW_FORM_sdata:
       Value.sval = data.getSLEB128(offset_ptr);
       break;
-    case DW_FORM_strp: {
-      Value.uval = data.getU32(offset_ptr);
-      if (!cu)
-        break;
-      RelocAddrMap::const_iterator AI = cu->getRelocMap()->find(*offset_ptr-4);
-      if (AI != cu->getRelocMap()->end())
-        Value.uval += AI->second.second;
-      break;
-    }
     case DW_FORM_udata:
     case DW_FORM_ref_udata:
       Value.uval = data.getULEB128(offset_ptr);
@@ -219,14 +213,18 @@ bool DWARFFormValue::extractValue(DataExtractor data, uint32_t *offset_ptr,
       Form = data.getULEB128(offset_ptr);
       indirect = true;
       break;
-    case DW_FORM_sec_offset: {
+    case DW_FORM_sec_offset:
+    case DW_FORM_strp:
+    case DW_FORM_GNU_ref_alt:
+    case DW_FORM_GNU_strp_alt: {
       // FIXME: This is 64-bit for DWARF64.
       Value.uval = data.getU32(offset_ptr);
       if (!cu)
         break;
-      RelocAddrMap::const_iterator AI = cu->getRelocMap()->find(*offset_ptr-4);
+      RelocAddrMap::const_iterator AI =
+          cu->getRelocMap()->find(*offset_ptr - 4);
       if (AI != cu->getRelocMap()->end())
-        Value.uval +=  AI->second.second;
+        Value.uval += AI->second.second;
       break;
     }
     case DW_FORM_flag_present:
@@ -323,7 +321,6 @@ DWARFFormValue::skipValue(uint16_t form, DataExtractor debug_info_data,
       return true;
 
     // 4 byte values
-    case DW_FORM_strp:
     case DW_FORM_data4:
     case DW_FORM_ref4:
       *offset_ptr += 4;
@@ -353,6 +350,9 @@ DWARFFormValue::skipValue(uint16_t form, DataExtractor debug_info_data,
 
     // FIXME: 4 for DWARF32, 8 for DWARF64.
     case DW_FORM_sec_offset:
+    case DW_FORM_strp:
+    case DW_FORM_GNU_ref_alt:
+    case DW_FORM_GNU_strp_alt:
       *offset_ptr += 4;
       return true;
 
@@ -424,24 +424,17 @@ DWARFFormValue::dump(raw_ostream &OS, const DWARFUnit *cu) const {
   case DW_FORM_udata:     OS << Value.uval; break;
   case DW_FORM_strp: {
     OS << format(" .debug_str[0x%8.8x] = ", (uint32_t)uvalue);
-    Optional<const char *> DbgStr = getAsCString(cu);
-    if (DbgStr.hasValue()) {
-      raw_ostream &COS = WithColor(OS, syntax::String);
-      COS << '"';
-      COS.write_escaped(DbgStr.getValue());
-      COS << '"';
-    }
+    dumpString(OS, cu);
     break;
   }
   case DW_FORM_GNU_str_index: {
     OS << format(" indexed (%8.8x) string = ", (uint32_t)uvalue);
-    Optional<const char *> DbgStr = getAsCString(cu);
-    if (DbgStr.hasValue()) {
-      raw_ostream &COS = WithColor(OS, syntax::String);
-      COS << '"';
-      COS.write_escaped(DbgStr.getValue());
-      COS << '"';
-    }
+    dumpString(OS, cu);
+    break;
+  }
+  case DW_FORM_GNU_strp_alt: {
+    OS << format("alt indirect string, offset: 0x%" PRIx64 "", uvalue);
+    dumpString(OS, cu);
     break;
   }
   case DW_FORM_ref_addr:
@@ -467,6 +460,9 @@ DWARFFormValue::dump(raw_ostream &OS, const DWARFUnit *cu) const {
     cu_relative_offset = true;
     OS << format("cu + 0x%" PRIx64, uvalue);
     break;
+  case DW_FORM_GNU_ref_alt:
+    OS << format("<alt 0x%" PRIx64 ">", uvalue);
+    break;
 
     // All DW_FORM_indirect attributes should be resolved prior to calling
     // this function
@@ -492,12 +488,23 @@ DWARFFormValue::dump(raw_ostream &OS, const DWARFUnit *cu) const {
   }
 }
 
+void DWARFFormValue::dumpString(raw_ostream &OS, const DWARFUnit *U) const {
+  Optional<const char *> DbgStr = getAsCString(U);
+  if (DbgStr.hasValue()) {
+    raw_ostream &COS = WithColor(OS, syntax::String);
+    COS << '"';
+    COS.write_escaped(DbgStr.getValue());
+    COS << '"';
+  }
+}
+
 Optional<const char *> DWARFFormValue::getAsCString(const DWARFUnit *U) const {
   if (!isFormClass(FC_String))
     return None;
   if (Form == DW_FORM_string)
     return Value.cstr;
-  if (!U)
+  // FIXME: Add support for DW_FORM_GNU_strp_alt
+  if (Form == DW_FORM_GNU_strp_alt || U == nullptr)
     return None;
   uint32_t Offset = Value.uval;
   if (Form == DW_FORM_GNU_str_index) {
@@ -539,9 +546,9 @@ Optional<uint64_t> DWARFFormValue::getAsReference(const DWARFUnit *U) const {
     return Value.uval + U->getOffset();
   case DW_FORM_ref_addr:
     return Value.uval;
-  // FIXME: Add proper support for DW_FORM_ref_sig8
+  // FIXME: Add proper support for DW_FORM_ref_sig8 and DW_FORM_GNU_ref_alt.
   default:
-    return Value.uval;
+    return None;
   }
 }
 
index 95c4bc32701f29dfd14819fc984f862bf1e349ec..6229825a8ee29a039583fb2d58f1d84d0030d2e3 100644 (file)
@@ -233,6 +233,10 @@ const char *llvm::dwarf::FormEncodingString(unsigned Encoding) {
     // DWARF5 Fission Extension Forms
   case DW_FORM_GNU_addr_index:           return "DW_FORM_GNU_addr_index";
   case DW_FORM_GNU_str_index:            return "DW_FORM_GNU_str_index";
+
+  // Alternate debug sections proposal (output of "dwz" tool).
+  case DW_FORM_GNU_ref_alt:              return "DW_FORM_GNU_ref_alt";
+  case DW_FORM_GNU_strp_alt:             return "DW_FORM_GNU_strp_alt";
   }
   return nullptr;
 }
diff --git a/test/DebugInfo/Inputs/dwarfdump-test-dwz.elf-x86-64 b/test/DebugInfo/Inputs/dwarfdump-test-dwz.elf-x86-64
new file mode 100755 (executable)
index 0000000..937961b
Binary files /dev/null and b/test/DebugInfo/Inputs/dwarfdump-test-dwz.elf-x86-64 differ
index 4089998649071bd77ca3f90a415d4827e9312b21..14295d3cffa0a15ad41273732d5e3b86b6514ec5 100644 (file)
@@ -21,3 +21,9 @@ int main() {
 // $ cp dwarfdump-test.cc /tmp/dbginfo
 // $ cd /tmp/dbginfo
 // $ clang++ -g dwarfdump-test.cc -o <output>
+
+// The result is also used as an input to .dwz tool:
+// $ cp <output> output1.dwz
+// $ cp <output> output2.dwz
+// $ dwz -m output.dwz -r output1.dwz output2.dwz
+// $ rm output2.dwz
diff --git a/test/DebugInfo/Inputs/dwarfdump-test.elf-x86-64.dwz b/test/DebugInfo/Inputs/dwarfdump-test.elf-x86-64.dwz
new file mode 100644 (file)
index 0000000..32db372
Binary files /dev/null and b/test/DebugInfo/Inputs/dwarfdump-test.elf-x86-64.dwz differ
diff --git a/test/DebugInfo/dwarfdump-dwz.test b/test/DebugInfo/dwarfdump-dwz.test
new file mode 100644 (file)
index 0000000..e5f67eb
--- /dev/null
@@ -0,0 +1,14 @@
+; RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test-dwz.elf-x86-64 -debug-dump=info | FileCheck %s -check-prefix DUMP_INFO
+
+; DUMP_INFO: .debug_info
+; DUMP_INFO: DW_TAG_compile_unit [2] *
+; DUMP_INFO-NEXT: DW_AT_producer [DW_FORM_GNU_strp_alt]     (alt indirect string, offset: 0x0)
+; DUMP_INFO-NEXT: DW_AT_language [DW_FORM_data2]    (DW_LANG_C_plus_plus)
+; DUMP_INFO-NEXT: DW_AT_name [DW_FORM_GNU_strp_alt] (alt indirect string, offset: 0x31)
+; DUMP_INFO-NEXT: DW_AT_low_pc [DW_FORM_addr]       (0x0000000000000000)
+; DUMP_INFO-NEXT: DW_AT_stmt_list [DW_FORM_data4]   (0x00000000)
+; DUMP_INFO-NEXT: DW_AT_comp_dir [DW_FORM_GNU_strp_alt]     (alt indirect string, offset: 0x6b)
+
+; DUMP_INFO: DW_TAG_imported_unit [4]
+; DUMP_INFO-NEXT: DW_AT_import [DW_FORM_GNU_ref_alt]      (<alt 0xb>)
+