diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -2712,8 +2712,9 @@ for (const LLDDWARFSection *pub : {&pubNames, &pubTypes}) { DWARFDataExtractor data(obj, *pub, config->isLE, config->wordsize); DWARFDebugPubTable table; - if (Error e = table.extract(data, /*GnuStyle=*/true)) + table.extract(data, /*GnuStyle=*/true, [&](Error e) { warn(toString(pub->sec) + ": " + toString(std::move(e))); + }); for (const DWARFDebugPubTable::Set &set : table.getData()) { // The value written into the constant pool is kind << 24 | cuIndex. As we // don't know how many compilation units precede this object to compute diff --git a/lld/test/ELF/Inputs/gdb-index.s b/lld/test/ELF/Inputs/gdb-index.s --- a/lld/test/ELF/Inputs/gdb-index.s +++ b/lld/test/ELF/Inputs/gdb-index.s @@ -53,7 +53,7 @@ .byte 0 .section .debug_gnu_pubnames,"",@progbits -.long 0x18 +.long 0x24 .value 0x2 .long 0 .long 0x33 diff --git a/lld/test/ELF/gdb-index-invalid-pubnames.s b/lld/test/ELF/gdb-index-invalid-pubnames.s --- a/lld/test/ELF/gdb-index-invalid-pubnames.s +++ b/lld/test/ELF/gdb-index-invalid-pubnames.s @@ -2,7 +2,7 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t # RUN: ld.lld --gdb-index %t -o /dev/null 2>&1 | FileCheck %s -# CHECK: warning: {{.*}}(.debug_gnu_pubnames): unexpected end of data at offset 0x1 while reading [0x0, 0x4) +# CHECK: warning: {{.*}}(.debug_gnu_pubnames): name lookup table at offset 0x0 parsing failed: unexpected end of data at offset 0x1 while reading [0x0, 0x4) .section .debug_abbrev,"",@progbits .byte 1 # Abbreviation Code diff --git a/lld/test/ELF/gdb-index.s b/lld/test/ELF/gdb-index.s --- a/lld/test/ELF/gdb-index.s +++ b/lld/test/ELF/gdb-index.s @@ -109,7 +109,7 @@ .byte 0 .section .debug_gnu_pubnames,"",@progbits -.long 0x18 +.long 0x1e .value 0x2 .long 0 .long 0x33 diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugPubTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugPubTable.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugPubTable.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugPubTable.h @@ -73,7 +73,8 @@ public: DWARFDebugPubTable() = default; - Error extract(DWARFDataExtractor Data, bool GnuStyle); + void extract(DWARFDataExtractor Data, bool GnuStyle, + function_ref RecoverableErrorHandler); void dump(raw_ostream &OS) const; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -339,8 +339,7 @@ static void dumpPubTableSection(raw_ostream &OS, DIDumpOptions DumpOpts, DWARFDataExtractor Data, bool GnuStyle) { DWARFDebugPubTable Table; - if (Error E = Table.extract(Data, GnuStyle)) - DumpOpts.RecoverableErrorHandler(std::move(E)); + Table.extract(Data, GnuStyle, DumpOpts.RecoverableErrorHandler); Table.dump(OS); } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp @@ -11,6 +11,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include @@ -18,32 +19,75 @@ using namespace llvm; using namespace dwarf; -Error DWARFDebugPubTable::extract(DWARFDataExtractor Data, bool GnuStyle) { +void DWARFDebugPubTable::extract( + DWARFDataExtractor Data, bool GnuStyle, + function_ref RecoverableErrorHandler) { this->GnuStyle = GnuStyle; Sets.clear(); - DataExtractor::Cursor C(0); - while (C && Data.isValidOffset(C.tell())) { + uint64_t Offset = 0; + while (Data.isValidOffset(Offset)) { + uint64_t SetOffset = Offset; Sets.push_back({}); - Set &SetData = Sets.back(); + Set &NewSet = Sets.back(); - std::tie(SetData.Length, SetData.Format) = Data.getInitialLength(C); - const unsigned OffsetSize = dwarf::getDwarfOffsetByteSize(SetData.Format); + DataExtractor::Cursor C(Offset); + std::tie(NewSet.Length, NewSet.Format) = Data.getInitialLength(C); + if (!C) { + // Drop the newly added set because it does not contain anything useful + // to dump. + Sets.pop_back(); + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "name lookup table at offset 0x%" PRIx64 " parsing failed: %s", + SetOffset, toString(C.takeError()).c_str())); + return; + } + + Offset = C.tell() + NewSet.Length; + DWARFDataExtractor SetData(Data, Offset); + const unsigned OffsetSize = dwarf::getDwarfOffsetByteSize(NewSet.Format); + + NewSet.Version = SetData.getU16(C); + NewSet.Offset = SetData.getRelocatedValue(C, OffsetSize); + NewSet.Size = SetData.getUnsigned(C, OffsetSize); - SetData.Version = Data.getU16(C); - SetData.Offset = Data.getRelocatedValue(C, OffsetSize); - SetData.Size = Data.getUnsigned(C, OffsetSize); + if (!C) { + // Preserve the newly added set because at least some fields of the header + // are read and can be dumped. + RecoverableErrorHandler( + createStringError(errc::invalid_argument, + "name lookup table at offset 0x%" PRIx64 + " does not have a complete header: %s", + SetOffset, toString(C.takeError()).c_str())); + continue; + } while (C) { - uint64_t DieRef = Data.getUnsigned(C, OffsetSize); + uint64_t DieRef = SetData.getUnsigned(C, OffsetSize); if (DieRef == 0) break; - uint8_t IndexEntryValue = GnuStyle ? Data.getU8(C) : 0; - StringRef Name = Data.getCStrRef(C); - SetData.Entries.push_back( - {DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name}); + uint8_t IndexEntryValue = GnuStyle ? SetData.getU8(C) : 0; + StringRef Name = SetData.getCStrRef(C); + if (C) + NewSet.Entries.push_back( + {DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name}); + } + + if (!C) { + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "name lookup table at offset 0x%" PRIx64 " parsing failed: %s", + SetOffset, toString(std::move(C.takeError())).c_str())); + continue; } + if (C.tell() != Offset) + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "name lookup table at offset 0x%" PRIx64 + " has a terminator at offset 0x%" PRIx64 + " before the expected end at 0x%" PRIx64, + SetOffset, C.tell() - OffsetSize, Offset - OffsetSize)); } - return C.takeError(); } void DWARFDebugPubTable::dump(raw_ostream &OS) const { diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_error_cases.s b/llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_error_cases.s new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_error_cases.s @@ -0,0 +1,150 @@ +# RUN: llvm-mc -triple x86_64 %s -filetype=obj -o %t + +## All four name lookup table sections share the same parser, but slightly +## different code paths are used to reach it. Do a comprehensive check for one +## of the sections and minimal checks for the others. + +# RUN: not llvm-dwarfdump -debug-gnu-pubnames %t 2> %t.err | FileCheck %s +# RUN: FileCheck %s --input-file=%t.err --check-prefix=ERR + +# RUN: not llvm-dwarfdump -debug-pubnames -debug-pubtypes -debug-gnu-pubtypes %t 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERR-MIN + + .section .debug_gnu_pubnames,"",@progbits +# CHECK: .debug_gnu_pubnames contents: + +## The next few sets do not contain all required fields in the header. +# ERR: error: name lookup table at offset 0x0 does not have a complete header: unexpected end of data at offset 0x5 while reading [0x4, 0x6) +# CHECK-NEXT: length = 0x00000001, format = DWARF32, version = 0x0000, unit_offset = 0x00000000, unit_size = 0x00000000 +# CHECK-NEXT: Offset Linkage Kind Name +# CHECK-NOT: 0x + .long .LSet0End-.LSet0 # Length +.LSet0: + .byte 1 # Version (truncated) +.LSet0End: + +# ERR: error: name lookup table at offset 0x5 does not have a complete header: unexpected end of data at offset 0xe while reading [0xb, 0xf) +# CHECK-NEXT: length = 0x00000005, format = DWARF32, version = 0x0002, unit_offset = 0x00000000, unit_size = 0x00000000 +# CHECK-NEXT: Offset Linkage Kind Name +# CHECK-NOT: 0x + .long .LSet1End-.LSet1 # Length +.LSet1: + .short 2 # Version + .byte 1, 2, 3 # Debug Info Offset (truncated) +.LSet1End: + +# ERR: error: name lookup table at offset 0xe does not have a complete header: unexpected end of data at offset 0x1b while reading [0x18, 0x1c) +# CHECK-NEXT: length = 0x00000009, format = DWARF32, version = 0x0002, unit_offset = 0x00000032, unit_size = 0x00000000 +# CHECK-NEXT: Offset Linkage Kind Name +# CHECK-NOT: 0x + .long .LSet2End-.LSet2 # Length +.LSet2: + .short 2 # Version + .long 0x32 # Debug Info Offset + .byte 1, 2, 3 # Debug Info Length (truncated) +.LSet2End: + +## This set is terminated just after the header. +# ERR: error: name lookup table at offset 0x1b parsing failed: unexpected end of data at offset 0x29 while reading [0x29, 0x2d) +# CHECK-NEXT: length = 0x0000000a, format = DWARF32, version = 0x0002, unit_offset = 0x00000048, unit_size = 0x00000064 +# CHECK-NEXT: Offset Linkage Kind Name +# CHECK-NOT: 0x + .long .LSet3End-.LSet3 # Length +.LSet3: + .short 2 # Version + .long 0x48 # Debug Info Offset + .long 0x64 # Debug Info Length +.LSet3End: + +## The offset in the first pair is truncated. +# ERR: error: name lookup table at offset 0x29 parsing failed: unexpected end of data at offset 0x3a while reading [0x37, 0x3b) +# CHECK-NEXT: length = 0x0000000d, format = DWARF32, version = 0x0002, unit_offset = 0x000000ac, unit_size = 0x00000036 +# CHECK-NEXT: Offset Linkage Kind Name +# CHECK-NOT: 0x + .long .LSet4End-.LSet4 # Length +.LSet4: + .short 2 # Version + .long 0xac # Debug Info Offset + .long 0x36 # Debug Info Length + .byte 1, 2, 3 # Offset (truncated) +.LSet4End: + +## The set is truncated just after the offset of the first pair. +# ERR: error: name lookup table at offset 0x3a parsing failed: unexpected end of data at offset 0x4c while reading [0x4c, 0x4d) +# CHECK-NEXT: length = 0x0000000e, format = DWARF32, version = 0x0002, unit_offset = 0x000000e2, unit_size = 0x00000015 +# CHECK-NEXT: Offset Linkage Kind Name +# CHECK-NOT: 0x + .long .LSet5End-.LSet5 # Length +.LSet5: + .short 2 # Version + .long 0xe2 # Debug Info Offset + .long 0x15 # Debug Info Length + .long 0xf4 # Offset +.LSet5End: + +## The set is truncated just after the index entry field of the first pair. +# ERR: error: name lookup table at offset 0x4c parsing failed: no null terminated string at offset 0x5f +# CHECK-NEXT: length = 0x0000000f, format = DWARF32, version = 0x0002, unit_offset = 0x000000f7, unit_size = 0x00000010 +# CHECK-NEXT: Offset Linkage Kind Name +# CHECK-NOT: 0x + .long .LSet6End-.LSet6 # Length +.LSet6: + .short 2 # Version + .long 0xf7 # Debug Info Offset + .long 0x10 # Debug Info Length + .long 0xf4 # Offset + .byte 0x30 # Index Entry +.LSet6End: + +## This set contains a string which is not properly terminated. +# ERR: error: name lookup table at offset 0x5f parsing failed: no null terminated string at offset 0x72 +# CHECK-NEXT: length = 0x00000012, format = DWARF32, version = 0x0002, unit_offset = 0x00000107, unit_size = 0x0000004b +# CHECK-NEXT: Offset Linkage Kind Name +# CHECK-NOT: 0x + .long .LSet7End-.LSet7 # Length +.LSet7: + .short 2 # Version + .long 0x107 # Debug Info Offset + .long 0x4b # Debug Info Length + .long 0x111 # Offset + .byte 0x30 # Index Entry + .ascii "foo" # The string does not terminate before the set data ends. +.LSet7End: + +## This set occupies some space after the terminator. +# ERR: error: name lookup table at offset 0x75 has a terminator at offset 0x8c before the expected end at 0x8d +# CHECK-NEXT: length = 0x00000018, format = DWARF32, version = 0x0002, unit_offset = 0x00000154, unit_size = 0x000002ac +# CHECK-NEXT: Offset Linkage Kind Name +# CHECK-NEXT: 0x0000018e EXTERNAL FUNCTION "foo" +# CHECK-NOT: 0x + .long .LSet8End-.LSet8 # Length +.LSet8: + .short 2 # Version + .long 0x154 # Debug Info Offset + .long 0x2ac # Debug Info Length + .long 0x18e # Offset + .byte 0x30 # Index Entry + .asciz "foo" # Name + .long 0 # Terminator + .space 1 +.LSet8End: + +## The remaining space in the section is too short to even contain a unit length +## field. +# ERR: error: name lookup table at offset 0x91 parsing failed: unexpected end of data at offset 0x94 while reading [0x91, 0x95) +# CHECK-NOT: length = + .space 3 + +# ERR-MIN: .debug_pubnames contents: +# ERR-MIN-NEXT: error: name lookup table at offset 0x0 parsing failed: unexpected end of data at offset 0x1 while reading [0x0, 0x4) +# ERR-MIN: .debug_pubtypes contents: +# ERR-MIN-NEXT: error: name lookup table at offset 0x0 parsing failed: unexpected end of data at offset 0x1 while reading [0x0, 0x4) +# ERR-MIN: .debug_gnu_pubtypes contents: +# ERR-MIN-NEXT: error: name lookup table at offset 0x0 parsing failed: unexpected end of data at offset 0x1 while reading [0x0, 0x4) + + .section .debug_pubnames,"",@progbits + .byte 0 + .section .debug_pubtypes,"",@progbits + .byte 0 + .section .debug_gnu_pubtypes,"",@progbits + .byte 0 diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_invalid.s b/llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_invalid.s deleted file mode 100644 --- a/llvm/test/tools/llvm-dwarfdump/X86/debug_pub_tables_invalid.s +++ /dev/null @@ -1,26 +0,0 @@ -# RUN: llvm-mc -triple x86_64 %s -filetype=obj -o %t -# RUN: not llvm-dwarfdump -v %t 2>&1 | FileCheck %s - -# CHECK: .debug_pubnames contents: -# CHECK-NEXT: error: unexpected end of data at offset 0x1 while reading [0x0, 0x4) - -# CHECK: .debug_pubtypes contents: -# CHECK-NEXT: error: unexpected end of data at offset 0x1 while reading [0x0, 0x4) - -# CHECK: .debug_gnu_pubnames contents: -# CHECK-NEXT: error: unexpected end of data at offset 0x1 while reading [0x0, 0x4) - -# CHECK: .debug_gnu_pubtypes contents: -# CHECK-NEXT: error: unexpected end of data at offset 0x1 while reading [0x0, 0x4) - - .section .debug_pubnames,"",@progbits - .byte 0 - - .section .debug_pubtypes,"",@progbits - .byte 0 - - .section .debug_gnu_pubnames,"",@progbits - .byte 0 - - .section .debug_gnu_pubtypes,"",@progbits - .byte 0