diff --git a/include/llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h b/include/llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h index 0681a2e33a5..3d5852ee151 100644 --- a/include/llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h +++ b/include/llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h @@ -60,7 +60,8 @@ public: DWARFDebugArangeSet() { clear(); } void clear(); - Error extract(DWARFDataExtractor data, uint64_t *offset_ptr); + Error extract(DWARFDataExtractor data, uint64_t *offset_ptr, + function_ref WarningHandler); void dump(raw_ostream &OS) const; uint64_t getCompileUnitDIEOffset() const { return HeaderData.CuOffset; } diff --git a/lib/DebugInfo/DWARF/DWARFContext.cpp b/lib/DebugInfo/DWARF/DWARFContext.cpp index bf621949777..3bcde8fafb1 100644 --- a/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -502,7 +502,8 @@ void DWARFContext::dump( 0); DWARFDebugArangeSet set; while (arangesData.isValidOffset(offset)) { - if (Error E = set.extract(arangesData, &offset)) { + if (Error E = + set.extract(arangesData, &offset, DumpOpts.WarningHandler)) { RecoverableErrorHandler(std::move(E)); break; } diff --git a/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp b/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp index 608fc0388af..381dd476cd5 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp @@ -32,7 +32,8 @@ void DWARFDebugArangeSet::clear() { } Error DWARFDebugArangeSet::extract(DWARFDataExtractor data, - uint64_t *offset_ptr) { + uint64_t *offset_ptr, + function_ref WarningHandler) { assert(data.isValidOffset(*offset_ptr)); ArangeDescriptors.clear(); Offset = *offset_ptr; @@ -132,19 +133,20 @@ Error DWARFDebugArangeSet::extract(DWARFDataExtractor data, uint64_t end_offset = Offset + full_length; while (*offset_ptr < end_offset) { + uint64_t EntryOffset = *offset_ptr; arangeDescriptor.Address = data.getUnsigned(offset_ptr, HeaderData.AddrSize); arangeDescriptor.Length = data.getUnsigned(offset_ptr, HeaderData.AddrSize); - if (arangeDescriptor.Length == 0) { - // Each set of tuples is terminated by a 0 for the address and 0 - // for the length. - if (arangeDescriptor.Address == 0 && *offset_ptr == end_offset) + // Each set of tuples is terminated by a 0 for the address and 0 + // for the length. + if (arangeDescriptor.Length == 0 && arangeDescriptor.Address == 0) { + if (*offset_ptr == end_offset) return ErrorSuccess(); - return createStringError( + WarningHandler(createStringError( errc::invalid_argument, "address range table at offset 0x%" PRIx64 - " has an invalid tuple (length = 0) at offset 0x%" PRIx64, - Offset, *offset_ptr - tuple_size); + " has a premature terminator entry at offset 0x%" PRIx64, + Offset, EntryOffset)); } ArangeDescriptors.push_back(arangeDescriptor); diff --git a/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp b/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp index e8ed6307505..e0db469752c 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp @@ -28,7 +28,8 @@ void DWARFDebugAranges::extract( DWARFDebugArangeSet Set; while (DebugArangesData.isValidOffset(Offset)) { - if (Error E = Set.extract(DebugArangesData, &Offset)) { + if (Error E = + Set.extract(DebugArangesData, &Offset, RecoverableErrorHandler)) { RecoverableErrorHandler(std::move(E)); return; } diff --git a/test/tools/obj2yaml/ELF/DWARF/debug-aranges.yaml b/test/tools/obj2yaml/ELF/DWARF/debug-aranges.yaml index 95abc4e03d3..92d3514aee9 100644 --- a/test/tools/obj2yaml/ELF/DWARF/debug-aranges.yaml +++ b/test/tools/obj2yaml/ELF/DWARF/debug-aranges.yaml @@ -4,16 +4,24 @@ ## The .debug_aranges should be written to the 'DWARF' entry and the 'Sections' entry should remain empty. # RUN: yaml2obj --docnum=1 -DBITS=32 -DENDIAN=LSB %s | obj2yaml | \ -# RUN: FileCheck -DLENGTH1=24 -DLENGTH2=24 -DADDRSIZE=0x04 %s --check-prefix=BASIC --implicit-check-not=Sections +# RUN: FileCheck %s --check-prefix=BASIC --implicit-check-not=Sections \ +# RUN: -DLENGTH1=24 -DLENGTH2=24 -DADDRSIZE=0x04 \ +# RUN: -DVARADDR=0x0000000000001234 -DVARLEN=0x0000000000005678 # RUN: yaml2obj --docnum=1 -DBITS=32 -DENDIAN=MSB %s | obj2yaml | \ -# RUN: FileCheck -DLENGTH1=24 -DLENGTH2=24 -DADDRSIZE=0x04 %s --check-prefix=BASIC --implicit-check-not=Sections +# RUN: FileCheck %s --check-prefix=BASIC --implicit-check-not=Sections \ +# RUN: -DLENGTH1=24 -DLENGTH2=24 -DADDRSIZE=0x04 \ +# RUN: -DVARADDR=0x0000000000001234 -DVARLEN=0x0000000000005678 # RUN: yaml2obj --docnum=1 -DBITS=64 -DENDIAN=LSB %s | obj2yaml | \ -# RUN: FileCheck -DLENGTH1=3C -DLENGTH2=44 -DADDRSIZE=0x08 %s --check-prefix=BASIC --implicit-check-not=Sections +# RUN: FileCheck %s --check-prefix=BASIC --implicit-check-not=Sections \ +# RUN: -DLENGTH1=3C -DLENGTH2=44 -DADDRSIZE=0x08 \ +# RUN: -DVARADDR=0x0000000000001234 -DVARLEN=0x0000000000005678 # RUN: yaml2obj --docnum=1 -DBITS=64 -DENDIAN=MSB %s | obj2yaml | \ -# RUN: FileCheck -DLENGTH1=3C -DLENGTH2=44 -DADDRSIZE=0x08 %s --check-prefix=BASIC --implicit-check-not=Sections +# RUN: FileCheck %s --check-prefix=BASIC --implicit-check-not=Sections \ +# RUN: -DLENGTH1=3C -DLENGTH2=44 -DADDRSIZE=0x08 \ +# RUN: -DVARADDR=0x0000000000001234 -DVARLEN=0x0000000000005678 # BASIC: DWARF: # BASIC-NEXT: debug_aranges: @@ -32,8 +40,8 @@ # BASIC-NEXT: CuOffset: 0x1234567890ABCDEF # BASIC-NEXT: AddressSize: [[ADDRSIZE]] # BASIC-NEXT: Descriptors: -# BASIC-NEXT: - Address: 0x0000000000001234 -# BASIC-NEXT: Length: 0x0000000000005678 +# BASIC-NEXT: - Address: [[VARADDR]] +# BASIC-NEXT: Length: [[VARLEN]] # BASIC-NEXT: - Address: 0x0000000000001234 # BASIC-NEXT: Length: 0x0000000000005678 # BASIC-NEXT: ... @@ -57,8 +65,8 @@ DWARF: Version: 2 CuOffset: 0x1234567890abcdef Descriptors: - - Address: 0x1234 - Length: 0x5678 + - Address: [[ADDR=0x1234]] + Length: [[LENGTH=0x5678]] - Address: 0x1234 Length: 0x5678 @@ -187,3 +195,11 @@ FileHeader: Machine: EM_X86_64 DWARF: debug_aranges: [] + +## f) Show that dumping a table with a premature terminator entry still uses the +## DWARF tag. + +# RUN: yaml2obj --docnum=1 %s -DADDR=0 -DLENGTH=0 -DBITS=64 -DENDIAN=LSB | obj2yaml | \ +# RUN: FileCheck %s --check-prefix=BASIC --implicit-check-not=Sections \ +# RUN: -DLENGTH1=3C -DLENGTH2=44 -DADDRSIZE=0x08 \ +# RUN: -DVARADDR=0x0000000000000000 -DVARLEN=0x0000000000000000 diff --git a/tools/obj2yaml/dwarf2yaml.cpp b/tools/obj2yaml/dwarf2yaml.cpp index d4e48927d06..4e867718464 100644 --- a/tools/obj2yaml/dwarf2yaml.cpp +++ b/tools/obj2yaml/dwarf2yaml.cpp @@ -65,8 +65,14 @@ Error dumpDebugARanges(DWARFContext &DCtx, DWARFYAML::Data &Y) { uint64_t Offset = 0; DWARFDebugArangeSet Set; std::vector DebugAranges; + + // We ignore any errors that don't prevent parsing the section, since we can + // still represent such sections. These errors are recorded via the + // WarningHandler parameter of Set.extract(). + auto DiscardError = [](Error Err) { consumeError(std::move(Err)); }; + while (ArangesData.isValidOffset(Offset)) { - if (Error E = Set.extract(ArangesData, &Offset)) + if (Error E = Set.extract(ArangesData, &Offset, DiscardError)) return E; DWARFYAML::ARange Range; Range.Format = Set.getHeader().Format; diff --git a/unittests/DebugInfo/DWARF/DWARFDebugArangeSetTest.cpp b/unittests/DebugInfo/DWARF/DWARFDebugArangeSetTest.cpp index 4ec9c5d1c0b..face8ec024f 100644 --- a/unittests/DebugInfo/DWARF/DWARFDebugArangeSetTest.cpp +++ b/unittests/DebugInfo/DWARF/DWARFDebugArangeSetTest.cpp @@ -7,12 +7,23 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" +#include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace llvm; namespace { +struct WarningHandler { + ~WarningHandler() { EXPECT_THAT_ERROR(std::move(Err), Succeeded()); } + + void operator()(Error E) { Err = joinErrors(std::move(Err), std::move(E)); } + + Error getWarning() { return std::move(Err); } + + Error Err = Error::success(); +}; + template void ExpectExtractError(const char (&SecDataRaw)[SecSize], const char *ErrorMessage) { @@ -21,7 +32,8 @@ void ExpectExtractError(const char (&SecDataRaw)[SecSize], /* AddressSize = */ 4); DWARFDebugArangeSet Set; uint64_t Offset = 0; - Error E = Set.extract(Extractor, &Offset); + WarningHandler Warnings; + Error E = Set.extract(Extractor, &Offset, Warnings); ASSERT_TRUE(E.operator bool()); EXPECT_STREQ(ErrorMessage, toString(std::move(E)).c_str()); } @@ -166,9 +178,9 @@ TEST(DWARFDebugArangeSet, UnevenLength) { "of the tuple size"); } -TEST(DWARFDebugArangeSet, ZeroLengthEntry) { +TEST(DWARFDebugArangeSet, ZeroAddressEntry) { static const char DebugArangesSecRaw[] = - "\x24\x00\x00\x00" // Length + "\x1c\x00\x00\x00" // Length "\x02\x00" // Version "\x00\x00\x00\x00" // Debug Info Offset "\x04" // Address Size @@ -176,14 +188,84 @@ TEST(DWARFDebugArangeSet, ZeroLengthEntry) { "\x00\x00\x00\x00" // Padding "\x00\x00\x00\x00" // Entry1: Address "\x01\x00\x00\x00" // Length - "\x01\x00\x00\x00" // Entry2: Address - "\x00\x00\x00\x00" // Length (invalid) "\x00\x00\x00\x00" // Termination tuple "\x00\x00\x00\x00"; - ExpectExtractError( - DebugArangesSecRaw, - "address range table at offset 0x0 has an invalid tuple (length = 0) " - "at offset 0x18"); + DWARFDataExtractor Extractor( + StringRef(DebugArangesSecRaw, sizeof(DebugArangesSecRaw) - 1), + /*IsLittleEndian=*/true, + /*AddressSize=*/4); + DWARFDebugArangeSet Set; + uint64_t Offset = 0; + ASSERT_THAT_ERROR(Set.extract(Extractor, &Offset, WarningHandler()), + Succeeded()); + auto Range = Set.descriptors(); + auto Iter = Range.begin(); + ASSERT_EQ(std::distance(Iter, Range.end()), 1u); + EXPECT_EQ(Iter->Address, 0u); + EXPECT_EQ(Iter->Length, 1u); +} + +TEST(DWARFDebugArangeSet, ZeroLengthEntry) { + static const char DebugArangesSecRaw[] = + "\x1c\x00\x00\x00" // Length + "\x02\x00" // Version + "\x00\x00\x00\x00" // Debug Info Offset + "\x04" // Address Size + "\x00" // Segment Selector Size + "\x00\x00\x00\x00" // Padding + "\x01\x00\x00\x00" // Entry1: Address + "\x00\x00\x00\x00" // Length + "\x00\x00\x00\x00" // Termination tuple + "\x00\x00\x00\x00"; + DWARFDataExtractor Extractor( + StringRef(DebugArangesSecRaw, sizeof(DebugArangesSecRaw) - 1), + /*IsLittleEndian=*/true, + /*AddressSize=*/4); + DWARFDebugArangeSet Set; + uint64_t Offset = 0; + ASSERT_THAT_ERROR(Set.extract(Extractor, &Offset, WarningHandler()), + Succeeded()); + auto Range = Set.descriptors(); + auto Iter = Range.begin(); + ASSERT_EQ(std::distance(Iter, Range.end()), 1u); + EXPECT_EQ(Iter->Address, 1u); + EXPECT_EQ(Iter->Length, 0u); +} + +TEST(DWARFDebugArangesSet, PrematureTerminator) { + static const char DebugArangesSecRaw[] = + "\x24\x00\x00\x00" // Length + "\x02\x00" // Version + "\x00\x00\x00\x00" // Debug Info Offset + "\x04" // Address Size + "\x00" // Segment Selector Size + "\x00\x00\x00\x00" // Padding + "\x00\x00\x00\x00" // Entry1: Premature + "\x00\x00\x00\x00" // terminator + "\x01\x00\x00\x00" // Entry2: Address + "\x01\x00\x00\x00" // Length + "\x00\x00\x00\x00" // Termination tuple + "\x00\x00\x00\x00"; + DWARFDataExtractor Extractor( + StringRef(DebugArangesSecRaw, sizeof(DebugArangesSecRaw) - 1), + /*IsLittleEndian=*/true, + /*AddressSize=*/4); + DWARFDebugArangeSet Set; + uint64_t Offset = 0; + WarningHandler Warnings; + ASSERT_THAT_ERROR(Set.extract(Extractor, &Offset, Warnings), Succeeded()); + auto Range = Set.descriptors(); + auto Iter = Range.begin(); + ASSERT_EQ(std::distance(Iter, Range.end()), 2u); + EXPECT_EQ(Iter->Address, 0u); + EXPECT_EQ(Iter->Length, 0u); + ++Iter; + EXPECT_EQ(Iter->Address, 1u); + EXPECT_EQ(Iter->Length, 1u); + EXPECT_THAT_ERROR( + Warnings.getWarning(), + FailedWithMessage("address range table at offset 0x0 has a premature " + "terminator entry at offset 0x10")); } } // end anonymous namespace