diff --git a/llvm/docs/CommandGuide/llvm-dwarfdump.rst b/llvm/docs/CommandGuide/llvm-dwarfdump.rst --- a/llvm/docs/CommandGuide/llvm-dwarfdump.rst +++ b/llvm/docs/CommandGuide/llvm-dwarfdump.rst @@ -119,6 +119,11 @@ Abbreviate the description of type unit entries. +.. option:: --type-compat-dump + + Dump the output in a format that is more friendly for comparing + types inside DWARF output from two different files. + .. option:: -x, --regex Treat any strings as regular expressions when searching diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h @@ -68,8 +68,8 @@ bool dumpLocationList(uint64_t *Offset, raw_ostream &OS, Optional BaseAddr, const MCRegisterInfo *MRI, const DWARFObject &Obj, - DWARFUnit *U, DIDumpOptions DumpOpts, - unsigned Indent) const; + DWARFUnit *U, DIDumpOptions DumpOpts, unsigned Indent, + bool DumpListOffset = true) const; Error visitAbsoluteLocationList( uint64_t Offset, Optional BaseAddr, diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -120,19 +120,18 @@ DWARFExpression(Extractor, AddressSize).print(OS, DumpOpts, MRI, U); } -bool DWARFLocationTable::dumpLocationList(uint64_t *Offset, raw_ostream &OS, - Optional BaseAddr, - const MCRegisterInfo *MRI, - const DWARFObject &Obj, DWARFUnit *U, - DIDumpOptions DumpOpts, - unsigned Indent) const { +bool DWARFLocationTable::dumpLocationList( + uint64_t *Offset, raw_ostream &OS, Optional BaseAddr, + const MCRegisterInfo *MRI, const DWARFObject &Obj, DWARFUnit *U, + DIDumpOptions DumpOpts, unsigned Indent, bool DumpListOffset) const { DWARFLocationInterpreter Interp( BaseAddr, [U](uint32_t Index) -> Optional { if (U) return U->getAddrOffsetSectionItem(Index); return None; }); - OS << format("0x%8.8" PRIx64 ": ", *Offset); + if (DumpListOffset) + OS << format("0x%8.8" PRIx64 ": ", *Offset); Error E = visitLocationList(Offset, [&](const DWARFLocationEntry &E) { Expected> Loc = Interp.Interpret(E); if (!Loc || DumpOpts.DisplayRawContents) diff --git a/llvm/test/tools/llvm-dwarfdump/type-compat/basic.yaml b/llvm/test/tools/llvm-dwarfdump/type-compat/basic.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/type-compat/basic.yaml @@ -0,0 +1,259 @@ +## This test checks basic output for --type-compat-dump option. +## All type dies should be removed. Instead, the type name should be +## printed for DW_AT_type attribute. + +# RUN: yaml2obj %s -o - | llvm-dwarfdump --type-compat-dump - | FileCheck %s + +# CHECK: DW_TAG_compile_unit [0] +# CHECK: DW_AT_producer "by_hand" +# CHECK: DW_AT_language DW_LANG_C_plus_plus +# CHECK: DW_AT_name "CU1" +# CHECK: DW_AT_low_pc 0x0000000000001000 +# CHECK: DW_AT_high_pc 0x000000000000101b + +# CHECK-NOT: DW_TAG_class_type +# CHECK-NOT: DW_TAG_member +# CHECK-NOT: DW_TAG_base_type + +# CHECK: DW_TAG_subprogram [1] +# CHECK: DW_AT_name "foo1" +# CHECK: DW_AT_low_pc 0x0000000000001000 +# CHECK: DW_AT_high_pc 0x0000000000001010 +# CHECK: DW_AT_type "class1" + +# CHECK: DW_TAG_compile_unit [0] +# CHECK: DW_AT_producer "by_hand" +# CHECK: DW_AT_language DW_LANG_C_plus_plus +# CHECK: DW_AT_name "CU1" +# CHECK: DW_AT_low_pc 0x0000000000002000 +# CHECK: DW_AT_high_pc 0x000000000000201b + +# CHECK-NOT: DW_TAG_class_type +# CHECK-NOT: DW_TAG_member +# CHECK-NOT: DW_TAG_base_type + +# CHECK: DW_TAG_subprogram [1] +# CHECK: DW_AT_name "foo2" +# CHECK: DW_AT_low_pc 0x0000000000002000 +# CHECK: DW_AT_high_pc 0x0000000000002010 +# CHECK: DW_AT_type "class2" + + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_string + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Table: + - Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_string + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x1000 + - Value: 0x1b + - AbbrCode: 3 + Values: + - CStr: class1 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class2 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class3 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 8 + Values: + - CStr: int + - AbbrCode: 2 + Values: + - CStr: foo1 + - Value: 0x1000 + - Value: 0x10 + - Value: 0x0000002a + - AbbrCode: 0 + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x2000 + - Value: 0x1b + - AbbrCode: 3 + Values: + - CStr: class1 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class2 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class3 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 8 + Values: + - CStr: int + - AbbrCode: 2 + Values: + - CStr: foo2 + - Value: 0x2000 + - Value: 0x10 + - Value: 0x00000040 + - AbbrCode: 0 +... diff --git a/llvm/test/tools/llvm-dwarfdump/type-compat/loc.yaml b/llvm/test/tools/llvm-dwarfdump/type-compat/loc.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/type-compat/loc.yaml @@ -0,0 +1,78 @@ +## This test checks how DWARF v4 location lists are printed +## by --type-compat-dump option. + +# RUN: yaml2obj %s | llvm-dwarfdump --type-compat-dump - | FileCheck %s + +# CHECK: == DW_TAG_compile_unit [0] +# CHECK: DW_AT_low_pc 0x0000000000000000 +# CHECK: DW_AT_high_pc 0x000000000000000b + +# CHECK: == DW_TAG_subprogram [1] +# CHECK: DW_AT_low_pc 0x0000000000000000 +# CHECK: DW_AT_high_pc 0x000000000000000b + +# CHECK: == DW_TAG_variable [2] +# CHECK: DW_AT_location +# CHECK: [0x0000000000000000, 0x0000000000000006): DW_OP_reg5 + +# CHECK: == DW_TAG_variable [2] +# CHECK: DW_AT_location +# CHECK: [0x0000000000000003, 0x0000000000000005): DW_OP_reg2 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_loc + Type: SHT_PROGBITS + AddressAlign: 0x01 + Content: '00000000000000000600000000000000010055000000000000000000000000000000000300000000000000050000000000000001005200000000000000000000000000000000' +DWARF: + debug_abbrev: + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Code: 2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Code: 3 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_location + Form: DW_FORM_sec_offset + debug_info: + - Version: 4 + AbbrOffset: 0x00 + Entries: + - AbbrCode: 1 ## DW_TAG_compile_unit + Values: + - Value: 0x00 ## DW_AT_low_pc + - Value: 0x0b ## DW_AT_high_pc + - AbbrCode: 2 ## DW_TAG_subprogram + Values: + - Value: 0x00 ## DW_AT_low_pc + - Value: 0x0b ## DW_AT_high_pc + - AbbrCode: 3 ## DW_TAG_variable + Values: + - Value: 0x00 ## DW_AT_sec_offset + - AbbrCode: 3 ## DW_TAG_variable + Values: + - Value: 0x23 ## DW_AT_sec_offset + - AbbrCode: 0 ## NULL + - AbbrCode: 0 ## NULL + - AbbrCode: 0 ## NULL diff --git a/llvm/test/tools/llvm-dwarfdump/type-compat/loclists.yaml b/llvm/test/tools/llvm-dwarfdump/type-compat/loclists.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/type-compat/loclists.yaml @@ -0,0 +1,83 @@ +## This test checks how DWARF v5 location lists are printed +## by --type-compat-dump option. + +# RUN: yaml2obj %s | llvm-dwarfdump --type-compat-dump - | FileCheck %s + +# CHECK: == DW_TAG_compile_unit [0] +# CHECK-NOT: DW_AT_addr_base +# CHECK-NOT: DW_AT_loclists_base +# CHECK: DW_AT_low_pc 0x0000000000000000 +# CHECK: DW_AT_high_pc 0x0000000000000008 + +# CHECK: == DW_TAG_subprogram [1] +# CHECK: DW_AT_low_pc 0x0000000000000000 +# CHECK: DW_AT_high_pc 0x000000000000000b + +# CHECK: == DW_TAG_formal_parameter [2] +# CHECK: DW_AT_location +# CHECK: DW_LLE_startx_endx (0x0000000000000000, 0x0000000000000011): DW_OP_breg16 +# CHECK: DW_LLE_startx_length (0x0000000000000011, 0x0000000000000012): DW_OP_breg17 +# CHECK: [0x0000000000000012, 0x0000000000000013): DW_OP_breg18 +# CHECK: : DW_OP_breg19 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_loclists + Type: SHT_PROGBITS + AddressAlign: 0x01 + Content: '1b000000050008000000000002001101800311120181041213018205018300' +DWARF: + debug_abbrev: + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_addr_base + Form: DW_FORM_sec_offset + - Attribute: DW_AT_loclists_base + Form: DW_FORM_sec_offset + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Code: 2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Code: 3 + Tag: DW_TAG_formal_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_location + Form: DW_FORM_sec_offset + debug_info: + - Version: 5 + UnitType: DW_UT_compile + AddrSize: 8 + Entries: + - AbbrCode: 1 ## DW_TAG_compile_unit + Values: + - Value: 0x08 ## DW_AT_addr_base + - Value: 0x0c ## DW_AT_loclists_base + - Value: 0x00 ## DW_AT_low_pc + - Value: 0x08 ## DW_AT_high_pc + - AbbrCode: 2 ## DW_TAG_subprogram + Values: + - Value: 0x00 ## DW_AT_low_pc + - Value: 0x0b ## DW_AT_high_pc + - AbbrCode: 3 ## DW_TAG_formal_parameter + Values: + - Value: 0x0c ## DW_AT_sec_offset + - AbbrCode: 0 ## NULL + - AbbrCode: 0 ## NULL + - AbbrCode: 0 ## NULL diff --git a/llvm/test/tools/llvm-dwarfdump/type-compat/ranges-error.yaml b/llvm/test/tools/llvm-dwarfdump/type-compat/ranges-error.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/type-compat/ranges-error.yaml @@ -0,0 +1,76 @@ +## This test checks error message displayed for corrupted .debug_ranges section. + +# RUN: yaml2obj %s | llvm-dwarfdump --type-compat-dump - | FileCheck %s + +# CHECK: == DW_TAG_compile_unit [0] +# CHECK: DW_AT_low_pc 0x0000000000000000 +# CHECK: DW_AT_high_pc 0x000000000000000b + +# CHECK: == DW_TAG_subprogram [1] +# CHECK: DW_AT_low_pc 0x0000000000000000 +# CHECK: DW_AT_high_pc 0x000000000000000b + +# CHECK: == DW_TAG_lexical_block [2] +# CHECK: DW_AT_ranges +# CHECK: decoding address ranges: invalid range list entry at offset 0x0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_ranges + Type: SHT_PROGBITS + AddressAlign: 0x01 + Content: '000000000000000003000000' +DWARF: + debug_abbrev: + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Code: 2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Code: 3 + Tag: DW_TAG_lexical_block + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_ranges + Form: DW_FORM_sec_offset + - Code: 4 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_location + Form: DW_FORM_sec_offset + debug_info: + - Version: 4 + AbbrOffset: 0x00 + Entries: + - AbbrCode: 1 ## DW_TAG_compile_unit + Values: + - Value: 0x00 ## DW_AT_low_pc + - Value: 0x0b ## DW_AT_high_pc + - AbbrCode: 2 ## DW_TAG_subprogram + Values: + - Value: 0x00 ## DW_AT_low_pc + - Value: 0x0b ## DW_AT_high_pc + - AbbrCode: 3 ## DW_TAG_lexical_block + Values: + - Value: 0x00 ## DW_AT_ranges + - AbbrCode: 0 ## NULL + - AbbrCode: 0 ## NULL + - AbbrCode: 0 ## NULL diff --git a/llvm/test/tools/llvm-dwarfdump/type-compat/ranges.yaml b/llvm/test/tools/llvm-dwarfdump/type-compat/ranges.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/type-compat/ranges.yaml @@ -0,0 +1,78 @@ +## This test checks how DWARF v4 range list is printed +## by --type-compat-dump option. + +# RUN: yaml2obj %s | llvm-dwarfdump --type-compat-dump - | FileCheck %s + +# CHECK: == DW_TAG_compile_unit [0] +# CHECK: DW_AT_low_pc 0x0000000000000000 +# CHECK: DW_AT_high_pc 0x000000000000000b + +# CHECK: == DW_TAG_subprogram [1] +# CHECK: DW_AT_low_pc 0x0000000000000000 +# CHECK: DW_AT_high_pc 0x000000000000000b + +# CHECK: == DW_TAG_lexical_block [2] +# CHECK: DW_AT_ranges +# CHECK: [0x0000000000000000, 0x0000000000000003) +# CHECK: [0x0000000000000005, 0x0000000000000008) + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_ranges + Type: SHT_PROGBITS + AddressAlign: 0x01 + Content: '000000000000000003000000000000000500000000000000080000000000000000000000000000000000000000000000' +DWARF: + debug_abbrev: + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Code: 2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Code: 3 + Tag: DW_TAG_lexical_block + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_ranges + Form: DW_FORM_sec_offset + - Code: 4 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_location + Form: DW_FORM_sec_offset + debug_info: + - Version: 4 + AbbrOffset: 0x00 + Entries: + - AbbrCode: 1 ## DW_TAG_compile_unit + Values: + - Value: 0x00 ## DW_AT_low_pc + - Value: 0x0b ## DW_AT_high_pc + - AbbrCode: 2 ## DW_TAG_subprogram + Values: + - Value: 0x00 ## DW_AT_low_pc + - Value: 0x0b ## DW_AT_high_pc + - AbbrCode: 3 ## DW_TAG_lexical_block + Values: + - Value: 0x00 ## DW_AT_ranges + - AbbrCode: 0 ## NULL + - AbbrCode: 0 ## NULL + - AbbrCode: 0 ## NULL diff --git a/llvm/test/tools/llvm-dwarfdump/type-compat/rnglists.yaml b/llvm/test/tools/llvm-dwarfdump/type-compat/rnglists.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/type-compat/rnglists.yaml @@ -0,0 +1,45 @@ +## This test checks how DWARF v5 range list is printed +## by --type-compat-dump option. + +# RUN: yaml2obj %s -o - | llvm-dwarfdump --type-compat-dump - | FileCheck %s + +# CHECK: == DW_TAG_compile_unit [0] +# CHECK: DW_AT_name "by_hand" +# CHECK: DW_AT_ranges +# CHECK: [0x0000000000000010, 0x0000000000000020) +# CHECK: [0x0000000000000025, 0x00000000000000a5) + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_rnglists + Type: SHT_PROGBITS + AddressAlign: 0x01 + Content: '2500000005000800000000000610000000000000002000000000000000072500000000000000800100' +DWARF: + debug_str: + - '' + - /tmp/main.c + debug_abbrev: + - Table: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_ranges + Form: DW_FORM_sec_offset + debug_info: + - Version: 5 + UnitType: DW_UT_compile + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - CStr: by_hand + - Value: 0x0c diff --git a/llvm/test/tools/llvm-dwarfdump/type-compat/specification.yaml b/llvm/test/tools/llvm-dwarfdump/type-compat/specification.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/type-compat/specification.yaml @@ -0,0 +1,153 @@ +## This test checks how DW_AT_specification attribute +## is printed by --type-compat-dump option. + +# RUN: yaml2obj %s -o - | llvm-dwarfdump --type-compat-dump - | FileCheck %s + +# CHECK: DW_TAG_compile_unit [0] +# CHECK: DW_AT_producer "by_hand" +# CHECK: DW_AT_language DW_LANG_C_plus_plus +# CHECK: DW_AT_name "CU1" +# CHECK: DW_AT_low_pc 0x0000000000001000 +# CHECK: DW_AT_high_pc 0x000000000000101b + +# CHECK-NOT: DW_TAG_class_type +# CHECK-NOT: DW_TAG_member +# CHECK-NOT: DW_TAG_base_type + +# CHECK: DW_TAG_subprogram [1] +# CHECK: DW_AT_name "foo1" +# CHECK: DW_AT_low_pc 0x0000000000001000 +# CHECK: DW_AT_high_pc 0x0000000000001010 +# CHECK: DW_AT_type "class1" +# CHECK: DW_AT_specification "foo1": "class1" + +# CHECK-NOT: DW_TAG_subprogram + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_string + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_specification + Form: DW_FORM_ref4 + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x1000 + - Value: 0x1b + - AbbrCode: 3 + Values: + - CStr: class1 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class2 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class3 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 8 + Values: + - CStr: int + - AbbrCode: 2 + Values: + - CStr: foo1 + - Value: 0x1000 + - Value: 0x10 + - Value: 0x0000002a + - Value: 0x0000008f + - AbbrCode: 9 + Values: + - CStr: foo1 + - Value: 0x0000002a + - AbbrCode: 0 +... diff --git a/llvm/tools/llvm-dwarfdump/CMakeLists.txt b/llvm/tools/llvm-dwarfdump/CMakeLists.txt --- a/llvm/tools/llvm-dwarfdump/CMakeLists.txt +++ b/llvm/tools/llvm-dwarfdump/CMakeLists.txt @@ -2,6 +2,7 @@ DebugInfoDWARF AllTargetsDescs AllTargetsInfos + BinaryFormat MC Object Support diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp --- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -18,12 +18,17 @@ #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFLocationExpression.h" #include "llvm/Object/Archive.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Regex.h" @@ -160,6 +165,14 @@ Diff("diff", desc("Emit diff-friendly output by omitting offsets and addresses."), cat(DwarfDumpCategory)); + +static cl::opt + TypeCompatDump("type-compat-dump", + cl::desc("Dump the output in a format that is more friendly" + " for comparing types inside DWARF output from two" + " different files."), + cat(DwarfDumpCategory)); + static list Find("find", desc("Search for the exact match for in the accelerator tables " @@ -515,6 +528,250 @@ return Result; } +// \returns true if specified \p Die should be printed by type compatibility +// dump. +static bool shouldBePrintedByTypeCompatDump(const DWARFDie &Die) { + switch (Die.getTag()) { + case dwarf::DW_TAG_formal_parameter: + case dwarf::DW_TAG_template_type_parameter: + case dwarf::DW_TAG_unspecified_parameters: + case dwarf::DW_TAG_call_site_parameter: + case dwarf::DW_TAG_template_value_parameter: + case dwarf::DW_TAG_GNU_template_parameter_pack: + case dwarf::DW_TAG_GNU_formal_parameter_pack: + return true; + + default: + return Die + .find({dwarf::DW_AT_low_pc, dwarf::DW_AT_ranges, dwarf::DW_AT_location}) + .hasValue(); + } + + llvm_unreachable("unknown Die tag"); +} + +// \returns value of indention depending on \p Depth. +static uint32_t getTypeCompatDumpIndent(unsigned Depth) { + return Depth * 2 + 3; +} + +// Dump location list. +static void dumpTypeCompatLocationList(raw_ostream &OS, + const DWARFFormValue &FormValue, + DWARFUnit *Unit, unsigned Depth) { + assert(FormValue.isFormClass(DWARFFormValue::FC_SectionOffset) && + "bad FORM for location list"); + DWARFContext &Ctx = Unit->getContext(); + const MCRegisterInfo *MRI = Ctx.getRegisterInfo(); + uint64_t Offset = *FormValue.getAsSectionOffset(); + + if (FormValue.getForm() == dwarf::DW_FORM_loclistx) { + if (auto LoclistOffset = Unit->getLoclistOffset(Offset)) + Offset = *LoclistOffset; + else + return; + } + + Unit->getLocationTable().dumpLocationList( + &Offset, OS, Unit->getBaseAddress(), MRI, Ctx.getDWARFObj(), Unit, + DIDumpOptions(), getTypeCompatDumpIndent(Depth), false); +} + +// Dump address ranges. +static void dumpTypeCompatRanges(raw_ostream &OS, const DWARFDie &Die, + DWARFUnit *Unit, unsigned Depth) { + Expected RangesOrError = Die.getAddressRanges(); + if (!RangesOrError) { + OS << '\n'; + OS.indent(getTypeCompatDumpIndent(Depth) + 2); + OS << "decoding address ranges: " << toString(RangesOrError.takeError()); + return; + } + + // TODO: combine overlapped address ranges. + for (const DWARFAddressRange &R : *RangesOrError) { + OS << '\n'; + OS.indent(getTypeCompatDumpIndent(Depth) + 2); + + R.dump(OS, Unit->getAddressByteSize(), DIDumpOptions(), nullptr); + } +} + +// Dump location expression. +static void dumpTypeCompatLocationExpr(raw_ostream &OS, + const DWARFFormValue &FormValue, + DWARFUnit *Unit) { + assert((FormValue.isFormClass(DWARFFormValue::FC_Block) || + FormValue.isFormClass(DWARFFormValue::FC_Exprloc)) && + "bad FORM for location expression"); + DWARFContext &Ctx = Unit->getContext(); + const MCRegisterInfo *MRI = Ctx.getRegisterInfo(); + ArrayRef Expr = *FormValue.getAsBlock(); + DataExtractor Data(StringRef((const char *)Expr.data(), Expr.size()), + Ctx.isLittleEndian(), 0); + DWARFExpression(Data, Unit->getAddressByteSize(), + Unit->getFormParams().Format) + .print(OS, DIDumpOptions(), MRI, Unit); +} + +// Dump Die attribute. +static void dumpTypeCompatAttribute(raw_ostream &OS, const DWARFDie &Die, + const DWARFAttribute &AttrValue, + unsigned Depth) { + dwarf::Attribute Attr = AttrValue.Attr; + const DWARFFormValue &FormValue = AttrValue.Value; + DWARFUnit *Unit = Die.getDwarfUnit(); + if (Unit == nullptr) + return; + + if (Attr == dwarf::DW_AT_sibling || Attr == dwarf::DW_AT_loclists_base || + Attr == dwarf::DW_AT_rnglists_base || + Attr == dwarf::DW_AT_str_offsets_base || Attr == dwarf::DW_AT_addr_base) + // Skip attribute. + return; + + OS << "\n"; + OS.indent(getTypeCompatDumpIndent(Depth)); + WithColor(OS, HighlightColor::Attribute) << formatv("{0} ", Attr); + + switch (Attr) { + case dwarf::DW_AT_object_pointer: + case dwarf::DW_AT_specification: + case dwarf::DW_AT_abstract_origin: + case dwarf::DW_AT_import: + if (DWARFDie ReferencedDie = + Die.getAttributeValueAsReferencedDie(FormValue)) { + if (const char *Name = ReferencedDie.getLinkageName()) + OS << "\"" << Name << "\""; + else { + if (const char *Name = ReferencedDie.getShortName()) + OS << "\"" << Name << "\""; + else + OS << "None"; + + if (llvm::Optional TypeRef = + ReferencedDie.find(dwarf::DW_AT_type)) { + OS << ": "; + if (DWARFDie TypeDie = + Die.getAttributeValueAsReferencedDie(*TypeRef)) { + OS << '"'; + dumpTypeQualifiedName(TypeDie, OS); + OS << '"'; + } + } + } + } + break; + + case dwarf::DW_AT_type: + case dwarf::DW_AT_containing_type: + if (DWARFDie TypeDie = Die.getAttributeValueAsReferencedDie(FormValue)) { + OS << '"'; + dumpTypeQualifiedName(TypeDie, OS); + OS << '"'; + } + break; + + case dwarf::DW_AT_ranges: + dumpTypeCompatRanges(OS, Die, Unit, Depth); + break; + + case dwarf::DW_AT_high_pc: + uint64_t LowPC, HighPC, Index; + if (Die.getLowAndHighPC(LowPC, HighPC, Index)) + DWARFFormValue::dumpAddress(OS, Unit->getAddressByteSize(), HighPC); + else + FormValue.dump(OS); + break; + + case dwarf::DW_AT_accessibility: + case dwarf::DW_AT_virtuality: + case dwarf::DW_AT_language: + case dwarf::DW_AT_encoding: + case dwarf::DW_AT_decimal_sign: + case dwarf::DW_AT_endianity: + case dwarf::DW_AT_visibility: + case dwarf::DW_AT_identifier_case: + case dwarf::DW_AT_calling_convention: + case dwarf::DW_AT_inline: + case dwarf::DW_AT_ordering: + case dwarf::DW_AT_APPLE_runtime_class: + case dwarf::DW_AT_defaulted: + if (llvm::Optional Value = + AttrValue.Value.getAsUnsignedConstant()) + OS << dwarf::AttributeValueString(Attr, *Value); + else + OS << "None"; + break; + + case dwarf::DW_AT_decl_file: + case dwarf::DW_AT_call_file: { + std::string File; + if (const auto *LT = Unit->getContext().getLineTableForUnit(Unit)) + if (LT->getFileNameByIndex( + FormValue.getAsUnsignedConstant().getValue(), + Unit->getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File)) { + OS << '"' << File << '"'; + } + } break; + + default: + if (DWARFAttribute::mayHaveLocationList(Attr) && + FormValue.isFormClass(DWARFFormValue::FC_SectionOffset)) + dumpTypeCompatLocationList(OS, FormValue, Unit, Depth * 2 + 4); + else if (FormValue.isFormClass(DWARFFormValue::FC_Exprloc) || + (DWARFAttribute::mayHaveLocationExpr(Attr) && + FormValue.isFormClass(DWARFFormValue::FC_Block))) + dumpTypeCompatLocationExpr(OS, FormValue, Unit); + else + FormValue.dump(OS); + + break; + }; +} + +// Dump \p Die recursively. +static void dumpTypeCompatDie(raw_ostream &OS, const DWARFDie &Die, + unsigned Depth) { + if (!Die.isValid()) + return; + + if (!shouldBePrintedByTypeCompatDump(Die)) + return; + + OS << '\n'; + WithColor(OS, HighlightColor::Tag).get().indent(Depth * 2) + << formatv("== {0} [{1}]", Die.getTag(), Depth); + + // Dump DIEs attributes. + for (const DWARFAttribute &AttrValue : Die.attributes()) + dumpTypeCompatAttribute(OS, Die, AttrValue, Depth); + + OS << '\n'; + for (DWARFDie Child = Die.getFirstChild(); Child; Child = Child.getSibling()) + dumpTypeCompatDie(OS, Child, Depth + 1); +} + +// The typeCompatDump prints incoming DWARF in such a way that it +// tolerant to type dies order and amount, dies offsets, strings order. +// It allows to compare dumps if they are semantically correct but differ +// in binary contents. f.e. it is possible to compare DWARF file built +// with odr deduplication and without(typeCompatDump should generate +// equal result for both cases). +static bool typeCompatDump(ObjectFile &Obj, DWARFContext &DICtx, + const Twine &Filename, raw_ostream &Stream) { + raw_ostream &OS = Quiet ? nulls() : Stream; + OS << "type compatibility dump.\n\n" + << "'" << Filename.str() << "'" + << ", file format " << Obj.getFileFormatName() << ":\n\n"; + + for (std::unique_ptr &CU : DICtx.normal_units()) + dumpTypeCompatDie(OS, CU->getUnitDIE(false), 0); + + return true; +} + static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, HandlerFn HandleObj, raw_ostream &OS); @@ -679,6 +936,9 @@ } else if (ShowSectionSizes) { for (auto Object : Objects) Success &= handleFile(Object, collectObjectSectionSizes, OutputFile.os()); + } else if (TypeCompatDump) { + for (auto Object : Objects) + Success &= handleFile(Object, typeCompatDump, OutputFile.os()); } else { for (auto Object : Objects) Success &= handleFile(Object, dumpObjectFile, OutputFile.os());