Fix the verification of DIEs with DW_AT_ranges.

Summary: Previous code would try to verify DW_AT_ranges and if any ranges would overlap, it would stop attributing any ranges after this to the DIE which caused incorrect errors to be reported that a DIE's address ranges were not contained in the parent DIE's ranges. Added a fix and a test.

Reviewers: aprantl, labath, probinson, JDevlieghere, jhenderson

Subscribers: hiraditya, MaskRay, cmtice, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D79962
This commit is contained in:
Greg Clayton 2020-05-13 16:51:44 -07:00
parent bd7defeb94
commit ccf5a44917
4 changed files with 310 additions and 19 deletions

View File

@ -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<uint64_t>(LowPC, RHS.LowPC);
HighPC = std::max<uint64_t>(HighPC, RHS.HighPC);
return true;
}
void dump(raw_ostream &OS, uint32_t AddressSize, DIDumpOptions DumpOpts = {},
const DWARFObject *Obj = nullptr) const;
};

View File

@ -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 @@ public:
typedef std::set<DieRangeInfo>::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<DWARFAddressRange> insert(const DWARFAddressRange &R);
/// Finds an address range in the sorted vector of ranges.
address_range_iterator findRange(const DWARFAddressRange &R) const {

View File

@ -26,24 +26,26 @@ using namespace llvm;
using namespace dwarf;
using namespace object;
DWARFVerifier::DieRangeInfo::address_range_iterator
Optional<DWARFAddressRange>
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.

View File

@ -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: []
...