[DebugInfo] Make most debug line prologue errors non-fatal to parsing
authorJames Henderson <james.henderson@sony.com>
Tue, 28 Jan 2020 14:04:42 +0000 (14:04 +0000)
committerJames Henderson <james.henderson@sony.com>
Wed, 29 Jan 2020 10:23:41 +0000 (10:23 +0000)
Many of the debug line prologue errors are not inherently fatal. In most
cases, we can make reasonable assumptions and carry on. This patch does
exactly that. In the case of length problems, the approach of "assume
stated length is correct" is taken which means the offset might need
adjusting.

This is a relanding of b94191fe, fixing an LLD test and the LLDB build.

Reviewed by: dblaikie, labath

Differential Revision: https://reviews.llvm.org/D72158

lld/test/ELF/Inputs/undef-bad-debug.s
lld/test/ELF/undef.s
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h
llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp
llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s
llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test
llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp

index d52710b..bf517f3 100644 (file)
@@ -5,6 +5,8 @@ sym2:
     .quad zed6b
 sym3:
     .quad zed7
+sym4:
+    .quad zed8
 
 .section .debug_line,"",@progbits
 .Lunit:
@@ -47,6 +49,12 @@ sym3:
 .Lunit2:
     .long .Lunit2_end - .Lunit2_start # unit length
 .Lunit2_start:
+    .short 1                        # version
+.Lunit2_end:
+
+.Lunit3:
+    .long .Lunit3_end - .Lunit3_start # unit length
+.Lunit3_start:
     .short 4                        # version
     .long .Lprologue2_end - .Lprologue2_start # prologue length
 .Lprologue2_start:
@@ -64,7 +72,7 @@ sym3:
     .byte 0
 .Lprologue2_end:
     .byte 0, 9, 2                   # DW_LNE_set_address
-    .quad sym3
+    .quad sym4
     .byte 3                         # DW_LNS_advance_line
     .byte 10
     .byte 1                         # DW_LNS_copy
@@ -79,7 +87,7 @@ sym3:
     .byte 99                        # DW_LNS_advance_pc
     .byte 119
     # Missing end of sequence.
-.Lunit2_end:
+.Lunit3_end:
 
 .section .debug_info,"",@progbits
     .long   .Lcu_end - .Lcu_start   # Length of Unit
@@ -117,6 +125,21 @@ sym3:
     .byte   0                       # End Of Children Mark
 .Lcu2_end:
 
+    .long   .Lcu3_end - .Lcu3_start # Length of Unit
+.Lcu3_start:
+    .short  4                       # DWARF version number
+    .long   .Lsection_abbrev        # Offset Into Abbrev. Section
+    .byte   8                       # Address Size (in bytes)
+    .byte   1                       # Abbrev [1] 0xb:0x79 DW_TAG_compile_unit
+    .long   .Lunit3                 # DW_AT_stmt_list
+    .byte   2                       # Abbrev [2] 0x2a:0x15 DW_TAG_variable
+    .long   .Linfo3_string          # DW_AT_name
+                                        # DW_AT_external
+    .byte   1                       # DW_AT_decl_file
+    .byte   3                       # DW_AT_decl_line
+    .byte   0                       # End Of Children Mark
+.Lcu3_end:
+
 .section .debug_abbrev,"",@progbits
 .Lsection_abbrev:
     .byte   1                       # Abbreviation Code
@@ -148,3 +171,5 @@ sym3:
     .asciz "sym2"
 .Linfo2_string:
     .asciz "sym3"
+.Linfo3_string:
+    .asciz "sym4"
index 2ca733a..b8df045 100644 (file)
@@ -22,7 +22,7 @@
 # CHECK-NEXT: >>> referenced by undef.s
 # CHECK-NEXT: >>>               {{.*}}:(.text+0x10)
 
-# CHECK:      error: undefined symbol: vtable for Foo
+# CHECK:      error: undefined symbol: vtable for Foo 
 # CHECK-NEXT: >>> referenced by undef.s
 # CHECK-NEXT: >>>               {{.*}}:(.text+0x15)
 # CHECK-NEXT: the vtable symbol may be undefined because the class is missing its key function (see https://lld.llvm.org/missingkeyfunction)
 # is requested, even if that particular part of the line information is not currently required.
 # Also show that the warnings are only printed once.
 # CHECK:      warning: parsing line table prologue at 0x00000000 should have ended at 0x00000038 but it ended at 0x00000037
