Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -2712,8 +2712,9 @@ for (const LLDDWARFSection *pub : {&pubNames, &pubTypes}) { DWARFDataExtractor data(obj, *pub, config->isLE, 0); 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 Index: lld/test/ELF/Inputs/gdb-index.s =================================================================== --- lld/test/ELF/Inputs/gdb-index.s +++ 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 Index: lld/test/ELF/gdb-index-invalid-pubnames.s =================================================================== --- lld/test/ELF/gdb-index-invalid-pubnames.s +++ 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): parsing name lookup table at offset 0x0: unexpected end of data at offset 0x1 while reading [0x0, 0x4) .section .debug_abbrev,"",@progbits .byte 1 # Abbreviation Code Index: lld/test/ELF/gdb-index.s =================================================================== --- lld/test/ELF/gdb-index.s +++ 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 Index: llvm/include/llvm/DebugInfo/DWARF/DWARFDebugPubTable.h =================================================================== --- llvm/include/llvm/DebugInfo/DWARF/DWARFDebugPubTable.h +++ 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; Index: llvm/lib/DebugInfo/DWARF/DWARFContext.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ 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); } Index: llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp +++ 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(); + auto &Set = Sets.back(); - std::tie(SetData.Length, SetData.Format) = Data.getInitialLength(C); - const unsigned OffsetSize = dwarf::getDwarfOffsetByteSize(SetData.Format); + Error Err = Error::success(); + std::tie(Set.Length, Set.Format) = Data.getInitialLength(&Offset, &Err); + if (Err) { + Sets.pop_back(); + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "parsing name lookup table at offset 0x%" PRIx64 ": %s", SetOffset, + toString(std::move(Err)).c_str())); + return; + } + + DataExtractor::Cursor C(Offset); + Offset += Set.Length; + DWARFDataExtractor SetData(Data, Offset); + const unsigned OffsetSize = dwarf::getDwarfOffsetByteSize(Set.Format); + + Set.Version = SetData.getU16(C); + Set.Offset = SetData.getRelocatedValue(C, OffsetSize); + Set.Size = SetData.getUnsigned(C, OffsetSize); - SetData.Version = Data.getU16(C); - SetData.Offset = Data.getRelocatedValue(C, OffsetSize); - SetData.Size = Data.getUnsigned(C, OffsetSize); + if (!C) { + Sets.pop_back(); + 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) + Set.Entries.push_back( + {DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name}); + } + + // Note: do not drop the last set even if there are parsing errors. + // This way we can, at least, dump the header and possibly some name tuples. + if (!C) { + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "name lookup table at offset 0x%" PRIx64 + " terminated prematurely: %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 an unexpected terminator at offset 0x%" PRIx64, + SetOffset, C.tell() - OffsetSize)); } - return C.takeError(); } void DWARFDebugPubTable::dump(raw_ostream &OS) const { Index: llvm/test/DebugInfo/X86/dwarfdump-debug-pubnames-invalid.s =================================================================== --- llvm/test/DebugInfo/X86/dwarfdump-debug-pubnames-invalid.s +++ llvm/test/DebugInfo/X86/dwarfdump-debug-pubnames-invalid.s @@ -1,7 +1,63 @@ # RUN: llvm-mc -triple x86_64 %s -filetype=obj -o %t -# RUN: not llvm-dwarfdump -debug-pubnames %t 2>&1 | FileCheck %s - -# CHECK: error: unexpected end of data at offset 0x1 while reading [0x0, 0x4) +# RUN: not llvm-dwarfdump -debug-pubnames %t 2> %t.err | FileCheck %s +# RUN: FileCheck %s --input-file=%t.err --check-prefix=ERR .section .debug_pubnames,"",@progbits - .byte 0 +# CHECK: .debug_pubnames contents: + +## This set does not contain all required fields in the header. +# CHECK-NOT: length = +# ERR: error: name lookup table at offset 0x0 does not have a complete header: unexpected end of data at offset 0x6 while reading [0x6, 0xa) + .long .LSet0End-.LSet0 # Length +.LSet0: + .short 2 # Version +.LSet0End: + +## This set is terminated just after the header. +# CHECK-NEXT: length = 0x0000000a, format = DWARF32, version = 0x0002, unit_offset = 0x00000000, unit_size = 0x00000064 +# CHECK-NEXT: Offset Name +# CHECK-NOT: 0x +# ERR: error: name lookup table at offset 0x6 terminated prematurely: unexpected end of data at offset 0x14 while reading [0x14, 0x18) + .long .LSet1End-.LSet1 +.LSet1: + .short 2 # Version + .long 0 # Debug Info offset + .long 0x64 # Debug Info Length +.LSet1End: + +## This set contains a string which is not preperly terminated. +# CHECK-NEXT: length = 0x00000011, format = DWARF32, version = 0x0002, unit_offset = 0x00000064, unit_size = 0x000000f0 +# CHECK-NEXT: Offset Name +# CHECK-NOT: 0x +# ERR: error: name lookup table at offset 0x14 terminated prematurely: no null terminated string at offset 0x26 + .long .LSet2End-.LSet2 +.LSet2: + .short 2 # Version + .long 0x64 # Debug Info offset + .long 0xf0 # Debug Info Length + .long 0x78 # Offset + .ascii "foo" # The string does not terminate before the set data ends. +.LSet2End: + +## This set occupies some space after the terminator. +# CHECK-NEXT: length = 0x00000017, format = DWARF32, version = 0x0002, unit_offset = 0x00000154, unit_size = 0x000002ac +# CHECK-NEXT: Offset Name +# CHECK-NEXT: 0x0000018e "foo" +# CHECK-NOT: 0x +# ERR: error: name lookup table at offset 0x29 has an unexpected terminator at offset 0x3f + .long .LSet3End-.LSet3 +.LSet3: + .short 2 # Version + .long 0x154 # Debug Info offset + .long 0x2ac # Debug Info Length + .long 0x18e # Offset + .asciz "foo" # Name + .long 0 # Terminator + .space 1 +.LSet3End: + +## The remaining space in the section is too short to even contein a unit length +## field. +# CHECK-NOT: length = +# ERR: error: parsing name lookup table at offset 0x44: unexpected end of data at offset 0x47 while reading [0x44, 0x48) + .space 3