diff --git a/lld/test/ELF/undef.s b/lld/test/ELF/undef.s --- a/lld/test/ELF/undef.s +++ b/lld/test/ELF/undef.s @@ -5,8 +5,10 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/undef-bad-debug.s -o %t4.o # RUN: rm -f %t2.a # RUN: llvm-ar rc %t2.a %t2.o -# RUN: not ld.lld %t.o %t2.a %t3.o %t4.o -o %t.exe 2>&1 | FileCheck %s -# RUN: not ld.lld -pie %t.o %t2.a %t3.o %t4.o -o %t.exe 2>&1 | FileCheck %s +# RUN: not ld.lld %t.o %t2.a %t3.o %t4.o -o %t.exe 2>&1 \ +# RUN: | FileCheck %s --implicit-check-not="error:" --implicit-check-not="warning:" +# RUN: not ld.lld -pie %t.o %t2.a %t3.o %t4.o -o %t.exe 2>&1 \ +# RUN: | FileCheck %s --implicit-check-not="error:" --implicit-check-not="warning:" # CHECK: error: undefined symbol: foo # CHECK: >>> referenced by undef.s @@ -49,7 +51,8 @@ # CHECK: warning: parsing line table prologue at 0x00000000 should have ended at 0x00000038 but it ended at 0x00000037 # CHECK: warning: last sequence in debug line table is not terminated! # CHECK: error: undefined symbol: zed6 -# CHECK: >>> referenced by {{.*}}tmp4.o:(.text+0x0) +# CHECK: >>> referenced by undef-bad-debug.s:11 (dir{{/|\\}}undef-bad-debug.s:11) +# CHECK: >>> {{.*}}tmp4.o:(.text+0x0) # Show that a problem with one line table's information doesn't affect getting information from # a different one in the same object. diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h @@ -138,6 +138,7 @@ void clear(); void dump(raw_ostream &OS, DIDumpOptions DumpOptions) const; Error parse(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, + std::function RecoverableErrorCallback, const DWARFContext &Ctx, const DWARFUnit *U = nullptr); }; @@ -342,9 +343,12 @@ /// Skip the current line table and go to the following line table (if /// present) immediately. /// - /// \param ErrorCallback - report any prologue parsing issues via this - /// callback. - void skip(function_ref ErrorCallback); + /// \param RecoverableErrorCallback - report any recoverable prologue + /// parsing issues via this callback. + /// \param UnrecoverableErrorCallback - report any unrecoverable prologue + /// parsing issues via this callback. + void skip(function_ref RecoverableErrorCallback, + function_ref UnrecoverableErrorCallback); /// Indicates if the parser has parsed as much as possible. /// 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 @@ -462,7 +462,7 @@ Optional DumpOffset) { while (!Parser.done()) { if (DumpOffset && Parser.getOffset() != *DumpOffset) { - Parser.skip(dumpWarning); + Parser.skip(dumpWarning, dumpWarning); continue; } OS << "debug_line[" << format("0x%8.8" PRIx64, Parser.getOffset()) diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -190,20 +190,11 @@ // the end of the prologue. static llvm::Expected parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, - uint64_t EndPrologueOffset, DWARFDebugLine::ContentTypeTracker *ContentTypes) { ContentDescriptors Descriptors; int FormatCount = DebugLineData.getU8(OffsetPtr); bool HasPath = false; for (int I = 0; I != FormatCount; ++I) { - if (*OffsetPtr >= EndPrologueOffset) - return createStringError( - errc::invalid_argument, - "failed to parse entry content descriptions at offset " - "0x%8.8" PRIx64 - " because offset extends beyond the prologue end at offset " - "0x%8.8" PRIx64, - *OffsetPtr, EndPrologueOffset); ContentDescriptor Descriptor; Descriptor.Type = dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr)); @@ -224,29 +215,20 @@ static Error parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, - uint64_t *OffsetPtr, uint64_t EndPrologueOffset, - const dwarf::FormParams &FormParams, + uint64_t *OffsetPtr, const dwarf::FormParams &FormParams, const DWARFContext &Ctx, const DWARFUnit *U, DWARFDebugLine::ContentTypeTracker &ContentTypes, std::vector &IncludeDirectories, std::vector &FileNames) { // Get the directory entry description. llvm::Expected DirDescriptors = - parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, nullptr); + parseV5EntryFormat(DebugLineData, OffsetPtr, nullptr); if (!DirDescriptors) return DirDescriptors.takeError(); // Get the directory entries, according to the format described above. int DirEntryCount = DebugLineData.getU8(OffsetPtr); for (int I = 0; I != DirEntryCount; ++I) { - if (*OffsetPtr >= EndPrologueOffset) - return createStringError( - errc::invalid_argument, - "failed to parse directory entry at offset " - "0x%8.8" PRIx64 - " because offset extends beyond the prologue end at offset " - "0x%8.8" PRIx64, - *OffsetPtr, EndPrologueOffset); for (auto Descriptor : *DirDescriptors) { DWARFFormValue Value(Descriptor.Form); switch (Descriptor.Type) { @@ -268,21 +250,13 @@ // Get the file entry description. llvm::Expected FileDescriptors = parseV5EntryFormat( - DebugLineData, OffsetPtr, EndPrologueOffset, &ContentTypes); + DebugLineData, OffsetPtr, &ContentTypes); if (!FileDescriptors) return FileDescriptors.takeError(); // Get the file entries, according to the format described above. int FileEntryCount = DebugLineData.getU8(OffsetPtr); for (int I = 0; I != FileEntryCount; ++I) { - if (*OffsetPtr >= EndPrologueOffset) - return createStringError( - errc::invalid_argument, - "failed to parse file entry at offset " - "0x%8.8" PRIx64 - " because offset extends beyond the prologue end at offset " - "0x%8.8" PRIx64, - *OffsetPtr, EndPrologueOffset); DWARFDebugLine::FileNameEntry FileEntry; for (auto Descriptor : *FileDescriptors) { DWARFFormValue Value(Descriptor.Form); @@ -323,10 +297,10 @@ return Error::success(); } -Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, - uint64_t *OffsetPtr, - const DWARFContext &Ctx, - const DWARFUnit *U) { +Error DWARFDebugLine::Prologue::parse( + const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, + std::function RecoverableErrorCallback, + const DWARFContext &Ctx, const DWARFUnit *U) { const uint64_t PrologueOffset = *OffsetPtr; clear(); @@ -335,17 +309,24 @@ FormParams.Format = dwarf::DWARF64; TotalLength = DebugLineData.getU64(OffsetPtr); } else if (TotalLength >= dwarf::DW_LENGTH_lo_reserved) { - return createStringError(errc::invalid_argument, + // Treat this error as unrecoverable - we have no way of knowing where the + // table ends. + return createStringError( + errc::invalid_argument, "parsing line table prologue at offset 0x%8.8" PRIx64 " unsupported reserved unit length found of value 0x%8.8" PRIx64, PrologueOffset, TotalLength); } FormParams.Version = DebugLineData.getU16(OffsetPtr); if (getVersion() < 2) - return createStringError(errc::not_supported, - "parsing line table prologue at offset 0x%8.8" PRIx64 - " found unsupported version 0x%2.2" PRIx16, - PrologueOffset, getVersion()); + // Treat this error as unrecoverable - we cannot be sure what any of + // the data represents including the length field, so cannot skip it or make + // any reasonable assumptions. + return createStringError( + errc::not_supported, + "parsing line table prologue at offset 0x%8.8" PRIx64 + " found unsupported version 0x%2.2" PRIx16, + PrologueOffset, getVersion()); if (getVersion() >= 5) { FormParams.AddrSize = DebugLineData.getU8(OffsetPtr); @@ -373,28 +354,35 @@ } if (getVersion() >= 5) { - if (Error e = parseV5DirFileTables( - DebugLineData, OffsetPtr, EndPrologueOffset, FormParams, Ctx, U, - ContentTypes, IncludeDirectories, FileNames)) { - return joinErrors( + if (Error E = + parseV5DirFileTables(DebugLineData, OffsetPtr, FormParams, Ctx, U, + ContentTypes, IncludeDirectories, FileNames)) { + RecoverableErrorCallback(joinErrors( createStringError( errc::invalid_argument, "parsing line table prologue at 0x%8.8" PRIx64 " found an invalid directory or file table description at" " 0x%8.8" PRIx64, PrologueOffset, *OffsetPtr), - std::move(e)); + std::move(E))); + // Skip to the end of the prologue, since the chances are that the parser + // did not read the whole table. This prevents the length check from + // executing. + if (*OffsetPtr < EndPrologueOffset) + *OffsetPtr = EndPrologueOffset; } } else parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, ContentTypes, IncludeDirectories, FileNames); if (*OffsetPtr != EndPrologueOffset) - return createStringError(errc::invalid_argument, - "parsing line table prologue at 0x%8.8" PRIx64 - " should have ended at 0x%8.8" PRIx64 - " but it ended at 0x%8.8" PRIx64, - PrologueOffset, EndPrologueOffset, *OffsetPtr); + RecoverableErrorCallback(createStringError( + errc::invalid_argument, + "parsing line table prologue at 0x%8.8" PRIx64 + " should have ended at 0x%8.8" PRIx64 " but it ended at 0x%8.8" PRIx64, + PrologueOffset, EndPrologueOffset, *OffsetPtr)); + if (*OffsetPtr < EndPrologueOffset) + *OffsetPtr = EndPrologueOffset; return Error::success(); } @@ -540,7 +528,8 @@ clear(); - Error PrologueErr = Prologue.parse(DebugLineData, OffsetPtr, Ctx, U); + Error PrologueErr = Prologue.parse(DebugLineData, OffsetPtr, + RecoverableErrorCallback, Ctx, U); if (OS) { // The presence of OS signals verbose dumping. @@ -618,19 +607,26 @@ // // Make sure the extractor knows the address size. If not, infer it // from the size of the operand. - if (DebugLineData.getAddressSize() == 0) + { + uint8_t ExtractorAddressSize = DebugLineData.getAddressSize(); + // Assume that the line table is correct and parse accordingly, + // before resetting the address size later on, if it didn't match. DebugLineData.setAddressSize(Len - 1); - else if (DebugLineData.getAddressSize() != Len - 1) { - return createStringError(errc::invalid_argument, - "mismatching address size at offset 0x%8.8" PRIx64 - " expected 0x%2.2" PRIx8 " found 0x%2.2" PRIx64, - ExtOffset, DebugLineData.getAddressSize(), - Len - 1); + if (ExtractorAddressSize == 0) { + ExtractorAddressSize = Len - 1; + } else if (ExtractorAddressSize != Len - 1) { + RecoverableErrorCallback(createStringError( + errc::invalid_argument, + "mismatching address size at offset 0x%8.8" PRIx64 + " expected 0x%2.2" PRIx8 " found 0x%2.2" PRIx64, + ExtOffset, ExtractorAddressSize, Len - 1)); + } + State.Row.Address.Address = DebugLineData.getRelocatedAddress( + OffsetPtr, &State.Row.Address.SectionIndex); + DebugLineData.setAddressSize(ExtractorAddressSize); + if (OS) + *OS << format(" (0x%16.16" PRIx64 ")", State.Row.Address.Address); } - State.Row.Address.Address = DebugLineData.getRelocatedAddress( - OffsetPtr, &State.Row.Address.SectionIndex); - if (OS) - *OS << format(" (0x%16.16" PRIx64 ")", State.Row.Address.Address); break; case DW_LNE_define_file: @@ -686,12 +682,17 @@ break; } // Make sure the stated and parsed lengths are the same. - // Otherwise we have an unparseable line-number program. - if (*OffsetPtr - ExtOffset != Len) - return createStringError(errc::illegal_byte_sequence, - "unexpected line op length at offset 0x%8.8" PRIx64 - " expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx64, - ExtOffset, Len, *OffsetPtr - ExtOffset); + // If they aren't, continue from the offset that is greater. + uint64_t End = ExtOffset + Len; + if (*OffsetPtr != End) { + RecoverableErrorCallback(createStringError( + errc::illegal_byte_sequence, + "unexpected line op length at offset 0x%8.8" PRIx64 + " expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx64, + ExtOffset, Len, *OffsetPtr - ExtOffset)); + if (*OffsetPtr < End) + *OffsetPtr = End; + } } else if (Opcode < Prologue.OpcodeBase) { if (OS) *OS << LNStandardString(Opcode); @@ -1148,14 +1149,16 @@ } void DWARFDebugLine::SectionParser::skip( - function_ref ErrorCallback) { + function_ref RecoverableErrorCallback, + function_ref UnrecoverableErrorCallback) { assert(DebugLineData.isValidOffset(Offset) && "parsing should have terminated"); DWARFUnit *U = prepareToParse(Offset); uint64_t OldOffset = Offset; LineTable LT; - if (Error Err = LT.Prologue.parse(DebugLineData, &Offset, Context, U)) - ErrorCallback(std::move(Err)); + if (Error Err = LT.Prologue.parse(DebugLineData, &Offset, + RecoverableErrorCallback, Context, U)) + UnrecoverableErrorCallback(std::move(Err)); moveToNextTable(OldOffset, LT.Prologue); } diff --git a/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s b/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s --- a/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s +++ b/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s @@ -53,15 +53,18 @@ .byte 14 # Line Range .byte 13 # Opcode Base .byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths -.byte 0 # directory table (invalid) +.byte 0 # directory table (invalid as no path component) .Lprologue_v5_end: +.byte 0, 9, 2 # DW_LNE_set_address +.quad 0x8877665544332211 +.byte 0, 1, 1 # DW_LNE_end_sequence .Lunit_v5_end: # Short prologue .long .Lunit_short_prologue_end - .Lunit_short_prologue_start # unit length .Lunit_short_prologue_start: .short 4 # version -.long .Lprologue_short_prologue_end-.Lprologue_short_prologue_start - 2 # Length of Prologue +.long .Lprologue_short_prologue_end-.Lprologue_short_prologue_start - 1 # Length of Prologue .Lprologue_short_prologue_start: .byte 1 # Minimum Instruction Length .byte 1 # Maximum Operations per Instruction @@ -76,16 +79,20 @@ .asciz "file1" # File table .byte 0, 0, 0 .asciz "file2" -.byte 1, 0, 0 -.byte 0 +.byte 1, 2, 3 +# FIXME: There should be an additional 0 byte here, but the file name parsing +# code does not recognise a missing null terminator. .Lprologue_short_prologue_end: +.byte 0, 9, 2 # DW_LNE_set_address +.quad 0x1122334455667788 +.byte 0, 1, 1 # DW_LNE_end_sequence .Lunit_short_prologue_end: # Over-long prologue .long .Lunit_long_prologue_end - .Lunit_long_prologue_start # unit length .Lunit_long_prologue_start: .short 4 # version -.long .Lprologue_long_prologue_end-.Lprologue_long_prologue_start + 1 # Length of Prologue +.long .Lprologue_long_prologue_end-.Lprologue_long_prologue_start # Length of Prologue .Lprologue_long_prologue_start: .byte 1 # Minimum Instruction Length .byte 1 # Maximum Operations per Instruction @@ -100,12 +107,17 @@ .asciz "file1" # File table .byte 0, 0, 0 .asciz "file2" -.byte 1, 0, 0 +.byte 1, 2, 3 .byte 0 +# Skipped byte (treated as part of prologue). Would be DW_LNS_negate_stmt. +.byte 6 .Lprologue_long_prologue_end: +.byte 0, 9, 2 # DW_LNE_set_address +.quad 0x1111222233334444 +.byte 0, 1, 1 # DW_LNE_end_sequence .Lunit_long_prologue_end: -# Over-long extended opcode +# Incorrect length extended opcodes. .long .Lunit_long_opcode_end - .Lunit_long_opcode_start # unit length .Lunit_long_opcode_start: .short 4 # version @@ -129,10 +141,13 @@ .Lprologue_long_opcode_end: .byte 0, 9, 2 # DW_LNE_set_address .quad 0xabbadaba -.byte 0, 2, 1 # DW_LNE_end_sequence (wrong length) +.byte 0, 2, 1 # DW_LNE_end_sequence (too long) +.byte 6 # DW_LNS_negate_stmt (but will be consumed with the end sequence above). +.byte 0, 1, 4 # DW_LNE_set_discriminator (too short) +.byte 0xa # Parsed as argument for set_discriminator and not DW_LNS_set_prologue_end. .byte 0, 9, 2 # DW_LNE_set_address .quad 0xbabb1e45 -.byte 0, 1, 1 # DW_LNE_end_sequence (wrong length) +.byte 0, 1, 1 # DW_LNE_end_sequence .Lunit_long_opcode_end: # No end of sequence @@ -162,7 +177,7 @@ .byte 1 # DW_LNS_copy .Lunit_no_eos_end: -# Invalid prologue length +# V5 very short prologue length. .long .Linvalid_description_end0-.Linvalid_description_start0 # Length of Unit .Linvalid_description_start0: .short 5 # DWARF version number @@ -193,15 +208,14 @@ # File table entries .byte 1 # 1 file .asciz "a.c" -.byte 0 +.byte 1 .Linvalid_description_header_end0: -.byte 0,2,4,1 # DW_LNE_set_discriminator 1 -.byte 1 # DW_LNS_copy -.byte 33 # address += 1, line += 1 -.byte 0,1,1 # DW_LNE_end_sequence +.byte 0, 9, 2 # DW_LNE_set_address +.quad 0xbabb1ebabb1e +.byte 0, 1, 1 # DW_LNE_end_sequence .Linvalid_description_end0: -# Invalid file entry +# V5 prologue ends during file table. .long .Linvalid_file_end0-.Linvalid_file_start0 # Length of Unit .Linvalid_file_start0: .short 5 # DWARF version number @@ -232,15 +246,14 @@ # File table entries .byte 1 # 1 file .asciz "a.c" -.byte 0 +.byte 1 .Linvalid_file_header_end0: -.byte 0,2,4,1 # DW_LNE_set_discriminator 1 -.byte 1 # DW_LNS_copy -.byte 33 # address += 1, line += 1 -.byte 0,1,1 # DW_LNE_end_sequence +.byte 0, 9, 2 # DW_LNE_set_address +.quad 0xab4acadab4a +.byte 0, 1, 1 # DW_LNE_end_sequence .Linvalid_file_end0: -# Invalid directory entry +# V5 prologue ends during directory table. .long .Linvalid_dir_end0-.Linvalid_dir_start0 # Length of Unit .Linvalid_dir_start0: .short 5 # DWARF version number @@ -271,21 +284,20 @@ # File table entries .byte 1 # 1 file .asciz "a.c" -.byte 0 +.byte 1 .Linvalid_dir_header_end0: -.byte 0,2,4,1 # DW_LNE_set_discriminator 1 -.byte 1 # DW_LNS_copy -.byte 33 # address += 1, line += 1 -.byte 0,1,1 # DW_LNE_end_sequence +.byte 0, 9, 2 # DW_LNE_set_address +.quad 0x4444333322221111 +.byte 0, 1, 1 # DW_LNE_end_sequence .Linvalid_dir_end0: -# Invalid MD5 hash +# Invalid MD5 hash, where there is data still to be read afterwards. .long .Linvalid_md5_end0-.Linvalid_md5_start0 # Length of Unit .Linvalid_md5_start0: .short 5 # DWARF version number .byte 8 # Address Size .byte 0 # Segment Selector Size -.long .Linvalid_md5_header_end0-.Linvalid_md5_params0 # Length of Prologue (invalid) +.long .Linvalid_md5_header_end0-.Linvalid_md5_params0 # Length of Prologue .Linvalid_md5_params0: .byte 1 # Minimum Instruction Length .byte 1 # Maximum Operations per Instruction @@ -302,24 +314,69 @@ .byte 1 # 1 directory .asciz "/tmp" # File table format -.byte 3 # 2 elements per file entry +.byte 3 # 3 elements per file entry .byte 1 # DW_LNCT_path .byte 0x08 # DW_FORM_string +.byte 5 # DW_LNCT_MD5 +.byte 0x0b # DW_FORM_data1 .byte 2 # DW_LNCT_directory_index .byte 0x0b # DW_FORM_data1 -.byte 5 # DW_LNCT_MD5 -.byte 0x09 # DW_FORM_data1 # File table entries .byte 1 # 1 file .asciz "a.c" .byte 0 +# Data to show that the rest of the prologue is skipped. +.byte 6 # Would be interpreted as DW_LNS_negate_stmt. .Linvalid_md5_header_end0: -.byte 0,2,4,1 # DW_LNE_set_discriminator 1 -.byte 1 # DW_LNS_copy -.byte 33 # address += 1, line += 1 -.byte 0,1,1 # DW_LNE_end_sequence +.byte 0, 9, 2 # DW_LNE_set_address +.quad 0x1234123412341234 +.byte 0, 1, 1 # DW_LNE_end_sequence .Linvalid_md5_end0: +# Invalid MD5 hash, when data beyond the prologue length has +# been read before the MD5 problem is identified. +.long .Linvalid_md5_end1-.Linvalid_md5_start1 # Length of Unit +.Linvalid_md5_start1: +.short 5 # DWARF version number +.byte 8 # Address Size +.byte 0 # Segment Selector Size +.long .Linvalid_md5_header_end1-.Linvalid_md5_params1 - 10 # Length of Prologue +.Linvalid_md5_params1: +.byte 1 # Minimum Instruction Length +.byte 1 # Maximum Operations per Instruction +.byte 1 # Default is_stmt +.byte -5 # Line Base +.byte 14 # Line Range +.byte 13 # Opcode Base +.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths +# Directory table format +.byte 1 # One element per directory entry +.byte 1 # DW_LNCT_path +.byte 0x08 # DW_FORM_string +# Directory table entries +.byte 1 # 1 directory +.asciz "/tmp" +# File table format +.byte 3 # 2 elements per file entry +.byte 1 # DW_LNCT_path +.byte 0x08 # DW_FORM_string +.byte 5 # DW_LNCT_MD5 +.byte 0x0b # DW_FORM_data1 +.byte 2 # DW_LNCT_directory_index +.byte 0x0b # DW_FORM_data1 +# File table entries +.byte 1 # 1 file +.asciz "a.c" +.byte 6 # This byte will be consumed when reading the MD5 value and + # not interpreted as DW_LNS_negate_stmt. +.byte 0xb # This byte will not be read as part of the prologue, and + # treated as part of the line table (DW_LNS_set_epilogue_begin). +.Linvalid_md5_header_end1: +.byte 0, 9, 2 # DW_LNE_set_address +.quad 0x4321432143214321 +.byte 0, 1, 1 # DW_LNE_end_sequence +.Linvalid_md5_end1: + # Trailing good section .long .Lunit_good_end - .Lunit_good_start # Length of Unit (DWARF-32 format) .Lunit_good_start: diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test b/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test --- a/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test +++ b/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test @@ -1,31 +1,39 @@ -# Test the different error cases in the debug line parsing and how they prevent -# or don't prevent further dumping of section contents. +## Test the different error cases in the debug line parsing and how they prevent +## or don't prevent further dumping of section contents. # RUN: llvm-mc -triple x86_64-pc-linux %S/Inputs/debug_line_reserved_length.s -filetype=obj -o %t-reserved.o -# RUN: llvm-dwarfdump -debug-line %t-reserved.o 2> %t-reserved.err | FileCheck %s --check-prefixes=FIRST,FATAL +# RUN: llvm-dwarfdump -debug-line %t-reserved.o 2> %t-reserved.err \ +# RUN: | FileCheck %s --check-prefixes=FIRST,FATAL # RUN: FileCheck %s --input-file=%t-reserved.err --check-prefix=RESERVED -# RUN: llvm-dwarfdump -debug-line %t-reserved.o -verbose 2> %t-reserved-verbose.err | FileCheck %s --check-prefixes=FIRST,FATAL +# RUN: llvm-dwarfdump -debug-line %t-reserved.o -verbose 2> %t-reserved-verbose.err \ +# RUN: | FileCheck %s --check-prefixes=FIRST,FATAL # RUN: FileCheck %s --input-file=%t-reserved-verbose.err --check-prefix=RESERVED # We should still produce warnings for malformed tables after the specified unit. -# RUN: llvm-dwarfdump -debug-line=0 %t-reserved.o 2> %t-reserved-off-first.err | FileCheck %s --check-prefixes=FIRST,NOLATER +# RUN: llvm-dwarfdump -debug-line=0 %t-reserved.o 2> %t-reserved-off-first.err \ +# RUN: | FileCheck %s --check-prefixes=FIRST,NOLATER # RUN: FileCheck %s --input-file=%t-reserved-off-first.err --check-prefix=RESERVED # Stop looking for the specified unit, if a fatally-bad prologue is detected. -# RUN: llvm-dwarfdump -debug-line=0x4b %t-reserved.o 2> %t-reserved-off-last.err | FileCheck %s --check-prefixes=NOFIRST,NOLATER +# RUN: llvm-dwarfdump -debug-line=0x4b %t-reserved.o 2> %t-reserved-off-last.err \ +# RUN: | FileCheck %s --check-prefixes=NOFIRST,NOLATER # RUN: FileCheck %s --input-file=%t-reserved-off-last.err --check-prefix=RESERVED # RUN: llvm-mc -triple x86_64-pc-linux %S/Inputs/debug_line_malformed.s -filetype=obj -o %t-malformed.o -# RUN: llvm-dwarfdump -debug-line %t-malformed.o 2> %t-malformed.err | FileCheck %s --check-prefixes=FIRST,NONFATAL +# RUN: llvm-dwarfdump -debug-line %t-malformed.o 2> %t-malformed.err \ +# RUN: | FileCheck %s --check-prefixes=FIRST,NONFATAL,LAST # RUN: FileCheck %s --input-file=%t-malformed.err --check-prefixes=ALL,OTHER -# RUN: llvm-dwarfdump -debug-line %t-malformed.o -verbose 2> %t-malformed-verbose.err | FileCheck %s --check-prefixes=FIRST,NONFATAL +# RUN: llvm-dwarfdump -debug-line %t-malformed.o -verbose 2> %t-malformed-verbose.err \ +# RUN: | FileCheck %s --check-prefixes=FIRST,NONFATAL,LAST # RUN: FileCheck %s --input-file=%t-malformed-verbose.err --check-prefixes=ALL,OTHER -# RUN: llvm-dwarfdump -debug-line=0 %t-malformed.o 2> %t-malformed-off-first.err | FileCheck %s --check-prefixes=FIRST,NOLATER +# RUN: llvm-dwarfdump -debug-line=0 %t-malformed.o 2> %t-malformed-off-first.err \ +# RUN: | FileCheck %s --check-prefixes=FIRST,NOLATER # RUN: FileCheck %s --input-file=%t-malformed-off-first.err --check-prefix=ALL # Don't stop looking for the later unit if non-fatal issues are found. -# RUN: llvm-dwarfdump -debug-line=0x271 %t-malformed.o 2> %t-malformed-off-last.err | FileCheck %s --check-prefixes=LASTONLY +# RUN: llvm-dwarfdump -debug-line=0x2f8 %t-malformed.o 2> %t-malformed-off-last.err \ +# RUN: | FileCheck %s --check-prefixes=LAST --implicit-check-not='debug_line[{{.*}}]' # RUN: FileCheck %s --input-file=%t-malformed-off-last.err --check-prefix=ALL # FIRST: debug_line[0x00000000] @@ -35,71 +43,122 @@ # NOLATER-NOT: debug_line[{{.*}}] # NOLATER-NOT: end_sequence -# For fatal issues, the following table(s) should not be dumped. +## For fatal issues, the following table(s) should not be dumped. # FATAL: debug_line[0x00000048] # FATAL-NEXT: Line table prologue # FATAL-NEXT: total_length: 0xfffffffe # FATAL-NOT: debug_line -# For non-fatal prologue issues, the table prologue should be dumped, and any subsequent tables should also be. +## For non-fatal issues, the table data should be dumped. +## Case 1: Version 0 table # NONFATAL: debug_line[0x00000048] # NONFATAL-NEXT: Line table prologue # NONFATAL-NOT: Address + +## Case 2: Version 1 table # NONFATAL: debug_line[0x0000004e] # NONFATAL-NEXT: Line table prologue # NONFATAL-NOT: Address + +## Case 3: Malformed directory format with no path component. # NONFATAL: debug_line[0x00000054] # NONFATAL-NEXT: Line table prologue -# NONFATAL-NOT: Address -# NONFATAL: debug_line[0x00000073] +# NONFATAL-NOT: include_directories +# NONFATAL-NOT: file_names +# NONFATAL: 0x8877665544332211 {{.*}} end_sequence + +## Case 4: Prologue with length shorter than parsed. +# NONFATAL: debug_line[0x00000081] # NONFATAL-NEXT: Line table prologue -# NONFATAL-NOT: Address -# NONFATAL: debug_line[0x000000ad] +# NONFATAL: file_names[ 2]: +# NONFATAL-NEXT: name: "file2" +# NONFATAL-NEXT: dir_index: 1 +# NONFATAL-NEXT: mod_time: 0x00000002 +# NONFATAL-NEXT: length: 0x00000003 +# NONFATAL: 0x1122334455667788 {{.*}} end_sequence + +## Case 5: Prologue with length greater than parsed. +# NONFATAL: debug_line[0x000000c8] # NONFATAL-NEXT: Line table prologue -# NONFATAL-NOT: Address -# NONFATAL: debug_line[0x000000e7] -# Dumping prints the line table prologue and any valid operations up to the point causing the problem. +# NONFATAL: file_names[ 2]: +# NONFATAL-NEXT: name: "file2" +# NONFATAL-NEXT: dir_index: 1 +# NONFATAL-NEXT: mod_time: 0x00000002 +# NONFATAL-NEXT: length: 0x00000003 +# NONFATAL-NOT: file_names +# NONFATAL: 0x1111222233334444 {{.*}} is_stmt end_sequence + +## Case 6: Extended opcodes with incorrect lengths versus expected. +# NONFATAL: debug_line[0x00000111] # NONFATAL-NEXT: Line table prologue # NONFATAL: 0x00000000abbadaba {{.*}} end_sequence -# NONFATAL-NOT: is_stmt +# NONFATAL: 0x00000000babb1e45 {{.*}} 10 is_stmt end_sequence{{$}} -# For minor issues, we can dump the table. -# NONFATAL: debug_line[0x0000013d] +## Case 7: No end of sequence. +# NONFATAL: debug_line[0x0000016c] # NONFATAL-NEXT: Line table prologue -# NONFATAL-NOT: debug_line[{{.*}}] -# NONFATAL: 0x00000000deadfade {{.*}} -# NONFATAL: debug_line[0x00000183] +# NONFATAL: 0x00000000deadfade {{.*}} is_stmt +# NONFATAL-NOT: end_sequence + +## Case 8: Very short prologue length for V5 (ends during parameters). +# NONFATAL: debug_line[0x000001b2] # NONFATAL-NEXT: Line table prologue -# NONFATAL: debug_line[0x00000271] -# NONFATAL-NOT: debug_line[{{.*}}] -# NONFATAL: 0x00000000cafebabe {{.*}} end_sequence -# NONFATAL-NOT: debug_line[{{.*}}] +# NONFATAL: standard_opcode_lengths[DW_LNS_set_isa] = 1 +# NONFATAL-NEXT: include_directories[ 0] = "/tmp" +# NONFATAL-NEXT: file_names[ 0]: +# NONFATAL-NEXT: name: "a.c" +# NONFATAL-NEXT: dir_index: 1 +# NONFATAL: 0x0000babb1ebabb1e {{.*}} end_sequence -# LASTONLY-NOT: debug_line[{{.*}}] -# LASTONLY: debug_line[0x00000271] -# LASTONLY: 0x00000000cafebabe {{.*}} end_sequence +## Case 9: V5 prologue ends during file table. +# NONFATAL: debug_line[0x000001f2] +# NONFATAL-NEXT: Line table prologue +# NONFATAL: file_names[ 0]: +# NONFATAL-NEXT: name: "a.c" +# NONFATAL-NEXT: dir_index: 1 +# NONFATAL: 0x00000ab4acadab4a {{.*}} end_sequence -# RESERVED: warning: parsing line table prologue at offset 0x00000048 unsupported reserved unit length found of value 0xfffffffe +## Case 10: V5 prologue ends during directory table. +# NONFATAL: debug_line[0x00000232] +# NONFATAL-NEXT: Line table prologue +# NONFATAL: include_directories[ 0] = "/tmp" +# NONFATAL-NEXT: file_names[ 0]: +# NONFATAL-NEXT: name: "a.c" +# NONFATAL-NEXT: dir_index: 1 +# NONFATAL: 0x4444333322221111 {{.*}} end_sequence -# MD5: warning: parsing line table prologue at 0x00000000 found an invalid directory or file table description at 0x0000003b -# MD5-NEXT: warning: failed to parse file entry because the MD5 hash is invalid +## Case 11: V5 invalid MD5 hash form when there is still data to be read. +# NONFATAL: debug_line[0x00000272] +# NONFATAL-NEXT: Line table prologue +# NONFATAL: 0x1234123412341234 {{.*}} is_stmt end_sequence + +## Case 12: V5 invalid MD5 hash form when data beyond the prologue length has +## been read before the MD5 problem is identified. +# NONFATAL: debug_line[0x000002b5] +# NONFATAL-NEXT: Line table prologue +# NONFATAL: 0x4321432143214321 {{.*}} is_stmt epilogue_begin end_sequence + +# LAST: debug_line[0x000002f8] +# LAST: 0x00000000cafebabe {{.*}} end_sequence + +# RESERVED: warning: parsing line table prologue at offset 0x00000048 unsupported reserved unit length found of value 0xfffffffe # ALL-NOT: warning: # ALL: warning: parsing line table prologue at offset 0x00000048 found unsupported version 0x00 # ALL-NEXT: warning: parsing line table prologue at offset 0x0000004e found unsupported version 0x01 # ALL-NEXT: warning: parsing line table prologue at 0x00000054 found an invalid directory or file table description at 0x00000073 # ALL-NEXT: warning: failed to parse entry content descriptions because no path was found -# FIXME - The latter offset in the next line should be 0xad. The filename parsing code does not notice a missing terminating byte. -# ALL-NEXT: warning: parsing line table prologue at 0x00000073 should have ended at 0x000000ab but it ended at 0x000000ac -# ALL-NEXT: warning: parsing line table prologue at 0x000000ad should have ended at 0x000000e8 but it ended at 0x000000e7 -# OTHER-NEXT: warning: unexpected line op length at offset 0x0000012e expected 0x02 found 0x01 +# ALL-NEXT: warning: parsing line table prologue at 0x00000081 should have ended at 0x000000b9 but it ended at 0x000000ba +# ALL-NEXT: warning: parsing line table prologue at 0x000000c8 should have ended at 0x00000103 but it ended at 0x00000102 +# OTHER-NEXT: warning: unexpected line op length at offset 0x00000158 expected 0x02 found 0x01 +# OTHER-NEXT: warning: unexpected line op length at offset 0x0000015c expected 0x01 found 0x02 # OTHER-NEXT: warning: last sequence in debug line table is not terminated! -# ALL-NEXT: warning: parsing line table prologue at 0x00000183 found an invalid directory or file table description at 0x000001a2 -# ALL-NEXT: warning: failed to parse entry content descriptions at offset 0x000001a2 because offset extends beyond the prologue end at offset 0x0000019e -# ALL-NEXT: warning: parsing line table prologue at 0x000001be found an invalid directory or file table description at 0x000001eb -# ALL-NEXT: warning: failed to parse file entry at offset 0x000001eb because offset extends beyond the prologue end at offset 0x000001e9 -# ALL-NEXT: warning: parsing line table prologue at 0x000001f9 found an invalid directory or file table description at 0x0000021b -# ALL-NEXT: warning: failed to parse directory entry at offset 0x0000021b because offset extends beyond the prologue end at offset 0x0000021b -# ALL-NEXT: warning: parsing line table prologue at 0x00000234 found an invalid directory or file table description at 0x00000269 +# ALL-NEXT: warning: parsing line table prologue at 0x000001b2 should have ended at 0x000001cd but it ended at 0x000001e4 +# ALL-NEXT: warning: parsing line table prologue at 0x000001f2 should have ended at 0x0000021d but it ended at 0x00000224 +# ALL-NEXT: warning: parsing line table prologue at 0x00000232 should have ended at 0x00000254 but it ended at 0x00000264 +# ALL-NEXT: warning: parsing line table prologue at 0x00000272 found an invalid directory or file table description at 0x000002a6 +# ALL-NEXT: warning: failed to parse file entry because the MD5 hash is invalid +# ALL-NEXT: warning: parsing line table prologue at 0x000002b5 found an invalid directory or file table description at 0x000002e9 # ALL-NEXT: warning: failed to parse file entry because the MD5 hash is invalid +# ALL-NEXT: warning: parsing line table prologue at 0x000002b5 should have ended at 0x000002e0 but it ended at 0x000002e9 # ALL-NOT: warning: diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp @@ -36,8 +36,10 @@ EXPECT_FALSE(Unrecoverable); } - bool setupGenerator(uint16_t Version = 4) { - Triple T = getDefaultTargetTripleForAddrSize(8); + bool setupGenerator(uint16_t Version = 4, uint8_t AddrSize = 8) { + AddressSize = AddrSize; + Triple T = + getDefaultTargetTripleForAddrSize(AddressSize == 0 ? 8 : AddressSize); if (!isConfigurationSupported(T)) return false; auto ExpectedGenerator = Generator::create(T, Version); @@ -50,9 +52,11 @@ Context = createContext(); assert(Context != nullptr && "test state is not valid"); const DWARFObject &Obj = Context->getDWARFObj(); + uint8_t TargetAddrSize = AddressSize == 0 ? 8 : AddressSize; LineData = DWARFDataExtractor( Obj, Obj.getLineSection(), - getDefaultTargetTripleForAddrSize(8).isLittleEndian(), 8); + getDefaultTargetTripleForAddrSize(TargetAddrSize).isLittleEndian(), + AddressSize); } std::unique_ptr createContext() { @@ -110,8 +114,8 @@ checkError(ArrayRef{ExpectedMsg}, std::move(Err)); } - void checkGetOrParseLineTableEmitsError(StringRef ExpectedMsg, - uint64_t Offset = 0) { + void checkGetOrParseLineTableEmitsFatalError(StringRef ExpectedMsg, + uint64_t Offset = 0) { auto ExpectedLineTable = Line.getOrParseLineTable( LineData, Offset, *Context, nullptr, RecordRecoverable); EXPECT_FALSE(ExpectedLineTable); @@ -120,8 +124,8 @@ checkError(ExpectedMsg, ExpectedLineTable.takeError()); } - void checkGetOrParseLineTableEmitsError(ArrayRef ExpectedMsgs, - uint64_t Offset = 0) { + void checkGetOrParseLineTableEmitsFatalError(ArrayRef ExpectedMsgs, + uint64_t Offset = 0) { auto ExpectedLineTable = Line.getOrParseLineTable( LineData, Offset, *Context, nullptr, RecordRecoverable); EXPECT_FALSE(ExpectedLineTable); @@ -130,6 +134,7 @@ checkError(ExpectedMsgs, ExpectedLineTable.takeError()); } + uint8_t AddressSize; std::unique_ptr Gen; std::unique_ptr Context; DWARFDataExtractor LineData; @@ -207,13 +212,13 @@ return; generate(); - checkGetOrParseLineTableEmitsError( + checkGetOrParseLineTableEmitsFatalError( "offset 0x00000000 is not a valid debug line section offset", 0); // Repeat to show that an error is reported each time. - checkGetOrParseLineTableEmitsError( + checkGetOrParseLineTableEmitsFatalError( "offset 0x00000000 is not a valid debug line section offset", 0); // Show that an error is reported for later offsets too. - checkGetOrParseLineTableEmitsError( + checkGetOrParseLineTableEmitsFatalError( "offset 0x00000001 is not a valid debug line section offset", 1); } @@ -226,7 +231,7 @@ generate(); - checkGetOrParseLineTableEmitsError( + checkGetOrParseLineTableEmitsFatalError( "offset 0x00000001 is not a valid debug line section offset", 1); } @@ -305,7 +310,7 @@ generate(); - checkGetOrParseLineTableEmitsError( + checkGetOrParseLineTableEmitsFatalError( "parsing line table prologue at offset 0x00000000 unsupported reserved " "unit length found of value 0xfffffff0"); } @@ -320,9 +325,10 @@ generate(); - checkGetOrParseLineTableEmitsError("parsing line table prologue at offset " - "0x00000000 found unsupported version " - "0x01"); + checkGetOrParseLineTableEmitsFatalError( + "parsing line table prologue at offset " + "0x00000000 found unsupported version " + "0x01"); } TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) { @@ -353,10 +359,15 @@ generate(); - checkGetOrParseLineTableEmitsError( + auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context, + nullptr, RecordRecoverable); + EXPECT_THAT_EXPECTED(ExpectedLineTable, Succeeded()); + + checkError( {"parsing line table prologue at 0x00000000 found an invalid directory " "or file table description at 0x00000014", - "failed to parse entry content descriptions because no path was found"}); + "failed to parse entry content descriptions because no path was found"}, + std::move(Recoverable)); } TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) { @@ -373,14 +384,24 @@ generate(); + auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context, + nullptr, RecordRecoverable); + ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded()); + DWARFDebugLine::LineTable Result(**ExpectedLineTable); + // Undo the earlier modification so that it can be compared against a + // "default" prologue. + --Result.Prologue.PrologueLength; + checkDefaultPrologue(Version, Format, Result.Prologue, 0); + uint64_t ExpectedEnd = Prologue.TotalLength + 1 + Prologue.sizeofTotalLength(); - checkGetOrParseLineTableEmitsError( + checkError( (Twine("parsing line table prologue at 0x00000000 should have ended at " "0x000000") + Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" + Twine::utohexstr(ExpectedEnd - 1)) - .str()); + .str(), + std::move(Recoverable)); } TEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) { @@ -402,16 +423,29 @@ generate(); + auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context, + nullptr, RecordRecoverable); + ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded()); + DWARFDebugLine::LineTable Result(**ExpectedLineTable); + // Undo the earlier modification so that it can be compared against a + // "default" prologue. + if (Version < 5) + Result.Prologue.PrologueLength += 2; + else + Result.Prologue.PrologueLength += 1; + checkDefaultPrologue(Version, Format, Result.Prologue, 0); + uint64_t ExpectedEnd = Prologue.TotalLength - 1 + Prologue.sizeofTotalLength(); if (Version < 5) --ExpectedEnd; - checkGetOrParseLineTableEmitsError( + checkError( (Twine("parsing line table prologue at 0x00000000 should have ended at " "0x000000") + Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" + Twine::utohexstr(ExpectedEnd + 1)) - .str()); + .str(), + std::move(Recoverable)); } INSTANTIATE_TEST_CASE_P( @@ -422,35 +456,117 @@ std::make_pair(4, DWARF64), // Test v4 fields and DWARF64. std::make_pair(5, DWARF32), std::make_pair(5, DWARF64)), ); -TEST_F(DebugLineBasicFixture, ErrorForInvalidExtendedOpcodeLength) { +TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthSmallerThanExpected) { + if (!setupGenerator()) + return; + + LineTable < = Gen->addLineTable(); + LT.addByte(0xaa); + // The Length should be 1 + sizeof(ULEB) for a set discriminator opcode. + // If the parser were to continue assuming a length of 1, the operand would be + // treated as a negate_stmt. + LT.addExtendedOpcode(1, DW_LNE_set_discriminator, + {{DW_LNS_negate_stmt, LineTable::ULEB}}); + LT.addByte(0xbb); + LT.addStandardOpcode(DW_LNS_const_add_pc, {}); + LT.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + + generate(); + + auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context, + nullptr, RecordRecoverable); + checkError( + "unexpected line op length at offset 0x00000031 expected 0x01 found 0x02", + std::move(Recoverable)); + ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded()); + ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 3u); + EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u); + EXPECT_EQ((*ExpectedLineTable)->Rows[1].IsStmt, 1u); +} + +TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthLargerThanExpected) { if (!setupGenerator()) return; LineTable < = Gen->addLineTable(); + LT.addByte(0xaa); + LT.addStandardOpcode(DW_LNS_const_add_pc, {}); // The Length should be 1 for an end sequence opcode. LT.addExtendedOpcode(2, DW_LNE_end_sequence, {}); + // The negate statement opcode will be skipped. + LT.addStandardOpcode(DW_LNS_negate_stmt, {}); + LT.addByte(0xbb); + LT.addStandardOpcode(DW_LNS_const_add_pc, {}); + LT.addExtendedOpcode(1, DW_LNE_end_sequence, {}); generate(); - checkGetOrParseLineTableEmitsError("unexpected line op length at offset " - "0x00000030 expected 0x02 found 0x01"); + auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context, + nullptr, RecordRecoverable); + checkError( + "unexpected line op length at offset 0x00000032 expected 0x02 found 0x01", + std::move(Recoverable)); + ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded()); + ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 4u); + EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 2u); + ASSERT_EQ((*ExpectedLineTable)->Sequences[1].FirstRowIndex, 2u); + EXPECT_EQ((*ExpectedLineTable)->Rows[2].IsStmt, 1u); } TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) { - if (!setupGenerator()) + if (!setupGenerator(4, 8)) return; LineTable < = Gen->addLineTable(); // The line data extractor expects size 8 (Quad) addresses. - LT.addExtendedOpcode(5, DW_LNE_set_address, {{0x11223344, LineTable::Long}}); + uint64_t Addr1 = 0x11223344; + LT.addExtendedOpcode(5, DW_LNE_set_address, {{Addr1, LineTable::Long}}); LT.addStandardOpcode(DW_LNS_copy, {}); - LT.addByte(0xaa); + // Show that the expected address size is unchanged, so later valid lines + // don't cause a problem. + uint64_t Addr2 = 0x1122334455667788; + LT.addExtendedOpcode(9, DW_LNE_set_address, {{Addr2, LineTable::Quad}}); LT.addExtendedOpcode(1, DW_LNE_end_sequence, {}); generate(); - checkGetOrParseLineTableEmitsError( - "mismatching address size at offset 0x00000030 expected 0x08 found 0x04"); + auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context, + nullptr, RecordRecoverable); + checkError( + "mismatching address size at offset 0x00000030 expected 0x08 found 0x04", + std::move(Recoverable)); + ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded()); + ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 2u); + EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u); + EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, Addr1); + EXPECT_EQ((*ExpectedLineTable)->Rows[1].Address.Address, Addr2); +} + +TEST_F(DebugLineBasicFixture, + ErrorForMismatchedAddressSizeUnsetInitialAddress) { + if (!setupGenerator(4, 0)) + return; + + LineTable < = Gen->addLineTable(); + uint64_t Addr1 = 0x11223344; + LT.addExtendedOpcode(5, DW_LNE_set_address, {{Addr1, LineTable::Long}}); + LT.addStandardOpcode(DW_LNS_copy, {}); + uint64_t Addr2 = 0x1122334455667788; + LT.addExtendedOpcode(9, DW_LNE_set_address, {{Addr2, LineTable::Quad}}); + LT.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + + generate(); + + auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context, + nullptr, RecordRecoverable); + checkError( + "mismatching address size at offset 0x00000038 expected 0x04 found 0x08", + std::move(Recoverable)); + ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded()); + ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 2u); + EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u); + EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, Addr1); + EXPECT_EQ((*ExpectedLineTable)->Rows[1].Address.Address, Addr2); } TEST_F(DebugLineBasicFixture, CallbackUsedForUnterminatedSequence) { @@ -517,14 +633,15 @@ EXPECT_EQ(Parser.getOffset(), 0u); ASSERT_FALSE(Parser.done()); - Parser.skip(RecordUnrecoverable); + Parser.skip(RecordRecoverable, RecordUnrecoverable); EXPECT_EQ(Parser.getOffset(), 62u); ASSERT_FALSE(Parser.done()); - Parser.skip(RecordUnrecoverable); + Parser.skip(RecordRecoverable, RecordUnrecoverable); EXPECT_EQ(Parser.getOffset(), 136u); EXPECT_TRUE(Parser.done()); + EXPECT_FALSE(Recoverable); EXPECT_FALSE(Unrecoverable); } @@ -569,10 +686,11 @@ generate(); DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); - Parser.skip(RecordUnrecoverable); + Parser.skip(RecordRecoverable, RecordUnrecoverable); EXPECT_EQ(Parser.getOffset(), 4u); EXPECT_TRUE(Parser.done()); + EXPECT_FALSE(Recoverable); checkError("parsing line table prologue at offset 0x00000000 unsupported " "reserved unit length found of value 0xfffffff0", @@ -619,11 +737,11 @@ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); Parser.parseNext(RecordRecoverable, RecordUnrecoverable); - EXPECT_FALSE(Recoverable); + EXPECT_FALSE(Unrecoverable); ASSERT_FALSE(Parser.done()); checkError( "unexpected line op length at offset 0x00000030 expected 0x42 found 0x01", - std::move(Unrecoverable)); + std::move(Recoverable)); // Reset the error state so that it does not confuse the next set of checks. Unrecoverable = Error::success(); @@ -647,11 +765,12 @@ generate(); DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); - Parser.skip(RecordUnrecoverable); + Parser.skip(RecordRecoverable, RecordUnrecoverable); ASSERT_FALSE(Parser.done()); - Parser.skip(RecordUnrecoverable); + Parser.skip(RecordRecoverable, RecordUnrecoverable); EXPECT_TRUE(Parser.done()); + EXPECT_FALSE(Recoverable); checkError({"parsing line table prologue at offset 0x00000000 found " "unsupported version 0x00", @@ -669,9 +788,10 @@ generate(); DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); - Parser.skip(RecordUnrecoverable); + Parser.skip(RecordRecoverable, RecordUnrecoverable); EXPECT_TRUE(Parser.done()); + EXPECT_FALSE(Recoverable); EXPECT_FALSE(Unrecoverable); }