-# CHECK-NEXT: warning: last sequence in debug line table at offset 0x0000005b is not terminated
+# CHECK-NEXT: warning: parsing line table prologue at offset 0x0000005b found unsupported version 0x01
+# CHECK-NEXT: warning: last sequence in debug line table at offset 0x00000061 is not terminated
 # CHECK:      error: undefined symbol: zed6a
-# CHECK-NEXT: >>> referenced by {{.*}}tmp4.o:(.text+0x0)
+# CHECK-NEXT: >>> referenced by undef-bad-debug.s:11 (dir{{/|\\}}undef-bad-debug.s:11)
+# CHECK-NEXT: >>>               {{.*}}4.o:(.text+0x0)
 # CHECK:      error: undefined symbol: zed6b
-# CHECK-NEXT: >>> referenced by {{.*}}tmp4.o:(.text+0x8)
+# CHECK-NEXT: >>> referenced by undef-bad-debug.s:21 (dir{{/|\\}}undef-bad-debug.s:21)
+# CHECK-NEXT: >>>               {{.*}}4.o:(.text+0x8)
+
+# Show that a problem in a line table that prevents further parsing of that
+# table means that no line information is displayed in the wardning.
+# CHECK:      error: undefined symbol: zed7
+# CHECK-NEXT: >>> referenced by {{.*}}4.o:(.text+0x10)
 
 # Show that a problem with one line table's information doesn't affect getting information from
 # a different one in the same object.
-# CHECK:      error: undefined symbol: zed7
+# CHECK:      error: undefined symbol: zed8
 # CHECK-NEXT: >>> referenced by undef-bad-debug2.s:11 (dir2{{/|\\}}undef-bad-debug2.s:11)
-# CHECK-NEXT: >>>               {{.*}}tmp4.o:(.text+0x10)
+# CHECK-NEXT: >>>               {{.*}}tmp4.o:(.text+0x18)
 
 # RUN: not ld.lld %t.o %t2.a -o %t.exe -no-demangle 2>&1 | \
 # RUN:   FileCheck -check-prefix=NO-DEMANGLE %s
index 708ee3a..51d05b8 100644 (file)
@@ -893,12 +893,15 @@ SymbolFileDWARF::GetTypeUnitSupportFiles(DWARFTypeUnit &tu) {
     llvm::DWARFDataExtractor data = m_context.getOrLoadLineData().GetAsLLVM();
     llvm::DWARFContext &ctx = m_context.GetAsLLVM();
     llvm::DWARFDebugLine::Prologue prologue;
-    llvm::Error error = prologue.parse(data, &line_table_offset, ctx);
-    if (error) {
+    auto report = [](llvm::Error error) {
       Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
       LLDB_LOG_ERROR(log, std::move(error),
                      "SymbolFileDWARF::GetTypeUnitSupportFiles failed to parse "
                      "the line table prologue");
+    };
+    llvm::Error error = prologue.parse(data, &line_table_offset, report, ctx);
+    if (error) {
+      report(std::move(error));
     } else {
       list = ParseSupportFilesFromPrologue(GetObjectFile()->GetModule(),
                                            prologue, tu.GetPathStyle());
index d5b6c72..e55f885 100644 (file)
@@ -138,6 +138,7 @@ public:
     void clear();
     void dump(raw_ostream &OS, DIDumpOptions DumpOptions) const;
     Error parse(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr,
+                function_ref<void(Error)> RecoverableErrorCallback,
                 const DWARFContext &Ctx, const DWARFUnit *U = nullptr);
   };
 
@@ -341,9 +342,12 @@ public:
     /// 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<void(Error)> 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<void(Error)> RecoverableErrorCallback,
+              function_ref<void(Error)> UnrecoverableErrorCallback);
 
     /// Indicates if the parser has parsed as much as possible.
     ///
index 01073af..e363290 100644 (file)
@@ -467,7 +467,7 @@ void DWARFContext::dump(
                              Optional<uint64_t> 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())
index 84803c8..4ca84f3 100644 (file)
@@ -299,10 +299,10 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData,
   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,
+    function_ref<void(Error)> RecoverableErrorCallback, const DWARFContext &Ctx,
+    const DWARFUnit *U) {
   const uint64_t PrologueOffset = *OffsetPtr;
 
   clear();
@@ -311,6 +311,8 @@ Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData,
     FormParams.Format = dwarf::DWARF64;
     TotalLength = DebugLineData.getU64(OffsetPtr);
   } else if (TotalLength >= dwarf::DW_LENGTH_lo_reserved) {
+    // 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,
@@ -318,6 +320,9 @@ Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData,
   }
   FormParams.Version = DebugLineData.getU16(OffsetPtr);
   if (getVersion() < 2)
