diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h index 7a728c2508cc8..154f7893aa174 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h @@ -45,6 +45,24 @@ struct DWARFAddressRange { return LowPC < RHS.HighPC && RHS.LowPC < HighPC; } + /// Union two address ranges if they intersect. + /// + /// This function will union two address ranges if they intersect by + /// modifying this range to be the union of both ranges. If the two ranges + /// don't intersect this range will be left alone. + /// + /// \param RHS Another address range to combine with. + /// + /// \returns false if the ranges don't intersect, true if they do and the + /// ranges were combined. + bool merge(const DWARFAddressRange &RHS) { + if (!intersects(RHS)) + return false; + LowPC = std::min(LowPC, RHS.LowPC); + HighPC = std::max(HighPC, RHS.HighPC); + return true; + } + void dump(raw_ostream &OS, uint32_t AddressSize, DIDumpOptions DumpOpts = {}, const DWARFObject *Obj = nullptr) const; }; diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h index a4a3a11d441b8..22b1d722fc89c 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h @@ -9,6 +9,7 @@ #ifndef LLVM_DEBUGINFO_DWARF_DWARFVERIFIER_H #define LLVM_DEBUGINFO_DWARF_DWARFVERIFIER_H +#include "llvm/ADT/Optional.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" @@ -56,11 +57,13 @@ class DWARFVerifier { typedef std::set::const_iterator die_range_info_iterator; /// Inserts the address range. If the range overlaps with an existing - /// range, the range is *not* added and an iterator to the overlapping - /// range is returned. + /// range, the range that it overlaps with will be returned and the two + /// address ranges will be unioned together in "Ranges". /// - /// This is used for finding overlapping ranges within the same DIE. - address_range_iterator insert(const DWARFAddressRange &R); + /// This is used for finding overlapping ranges in the DW_AT_ranges + /// attribute of a DIE. It is also used as a set of address ranges that + /// children address ranges must all be contained in. + Optional insert(const DWARFAddressRange &R); /// Finds an address range in the sorted vector of ranges. address_range_iterator findRange(const DWARFAddressRange &R) const { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp index a6dadd7f80212..3a83317a73a3b 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -26,24 +26,26 @@ using namespace llvm; using namespace dwarf; using namespace object; -DWARFVerifier::DieRangeInfo::address_range_iterator +Optional DWARFVerifier::DieRangeInfo::insert(const DWARFAddressRange &R) { auto Begin = Ranges.begin(); auto End = Ranges.end(); auto Pos = std::lower_bound(Begin, End, R); if (Pos != End) { - if (Pos->intersects(R)) - return std::move(Pos); - if (Pos != Begin) { - auto Iter = Pos - 1; - if (Iter->intersects(R)) - return std::move(Iter); - } + DWARFAddressRange Range(*Pos); + if (Pos->merge(R)) + return Range; + } + if (Pos != Begin) { + auto Iter = Pos - 1; + DWARFAddressRange Range(*Iter); + if (Iter->merge(R)) + return Range; } Ranges.insert(Pos, R); - return Ranges.end(); + return None; } DWARFVerifier::DieRangeInfo::die_range_info_iterator @@ -397,22 +399,30 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, // processing an object file. if (!IsObjectFile || IsMachOObject || Die.getTag() != DW_TAG_compile_unit) { + bool DumpDieAfterError = false; for (auto Range : Ranges) { if (!Range.valid()) { ++NumErrors; error() << "Invalid address range " << Range << "\n"; + DumpDieAfterError = true; continue; } - // Verify that ranges don't intersect. - const auto IntersectingRange = RI.insert(Range); - if (IntersectingRange != RI.Ranges.end()) { + // Verify that ranges don't intersect and also build up the DieRangeInfo + // address ranges. Don't break out of the loop below early, or we will + // think this DIE doesn't have all of the address ranges it is supposed + // to have. Compile units often have DW_AT_ranges that can contain one or + // more dead stripped address ranges which tend to all be at the same + // address: 0 or -1. + if (auto PrevRange = RI.insert(Range)) { ++NumErrors; - error() << "DIE has overlapping address ranges: " << Range << " and " - << *IntersectingRange << "\n"; - break; + error() << "DIE has overlapping ranges in DW_AT_ranges attribute: " + << *PrevRange << " and " << Range << '\n'; + DumpDieAfterError = true; } } + if (DumpDieAfterError) + dump(Die, 2) << '\n'; } // Verify that children don't intersect. diff --git a/llvm/test/tools/llvm-dwarfdump/X86/verify_overlapping_cu_ranges.yaml b/llvm/test/tools/llvm-dwarfdump/X86/verify_overlapping_cu_ranges.yaml new file mode 100644 index 0000000000000..37cc139115a43 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/X86/verify_overlapping_cu_ranges.yaml @@ -0,0 +1,260 @@ +# This test verifies that if a DW_TAG_compile_unit has DW_AT_ranges that +# overlap, that it doesn't end up producing invalid errors claiming a child +# DW_TAG_subprogram DIE is not in the parant (CU) ranges. Prior to the commit +# that fixed this, a loop was iterating over all DW_AT_ranges for a DIE and +# stopping the loop if any intersecting ranges were found. This would cause +# the DW_TAG_subprogram DIEs, like "stripped2" and "main", to improperly report +# that they were not contained in the parent's address ranges +# +# The DWARF looks like: +# 0x0000000b: DW_TAG_compile_unit +# DW_AT_name ("/tmp/main.c") +# DW_AT_language (DW_LANG_C) +# DW_AT_low_pc (0x0000000000000000) +# DW_AT_ranges (0x00000000 +# [0x0000000000002000, 0x0000000000003000) +# [0x0000000000000000, 0x0000000000000020) +# [0x0000000000000000, 0x0000000000000030) +# [0x0000000000001000, 0x0000000000002000)) +# +# 0x0000001e: DW_TAG_subprogram +# DW_AT_name ("stripped1") +# DW_AT_low_pc (0x0000000000000000) +# DW_AT_high_pc (0x0000000000000020) +# +# 0x0000002f: DW_TAG_subprogram +# DW_AT_name ("stripped2") +# DW_AT_low_pc (0x0000000000000000) +# DW_AT_high_pc (0x0000000000000030) +# +# 0x00000044: DW_TAG_subprogram +# DW_AT_name ("main") +# DW_AT_low_pc (0x0000000000001000) +# DW_AT_high_pc (0x0000000000002000) +# +# 0x00000055: DW_TAG_subprogram +# DW_AT_name ("foo") +# DW_AT_low_pc (0x0000000000002000) +# DW_AT_high_pc (0x0000000000003000) +# +# 0x00000066: NULL + +# RUN: yaml2obj %s | not llvm-dwarfdump --verify - | FileCheck %s --implicit-check-not=error: + +# CHECK: error: DIE has overlapping ranges in DW_AT_ranges attribute: [0x0000000000000000, 0x0000000000000020) and [0x0000000000000000, 0x0000000000000030) + +# CHECK: 0x0000000b: DW_TAG_compile_unit +# CHECK-NEXT: DW_AT_name ("/tmp/main.c") +# CHECK-NEXT: DW_AT_language (DW_LANG_C) +# CHECK-NEXT: DW_AT_low_pc (0x0000000000000000) +# CHECK-NEXT: DW_AT_ranges (0x00000000 +# CHECK-NEXT: [0x0000000000002000, 0x0000000000003000) +# CHECK-NEXT: [0x0000000000000000, 0x0000000000000020) +# CHECK-NEXT: [0x0000000000000000, 0x0000000000000030) +# CHECK-NEXT: [0x0000000000001000, 0x0000000000002000)) + +# CHECK: error: DIEs have overlapping address ranges: +# CHECK: 0x0000002f: DW_TAG_subprogram +# CHECK-NEXT: DW_AT_name ("stripped2") +# CHECK-NEXT: DW_AT_low_pc (0x0000000000000000) +# CHECK-NEXT: DW_AT_high_pc (0x0000000000000030) + +# CHECK: 0x0000001e: DW_TAG_subprogram +# CHECK-NEXT: DW_AT_name ("stripped1") +# CHECK-NEXT: DW_AT_low_pc (0x0000000000000000) +# CHECK-NEXT: DW_AT_high_pc (0x0000000000000020) + +# CHECK: Verifying .debug_info references... + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 4 + sizeofcmds: 464 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 392 + segname: '' + vmaddr: 0 + vmsize: 261 + fileoff: 528 + filesize: 261 + maxprot: 7 + initprot: 7 + nsects: 4 + flags: 0 + Sections: + - sectname: __debug_abbrev + segname: __DWARF + addr: 0x0000000000000000 + size: 36 + offset: 0x00000210 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 011101030E1305110155170000022E00030E110112060000032E00030E11011201000000 + - sectname: __debug_info + segname: __DWARF + addr: 0x0000000000000024 + size: 103 + offset: 0x00000234 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 630000000400000000000801010000000200000000000000000000000000020D0000000000000000000000200000000317000000000000000000000030000000000000000221000000001000000000000000100000022600000000200000000000000010000000 + - sectname: __debug_ranges + segname: __DWARF + addr: 0x000000000000008B + size: 80 + offset: 0x0000029B + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: '0020000000000000003000000000000000000000000000002000000000000000000000000000000030000000000000000010000000000000002000000000000000000000000000000000000000000000' + - sectname: __debug_str + segname: __DWARF + addr: 0x00000000000000DB + size: 42 + offset: 0x000002EB + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 002F746D702F6D61696E2E630073747269707065643100737472697070656432006D61696E00666F6F00 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 0 + nsyms: 0 + stroff: 792 + strsize: 8 + - cmd: LC_BUILD_VERSION + cmdsize: 32 + platform: 1 + minos: 658944 + sdk: 658944 + ntools: 1 + Tools: + - tool: 3 + version: 34734080 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 792 + datasize: 0 +LinkEditData: + StringTable: + - ' ' + - '' + - '' + - '' + - '' + - '' + - '' +DWARF: + debug_str: + - '' + - '/tmp/main.c' + - stripped1 + - stripped2 + - main + - foo + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_ranges + Form: DW_FORM_sec_offset + - Code: 0x00000002 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Code: 0x00000003 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + debug_ranges: + - Offset: 0x00000000 + AddrSize: 0x08 + Entries: + - LowOffset: 0x0000000000002000 + HighOffset: 0x0000000000003000 + - LowOffset: 0x0000000000000000 + HighOffset: 0x0000000000000020 + - LowOffset: 0x0000000000000000 + HighOffset: 0x0000000000000030 + - LowOffset: 0x0000000000001000 + HighOffset: 0x0000000000002000 + debug_info: + - Length: + TotalLength: 99 + Version: 4 + AbbrOffset: 0 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000000001 + - Value: 0x0000000000000002 + - Value: 0x0000000000000000 + - Value: 0x0000000000000000 + - AbbrCode: 0x00000002 + Values: + - Value: 0x000000000000000D + - Value: 0x0000000000000000 + - Value: 0x0000000000000020 + - AbbrCode: 0x00000003 + Values: + - Value: 0x0000000000000017 + - Value: 0x0000000000000000 + - Value: 0x0000000000000030 + - AbbrCode: 0x00000002 + Values: + - Value: 0x0000000000000021 + - Value: 0x0000000000001000 + - Value: 0x0000000000001000 + - AbbrCode: 0x00000002 + Values: + - Value: 0x0000000000000026 + - Value: 0x0000000000002000 + - Value: 0x0000000000001000 + - AbbrCode: 0x00000000 + Values: [] +...