forked from OSchip/llvm-project
[DWARF] Generalized verification of .apple_names accelerator table to be applicable to any acceleration table. Added verification for .apple_types, .apple_namespaces and .apple_objc sections.
Differential Revision: https://reviews.llvm.org/D35853 llvm-svn: 309068
This commit is contained in:
parent
603ea2df2a
commit
dc635f40bb
|
@ -23,6 +23,8 @@ class DWARFUnit;
|
||||||
class DWARFAcceleratorTable;
|
class DWARFAcceleratorTable;
|
||||||
class DWARFDataExtractor;
|
class DWARFDataExtractor;
|
||||||
class DWARFDebugAbbrev;
|
class DWARFDebugAbbrev;
|
||||||
|
class DataExtractor;
|
||||||
|
struct DWARFSection;
|
||||||
|
|
||||||
/// A class that verifies DWARF debug information given a DWARF Context.
|
/// A class that verifies DWARF debug information given a DWARF Context.
|
||||||
class DWARFVerifier {
|
class DWARFVerifier {
|
||||||
|
@ -33,7 +35,6 @@ class DWARFVerifier {
|
||||||
/// lies between to valid DIEs.
|
/// lies between to valid DIEs.
|
||||||
std::map<uint64_t, std::set<uint32_t>> ReferenceToDIEOffsets;
|
std::map<uint64_t, std::set<uint32_t>> ReferenceToDIEOffsets;
|
||||||
uint32_t NumDebugLineErrors = 0;
|
uint32_t NumDebugLineErrors = 0;
|
||||||
uint32_t NumAppleNamesErrors = 0;
|
|
||||||
|
|
||||||
/// Verifies the abbreviations section.
|
/// Verifies the abbreviations section.
|
||||||
///
|
///
|
||||||
|
@ -133,6 +134,25 @@ class DWARFVerifier {
|
||||||
/// - invalid file indexes
|
/// - invalid file indexes
|
||||||
void verifyDebugLineRows();
|
void verifyDebugLineRows();
|
||||||
|
|
||||||
|
/// Verify that an Apple-style accelerator table is valid.
|
||||||
|
///
|
||||||
|
/// This function currently checks that:
|
||||||
|
/// - The fixed part of the header fits in the section
|
||||||
|
/// - The size of the section is as large as what the header describes
|
||||||
|
/// - There is at least one atom
|
||||||
|
/// - The form for each atom is valid
|
||||||
|
/// - The buckets have a valid index, or they are empty
|
||||||
|
/// - Each hashdata offset is valid
|
||||||
|
/// - Each DIE is valid
|
||||||
|
///
|
||||||
|
/// \param AccelSection pointer to the section containing the acceleration table
|
||||||
|
/// \param StrData pointer to the string section
|
||||||
|
/// \param SectionName the name of the table we're verifying
|
||||||
|
///
|
||||||
|
/// \returns The number of errors occured during verification
|
||||||
|
unsigned verifyAccelTable(const DWARFSection *AccelSection,
|
||||||
|
DataExtractor *StrData, const char *SectionName);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DWARFVerifier(raw_ostream &S, DWARFContext &D)
|
DWARFVerifier(raw_ostream &S, DWARFContext &D)
|
||||||
: OS(S), DCtx(D) {}
|
: OS(S), DCtx(D) {}
|
||||||
|
@ -162,13 +182,14 @@ public:
|
||||||
/// \returns true if the .debug_line verifies successfully, false otherwise.
|
/// \returns true if the .debug_line verifies successfully, false otherwise.
|
||||||
bool handleDebugLine();
|
bool handleDebugLine();
|
||||||
|
|
||||||
/// Verify the information in the .apple_names accelerator table.
|
/// Verify the information in accelerator tables, if they exist.
|
||||||
///
|
///
|
||||||
/// Any errors are reported to the stream that was this object was
|
/// Any errors are reported to the stream that was this object was
|
||||||
/// constructed with.
|
/// constructed with.
|
||||||
///
|
///
|
||||||
/// \returns true if the .apple_names verifies successfully, false otherwise.
|
/// \returns true if the existing Apple-style accelerator tables verify
|
||||||
bool handleAppleNames();
|
/// successfully, false otherwise.
|
||||||
|
bool handleAccelTables();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace llvm
|
} // end namespace llvm
|
||||||
|
|
|
@ -427,10 +427,7 @@ bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) {
|
||||||
if (!verifier.handleDebugLine())
|
if (!verifier.handleDebugLine())
|
||||||
Success = false;
|
Success = false;
|
||||||
}
|
}
|
||||||
if (DumpType == DIDT_All || DumpType == DIDT_AppleNames) {
|
Success &= verifier.handleAccelTables();
|
||||||
if (!verifier.handleAppleNames())
|
|
||||||
Success = false;
|
|
||||||
}
|
|
||||||
return Success;
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -464,61 +464,62 @@ bool DWARFVerifier::handleDebugLine() {
|
||||||
return NumDebugLineErrors == 0;
|
return NumDebugLineErrors == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DWARFVerifier::handleAppleNames() {
|
unsigned DWARFVerifier::verifyAccelTable(const DWARFSection *AccelSection,
|
||||||
NumAppleNamesErrors = 0;
|
DataExtractor *StrData,
|
||||||
const DWARFObject &D = DCtx.getDWARFObj();
|
const char *SectionName) {
|
||||||
DWARFDataExtractor AppleNamesSection(D, D.getAppleNamesSection(),
|
unsigned NumErrors = 0;
|
||||||
DCtx.isLittleEndian(), 0);
|
DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), *AccelSection,
|
||||||
DataExtractor StrData(D.getStringSection(), DCtx.isLittleEndian(), 0);
|
DCtx.isLittleEndian(), 0);
|
||||||
DWARFAcceleratorTable AppleNames(AppleNamesSection, StrData);
|
DWARFAcceleratorTable AccelTable(AccelSectionData, *StrData);
|
||||||
|
|
||||||
if (!AppleNames.extract()) {
|
OS << "Verifying " << SectionName << "...\n";
|
||||||
return true;
|
// Verify that the fixed part of the header is not too short.
|
||||||
|
|
||||||
|
if (!AccelSectionData.isValidOffset(AccelTable.getSizeHdr())) {
|
||||||
|
OS << "\terror: Section is too small to fit a section header.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// Verify that the section is not too short.
|
||||||
|
if (!AccelTable.extract()) {
|
||||||
|
OS << "\terror: Section is smaller than size described in section header.\n";
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
OS << "Verifying .apple_names...\n";
|
|
||||||
|
|
||||||
// Verify that all buckets have a valid hash index or are empty.
|
// Verify that all buckets have a valid hash index or are empty.
|
||||||
uint32_t NumBuckets = AppleNames.getNumBuckets();
|
uint32_t NumBuckets = AccelTable.getNumBuckets();
|
||||||
uint32_t NumHashes = AppleNames.getNumHashes();
|
uint32_t NumHashes = AccelTable.getNumHashes();
|
||||||
|
|
||||||
uint32_t BucketsOffset =
|
uint32_t BucketsOffset =
|
||||||
AppleNames.getSizeHdr() + AppleNames.getHeaderDataLength();
|
AccelTable.getSizeHdr() + AccelTable.getHeaderDataLength();
|
||||||
uint32_t HashesBase = BucketsOffset + NumBuckets * 4;
|
uint32_t HashesBase = BucketsOffset + NumBuckets * 4;
|
||||||
uint32_t OffsetsBase = HashesBase + NumHashes * 4;
|
uint32_t OffsetsBase = HashesBase + NumHashes * 4;
|
||||||
|
|
||||||
for (uint32_t BucketIdx = 0; BucketIdx < NumBuckets; ++BucketIdx) {
|
for (uint32_t BucketIdx = 0; BucketIdx < NumBuckets; ++BucketIdx) {
|
||||||
uint32_t HashIdx = AppleNamesSection.getU32(&BucketsOffset);
|
uint32_t HashIdx = AccelSectionData.getU32(&BucketsOffset);
|
||||||
if (HashIdx >= NumHashes && HashIdx != UINT32_MAX) {
|
if (HashIdx >= NumHashes && HashIdx != UINT32_MAX) {
|
||||||
OS << format("error: Bucket[%d] has invalid hash index: %u\n", BucketIdx,
|
OS << format("\terror: Bucket[%d] has invalid hash index: %u.\n", BucketIdx,
|
||||||
HashIdx);
|
HashIdx);
|
||||||
++NumAppleNamesErrors;
|
++NumErrors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
uint32_t NumAtoms = AccelTable.getAtomsDesc().size();
|
||||||
uint32_t NumAtoms = AppleNames.getAtomsDesc().size();
|
|
||||||
if (NumAtoms == 0) {
|
if (NumAtoms == 0) {
|
||||||
OS << "error: no atoms; failed to read HashData\n";
|
OS << "\terror: no atoms; failed to read HashData.\n";
|
||||||
++NumAppleNamesErrors;
|
return 1;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
if (!AccelTable.validateForms()) {
|
||||||
if (!AppleNames.validateForms()) {
|
OS << "\terror: unsupported form; failed to read HashData.\n";
|
||||||
OS << "error: unsupported form; failed to read HashData\n";
|
return 1;
|
||||||
++NumAppleNamesErrors;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t HashIdx = 0; HashIdx < NumHashes; ++HashIdx) {
|
for (uint32_t HashIdx = 0; HashIdx < NumHashes; ++HashIdx) {
|
||||||
uint32_t HashOffset = HashesBase + 4 * HashIdx;
|
uint32_t HashOffset = HashesBase + 4 * HashIdx;
|
||||||
uint32_t DataOffset = OffsetsBase + 4 * HashIdx;
|
uint32_t DataOffset = OffsetsBase + 4 * HashIdx;
|
||||||
uint32_t Hash = AppleNamesSection.getU32(&HashOffset);
|
uint32_t Hash = AccelSectionData.getU32(&HashOffset);
|
||||||
uint32_t HashDataOffset = AppleNamesSection.getU32(&DataOffset);
|
uint32_t HashDataOffset = AccelSectionData.getU32(&DataOffset);
|
||||||
if (!AppleNamesSection.isValidOffsetForDataOfSize(HashDataOffset,
|
if (!AccelSectionData.isValidOffsetForDataOfSize(HashDataOffset,
|
||||||
sizeof(uint64_t))) {
|
sizeof(uint64_t))) {
|
||||||
OS << format("error: Hash[%d] has invalid HashData offset: 0x%08x\n",
|
OS << format("\terror: Hash[%d] has invalid HashData offset: 0x%08x.\n",
|
||||||
HashIdx, HashDataOffset);
|
HashIdx, HashDataOffset);
|
||||||
++NumAppleNamesErrors;
|
++NumErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t StrpOffset;
|
uint32_t StrpOffset;
|
||||||
|
@ -526,32 +527,51 @@ bool DWARFVerifier::handleAppleNames() {
|
||||||
uint32_t StringCount = 0;
|
uint32_t StringCount = 0;
|
||||||
uint32_t DieOffset = dwarf::DW_INVALID_OFFSET;
|
uint32_t DieOffset = dwarf::DW_INVALID_OFFSET;
|
||||||
|
|
||||||
while ((StrpOffset = AppleNamesSection.getU32(&HashDataOffset)) != 0) {
|
while ((StrpOffset = AccelSectionData.getU32(&HashDataOffset)) != 0) {
|
||||||
const uint32_t NumHashDataObjects =
|
const uint32_t NumHashDataObjects =
|
||||||
AppleNamesSection.getU32(&HashDataOffset);
|
AccelSectionData.getU32(&HashDataOffset);
|
||||||
for (uint32_t HashDataIdx = 0; HashDataIdx < NumHashDataObjects;
|
for (uint32_t HashDataIdx = 0; HashDataIdx < NumHashDataObjects;
|
||||||
++HashDataIdx) {
|
++HashDataIdx) {
|
||||||
DieOffset = AppleNames.readAtoms(HashDataOffset);
|
DieOffset = AccelTable.readAtoms(HashDataOffset);
|
||||||
if (!DCtx.getDIEForOffset(DieOffset)) {
|
if (!DCtx.getDIEForOffset(DieOffset)) {
|
||||||
const uint32_t BucketIdx =
|
const uint32_t BucketIdx =
|
||||||
NumBuckets ? (Hash % NumBuckets) : UINT32_MAX;
|
NumBuckets ? (Hash % NumBuckets) : UINT32_MAX;
|
||||||
StringOffset = StrpOffset;
|
StringOffset = StrpOffset;
|
||||||
const char *Name = StrData.getCStr(&StringOffset);
|
const char *Name = StrData->getCStr(&StringOffset);
|
||||||
if (!Name)
|
if (!Name)
|
||||||
Name = "<NULL>";
|
Name = "<NULL>";
|
||||||
|
|
||||||
OS << format(
|
OS << format(
|
||||||
"error: .apple_names Bucket[%d] Hash[%d] = 0x%08x "
|
"\terror: %s Bucket[%d] Hash[%d] = 0x%08x "
|
||||||
"Str[%u] = 0x%08x "
|
"Str[%u] = 0x%08x "
|
||||||
"DIE[%d] = 0x%08x is not a valid DIE offset for \"%s\".\n",
|
"DIE[%d] = 0x%08x is not a valid DIE offset for \"%s\".\n",
|
||||||
BucketIdx, HashIdx, Hash, StringCount, StrpOffset, HashDataIdx,
|
SectionName, BucketIdx, HashIdx, Hash, StringCount, StrpOffset,
|
||||||
DieOffset, Name);
|
HashDataIdx, DieOffset, Name);
|
||||||
|
|
||||||
++NumAppleNamesErrors;
|
++NumErrors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++StringCount;
|
++StringCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NumAppleNamesErrors == 0;
|
return NumErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DWARFVerifier::handleAccelTables() {
|
||||||
|
const DWARFObject &D = DCtx.getDWARFObj();
|
||||||
|
DataExtractor StrData(D.getStringSection(), DCtx.isLittleEndian(), 0);
|
||||||
|
unsigned NumErrors = 0;
|
||||||
|
if (!D.getAppleNamesSection().Data.empty())
|
||||||
|
NumErrors +=
|
||||||
|
verifyAccelTable(&D.getAppleNamesSection(), &StrData, ".apple_names");
|
||||||
|
if (!D.getAppleTypesSection().Data.empty())
|
||||||
|
NumErrors +=
|
||||||
|
verifyAccelTable(&D.getAppleTypesSection(), &StrData, ".apple_types");
|
||||||
|
if (!D.getAppleNamespacesSection().Data.empty())
|
||||||
|
NumErrors += verifyAccelTable(&D.getAppleNamespacesSection(), &StrData,
|
||||||
|
".apple_namespaces");
|
||||||
|
if (!D.getAppleObjCSection().Data.empty())
|
||||||
|
NumErrors +=
|
||||||
|
verifyAccelTable(&D.getAppleObjCSection(), &StrData, ".apple_objc");
|
||||||
|
return NumErrors == 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-objc.x86_64.o | FileCheck %s
|
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-objc.x86_64.o | FileCheck %s
|
||||||
RUN: llvm-dwarfdump -verify %p/Inputs/dwarfdump-objc.x86_64.o | FileCheck %s --check-prefix=VERIFY
|
RUN: not llvm-dwarfdump -verify %p/Inputs/dwarfdump-objc.x86_64.o | FileCheck %s --check-prefix=VERIFY
|
||||||
|
|
||||||
Gather some DIE indexes to verify the accelerator table contents.
|
Gather some DIE indexes to verify the accelerator table contents.
|
||||||
CHECK: .debug_info contents
|
CHECK: .debug_info contents
|
||||||
|
@ -66,5 +66,9 @@ CHECK: {Atom[0]: [[ASSIGN]]}
|
||||||
CHECK: {Atom[0]: [[SETASSIGN]]}
|
CHECK: {Atom[0]: [[SETASSIGN]]}
|
||||||
|
|
||||||
Verify the debug info in the apple_names accelerator table.
|
Verify the debug info in the apple_names accelerator table.
|
||||||
VERIFY: Verifying .apple_names
|
VERIFY: Verifying .apple_names...
|
||||||
VERIFY-NEXT: No errors.
|
VERIFY-NEXT: Verifying .apple_types...
|
||||||
|
VERIFY-NEXT: Verifying .apple_namespaces...
|
||||||
|
VERIFY-NEXT: error: Section is smaller than size described in section header.
|
||||||
|
VERIFY-NEXT: Verifying .apple_objc...
|
||||||
|
VERIFY-NEXT: Errors detected.
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
# RUN: | FileCheck %s
|
# RUN: | FileCheck %s
|
||||||
|
|
||||||
# CHECK: Verifying .apple_names...
|
# CHECK: Verifying .apple_names...
|
||||||
# CHECK-NEXT: error: Bucket[0] has invalid hash index: 4294967294
|
# CHECK-NEXT: error: Bucket[0] has invalid hash index: 4294967294.
|
||||||
# CHECK-NEXT: error: Hash[0] has invalid HashData offset: 0x000000b4
|
# CHECK-NEXT: error: Hash[0] has invalid HashData offset: 0x000000b4.
|
||||||
# CHECK-NEXT: error: .apple_names Bucket[1] Hash[1] = 0x0002b60f Str[0] = 0x0000005a DIE[0] = 0x00000001 is not a valid DIE offset for "j".
|
# CHECK-NEXT: error: .apple_names Bucket[1] Hash[1] = 0x0002b60f Str[0] = 0x0000005a DIE[0] = 0x00000001 is not a valid DIE offset for "j".
|
||||||
|
|
||||||
# This test is meant to verify that the -verify option
|
# This test is meant to verify that the -verify option
|
||||||
# in llvm-dwarfdump, correctly identifies
|
# in llvm-dwarfdump, correctly identifies
|
||||||
|
@ -41,11 +41,11 @@ Lnames_begin:
|
||||||
.long 1 ## HeaderData Atom Count
|
.long 1 ## HeaderData Atom Count
|
||||||
.short 1 ## DW_ATOM_die_offset
|
.short 1 ## DW_ATOM_die_offset
|
||||||
.short 25 ## DW_FORM_data4 -- error: .apple_names Bucket[1] Hash[1] = 0x0002b60f Str[0] = 0x0000005a DIE[0] = 0x00000001 is not a valid DIE offset for "j".
|
.short 25 ## DW_FORM_data4 -- error: .apple_names Bucket[1] Hash[1] = 0x0002b60f Str[0] = 0x0000005a DIE[0] = 0x00000001 is not a valid DIE offset for "j".
|
||||||
.long -2 ## Bucket 0 -- error: Bucket[0] has invalid hash index: 4294967294
|
.long -2 ## Bucket 0 -- error: Bucket[0] has invalid hash index: 4294967294.
|
||||||
.long 1 ## Bucket 1
|
.long 1 ## Bucket 1
|
||||||
.long 177678 ## Hash in Bucket 0
|
.long 177678 ## Hash in Bucket 0
|
||||||
.long 177679 ## Hash in Bucket 1
|
.long 177679 ## Hash in Bucket 1
|
||||||
.long Lsection_line ## Offset in Bucket 0 -- error: Hash[0] has invalid HashData offset: 0x000000b4
|
.long Lsection_line ## Offset in Bucket 0 -- error: Hash[0] has invalid HashData offset: 0x000000b4.
|
||||||
.long LNames1-Lnames_begin ## Offset in Bucket 1
|
.long LNames1-Lnames_begin ## Offset in Bucket 1
|
||||||
LNames0:
|
LNames0:
|
||||||
.long 84 ## i
|
.long 84 ## i
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# RUN: | FileCheck %s
|
# RUN: | FileCheck %s
|
||||||
|
|
||||||
# CHECK: Verifying .apple_names...
|
# CHECK: Verifying .apple_names...
|
||||||
# CHECK-NEXT: error: unsupported form; failed to read HashData
|
# CHECK-NEXT: error: unsupported form; failed to read HashData.
|
||||||
|
|
||||||
# This test is meant to verify that the -verify option
|
# This test is meant to verify that the -verify option
|
||||||
# in llvm-dwarfdump, correctly identifies that Atom[0].form is unsupported.
|
# in llvm-dwarfdump, correctly identifies that Atom[0].form is unsupported.
|
||||||
|
@ -34,7 +34,7 @@ Lnames_begin:
|
||||||
.long 0 ## HeaderData Die Offset Base
|
.long 0 ## HeaderData Die Offset Base
|
||||||
.long 1 ## HeaderData Atom Count
|
.long 1 ## HeaderData Atom Count
|
||||||
.short 1 ## DW_ATOM_die_offset
|
.short 1 ## DW_ATOM_die_offset
|
||||||
.short 400 ## DW_FORM_data4 -- error: unsupported form; failed to read HashData
|
.short 400 ## DW_FORM_data4 -- error: unsupported form; failed to read HashData.
|
||||||
.long 0 ## Bucket 0
|
.long 0 ## Bucket 0
|
||||||
.long 1 ## Bucket 1
|
.long 1 ## Bucket 1
|
||||||
.long 177678 ## Hash in Bucket 0
|
.long 177678 ## Hash in Bucket 0
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# RUN: | FileCheck %s
|
# RUN: | FileCheck %s
|
||||||
|
|
||||||
# CHECK: Verifying .apple_names...
|
# CHECK: Verifying .apple_names...
|
||||||
# CHECK-NEXT: error: no atoms; failed to read HashData
|
# CHECK-NEXT: error: no atoms; failed to read HashData.
|
||||||
|
|
||||||
# This test is meant to verify that the -verify option
|
# This test is meant to verify that the -verify option
|
||||||
# in llvm-dwarfdump, correctly identifies that there is not Atom.
|
# in llvm-dwarfdump, correctly identifies that there is not Atom.
|
||||||
|
@ -33,7 +33,7 @@ Lnames_begin:
|
||||||
.long 2 ## Header Hash Count
|
.long 2 ## Header Hash Count
|
||||||
.long 12 ## Header Data Length
|
.long 12 ## Header Data Length
|
||||||
.long 0 ## HeaderData Die Offset Base
|
.long 0 ## HeaderData Die Offset Base
|
||||||
.long 0 ## HeaderData Atom Count -- error: no atoms; failed to read HashData
|
.long 0 ## HeaderData Atom Count -- error: no atoms; failed to read HashData.
|
||||||
.short 1 ## DW_ATOM_die_offset
|
.short 1 ## DW_ATOM_die_offset
|
||||||
.short 6 ## DW_FORM_data4
|
.short 6 ## DW_FORM_data4
|
||||||
.long 0 ## Bucket 0
|
.long 0 ## Bucket 0
|
||||||
|
|
Loading…
Reference in New Issue