+    // 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,
@@ -352,25 +357,32 @@ Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData,
     if (Error E =
             parseV5DirFileTables(DebugLineData, OffsetPtr, FormParams, Ctx, U,
                                  ContentTypes, IncludeDirectories, FileNames)) {
-      return joinErrors(
+      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 below 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);
+  if (*OffsetPtr != EndPrologueOffset) {
+    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));
+    *OffsetPtr = EndPrologueOffset;
+  }
   return Error::success();
 }
 
@@ -516,7 +528,8 @@ Error DWARFDebugLine::LineTable::parse(
 
   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.
@@ -1158,14 +1171,16 @@ DWARFDebugLine::LineTable DWARFDebugLine::SectionParser::parseNext(
 }
 
 void DWARFDebugLine::SectionParser::skip(
-    function_ref<void(Error)> ErrorCallback) {
+    function_ref<void(Error)> RecoverableErrorCallback,
+    function_ref<void(Error)> 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);
 }
 
index 501b3ed..913ec62 100644 (file)
@@ -64,7 +64,7 @@
 .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 # Length of Prologue
 .Lprologue_short_prologue_start:
 .byte   1               # Minimum Instruction Length
 .byte   1               # Maximum Operations per Instruction
 .asciz "file1"          # File table
 .byte   0, 0, 0
 .asciz "file2"
-.byte   1, 2, 3
-.byte   0
+.byte   1, 2
 .Lprologue_short_prologue_end:
+.byte   6               # Read as part of the prologue,
+                        # then later again as DW_LNS_negate_stmt.
+# FIXME: There should be an additional 0 byte here, but the file name parsing
+#        code does not recognise a missing null terminator.
+# Header end
 .byte   0, 9, 2         # DW_LNE_set_address
 .quad   0x1122334455667788
 .byte   0, 1, 1         # DW_LNE_end_sequence
@@ -91,7 +95,7 @@
 .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
 .asciz "file2"
 .byte   1, 2, 3
 .byte   0
+# Skipped byte (treated as part of prologue).
+.byte   6
 .Lprologue_long_prologue_end:
 .byte   0, 9, 2        # DW_LNE_set_address
 .quad   0x1111222233334444
 .short  5               # DWARF version number
 .byte   8               # Address Size
 .byte   0               # Segment Selector Size
-.long   15              # Length of Prologue (invalid)
+.long   .Linvalid_description_header_end0 - .Linvalid_description_params0 # Length of Prologue (invalid)
 .Linvalid_description_params0:
 .byte   1               # Minimum Instruction Length
 .byte   1               # Maximum Operations per Instruction
 .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
+.byte   0, 1, 1, 1, 1, 0, 0, 0, 1, 0 # Standard Opcode Lengths
+.Linvalid_description_header_end0:
+# The bytes from here onwards will also be read as part of the main body.
+                        # --- Prologue interpretation --- | --- Main body interpretation ---
+.byte   0, 1            # More standard opcodes           | First part of DW_LNE_end_sequence
 # Directory table format
-.byte   1               # One element per directory entry
-.byte   1               # DW_LNCT_path
-.byte   0x08            # DW_FORM_string
+.byte   1               # One element per directory entry | End of DW_LNE_end_sequence
+.byte   1               # DW_LNCT_path                    | DW_LNS_copy
+.byte   0x08            # DW_FORM_string                  | DW_LNS_const_add_pc
 # Directory table entries
-.byte   1               # 1 directory
-.asciz  "/tmp"
+.byte   1               # 1 directory                     | DW_LNS_copy
+.asciz  "/tmp"          # Directory name                  | four special opcodes + start of DW_LNE_end_sequence
 # File table format
