mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-10-19 02:52:53 +02:00
Adds initial llvm-dwarfdump --verify support with unit tests.
lldb-dwarfdump gets a new "--verify" option that will verify a single file's DWARF debug info and will print out any errors that it finds. It will return an non-zero exit status if verification fails, and a zero exit status if verification succeeds. Adding the --quiet option will suppress any output the STDOUT or STDERR. The first part of the verify does the following: - verifies that all CU relative references (DW_FORM_ref1, DW_FORM_ref2, DW_FORM_ref4, DW_FORM_ref8, DW_FORM_ref_udata) have valid CU offsets - verifies that all DW_FORM_ref_addr references have valid .debug_info offsets - verifies that all DW_AT_ranges attributes have valid .debug_ranges offsets - verifies that all DW_AT_stmt_list attributes have valid .debug_line offsets - verifies that all DW_FORM_strp attributes have valid .debug_str offsets Unit tests were added for each of the above cases. Differential Revision: https://reviews.llvm.org/D32707 llvm-svn: 301844
This commit is contained in:
parent
3a58d4ac26
commit
5e0a89ef4f
@ -161,6 +161,10 @@ public:
|
||||
virtual void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All,
|
||||
bool DumpEH = false, bool SummarizeTypes = false) = 0;
|
||||
|
||||
virtual bool verify(raw_ostream &OS, DIDumpType DumpType = DIDT_All) {
|
||||
// No verifier? Just say things went well.
|
||||
return true;
|
||||
}
|
||||
virtual DILineInfo getLineInfoForAddress(uint64_t Address,
|
||||
DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0;
|
||||
virtual DILineInfoTable getLineInfoForAddressRange(uint64_t Address,
|
||||
|
@ -106,6 +106,8 @@ public:
|
||||
void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All,
|
||||
bool DumpEH = false, bool SummarizeTypes = false) override;
|
||||
|
||||
bool verify(raw_ostream &OS, DIDumpType DumpType = DIDT_All) override;
|
||||
|
||||
typedef DWARFUnitSection<DWARFCompileUnit>::iterator_range cu_iterator_range;
|
||||
typedef DWARFUnitSection<DWARFTypeUnit>::iterator_range tu_iterator_range;
|
||||
typedef iterator_range<decltype(TUs)::iterator> tu_section_iterator_range;
|
||||
|
@ -59,6 +59,7 @@ public:
|
||||
DWARFFormValue(dwarf::Form F = dwarf::Form(0)) : Form(F) {}
|
||||
|
||||
dwarf::Form getForm() const { return Form; }
|
||||
uint64_t getRawUValue() const { return Value.uval; }
|
||||
void setForm(dwarf::Form F) { Form = F; }
|
||||
void setUValue(uint64_t V) { Value.uval = V; }
|
||||
void setSValue(int64_t V) { Value.sval = V; }
|
||||
|
@ -284,6 +284,119 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType, bool DumpEH,
|
||||
getStringSection(), isLittleEndian());
|
||||
}
|
||||
|
||||
bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) {
|
||||
bool Success = true;
|
||||
if (DumpType == DIDT_All || DumpType == DIDT_Info) {
|
||||
OS << "Verifying .debug_info...\n";
|
||||
for (const auto &CU : 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()) {
|
||||
const auto Attr = AttrValue.Attr;
|
||||
const auto Form = AttrValue.Value.getForm();
|
||||
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 >= getRangeSection().Data.size()) {
|
||||
Success = false;
|
||||
OS << "error: DW_AT_ranges offset is beyond .debug_ranges "
|
||||
"bounds:\n";
|
||||
Die.dump(OS, 0);
|
||||
OS << "\n";
|
||||
}
|
||||
} else {
|
||||
Success = false;
|
||||
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 >= getLineSection().Data.size()) {
|
||||
Success = false;
|
||||
OS << "error: DW_AT_stmt_list offset is beyond .debug_line "
|
||||
"bounds: "
|
||||
<< format("0x%08" PRIx32, *SectionOffset) << "\n";
|
||||
CU->getUnitDIE().dump(OS, 0);
|
||||
OS << "\n";
|
||||
}
|
||||
} else {
|
||||
Success = false;
|
||||
OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n";
|
||||
Die.dump(OS, 0);
|
||||
OS << "\n";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
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) {
|
||||
Success = false;
|
||||
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";
|
||||
}
|
||||
}
|
||||
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 && *RefVal >= getInfoSection().Data.size()) {
|
||||
Success = false;
|
||||
OS << "error: DW_FORM_ref_addr offset beyond .debug_info "
|
||||
"bounds:\n";
|
||||
Die.dump(OS, 0);
|
||||
OS << "\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DW_FORM_strp: {
|
||||
auto SecOffset = AttrValue.Value.getAsSectionOffset();
|
||||
assert(SecOffset); // DW_FORM_strp is a section offset.
|
||||
if (SecOffset && *SecOffset >= getStringSection().size()) {
|
||||
Success = false;
|
||||
OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n";
|
||||
Die.dump(OS, 0);
|
||||
OS << "\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
const DWARFUnitIndex &DWARFContext::getCUIndex() {
|
||||
if (CUIndex)
|
||||
return *CUIndex;
|
||||
|
@ -309,8 +309,10 @@ bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const {
|
||||
}
|
||||
// In DWARF3 DW_FORM_data4 and DW_FORM_data8 served also as a section offset.
|
||||
// Don't check for DWARF version here, as some producers may still do this
|
||||
// by mistake.
|
||||
return (Form == DW_FORM_data4 || Form == DW_FORM_data8) &&
|
||||
// by mistake. Also accept DW_FORM_strp since this is .debug_str section
|
||||
// offset.
|
||||
return (Form == DW_FORM_data4 || Form == DW_FORM_data8 ||
|
||||
Form == DW_FORM_strp) &&
|
||||
FC == FC_SectionOffset;
|
||||
}
|
||||
|
||||
|
@ -78,6 +78,11 @@ static cl::opt<bool>
|
||||
SummarizeTypes("summarize-types",
|
||||
cl::desc("Abbreviate the description of type unit entries"));
|
||||
|
||||
static cl::opt<bool> Verify("verify", cl::desc("Verify the DWARF debug info"));
|
||||
|
||||
static cl::opt<bool> Quiet("quiet",
|
||||
cl::desc("Use with -verify to not emit to STDOUT."));
|
||||
|
||||
static void error(StringRef Filename, std::error_code EC) {
|
||||
if (!EC)
|
||||
return;
|
||||
@ -116,6 +121,46 @@ static void DumpInput(StringRef Filename) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool VerifyObjectFile(ObjectFile &Obj, Twine Filename) {
|
||||
std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(Obj));
|
||||
|
||||
// Verify the DWARF and exit with non-zero exit status if verification
|
||||
// fails.
|
||||
raw_ostream &stream = Quiet ? nulls() : outs();
|
||||
stream << "Verifying " << Filename.str() << ":\tfile format "
|
||||
<< Obj.getFileFormatName() << "\n";
|
||||
bool Result = DICtx->verify(stream, DumpType);
|
||||
if (Result)
|
||||
stream << "No errors.\n";
|
||||
else
|
||||
stream << "Errors detected.\n";
|
||||
return Result;
|
||||
}
|
||||
|
||||
static bool VerifyInput(StringRef Filename) {
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
|
||||
MemoryBuffer::getFileOrSTDIN(Filename);
|
||||
error(Filename, BuffOrErr.getError());
|
||||
std::unique_ptr<MemoryBuffer> Buff = std::move(BuffOrErr.get());
|
||||
|
||||
Expected<std::unique_ptr<Binary>> BinOrErr =
|
||||
object::createBinary(Buff->getMemBufferRef());
|
||||
if (!BinOrErr)
|
||||
error(Filename, errorToErrorCode(BinOrErr.takeError()));
|
||||
|
||||
bool Result = true;
|
||||
if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get()))
|
||||
Result = VerifyObjectFile(*Obj, Filename);
|
||||
else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get()))
|
||||
for (auto &ObjForArch : Fat->objects()) {
|
||||
auto MachOOrErr = ObjForArch.getAsObjectFile();
|
||||
error(Filename, errorToErrorCode(MachOOrErr.takeError()));
|
||||
if (!VerifyObjectFile(**MachOOrErr, Filename + " (" + ObjForArch.getArchFlagName() + ")"))
|
||||
Result = false;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// If the input path is a .dSYM bundle (as created by the dsymutil tool),
|
||||
/// replace it with individual entries for each of the object files inside the
|
||||
/// bundle otherwise return the input path.
|
||||
@ -168,7 +213,13 @@ int main(int argc, char **argv) {
|
||||
Objects.insert(Objects.end(), Objs.begin(), Objs.end());
|
||||
}
|
||||
|
||||
std::for_each(Objects.begin(), Objects.end(), DumpInput);
|
||||
if (Verify) {
|
||||
// If we encountered errors during verify, exit with a non-zero exit status.
|
||||
if (!std::all_of(Objects.begin(), Objects.end(), VerifyInput))
|
||||
exit(1);
|
||||
} else {
|
||||
std::for_each(Objects.begin(), Objects.end(), DumpInput);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -1667,4 +1667,245 @@ TEST(DWARFDebugInfo, TestImplicitConstAbbrevs) {
|
||||
EXPECT_EQ(DIEs.find(Val2)->second, AbbrevPtrVal2);
|
||||
}
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidCURef) {
|
||||
// Create a single compile unit with a single function that has a DW_AT_type
|
||||
// that is CU relative. The CU offset is not valid becuase it is larger than
|
||||
// the compile unit itself.
|
||||
|
||||
const char *yamldata = R"(
|
||||
debug_str:
|
||||
- ''
|
||||
- /tmp/main.c
|
||||
- main
|
||||
debug_abbrev:
|
||||
- Code: 0x00000001
|
||||
Tag: DW_TAG_compile_unit
|
||||
Children: DW_CHILDREN_yes
|
||||
Attributes:
|
||||
- Attribute: DW_AT_name
|
||||
Form: DW_FORM_strp
|
||||
- Code: 0x00000002
|
||||
Tag: DW_TAG_subprogram
|
||||
Children: DW_CHILDREN_no
|
||||
Attributes:
|
||||
- Attribute: DW_AT_name
|
||||
Form: DW_FORM_strp
|
||||
- Attribute: DW_AT_type
|
||||
Form: DW_FORM_ref4
|
||||
debug_info:
|
||||
- Length:
|
||||
TotalLength: 22
|
||||
Version: 4
|
||||
AbbrOffset: 0
|
||||
AddrSize: 8
|
||||
Entries:
|
||||
- AbbrCode: 0x00000001
|
||||
Values:
|
||||
- Value: 0x0000000000000001
|
||||
- AbbrCode: 0x00000002
|
||||
Values:
|
||||
- Value: 0x000000000000000D
|
||||
- Value: 0x0000000000001234
|
||||
- AbbrCode: 0x00000000
|
||||
Values:
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
|
||||
auto &DebugSections = *ErrOrSections;
|
||||
|
||||
DWARFContextInMemory DwarfContext(DebugSections, 8);
|
||||
|
||||
std::string str;
|
||||
raw_string_ostream strm(str);
|
||||
EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
|
||||
const char *err = "error: DW_FORM_ref4 CU offset 0x00001234 is invalid "
|
||||
"(must be less than CU size of 0x0000001a):";
|
||||
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
|
||||
}
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddr) {
|
||||
// Create a single compile unit with a single function that has an invalid
|
||||
// DW_AT_type with an invalid .debug_info offset in its DW_FORM_ref_addr.
|
||||
const char *yamldata = R"(
|
||||
debug_str:
|
||||
- ''
|
||||
- /tmp/main.c
|
||||
- main
|
||||
debug_abbrev:
|
||||
- Code: 0x00000001
|
||||
Tag: DW_TAG_compile_unit
|
||||
Children: DW_CHILDREN_yes
|
||||
Attributes:
|
||||
- Attribute: DW_AT_name
|
||||
Form: DW_FORM_strp
|
||||
- Code: 0x00000002
|
||||
Tag: DW_TAG_subprogram
|
||||
Children: DW_CHILDREN_no
|
||||
Attributes:
|
||||
- Attribute: DW_AT_name
|
||||
Form: DW_FORM_strp
|
||||
- Attribute: DW_AT_type
|
||||
Form: DW_FORM_ref_addr
|
||||
debug_info:
|
||||
- Length:
|
||||
TotalLength: 22
|
||||
Version: 4
|
||||
AbbrOffset: 0
|
||||
AddrSize: 8
|
||||
Entries:
|
||||
- AbbrCode: 0x00000001
|
||||
Values:
|
||||
- Value: 0x0000000000000001
|
||||
- AbbrCode: 0x00000002
|
||||
Values:
|
||||
- Value: 0x000000000000000D
|
||||
- Value: 0x0000000000001234
|
||||
- AbbrCode: 0x00000000
|
||||
Values:
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
|
||||
auto &DebugSections = *ErrOrSections;
|
||||
|
||||
DWARFContextInMemory DwarfContext(DebugSections, 8);
|
||||
|
||||
std::string str;
|
||||
raw_string_ostream strm(str);
|
||||
EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
|
||||
strm.flush();
|
||||
const char *err = "error: DW_FORM_ref_addr offset beyond .debug_info bounds:";
|
||||
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
|
||||
}
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRanges) {
|
||||
// Create a single compile unit with a DW_AT_ranges whose section offset
|
||||
// isn't valid.
|
||||
const char *yamldata = R"(
|
||||
debug_str:
|
||||
- ''
|
||||
- /tmp/main.c
|
||||
debug_abbrev:
|
||||
- Code: 0x00000001
|
||||
Tag: DW_TAG_compile_unit
|
||||
Children: DW_CHILDREN_no
|
||||
Attributes:
|
||||
- Attribute: DW_AT_name
|
||||
Form: DW_FORM_strp
|
||||
- Attribute: DW_AT_ranges
|
||||
Form: DW_FORM_sec_offset
|
||||
debug_info:
|
||||
- Length:
|
||||
TotalLength: 16
|
||||
Version: 4
|
||||
AbbrOffset: 0
|
||||
AddrSize: 8
|
||||
Entries:
|
||||
- AbbrCode: 0x00000001
|
||||
Values:
|
||||
- Value: 0x0000000000000001
|
||||
- Value: 0x0000000000001000
|
||||
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
|
||||
auto &DebugSections = *ErrOrSections;
|
||||
|
||||
DWARFContextInMemory DwarfContext(DebugSections, 8);
|
||||
|
||||
std::string str;
|
||||
raw_string_ostream strm(str);
|
||||
EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
|
||||
strm.flush();
|
||||
const char *err = "error: DW_AT_ranges offset is beyond .debug_ranges "
|
||||
"bounds:";
|
||||
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
|
||||
}
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStmtList) {
|
||||
// Create a single compile unit with a DW_AT_stmt_list whose section offset
|
||||
// isn't valid.
|
||||
const char *yamldata = R"(
|
||||
debug_str:
|
||||
- ''
|
||||
- /tmp/main.c
|
||||
debug_abbrev:
|
||||
- Code: 0x00000001
|
||||
Tag: DW_TAG_compile_unit
|
||||
Children: DW_CHILDREN_no
|
||||
Attributes:
|
||||
- Attribute: DW_AT_name
|
||||
Form: DW_FORM_strp
|
||||
- Attribute: DW_AT_stmt_list
|
||||
Form: DW_FORM_sec_offset
|
||||
debug_info:
|
||||
- Length:
|
||||
TotalLength: 16
|
||||
Version: 4
|
||||
AbbrOffset: 0
|
||||
AddrSize: 8
|
||||
Entries:
|
||||
- AbbrCode: 0x00000001
|
||||
Values:
|
||||
- Value: 0x0000000000000001
|
||||
- Value: 0x0000000000001000
|
||||
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
|
||||
auto &DebugSections = *ErrOrSections;
|
||||
|
||||
DWARFContextInMemory DwarfContext(DebugSections, 8);
|
||||
|
||||
std::string str;
|
||||
raw_string_ostream strm(str);
|
||||
EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
|
||||
strm.flush();
|
||||
const char *err = "error: DW_AT_stmt_list offset is beyond .debug_line "
|
||||
"bounds: 0x00001000";
|
||||
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
|
||||
}
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStrp) {
|
||||
// Create a single compile unit with a single function that has an invalid
|
||||
// DW_FORM_strp for the DW_AT_name.
|
||||
const char *yamldata = R"(
|
||||
debug_str:
|
||||
- ''
|
||||
debug_abbrev:
|
||||
- Code: 0x00000001
|
||||
Tag: DW_TAG_compile_unit
|
||||
Children: DW_CHILDREN_no
|
||||
Attributes:
|
||||
- Attribute: DW_AT_name
|
||||
Form: DW_FORM_strp
|
||||
debug_info:
|
||||
- Length:
|
||||
TotalLength: 12
|
||||
Version: 4
|
||||
AbbrOffset: 0
|
||||
AddrSize: 8
|
||||
Entries:
|
||||
- AbbrCode: 0x00000001
|
||||
Values:
|
||||
- Value: 0x0000000000001234
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
|
||||
auto &DebugSections = *ErrOrSections;
|
||||
|
||||
DWARFContextInMemory DwarfContext(DebugSections, 8);
|
||||
|
||||
std::string str;
|
||||
raw_string_ostream strm(str);
|
||||
EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
|
||||
strm.flush();
|
||||
const char *err = "error: DW_FORM_strp offset beyond .debug_str bounds:";
|
||||
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
Loading…
Reference in New Issue
Block a user