1 //===- DWARFDebugLineTest.cpp ---------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "DwarfGenerator.h"
10 #include "DwarfUtils.h"
11 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
12 #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
13 #include "llvm/Object/ObjectFile.h"
14 #include "llvm/Testing/Support/Error.h"
15 #include "gtest/gtest.h"
16 
17 using namespace llvm;
18 using namespace dwarf;
19 using namespace dwarfgen;
20 using namespace object;
21 using namespace utils;
22 using namespace testing;
23 
24 namespace {
25 struct CommonFixture {
26   CommonFixture()
27       : LineData("", true, 0), Recoverable(Error::success()),
28         RecordRecoverable(std::bind(&CommonFixture::recordRecoverable, this,
29                                     std::placeholders::_1)),
30         Unrecoverable(Error::success()),
31         RecordUnrecoverable(std::bind(&CommonFixture::recordUnrecoverable, this,
32                                       std::placeholders::_1)){};
33 
34   ~CommonFixture() {
35     EXPECT_FALSE(Recoverable);
36     EXPECT_FALSE(Unrecoverable);
37   }
38 
39   bool setupGenerator(uint16_t Version = 4) {
40     Triple T = getDefaultTargetTripleForAddrSize(8);
41     if (!isConfigurationSupported(T))
42       return false;
43     auto ExpectedGenerator = Generator::create(T, Version);
44     if (ExpectedGenerator)
45       Gen.reset(ExpectedGenerator->release());
46     return true;
47   }
48 
49   void generate() {
50     Context = createContext();
51     assert(Context != nullptr && "test state is not valid");
52     const DWARFObject &Obj = Context->getDWARFObj();
53     LineData = DWARFDataExtractor(
54         Obj, Obj.getLineSection(),
55         getDefaultTargetTripleForAddrSize(8).isLittleEndian(), 8);
56   }
57 
58   std::unique_ptr<DWARFContext> createContext() {
59     if (!Gen)
60       return nullptr;
61     StringRef FileBytes = Gen->generate();
62     MemoryBufferRef FileBuffer(FileBytes, "dwarf");
63     auto Obj = object::ObjectFile::createObjectFile(FileBuffer);
64     if (Obj)
65       return DWARFContext::create(**Obj);
66     return nullptr;
67   }
68 
69   DWARFDebugLine::SectionParser setupParser() {
70     LineTable &LT = Gen->addLineTable(DWARF32);
71     LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});
72     LT.addStandardOpcode(DW_LNS_copy, {});
73     LT.addByte(0xaa);
74     LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
75 
76     LineTable &LT2 = Gen->addLineTable(DWARF64);
77     LT2.addExtendedOpcode(9, DW_LNE_set_address,
78                           {{0x11223344, LineTable::Quad}});
79     LT2.addStandardOpcode(DW_LNS_copy, {});
80     LT2.addByte(0xbb);
81     LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
82 
83     generate();
84 
85     return DWARFDebugLine::SectionParser(LineData, *Context, CUs, TUs);
86   }
87 
88   void recordRecoverable(Error Err) {
89     Recoverable = joinErrors(std::move(Recoverable), std::move(Err));
90   }
91   void recordUnrecoverable(Error Err) {
92     Unrecoverable = joinErrors(std::move(Unrecoverable), std::move(Err));
93   }
94 
95   void checkError(ArrayRef<StringRef> ExpectedMsgs, Error Err) {
96     ASSERT_TRUE(Err.operator bool());
97     size_t WhichMsg = 0;
98     Error Remaining =
99         handleErrors(std::move(Err), [&](const ErrorInfoBase &Actual) {
100           ASSERT_LT(WhichMsg, ExpectedMsgs.size());
101           // Use .str(), because googletest doesn't visualise a StringRef
102           // properly.
103           EXPECT_EQ(Actual.message(), ExpectedMsgs[WhichMsg++].str());
104         });
105     EXPECT_EQ(WhichMsg, ExpectedMsgs.size());
106     EXPECT_FALSE(Remaining);
107   }
108 
109   void checkError(StringRef ExpectedMsg, Error Err) {
110     checkError(ArrayRef<StringRef>{ExpectedMsg}, std::move(Err));
111   }
112 
113   void checkGetOrParseLineTableEmitsError(StringRef ExpectedMsg,
114                                           uint64_t Offset = 0) {
115     auto ExpectedLineTable = Line.getOrParseLineTable(
116         LineData, Offset, *Context, nullptr, RecordRecoverable);
117     EXPECT_FALSE(ExpectedLineTable);
118     EXPECT_FALSE(Recoverable);
119 
120     checkError(ExpectedMsg, ExpectedLineTable.takeError());
121   }
122 
123   void checkGetOrParseLineTableEmitsError(ArrayRef<StringRef> ExpectedMsgs,
124                                           uint64_t Offset = 0) {
125     auto ExpectedLineTable = Line.getOrParseLineTable(
126         LineData, Offset, *Context, nullptr, RecordRecoverable);
127     EXPECT_FALSE(ExpectedLineTable);
128     EXPECT_FALSE(Recoverable);
129 
130     checkError(ExpectedMsgs, ExpectedLineTable.takeError());
131   }
132 
133   std::unique_ptr<Generator> Gen;
134   std::unique_ptr<DWARFContext> Context;
135   DWARFDataExtractor LineData;
136   DWARFDebugLine Line;
137   Error Recoverable;
138   std::function<void(Error)> RecordRecoverable;
139   Error Unrecoverable;
140   std::function<void(Error)> RecordUnrecoverable;
141 
142   SmallVector<std::unique_ptr<DWARFUnit>, 2> CUs;
143   SmallVector<std::unique_ptr<DWARFUnit>, 2> TUs;
144 };
145 
146 // Fixtures must derive from "Test", but parameterised fixtures from
147 // "TestWithParam". It does not seem possible to inherit from both, so we share
148 // the common state in a separate class, inherited by the two fixture classes.
149 struct DebugLineBasicFixture : public Test, public CommonFixture {};
150 
151 struct DebugLineParameterisedFixture
152     : public TestWithParam<std::pair<uint16_t, DwarfFormat>>,
153       public CommonFixture {
154   void SetUp() { std::tie(Version, Format) = GetParam(); }
155 
156   uint16_t Version;
157   DwarfFormat Format;
158 };
159 
160 void checkDefaultPrologue(uint16_t Version, DwarfFormat Format,
161                           DWARFDebugLine::Prologue Prologue,
162                           uint64_t BodyLength) {
163   // Check version specific fields and values.
164   uint64_t UnitLength;
165   uint64_t PrologueLength;
166   switch (Version) {
167   case 4:
168     PrologueLength = 36;
169     UnitLength = PrologueLength + 2;
170     EXPECT_EQ(Prologue.MaxOpsPerInst, 1u);
171     break;
172   case 2:
173   case 3:
174     PrologueLength = 35;
175     UnitLength = PrologueLength + 2;
176     break;
177   case 5:
178     PrologueLength = 39;
179     UnitLength = PrologueLength + 4;
180     EXPECT_EQ(Prologue.getAddressSize(), 8u);
181     EXPECT_EQ(Prologue.SegSelectorSize, 0u);
182     break;
183   default:
184     llvm_unreachable("unsupported DWARF version");
185   }
186   UnitLength += BodyLength + (Format == DWARF32 ? 4 : 8);
187 
188   EXPECT_EQ(Prologue.TotalLength, UnitLength);
189   EXPECT_EQ(Prologue.PrologueLength, PrologueLength);
190   EXPECT_EQ(Prologue.MinInstLength, 1u);
191   EXPECT_EQ(Prologue.DefaultIsStmt, 1u);
192   EXPECT_EQ(Prologue.LineBase, -5);
193   EXPECT_EQ(Prologue.LineRange, 14u);
194   EXPECT_EQ(Prologue.OpcodeBase, 13u);
195   std::vector<uint8_t> ExpectedLengths = {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1};
196   EXPECT_EQ(Prologue.StandardOpcodeLengths, ExpectedLengths);
197   ASSERT_EQ(Prologue.IncludeDirectories.size(), 1u);
198   ASSERT_EQ(Prologue.IncludeDirectories[0].getForm(), DW_FORM_string);
199   EXPECT_STREQ(*Prologue.IncludeDirectories[0].getAsCString(), "a dir");
200   ASSERT_EQ(Prologue.FileNames.size(), 1u);
201   ASSERT_EQ(Prologue.FileNames[0].Name.getForm(), DW_FORM_string);
202   EXPECT_STREQ(*Prologue.FileNames[0].Name.getAsCString(), "a file");
203 }
204 
205 TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffset) {
206   if (!setupGenerator())
207     return;
208   generate();
209 
210   checkGetOrParseLineTableEmitsError(
211       "offset 0x00000000 is not a valid debug line section offset", 0);
212   // Repeat to show that an error is reported each time.
213   checkGetOrParseLineTableEmitsError(
214       "offset 0x00000000 is not a valid debug line section offset", 0);
215   // Show that an error is reported for later offsets too.
216   checkGetOrParseLineTableEmitsError(
217       "offset 0x00000001 is not a valid debug line section offset", 1);
218 }
219 
220 TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffsetAfterData) {
221   if (!setupGenerator())
222     return;
223 
224   LineTable &LT = Gen->addLineTable();
225   LT.setCustomPrologue({{0, LineTable::Byte}});
226 
227   generate();
228 
229   checkGetOrParseLineTableEmitsError(
230       "offset 0x00000001 is not a valid debug line section offset", 1);
231 }
232 
233 TEST_P(DebugLineParameterisedFixture, GetOrParseLineTableValidTable) {
234   if (!setupGenerator(Version))
235     return;
236 
237   SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
238                (Format == DWARF64 ? "DWARF64" : "DWARF32"));
239 
240   LineTable &LT = Gen->addLineTable(Format);
241   LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});
242   LT.addStandardOpcode(DW_LNS_copy, {});
243   LT.addByte(0xaa);
244   LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
245 
246   LineTable &LT2 = Gen->addLineTable(Format);
247   LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x11223344, LineTable::Quad}});
248   LT2.addStandardOpcode(DW_LNS_copy, {});
249   LT2.addByte(0xbb);
250   LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
251   LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x55667788, LineTable::Quad}});
252   LT2.addStandardOpcode(DW_LNS_copy, {});
253   LT2.addByte(0xcc);
254   LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
255 
256   generate();
257 
258   auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
259                                                     nullptr, RecordRecoverable);
260   ASSERT_TRUE(ExpectedLineTable.operator bool());
261   EXPECT_FALSE(Recoverable);
262   const DWARFDebugLine::LineTable *Expected = *ExpectedLineTable;
263   checkDefaultPrologue(Version, Format, Expected->Prologue, 16);
264   EXPECT_EQ(Expected->Sequences.size(), 1u);
265 
266   uint64_t SecondOffset =
267       Expected->Prologue.sizeofTotalLength() + Expected->Prologue.TotalLength;
268   Recoverable = Error::success();
269   auto ExpectedLineTable2 = Line.getOrParseLineTable(
270       LineData, SecondOffset, *Context, nullptr, RecordRecoverable);
271   ASSERT_TRUE(ExpectedLineTable2.operator bool());
272   EXPECT_FALSE(Recoverable);
273   const DWARFDebugLine::LineTable *Expected2 = *ExpectedLineTable2;
274   checkDefaultPrologue(Version, Format, Expected2->Prologue, 32);
275   EXPECT_EQ(Expected2->Sequences.size(), 2u);
276 
277   EXPECT_NE(Expected, Expected2);
278 
279   // Check that if the same offset is requested, the exact same pointer is
280   // returned.
281   Recoverable = Error::success();
282   auto ExpectedLineTable3 = Line.getOrParseLineTable(
283       LineData, 0, *Context, nullptr, RecordRecoverable);
284   ASSERT_TRUE(ExpectedLineTable3.operator bool());
285   EXPECT_FALSE(Recoverable);
286   EXPECT_EQ(Expected, *ExpectedLineTable3);
287 
288   Recoverable = Error::success();
289   auto ExpectedLineTable4 = Line.getOrParseLineTable(
290       LineData, SecondOffset, *Context, nullptr, RecordRecoverable);
291   ASSERT_TRUE(ExpectedLineTable4.operator bool());
292   EXPECT_FALSE(Recoverable);
293   EXPECT_EQ(Expected2, *ExpectedLineTable4);
294 
295   // TODO: Add tests that show that the body of the programs have been read
296   // correctly.
297 }
298 
299 TEST_F(DebugLineBasicFixture, ErrorForReservedLength) {
300   if (!setupGenerator())
301     return;
302 
303   LineTable &LT = Gen->addLineTable();
304   LT.setCustomPrologue({{0xfffffff0, LineTable::Long}});
305 
306   generate();
307 
308   checkGetOrParseLineTableEmitsError(
309       "parsing line table prologue at offset 0x00000000 unsupported reserved "
310       "unit length found of value 0xfffffff0");
311 }
312 
313 TEST_F(DebugLineBasicFixture, ErrorForLowVersion) {
314   if (!setupGenerator())
315     return;
316 
317   LineTable &LT = Gen->addLineTable();
318   LT.setCustomPrologue(
319       {{LineTable::Half, LineTable::Long}, {1, LineTable::Half}});
320 
321   generate();
322 
323   checkGetOrParseLineTableEmitsError("parsing line table prologue at offset "
324                                      "0x00000000 found unsupported version "
325                                      "0x01");
326 }
327 
328 TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) {
329   if (!setupGenerator(5))
330     return;
331 
332   LineTable &LT = Gen->addLineTable();
333   LT.setCustomPrologue({
334       {19, LineTable::Long}, // unit length
335       {5, LineTable::Half},  // version
336       {8, LineTable::Byte},  // addr size
337       {0, LineTable::Byte},  // segment selector size
338       {11, LineTable::Long}, // prologue length
339       {1, LineTable::Byte},  // min instruction length
340       {1, LineTable::Byte},  // max ops per instruction
341       {1, LineTable::Byte},  // default is_stmt
342       {0, LineTable::Byte},  // line base
343       {14, LineTable::Byte}, // line range
344       {2, LineTable::Byte},  // opcode base (small to reduce the amount of
345                              // setup required).
346       {0, LineTable::Byte},  // standard opcode lengths
347       {0, LineTable::Byte},  // directory entry format count (should not be
348                              // zero).
349       {0, LineTable::ULEB},  // directories count
350       {0, LineTable::Byte},  // file name entry format count
351       {0, LineTable::ULEB}   // file name entry count
352   });
353 
354   generate();
355 
356   checkGetOrParseLineTableEmitsError(
357       {"parsing line table prologue at 0x00000000 found an invalid directory "
358        "or file table description at 0x00000014",
359        "failed to parse entry content descriptions because no path was found"});
360 }
361 
362 TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) {
363   if (!setupGenerator(Version))
364     return;
365 
366   SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
367                (Format == DWARF64 ? "DWARF64" : "DWARF32"));
368 
369   LineTable &LT = Gen->addLineTable(Format);
370   DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
371   ++Prologue.PrologueLength;
372   LT.setPrologue(Prologue);
373 
374   generate();
375 
376   uint64_t ExpectedEnd =
377       Prologue.TotalLength + 1 + Prologue.sizeofTotalLength();
378   checkGetOrParseLineTableEmitsError(
379       (Twine("parsing line table prologue at 0x00000000 should have ended at "
380              "0x000000") +
381        Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" +
382        Twine::utohexstr(ExpectedEnd - 1))
383           .str());
384 }
385 
386 TEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) {
387   if (!setupGenerator(Version))
388     return;
389 
390   SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
391                (Format == DWARF64 ? "DWARF64" : "DWARF32"));
392 
393   LineTable &LT = Gen->addLineTable(Format);
394   DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
395   // FIXME: Ideally, we'd test for 1 less than expected, but the code does not
396   // currently fail if missing only the terminator of a v2-4 file table.
397   if (Version < 5)
398     Prologue.PrologueLength -= 2;
399   else
400     Prologue.PrologueLength -= 1;
401   LT.setPrologue(Prologue);
402 
403   generate();
404 
405   uint64_t ExpectedEnd =
406       Prologue.TotalLength - 1 + Prologue.sizeofTotalLength();
407   if (Version < 5)
408     --ExpectedEnd;
409   checkGetOrParseLineTableEmitsError(
410       (Twine("parsing line table prologue at 0x00000000 should have ended at "
411              "0x000000") +
412        Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" +
413        Twine::utohexstr(ExpectedEnd + 1))
414           .str());
415 }
416 
417 INSTANTIATE_TEST_CASE_P(
418     LineTableTestParams, DebugLineParameterisedFixture,
419     Values(std::make_pair(
420                2, DWARF32), // Test lower-bound of v2-3 fields and DWARF32.
421            std::make_pair(3, DWARF32), // Test upper-bound of v2-3 fields.
422            std::make_pair(4, DWARF64), // Test v4 fields and DWARF64.
423            std::make_pair(5, DWARF32), std::make_pair(5, DWARF64)), );
424 
425 TEST_F(DebugLineBasicFixture, ErrorForInvalidExtendedOpcodeLength) {
426   if (!setupGenerator())
427     return;
428 
429   LineTable &LT = Gen->addLineTable();
430   // The Length should be 1 for an end sequence opcode.
431   LT.addExtendedOpcode(2, DW_LNE_end_sequence, {});
432 
433   generate();
434 
435   checkGetOrParseLineTableEmitsError("unexpected line op length at offset "
436                                      "0x00000030 expected 0x02 found 0x01");
437 }
438 
439 TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) {
440   if (!setupGenerator())
441     return;
442 
443   LineTable &LT = Gen->addLineTable();
444   // The line data extractor expects size 8 (Quad) addresses.
445   LT.addExtendedOpcode(5, DW_LNE_set_address, {{0x11223344, LineTable::Long}});
446   LT.addStandardOpcode(DW_LNS_copy, {});
447   LT.addByte(0xaa);
448   LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
449 
450   generate();
451 
452   checkGetOrParseLineTableEmitsError(
453       "mismatching address size at offset 0x00000030 expected 0x08 found 0x04");
454 }
455 
456 TEST_F(DebugLineBasicFixture, CallbackUsedForUnterminatedSequence) {
457   if (!setupGenerator())
458     return;
459 
460   LineTable &LT = Gen->addLineTable();
461   LT.addExtendedOpcode(9, DW_LNE_set_address,
462                        {{0x1122334455667788, LineTable::Quad}});
463   LT.addStandardOpcode(DW_LNS_copy, {});
464   LT.addByte(0xaa);
465   LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
466   LT.addExtendedOpcode(9, DW_LNE_set_address,
467                        {{0x99aabbccddeeff00, LineTable::Quad}});
468   LT.addStandardOpcode(DW_LNS_copy, {});
469   LT.addByte(0xbb);
470   LT.addByte(0xcc);
471 
472   generate();
473 
474   auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
475                                                     nullptr, RecordRecoverable);
476   checkError("last sequence in debug line table is not terminated!",
477              std::move(Recoverable));
478   ASSERT_TRUE(ExpectedLineTable.operator bool());
479   EXPECT_EQ((*ExpectedLineTable)->Rows.size(), 6u);
480   // The unterminated sequence is not added to the sequence list.
481   EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
482 }
483 
484 TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) {
485   if (!setupGenerator())
486     return;
487 
488   DWARFDebugLine::SectionParser Parser = setupParser();
489 
490   EXPECT_EQ(Parser.getOffset(), 0u);
491   ASSERT_FALSE(Parser.done());
492 
493   DWARFDebugLine::LineTable Parsed =
494       Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
495   checkDefaultPrologue(4, DWARF32, Parsed.Prologue, 16);
496   EXPECT_EQ(Parsed.Sequences.size(), 1u);
497   EXPECT_EQ(Parser.getOffset(), 62u);
498   ASSERT_FALSE(Parser.done());
499 
500   DWARFDebugLine::LineTable Parsed2 =
501       Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
502   checkDefaultPrologue(4, DWARF64, Parsed2.Prologue, 16);
503   EXPECT_EQ(Parsed2.Sequences.size(), 1u);
504   EXPECT_EQ(Parser.getOffset(), 136u);
505   EXPECT_TRUE(Parser.done());
506 
507   EXPECT_FALSE(Recoverable);
508   EXPECT_FALSE(Unrecoverable);
509 }
510 
511 TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) {
512   if (!setupGenerator())
513     return;
514 
515   DWARFDebugLine::SectionParser Parser = setupParser();
516 
517   EXPECT_EQ(Parser.getOffset(), 0u);
518   ASSERT_FALSE(Parser.done());
519 
520   Parser.skip(RecordUnrecoverable);
521   EXPECT_EQ(Parser.getOffset(), 62u);
522   ASSERT_FALSE(Parser.done());
523 
524   Parser.skip(RecordUnrecoverable);
525   EXPECT_EQ(Parser.getOffset(), 136u);
526   EXPECT_TRUE(Parser.done());
527 
528   EXPECT_FALSE(Unrecoverable);
529 }
530 
531 TEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) {
532   if (!setupGenerator())
533     return;
534 
535   generate();
536   DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
537 
538   EXPECT_TRUE(Parser.done());
539 }
540 
541 TEST_F(DebugLineBasicFixture, ParserMovesToEndForBadLengthWhenParsing) {
542   if (!setupGenerator())
543     return;
544 
545   LineTable &LT = Gen->addLineTable();
546   LT.setCustomPrologue({{0xfffffff0, LineTable::Long}});
547   Gen->addLineTable();
548   generate();
549 
550   DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
551   Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
552 
553   EXPECT_EQ(Parser.getOffset(), 4u);
554   EXPECT_TRUE(Parser.done());
555   EXPECT_FALSE(Recoverable);
556 
557   checkError("parsing line table prologue at offset 0x00000000 unsupported "
558              "reserved unit length found of value 0xfffffff0",
559              std::move(Unrecoverable));
560 }
561 
562 TEST_F(DebugLineBasicFixture, ParserMovesToEndForBadLengthWhenSkipping) {
563   if (!setupGenerator())
564     return;
565 
566   LineTable &LT = Gen->addLineTable();
567   LT.setCustomPrologue({{0xfffffff0, LineTable::Long}});
568   Gen->addLineTable();
569   generate();
570 
571   DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
572   Parser.skip(RecordUnrecoverable);
573 
574   EXPECT_EQ(Parser.getOffset(), 4u);
575   EXPECT_TRUE(Parser.done());
576 
577   checkError("parsing line table prologue at offset 0x00000000 unsupported "
578              "reserved unit length found of value 0xfffffff0",
579              std::move(Unrecoverable));
580 }
581 
582 TEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) {
583   if (!setupGenerator())
584     return;
585 
586   LineTable &LT = Gen->addLineTable(DWARF32);
587   LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});
588   LineTable &LT2 = Gen->addLineTable(DWARF32);
589   LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}});
590   generate();
591 
592   DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
593   Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
594   ASSERT_FALSE(Parser.done());
595   Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
596 
597   EXPECT_TRUE(Parser.done());
598   EXPECT_FALSE(Recoverable);
599 
600   checkError({"parsing line table prologue at offset 0x00000000 found "
601               "unsupported version 0x00",
602               "parsing line table prologue at offset 0x00000006 found "
603               "unsupported version 0x01"},
604              std::move(Unrecoverable));
605 }
606 
607 TEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) {
608   if (!setupGenerator())
609     return;
610 
611   LineTable &LT = Gen->addLineTable(DWARF32);
612   LT.addExtendedOpcode(0x42, DW_LNE_end_sequence, {});
613   LineTable &LT2 = Gen->addLineTable(DWARF32);
614   LT2.addExtendedOpcode(9, DW_LNE_set_address,
615                         {{0x1234567890abcdef, LineTable::Quad}});
616   LT2.addStandardOpcode(DW_LNS_copy, {});
617   LT2.addByte(0xbb);
618   generate();
619 
620   DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
621   Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
622   EXPECT_FALSE(Recoverable);
623   ASSERT_FALSE(Parser.done());
624   checkError(
625       "unexpected line op length at offset 0x00000030 expected 0x42 found 0x01",
626       std::move(Unrecoverable));
627 
628   // Reset the error state so that it does not confuse the next set of checks.
629   Unrecoverable = Error::success();
630   Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
631 
632   EXPECT_TRUE(Parser.done());
633   checkError("last sequence in debug line table is not terminated!",
634              std::move(Recoverable));
635   EXPECT_FALSE(Unrecoverable);
636 }
637 
638 TEST_F(DebugLineBasicFixture,
639        ParserReportsPrologueErrorsInEachTableWhenSkipping) {
640   if (!setupGenerator())
641     return;
642 
643   LineTable &LT = Gen->addLineTable(DWARF32);
644   LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});
645   LineTable &LT2 = Gen->addLineTable(DWARF32);
646   LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}});
647   generate();
648 
649   DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
650   Parser.skip(RecordUnrecoverable);
651   ASSERT_FALSE(Parser.done());
652   Parser.skip(RecordUnrecoverable);
653 
654   EXPECT_TRUE(Parser.done());
655 
656   checkError({"parsing line table prologue at offset 0x00000000 found "
657               "unsupported version 0x00",
658               "parsing line table prologue at offset 0x00000006 found "
659               "unsupported version 0x01"},
660              std::move(Unrecoverable));
661 }
662 
663 TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) {
664   if (!setupGenerator())
665     return;
666 
667   LineTable &LT = Gen->addLineTable(DWARF32);
668   LT.addExtendedOpcode(42, DW_LNE_end_sequence, {});
669   generate();
670 
671   DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
672   Parser.skip(RecordUnrecoverable);
673 
674   EXPECT_TRUE(Parser.done());
675   EXPECT_FALSE(Unrecoverable);
676 }
677 
678 } // end anonymous namespace
679