-.byte   2               # 2 elements per file entry
-.byte   1               # DW_LNCT_path
-.byte   0x08            # DW_FORM_string
-.byte   2               # DW_LNCT_directory_index
-.byte   0x0b            # DW_FORM_data1
+.byte   1               # 1 element per file entry        | DW_LNE_end_sequence length
+.byte   1               # DW_LNCT_path                    | DW_LNE_end_sequence opcode
+.byte   0x08            # DW_FORM_string                  | DW_LNS_const_add_pc
 # File table entries
-.byte   1               # 1 file
-.asciz  "a.c"
-.byte   1
-.Linvalid_description_header_end0:
-.byte   0, 9, 2         # DW_LNE_set_address
+.byte   1               # 1 file                          | DW_LNS_copy
+.asciz  "xyz"           # File name                       | three special opcodes + start of DW_LNE_set_address
+# Header end
+.byte   9, 2            # Remainder of DW_LNE_set_address
 .quad   0xbabb1ebabb1e
 .byte   0, 1, 1         # DW_LNE_end_sequence
 .Linvalid_description_end0:
 .short  5               # DWARF version number
 .byte   8               # Address Size
 .byte   0               # Segment Selector Size
-.long   .Linvalid_file_header_end0-.Linvalid_file_params0-7     # Length of Prologue (invalid)
+.long   .Linvalid_file_header_end0 - .Linvalid_file_params0 # Length of Prologue (invalid)
 .Linvalid_file_params0:
 .byte   1               # Minimum Instruction Length
 .byte   1               # Maximum Operations per Instruction
 .byte   1               # DW_LNCT_path
 .byte   0x08            # DW_FORM_string
 .byte   2               # DW_LNCT_directory_index
-.byte   0x0b            # DW_FORM_data1
-# File table entries
-.byte   1               # 1 file
-.asciz  "a.c"
-.byte   1
 .Linvalid_file_header_end0:
+# The bytes from here onwards will also be read as part of the main body.
+                        # --- Prologue interpretation --- | --- Main body interpretation ---
+.byte   0x0b            # DW_FORM_data1                   | DW_LNS_set_epilogue_begin
+# File table entries
+.byte   1               # 1 file                          | DW_LNS_copy
+.asciz  "xyz"           # File name                       | 3 special opcodes + start of DW_LNE_end_sequence
+.byte   1               # Dir index                       | DW_LNE_end_sequence length
+# Header end
+.byte   1               # DW_LNE_end_sequence opcode
 .byte   0, 9, 2         # DW_LNE_set_address
 .quad   0xab4acadab4a
 .byte   0, 1, 1         # DW_LNE_end_sequence
 .short  5               # DWARF version number
 .byte   8               # Address Size
 .byte   0               # Segment Selector Size
-.long   .Linvalid_dir_header_end0-.Linvalid_dir_params0-16     # Length of Prologue (invalid)
+.long   .Linvalid_dir_header_end0 - .Linvalid_dir_params0 # Length of Prologue (invalid)
 .Linvalid_dir_params0:
 .byte   1               # Minimum Instruction Length
 .byte   1               # Maximum Operations per Instruction
 .byte   0x08            # DW_FORM_string
 # Directory table entries
 .byte   1               # 1 directory
-.asciz  "/tmp"
+.Linvalid_dir_header_end0:
+# The bytes from here onwards will also be read as part of the main body.
+                        # --- Prologue interpretation --- | --- Main body interpretation ---
+.asciz  "/tmp"          # Directory name                  | 4 special opcodes + start of DW_LNE_end_sequence
 # File table format
-.byte   2               # 2 elements per file entry
-.byte   1               # DW_LNCT_path
-.byte   0x08            # DW_FORM_string
-.byte   2               # DW_LNCT_directory_index
-.byte   0x0b            # DW_FORM_data1
+.byte   1               # 1 element per file entry        | DW_LNE_end_sequence length
+.byte   1               # DW_LNCT_path                    | DW_LNE_end_sequence length opcode
+.byte   0x08            # DW_FORM_string                  | DW_LNS_const_add_pc
 # File table entries
-.byte   1               # 1 file
-.asciz  "a.c"
-.byte   1
-.Linvalid_dir_header_end0:
-.byte   0, 9, 2         # DW_LNE_set_address
+.byte   1               # 1 file                          | DW_LNS_copy
+.asciz  "xyz"           # File name                       | start of DW_LNE_set_address
+# Header end
+.byte   9, 2            # DW_LNE_set_address length + opcode
 .quad   0x4444333322221111
 .byte   0, 1, 1         # DW_LNE_end_sequence
 .Linvalid_dir_end0:
 .asciz  "a.c"
 .byte   0
 # Data to show that the rest of the prologue is skipped.
