This patch verifies the number of atoms, the validity of the form for each atom, as well as the validity of the hashdata. For hashdata, we're verifying that the hashdata offset is correct and that the offset in the .debug_info for each DIE in the hashdata is also valid. llvm-svn: 306735
371 lines
12 KiB
C++
371 lines
12 KiB
C++
//===- DWARFVerifier.cpp --------------------------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFSection.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <map>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
using namespace dwarf;
|
|
using namespace object;
|
|
|
|
void DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
|
|
DWARFAttribute &AttrValue) {
|
|
const auto Attr = AttrValue.Attr;
|
|
switch (Attr) {
|
|
case DW_AT_ranges:
|
|
// Make sure the offset in the DW_AT_ranges attribute is valid.
|
|
if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
|
|
if (*SectionOffset >= DCtx.getRangeSection().Data.size()) {
|
|
++NumDebugInfoErrors;
|
|
OS << "error: DW_AT_ranges offset is beyond .debug_ranges "
|
|
"bounds:\n";
|
|
Die.dump(OS, 0);
|
|
OS << "\n";
|
|
}
|
|
} else {
|
|
++NumDebugInfoErrors;
|
|
OS << "error: DIE has invalid DW_AT_ranges encoding:\n";
|
|
Die.dump(OS, 0);
|
|
OS << "\n";
|
|
}
|
|
break;
|
|
case DW_AT_stmt_list:
|
|
// Make sure the offset in the DW_AT_stmt_list attribute is valid.
|
|
if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
|
|
if (*SectionOffset >= DCtx.getLineSection().Data.size()) {
|
|
++NumDebugInfoErrors;
|
|
OS << "error: DW_AT_stmt_list offset is beyond .debug_line "
|
|
"bounds: "
|
|
<< format("0x%08" PRIx32, *SectionOffset) << "\n";
|
|
Die.dump(OS, 0);
|
|
OS << "\n";
|
|
}
|
|
} else {
|
|
++NumDebugInfoErrors;
|
|
OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n";
|
|
Die.dump(OS, 0);
|
|
OS << "\n";
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
|
|
DWARFAttribute &AttrValue) {
|
|
const auto Form = AttrValue.Value.getForm();
|
|
switch (Form) {
|
|
case DW_FORM_ref1:
|
|
case DW_FORM_ref2:
|
|
case DW_FORM_ref4:
|
|
case DW_FORM_ref8:
|
|
case DW_FORM_ref_udata: {
|
|
// Verify all CU relative references are valid CU offsets.
|
|
Optional<uint64_t> RefVal = AttrValue.Value.getAsReference();
|
|
assert(RefVal);
|
|
if (RefVal) {
|
|
auto DieCU = Die.getDwarfUnit();
|
|
auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset();
|
|
auto CUOffset = AttrValue.Value.getRawUValue();
|
|
if (CUOffset >= CUSize) {
|
|
++NumDebugInfoErrors;
|
|
OS << "error: " << FormEncodingString(Form) << " CU offset "
|
|
<< format("0x%08" PRIx32, CUOffset)
|
|
<< " is invalid (must be less than CU size of "
|
|
<< format("0x%08" PRIx32, CUSize) << "):\n";
|
|
Die.dump(OS, 0);
|
|
OS << "\n";
|
|
} else {
|
|
// Valid reference, but we will verify it points to an actual
|
|
// DIE later.
|
|
ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case DW_FORM_ref_addr: {
|
|
// Verify all absolute DIE references have valid offsets in the
|
|
// .debug_info section.
|
|
Optional<uint64_t> RefVal = AttrValue.Value.getAsReference();
|
|
assert(RefVal);
|
|
if (RefVal) {
|
|
if (*RefVal >= DCtx.getInfoSection().Data.size()) {
|
|
++NumDebugInfoErrors;
|
|
OS << "error: DW_FORM_ref_addr offset beyond .debug_info "
|
|
"bounds:\n";
|
|
Die.dump(OS, 0);
|
|
OS << "\n";
|
|
} else {
|
|
// Valid reference, but we will verify it points to an actual
|
|
// DIE later.
|
|
ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case DW_FORM_strp: {
|
|
auto SecOffset = AttrValue.Value.getAsSectionOffset();
|
|
assert(SecOffset); // DW_FORM_strp is a section offset.
|
|
if (SecOffset && *SecOffset >= DCtx.getStringSection().size()) {
|
|
++NumDebugInfoErrors;
|
|
OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n";
|
|
Die.dump(OS, 0);
|
|
OS << "\n";
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DWARFVerifier::verifyDebugInfoReferences() {
|
|
// Take all references and make sure they point to an actual DIE by
|
|
// getting the DIE by offset and emitting an error
|
|
OS << "Verifying .debug_info references...\n";
|
|
for (auto Pair : ReferenceToDIEOffsets) {
|
|
auto Die = DCtx.getDIEForOffset(Pair.first);
|
|
if (Die)
|
|
continue;
|
|
++NumDebugInfoErrors;
|
|
OS << "error: invalid DIE reference " << format("0x%08" PRIx64, Pair.first)
|
|
<< ". Offset is in between DIEs:\n";
|
|
for (auto Offset : Pair.second) {
|
|
auto ReferencingDie = DCtx.getDIEForOffset(Offset);
|
|
ReferencingDie.dump(OS, 0);
|
|
OS << "\n";
|
|
}
|
|
OS << "\n";
|
|
}
|
|
}
|
|
|
|
bool DWARFVerifier::handleDebugInfo() {
|
|
NumDebugInfoErrors = 0;
|
|
OS << "Verifying .debug_info...\n";
|
|
for (const auto &CU : DCtx.compile_units()) {
|
|
unsigned NumDies = CU->getNumDIEs();
|
|
for (unsigned I = 0; I < NumDies; ++I) {
|
|
auto Die = CU->getDIEAtIndex(I);
|
|
const auto Tag = Die.getTag();
|
|
if (Tag == DW_TAG_null)
|
|
continue;
|
|
for (auto AttrValue : Die.attributes()) {
|
|
verifyDebugInfoAttribute(Die, AttrValue);
|
|
verifyDebugInfoForm(Die, AttrValue);
|
|
}
|
|
}
|
|
}
|
|
verifyDebugInfoReferences();
|
|
return NumDebugInfoErrors == 0;
|
|
}
|
|
|
|
void DWARFVerifier::verifyDebugLineStmtOffsets() {
|
|
std::map<uint64_t, DWARFDie> StmtListToDie;
|
|
for (const auto &CU : DCtx.compile_units()) {
|
|
auto Die = CU->getUnitDIE();
|
|
// Get the attribute value as a section offset. No need to produce an
|
|
// error here if the encoding isn't correct because we validate this in
|
|
// the .debug_info verifier.
|
|
auto StmtSectionOffset = toSectionOffset(Die.find(DW_AT_stmt_list));
|
|
if (!StmtSectionOffset)
|
|
continue;
|
|
const uint32_t LineTableOffset = *StmtSectionOffset;
|
|
auto LineTable = DCtx.getLineTableForUnit(CU.get());
|
|
if (LineTableOffset < DCtx.getLineSection().Data.size()) {
|
|
if (!LineTable) {
|
|
++NumDebugLineErrors;
|
|
OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset)
|
|
<< "] was not able to be parsed for CU:\n";
|
|
Die.dump(OS, 0);
|
|
OS << '\n';
|
|
continue;
|
|
}
|
|
} else {
|
|
// Make sure we don't get a valid line table back if the offset is wrong.
|
|
assert(LineTable == nullptr);
|
|
// Skip this line table as it isn't valid. No need to create an error
|
|
// here because we validate this in the .debug_info verifier.
|
|
continue;
|
|
}
|
|
auto Iter = StmtListToDie.find(LineTableOffset);
|
|
if (Iter != StmtListToDie.end()) {
|
|
++NumDebugLineErrors;
|
|
OS << "error: two compile unit DIEs, "
|
|
<< format("0x%08" PRIx32, Iter->second.getOffset()) << " and "
|
|
<< format("0x%08" PRIx32, Die.getOffset())
|
|
<< ", have the same DW_AT_stmt_list section offset:\n";
|
|
Iter->second.dump(OS, 0);
|
|
Die.dump(OS, 0);
|
|
OS << '\n';
|
|
// Already verified this line table before, no need to do it again.
|
|
continue;
|
|
}
|
|
StmtListToDie[LineTableOffset] = Die;
|
|
}
|
|
}
|
|
|
|
void DWARFVerifier::verifyDebugLineRows() {
|
|
for (const auto &CU : DCtx.compile_units()) {
|
|
auto Die = CU->getUnitDIE();
|
|
auto LineTable = DCtx.getLineTableForUnit(CU.get());
|
|
// If there is no line table we will have created an error in the
|
|
// .debug_info verifier or in verifyDebugLineStmtOffsets().
|
|
if (!LineTable)
|
|
continue;
|
|
uint32_t MaxFileIndex = LineTable->Prologue.FileNames.size();
|
|
uint64_t PrevAddress = 0;
|
|
uint32_t RowIndex = 0;
|
|
for (const auto &Row : LineTable->Rows) {
|
|
if (Row.Address < PrevAddress) {
|
|
++NumDebugLineErrors;
|
|
OS << "error: .debug_line["
|
|
<< format("0x%08" PRIx32,
|
|
*toSectionOffset(Die.find(DW_AT_stmt_list)))
|
|
<< "] row[" << RowIndex
|
|
<< "] decreases in address from previous row:\n";
|
|
|
|
DWARFDebugLine::Row::dumpTableHeader(OS);
|
|
if (RowIndex > 0)
|
|
LineTable->Rows[RowIndex - 1].dump(OS);
|
|
Row.dump(OS);
|
|
OS << '\n';
|
|
}
|
|
|
|
if (Row.File > MaxFileIndex) {
|
|
++NumDebugLineErrors;
|
|
OS << "error: .debug_line["
|
|
<< format("0x%08" PRIx32,
|
|
*toSectionOffset(Die.find(DW_AT_stmt_list)))
|
|
<< "][" << RowIndex << "] has invalid file index " << Row.File
|
|
<< " (valid values are [1," << MaxFileIndex << "]):\n";
|
|
DWARFDebugLine::Row::dumpTableHeader(OS);
|
|
Row.dump(OS);
|
|
OS << '\n';
|
|
}
|
|
if (Row.EndSequence)
|
|
PrevAddress = 0;
|
|
else
|
|
PrevAddress = Row.Address;
|
|
++RowIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DWARFVerifier::handleDebugLine() {
|
|
NumDebugLineErrors = 0;
|
|
OS << "Verifying .debug_line...\n";
|
|
verifyDebugLineStmtOffsets();
|
|
verifyDebugLineRows();
|
|
return NumDebugLineErrors == 0;
|
|
}
|
|
|
|
bool DWARFVerifier::handleAppleNames() {
|
|
NumAppleNamesErrors = 0;
|
|
|
|
DWARFDataExtractor AppleNamesSection(DCtx.getAppleNamesSection(),
|
|
DCtx.isLittleEndian(), 0);
|
|
DataExtractor StrData(DCtx.getStringSection(), DCtx.isLittleEndian(), 0);
|
|
DWARFAcceleratorTable AppleNames(AppleNamesSection, StrData);
|
|
|
|
if (!AppleNames.extract()) {
|
|
return true;
|
|
}
|
|
|
|
OS << "Verifying .apple_names...\n";
|
|
|
|
// Verify that all buckets have a valid hash index or are empty.
|
|
uint32_t NumBuckets = AppleNames.getNumBuckets();
|
|
uint32_t NumHashes = AppleNames.getNumHashes();
|
|
|
|
uint32_t BucketsOffset =
|
|
AppleNames.getSizeHdr() + AppleNames.getHeaderDataLength();
|
|
uint32_t HashesBase = BucketsOffset + NumBuckets * 4;
|
|
uint32_t OffsetsBase = HashesBase + NumHashes * 4;
|
|
|
|
for (uint32_t BucketIdx = 0; BucketIdx < NumBuckets; ++BucketIdx) {
|
|
uint32_t HashIdx = AppleNamesSection.getU32(&BucketsOffset);
|
|
if (HashIdx >= NumHashes && HashIdx != UINT32_MAX) {
|
|
OS << format("error: Bucket[%d] has invalid hash index: %u\n", BucketIdx,
|
|
HashIdx);
|
|
++NumAppleNamesErrors;
|
|
}
|
|
}
|
|
|
|
uint32_t NumAtoms = AppleNames.getAtomsDesc().size();
|
|
if (NumAtoms == 0) {
|
|
OS << "error: no atoms; failed to read HashData\n";
|
|
++NumAppleNamesErrors;
|
|
return false;
|
|
}
|
|
|
|
if (!AppleNames.validateForms()) {
|
|
OS << "error: unsupported form; failed to read HashData\n";
|
|
++NumAppleNamesErrors;
|
|
return false;
|
|
}
|
|
|
|
for (uint32_t HashIdx = 0; HashIdx < NumHashes; ++HashIdx) {
|
|
uint32_t HashOffset = HashesBase + 4 * HashIdx;
|
|
uint32_t DataOffset = OffsetsBase + 4 * HashIdx;
|
|
uint32_t Hash = AppleNamesSection.getU32(&HashOffset);
|
|
uint32_t HashDataOffset = AppleNamesSection.getU32(&DataOffset);
|
|
if (!AppleNamesSection.isValidOffsetForDataOfSize(HashDataOffset,
|
|
sizeof(uint64_t))) {
|
|
OS << format("error: Hash[%d] has invalid HashData offset: 0x%08x\n",
|
|
HashIdx, HashDataOffset);
|
|
++NumAppleNamesErrors;
|
|
}
|
|
|
|
uint32_t StrpOffset;
|
|
uint32_t StringOffset;
|
|
uint32_t StringCount = 0;
|
|
uint32_t DieOffset = dwarf::DW_INVALID_OFFSET;
|
|
|
|
while ((StrpOffset = AppleNamesSection.getU32(&HashDataOffset)) != 0) {
|
|
const uint32_t NumHashDataObjects =
|
|
AppleNamesSection.getU32(&HashDataOffset);
|
|
for (uint32_t HashDataIdx = 0; HashDataIdx < NumHashDataObjects;
|
|
++HashDataIdx) {
|
|
DieOffset = AppleNames.readAtoms(HashDataOffset);
|
|
if (!DCtx.getDIEForOffset(DieOffset)) {
|
|
const uint32_t BucketIdx =
|
|
NumBuckets ? (Hash % NumBuckets) : UINT32_MAX;
|
|
StringOffset = StrpOffset;
|
|
const char *Name = StrData.getCStr(&StringOffset);
|
|
if (!Name)
|
|
Name = "<NULL>";
|
|
|
|
OS << format(
|
|
"error: .apple_names Bucket[%d] Hash[%d] = 0x%08x "
|
|
"Str[%u] = 0x%08x "
|
|
"DIE[%d] = 0x%08x is not a valid DIE offset for \"%s\".\n",
|
|
BucketIdx, HashIdx, Hash, StringCount, StrpOffset, HashDataIdx,
|
|
DieOffset, Name);
|
|
|
|
++NumAppleNamesErrors;
|
|
}
|
|
}
|
|
++StringCount;
|
|
}
|
|
}
|
|
return NumAppleNamesErrors == 0;
|
|
}
|