From: Alexey Samsonov Date: Tue, 19 May 2015 20:29:28 +0000 (+0000) Subject: [DWARF parser] Add basic support for DWZ DWARF multifile extensions. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4ca606f22fe311dc20828242d3961cbafa040343;p=llvm [DWARF parser] Add basic support for DWZ DWARF multifile extensions. 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 --- diff --git a/include/llvm/DebugInfo/DWARF/DWARFFormValue.h b/include/llvm/DebugInfo/DWARF/DWARFFormValue.h index 02f468189d2..7ddcc0d81d5 100644 --- a/include/llvm/DebugInfo/DWARF/DWARFFormValue.h +++ b/include/llvm/DebugInfo/DWARF/DWARFFormValue.h @@ -87,6 +87,8 @@ public: static ArrayRef getFixedFormSizes(uint8_t AddrSize, uint16_t Version); +private: + void dumpString(raw_ostream &OS, const DWARFUnit *U) const; }; } diff --git a/include/llvm/Support/Dwarf.h b/include/llvm/Support/Dwarf.h index c294a72a04b..c3d94d19f91 100644 --- a/include/llvm/Support/Dwarf.h +++ b/include/llvm/Support/Dwarf.h @@ -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 { diff --git a/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp b/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp index e963b7ca6e6..5abbde4ac0f 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp @@ -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 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 OptVal = formValue.getAsUnsignedConstant()) dumpApplePropertyAttribute(OS, *OptVal); diff --git a/lib/DebugInfo/DWARF/DWARFFormValue.cpp b/lib/DebugInfo/DWARF/DWARFFormValue.cpp index 6946f833424..75ca7622139 100644 --- a/lib/DebugInfo/DWARF/DWARFFormValue.cpp +++ b/lib/DebugInfo/DWARF/DWARFFormValue.cpp @@ -113,14 +113,17 @@ bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const { if (Form < ArrayRef(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 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 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("", 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 DbgStr = getAsCString(U); + if (DbgStr.hasValue()) { + raw_ostream &COS = WithColor(OS, syntax::String); + COS << '"'; + COS.write_escaped(DbgStr.getValue()); + COS << '"'; + } +} + Optional 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 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; } } diff --git a/lib/Support/Dwarf.cpp b/lib/Support/Dwarf.cpp index 95c4bc32701..6229825a8ee 100644 --- a/lib/Support/Dwarf.cpp +++ b/lib/Support/Dwarf.cpp @@ -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 index 00000000000..937961b59be Binary files /dev/null and b/test/DebugInfo/Inputs/dwarfdump-test-dwz.elf-x86-64 differ diff --git a/test/DebugInfo/Inputs/dwarfdump-test.cc b/test/DebugInfo/Inputs/dwarfdump-test.cc index 40899986490..14295d3cffa 100644 --- a/test/DebugInfo/Inputs/dwarfdump-test.cc +++ b/test/DebugInfo/Inputs/dwarfdump-test.cc @@ -21,3 +21,9 @@ int main() { // $ cp dwarfdump-test.cc /tmp/dbginfo // $ cd /tmp/dbginfo // $ clang++ -g dwarfdump-test.cc -o + +// The result is also used as an input to .dwz tool: +// $ cp output1.dwz +// $ cp 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 index 00000000000..32db372fcda 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 index 00000000000..e5f67ebe487 --- /dev/null +++ b/test/DebugInfo/dwarfdump-dwz.test @@ -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] () +