-.byte   6
+.byte   1
 .Linvalid_md5_header_end0:
 .byte   0, 9, 2         # DW_LNE_set_address
 .quad   0x1234123412341234
 .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
+.long   .Linvalid_md5_header_end1 - .Linvalid_md5_params1 # Length of Prologue
 .Linvalid_md5_params1:
 .byte   1               # Minimum Instruction Length
 .byte   1               # Maximum Operations per Instruction
 .byte   1               # 1 directory
 .asciz  "/tmp"
 # File table format
-.byte   3               # 2 elements per file entry
+.byte   2               # 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.
-.byte   0xb             # This byte will not be read as part of the prologue.
 .Linvalid_md5_header_end1:
-.byte   0, 9, 2         # DW_LNE_set_address
+# The bytes from here onwards will also be read as part of the main body.
+                        # --- Prologue interpretation --- | --- Main body interpretation ---
+.byte   0x0b            # DW_FORM_data1                   | DW_LNS_set_epilogue_begin
+# File table entries
+.byte   1               # 1 file                          | DW_LNS_copy
+.asciz  "xyz"           # File name                       | 3 special opcodes + DW_LNE_set_address start
+.byte   9               # MD5 hash value                  | DW_LNE_set_address length
+# Header end
+.byte   2               # DW_LNE_set_address opcode
 .quad   0x4321432143214321
 .byte   0, 1, 1         # DW_LNE_end_sequence
 .Linvalid_md5_end1:
index 256d1c0..45bd52c 100644 (file)
@@ -36,7 +36,7 @@
 # RUN: FileCheck %s --input-file=%t-malformed-off-first.err --check-prefix=ALL\r
 \r
 ## Don't stop looking for the later unit if non-fatal issues are found.\r
-# RUN: llvm-dwarfdump -debug-line=0x2f8 %t-malformed.o 2> %t-malformed-off-last.err \\r
+# RUN: llvm-dwarfdump -debug-line=0x2ec %t-malformed.o 2> %t-malformed-off-last.err \\r
 # RUN:   | FileCheck %s --check-prefix=LAST --implicit-check-not='debug_line[{{.*}}]'\r
 # RUN: FileCheck %s --input-file=%t-malformed-off-last.err --check-prefix=ALL\r
 \r
@@ -53,8 +53,7 @@
 # FATAL-NEXT: total_length: 0xfffffffe\r
 # FATAL-NOT:  debug_line\r
 \r
-## For non-fatal prologue issues, the table prologue should be dumped, and any\r
-## subsequent tables should also be.\r
+## For non-fatal issues, the table data should be dumped.\r
 ## Case 1: Version 0 table.\r
 # NONFATAL:      debug_line[0x00000048]\r
 # NONFATAL-NEXT: Line table prologue\r
@@ -70,7 +69,7 @@
 # NONFATAL-NEXT: Line table prologue\r
 # NONFATAL-NOT:  include_directories\r
 # NONFATAL-NOT:  file_names\r
-# NONFATAL-NOT:  Address\r
+# NONFATAL:      0x8877665544332211 {{.*}} end_sequence\r
 \r
 ## Case 4: Prologue with length shorter than parsed.\r
 # NONFATAL:      debug_line[0x00000081]\r
 # NONFATAL-NEXT:            name: "file2"\r
 # NONFATAL-NEXT:       dir_index: 1\r
 # NONFATAL-NEXT:        mod_time: 0x00000002\r
-# NONFATAL-NEXT:          length: 0x00000003\r
-# NONFATAL-NOT:  file_names\r
-# NONFATAL-NOT:  Address\r
+# NONFATAL-NEXT:          length: 0x00000006\r
+# NONFATAL:      0x1122334455667788 {{.*}} 0 end_sequence{{$}}\r
 \r
 ## Case 5: Prologue with length longer than parsed.\r
-# NONFATAL:      debug_line[0x000000c9]\r
+# NONFATAL:      debug_line[0x000000c8]\r
 # NONFATAL-NEXT: Line table prologue\r
 # NONFATAL:      file_names[  2]:\r
 # NONFATAL-NEXT:            name: "file2"\r
@@ -92,7 +90,7 @@
 # NONFATAL-NEXT:        mod_time: 0x00000002\r
 # NONFATAL-NEXT:          length: 0x00000003\r
 # NONFATAL-NOT:  file_names\r
-# NONFATAL-NOT:  Address\r
+# NONFATAL:      0x1111222233334444 {{.*}} is_stmt end_sequence\r
 \r
 ## Case 6: Extended opcode with incorrect length versus expected.\r
 # NONFATAL:      debug_line[0x00000111]\r
 # NONFATAL:      standard_opcode_lengths[DW_LNS_set_isa] = 1\r
 # NONFATAL-NEXT: include_directories[  0] = "/tmp"\r
 # NONFATAL-NEXT: file_names[  0]:\r
-# NONFATAL-NEXT:            name: "a.c"\r
-# NONFATAL-NEXT:       dir_index: 1\r
-# NONFATAL-NOT:  Address\r
+# NONFATAL-NEXT:            name: "xyz"\r
+# NONFATAL:      0x0000000000000000 1 0 1 0 0 is_stmt end_sequence\r
+# NONFATAL:      0x0000babb1ebabb1e {{.*}} end_sequence\r
 \r
 ## Case 9: V5 prologue ends during file table.\r
-# NONFATAL:      debug_line[0x000001f2]\r
+# NONFATAL:      debug_line[0x000001ee]\r
 # NONFATAL-NEXT: Line table prologue\r
 # NONFATAL:      include_directories[  0] = "/tmp"\r
 # NONFATAL-NEXT: file_names[  0]:\r
-# NONFATAL-NEXT:            name: "a.c"\r
+# NONFATAL-NEXT:            name: "xyz"\r
 # NONFATAL-NEXT:       dir_index: 1\r
-# NONFATAL-NOT:  Address\r
+# NONFATAL:      0x0000000000000000 {{.*}} epilogue_begin\r
+# NONFATAL:      0x00000ab4acadab4a {{.*}} end_sequence\r
 \r
 ## Case 10: V5 prologue ends during directory table.\r
-# NONFATAL:      debug_line[0x00000232]\r
+# NONFATAL:      debug_line[0x0000022f]\r
 # NONFATAL-NEXT: Line table prologue\r
 # NONFATAL:      include_directories[  0] = "/tmp"\r
 # NONFATAL-NEXT: file_names[  0]:\r
-# NONFATAL-NEXT:            name: "a.c"\r
-# NONFATAL-NEXT:       dir_index: 1\r
-# NONFATAL-NOT:  Address\r
+# NONFATAL-NEXT:            name: "xyz"\r
+# NONFATAL:      0x0000000000000002 2 0 1 0 0 is_stmt{{$}}\r
+# NONFATAL:      0x4444333322221111 {{.*}} end_sequence\r
 \r
 ## Case 11: V5 invalid MD5 hash form when there is still data to be read.\r
-# NONFATAL:      debug_line[0x00000272]\r
+# NONFATAL:      debug_line[0x0000026b]\r
 # NONFATAL-NEXT: Line table prologue\r
 # NONFATAL:      include_directories[  0] = "/tmp"\r
 # NONFATAL-NOT:  file_names\r
-# NONFATAL-NOT:  Address\r
+# NONFATAL-NOT:  is_stmt\r
+# NONFATAL:      0x1234123412341234 {{.*}} end_sequence\r
 \r
 ## Case 12: V5 invalid MD5 hash form when data beyond the prologue length has\r
 ## been read before the MD5 problem is identified.\r
-# NONFATAL: debug_line[0x000002b5]\r
+# NONFATAL: debug_line[0x000002ae]\r
 # NONFATAL-NEXT: Line table prologue\r
 # NONFATAL:      include_directories[  0] = "/tmp"\r
 # NONFATAL-NOT:  file_names\r
-# NONFATAL-NOT:  Address\r
+# NONFATAL:      0x0000000000000000 {{.*}} epilogue_begin\r
+# NONFATAL:      0x4321432143214321 {{.*}} is_stmt end_sequence\r
 \r
-# LAST:          debug_line[0x000002f8]\r
+# LAST:          debug_line[0x000002ec]\r
 # LAST:          0x00000000cafebabe {{.*}} end_sequence\r
 \r
 # RESERVED: warning: parsing line table prologue at offset 0x00000048 unsupported reserved unit length found of value 0xfffffffe\r
 # ALL-NEXT: warning: parsing line table prologue at offset 0x0000004e found unsupported version 0x01\r
 # ALL-NEXT: warning: parsing line table prologue at 0x00000054 found an invalid directory or file table description at 0x00000073\r
 # ALL-NEXT: warning: failed to parse entry content descriptions because no path was found\r
-# FIXME - The latter offset in the next line should be 0xad. The filename parsing code does not notice a missing terminating byte.\r
 # ALL-NEXT: warning: parsing line table prologue at 0x00000081 should have ended at 0x000000b9 but it ended at 0x000000ba\r
-# ALL-NEXT: warning: parsing line table prologue at 0x000000c9 should have ended at 0x00000104 but it ended at 0x00000103\r
+# ALL-NEXT: warning: parsing line table prologue at 0x000000c8 should have ended at 0x00000103 but it ended at 0x00000102\r
 # OTHER-NEXT: warning: unexpected line op length at offset 0x00000158 expected 0x02 found 0x01\r
 # OTHER-NEXT: warning: unexpected line op length at offset 0x0000015c expected 0x01 found 0x02\r
 # OTHER-NEXT: warning: last sequence in debug line table at offset 0x0000016c is not terminated\r
-# ALL-NEXT: warning: parsing line table prologue at 0x000001b2 should have ended at 0x000001cd but it ended at 0x000001e4\r
-# ALL-NEXT: warning: parsing line table prologue at 0x000001f2 should have ended at 0x0000021d but it ended at 0x00000224\r
-# ALL-NEXT: warning: parsing line table prologue at 0x00000232 should have ended at 0x00000254 but it ended at 0x00000264\r
-# ALL-NEXT: warning: parsing line table prologue at 0x00000272 found an invalid directory or file table description at 0x000002a6\r
+# ALL-NEXT: warning: parsing line table prologue at 0x000001b2 should have ended at 0x000001ce but it ended at 0x000001e1\r
+# ALL-NEXT: warning: parsing line table prologue at 0x000001ee should have ended at 0x00000219 but it ended at 0x00000220\r
+# ALL-NEXT: warning: parsing line table prologue at 0x0000022f should have ended at 0x00000251 but it ended at 0x0000025e\r
+# ALL-NEXT: warning: parsing line table prologue at 0x0000026b found an invalid directory or file table description at 0x0000029f\r
 # ALL-NEXT: warning: failed to parse file entry because the MD5 hash is invalid\r
-# ALL-NEXT: warning: parsing line table prologue at 0x000002b5 found an invalid directory or file table description at 0x000002e9\r
+# ALL-NEXT: warning: parsing line table prologue at 0x000002ae found an invalid directory or file table description at 0x000002e0\r
 # ALL-NEXT: warning: failed to parse file entry because the MD5 hash is invalid\r
+# ALL-NEXT: warning: parsing line table prologue at 0x000002ae should have ended at 0x000002d9 but it ended at 0x000002e0\r
 # ALL-NOT:  warning:\r
index 731afaf..89c28bc 100644 (file)
@@ -359,10 +359,15 @@ TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) {
 \r
   generate();\r
 \r
-  checkGetOrParseLineTableEmitsFatalError(\r
+  auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,\r
+                                                    nullptr, RecordRecoverable);\r
+  EXPECT_THAT_EXPECTED(ExpectedLineTable, Succeeded());\r
+\r
+  checkError(\r
       {"parsing line table prologue at 0x00000000 found an invalid directory "\r
        "or file table description at 0x00000014",\r
-       "failed to parse entry content descriptions because no path was found"});\r
+       "failed to parse entry content descriptions because no path was found"},\r
+      std::move(Recoverable));\r
 }\r
 \r
 TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) {\r
@@ -379,14 +384,24 @@ TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) {
 \r
   generate();\r
 \r
+  auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,\r
+                                                    nullptr, RecordRecoverable);\r
+  ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());\r
+  DWARFDebugLine::LineTable Result(**ExpectedLineTable);\r
+  // Undo the earlier modification so that it can be compared against a\r
+  // "default" prologue.\r
+  --Result.Prologue.PrologueLength;\r
+  checkDefaultPrologue(Version, Format, Result.Prologue, 0);\r
+\r
   uint64_t ExpectedEnd =\r
       Prologue.TotalLength + 1 + Prologue.sizeofTotalLength();\r
-  checkGetOrParseLineTableEmitsFatalError(\r
+  checkError(\r
       (Twine("parsing line table prologue at 0x00000000 should have ended at "\r
              "0x000000") +\r
        Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" +\r
        Twine::utohexstr(ExpectedEnd - 1))\r
-          .str());\r
+          .str(),\r
+      std::move(Recoverable));\r
 }\r
 \r
 TEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) {\r
@@ -408,16 +423,29 @@ TEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) {
 \r
   generate();\r
 \r
+  auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,\r
+                                                    nullptr, RecordRecoverable);\r
+  ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());\r
+  DWARFDebugLine::LineTable Result(**ExpectedLineTable);\r
+  // Undo the earlier modification so that it can be compared against a\r
+  // "default" prologue.\r
+  if (Version < 5)\r
+    Result.Prologue.PrologueLength += 2;\r
+  else\r
+    Result.Prologue.PrologueLength += 1;\r
+  checkDefaultPrologue(Version, Format, Result.Prologue, 0);\r
+\r
   uint64_t ExpectedEnd =\r
       Prologue.TotalLength - 1 + Prologue.sizeofTotalLength();\r
   if (Version < 5)\r
     --ExpectedEnd;\r
-  checkGetOrParseLineTableEmitsFatalError(\r
+  checkError(\r
       (Twine("parsing line table prologue at 0x00000000 should have ended at "\r
              "0x000000") +\r
        Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" +\r
        Twine::utohexstr(ExpectedEnd + 1))\r
-          .str());\r
+          .str(),\r
+      std::move(Recoverable));\r
 }\r
 \r
 INSTANTIATE_TEST_CASE_P(\r
@@ -636,14 +664,15 @@ TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) {
   EXPECT_EQ(Parser.getOffset(), 0u);\r
   ASSERT_FALSE(Parser.done());\r
 \r
-  Parser.skip(RecordUnrecoverable);\r
+  Parser.skip(RecordRecoverable, RecordUnrecoverable);\r
   EXPECT_EQ(Parser.getOffset(), 62u);\r
   ASSERT_FALSE(Parser.done());\r
 \r
-  Parser.skip(RecordUnrecoverable);\r
+  Parser.skip(RecordRecoverable, RecordUnrecoverable);\r
   EXPECT_EQ(Parser.getOffset(), 136u);\r
   EXPECT_TRUE(Parser.done());\r
 \r
+  EXPECT_FALSE(Recoverable);\r
   EXPECT_FALSE(Unrecoverable);\r
 }\r
 \r
@@ -688,10 +717,11 @@ TEST_F(DebugLineBasicFixture, ParserMovesToEndForBadLengthWhenSkipping) {
   generate();\r
 \r
   DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);\r
-  Parser.skip(RecordUnrecoverable);\r
+  Parser.skip(RecordRecoverable, RecordUnrecoverable);\r
 \r
   EXPECT_EQ(Parser.getOffset(), 4u);\r
   EXPECT_TRUE(Parser.done());\r
+  EXPECT_FALSE(Recoverable);\r
 \r
   checkError("parsing line table prologue at offset 0x00000000 unsupported "\r
              "reserved unit length found of value 0xfffffff0",\r
@@ -767,11 +797,12 @@ TEST_F(DebugLineBasicFixture,
   generate();\r
 \r
   DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);\r
-  Parser.skip(RecordUnrecoverable);\r
+  Parser.skip(RecordRecoverable, RecordUnrecoverable);\r
   ASSERT_FALSE(Parser.done());\r
-  Parser.skip(RecordUnrecoverable);\r
+  Parser.skip(RecordRecoverable, RecordUnrecoverable);\r
 \r
   EXPECT_TRUE(Parser.done());\r
+  EXPECT_FALSE(Recoverable);\r
 \r
   checkError({"parsing line table prologue at offset 0x00000000 found "\r
               "unsupported version 0x00",\r
@@ -789,9 +820,10 @@ TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) {
   generate();\r
 \r
   DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);\r
-  Parser.skip(RecordUnrecoverable);\r
+  Parser.skip(RecordRecoverable, RecordUnrecoverable);\r
 \r
   EXPECT_TRUE(Parser.done());\r
+  EXPECT_FALSE(Recoverable);\r
   EXPECT_FALSE(Unrecoverable);\r
 }\r
 \r