diff options
Diffstat (limited to 'llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp')
-rw-r--r-- | llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp | 783 |
1 files changed, 542 insertions, 241 deletions
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp index 11adb1e47640..3ca21e97888c 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -16,6 +16,7 @@ #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -41,6 +42,10 @@ using ContentDescriptors = SmallVector<ContentDescriptor, 4>; } // end anonymous namespace +static bool versionIsSupported(uint16_t Version) { + return Version >= 2 && Version <= 5; +} + void DWARFDebugLine::ContentTypeTracker::trackContentType( dwarf::LineNumberEntryFormat ContentType) { switch (ContentType) { @@ -99,13 +104,21 @@ void DWARFDebugLine::Prologue::clear() { void DWARFDebugLine::Prologue::dump(raw_ostream &OS, DIDumpOptions DumpOptions) const { + if (!totalLengthIsValid()) + return; + int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(FormParams.Format); OS << "Line table prologue:\n" - << format(" total_length: 0x%8.8" PRIx64 "\n", TotalLength) + << format(" total_length: 0x%0*" PRIx64 "\n", OffsetDumpWidth, + TotalLength) + << " format: " << dwarf::FormatString(FormParams.Format) << "\n" << format(" version: %u\n", getVersion()); + if (!versionIsSupported(getVersion())) + return; if (getVersion() >= 5) OS << format(" address_size: %u\n", getAddressSize()) << format(" seg_select_size: %u\n", SegSelectorSize); - OS << format(" prologue_length: 0x%8.8" PRIx64 "\n", PrologueLength) + OS << format(" prologue_length: 0x%0*" PRIx64 "\n", OffsetDumpWidth, + PrologueLength) << format(" min_inst_length: %u\n", MinInstLength) << format(getVersion() >= 4 ? "max_ops_per_inst: %u\n" : "", MaxOpsPerInst) << format(" default_is_stmt: %u\n", DefaultIsStmt) @@ -114,8 +127,9 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS, << format(" opcode_base: %u\n", OpcodeBase); for (uint32_t I = 0; I != StandardOpcodeLengths.size(); ++I) - OS << format("standard_opcode_lengths[%s] = %u\n", - LNStandardString(I + 1).data(), StandardOpcodeLengths[I]); + OS << formatv("standard_opcode_lengths[{0}] = {1}\n", + static_cast<dwarf::LineNumberOps>(I + 1), + StandardOpcodeLengths[I]); if (!IncludeDirectories.empty()) { // DWARF v5 starts directory indexes at 0. @@ -153,14 +167,21 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS, } // Parse v2-v4 directory and file tables. -static void +static Error parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, - uint64_t *OffsetPtr, uint64_t EndPrologueOffset, + uint64_t *OffsetPtr, DWARFDebugLine::ContentTypeTracker &ContentTypes, std::vector<DWARFFormValue> &IncludeDirectories, std::vector<DWARFDebugLine::FileNameEntry> &FileNames) { - while (*OffsetPtr < EndPrologueOffset) { - StringRef S = DebugLineData.getCStrRef(OffsetPtr); + while (true) { + Error Err = Error::success(); + StringRef S = DebugLineData.getCStrRef(OffsetPtr, &Err); + if (Err) { + consumeError(std::move(Err)); + return createStringError(errc::invalid_argument, + "include directories table was not null " + "terminated before the end of the prologue"); + } if (S.empty()) break; DWARFFormValue Dir = @@ -168,21 +189,33 @@ parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, IncludeDirectories.push_back(Dir); } - while (*OffsetPtr < EndPrologueOffset) { - StringRef Name = DebugLineData.getCStrRef(OffsetPtr); - if (Name.empty()) + ContentTypes.HasModTime = true; + ContentTypes.HasLength = true; + + while (true) { + Error Err = Error::success(); + StringRef Name = DebugLineData.getCStrRef(OffsetPtr, &Err); + if (!Err && Name.empty()) break; + DWARFDebugLine::FileNameEntry FileEntry; FileEntry.Name = DWARFFormValue::createFromPValue(dwarf::DW_FORM_string, Name.data()); - FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); - FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); - FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); + FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr, &Err); + FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr, &Err); + FileEntry.Length = DebugLineData.getULEB128(OffsetPtr, &Err); + + if (Err) { + consumeError(std::move(Err)); + return createStringError( + errc::invalid_argument, + "file names table was not null terminated before " + "the end of the prologue"); + } FileNames.push_back(FileEntry); } - ContentTypes.HasModTime = true; - ContentTypes.HasLength = true; + return Error::success(); } // Parse v5 directory/file entry content descriptions. @@ -191,14 +224,15 @@ parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, static llvm::Expected<ContentDescriptors> parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, DWARFDebugLine::ContentTypeTracker *ContentTypes) { + Error Err = Error::success(); ContentDescriptors Descriptors; - int FormatCount = DebugLineData.getU8(OffsetPtr); + int FormatCount = DebugLineData.getU8(OffsetPtr, &Err); bool HasPath = false; - for (int I = 0; I != FormatCount; ++I) { + for (int I = 0; I != FormatCount && !Err; ++I) { ContentDescriptor Descriptor; Descriptor.Type = - dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr)); - Descriptor.Form = dwarf::Form(DebugLineData.getULEB128(OffsetPtr)); + dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr, &Err)); + Descriptor.Form = dwarf::Form(DebugLineData.getULEB128(OffsetPtr, &Err)); if (Descriptor.Type == dwarf::DW_LNCT_path) HasPath = true; if (ContentTypes) @@ -206,6 +240,11 @@ parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, Descriptors.push_back(Descriptor); } + if (Err) + return createStringError(errc::invalid_argument, + "failed to parse entry content descriptors: %s", + toString(std::move(Err)).c_str()); + if (!HasPath) return createStringError(errc::invalid_argument, "failed to parse entry content descriptions" @@ -227,8 +266,8 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, 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) { + uint64_t DirEntryCount = DebugLineData.getULEB128(OffsetPtr); + for (uint64_t I = 0; I != DirEntryCount; ++I) { for (auto Descriptor : *DirDescriptors) { DWARFFormValue Value(Descriptor.Form); switch (Descriptor.Type) { @@ -236,14 +275,14 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) return createStringError(errc::invalid_argument, "failed to parse directory entry because " - "extracting the form value failed."); + "extracting the form value failed"); IncludeDirectories.push_back(Value); break; default: if (!Value.skipValue(DebugLineData, OffsetPtr, FormParams)) return createStringError(errc::invalid_argument, "failed to parse directory entry because " - "skipping the form value failed."); + "skipping the form value failed"); } } } @@ -255,15 +294,15 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, 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) { + uint64_t FileEntryCount = DebugLineData.getULEB128(OffsetPtr); + for (uint64_t I = 0; I != FileEntryCount; ++I) { DWARFDebugLine::FileNameEntry FileEntry; for (auto Descriptor : *FileDescriptors) { DWARFFormValue Value(Descriptor.Form); if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) return createStringError(errc::invalid_argument, "failed to parse file entry because " - "extracting the form value failed."); + "extracting the form value failed"); switch (Descriptor.Type) { case DW_LNCT_path: FileEntry.Name = Value; @@ -297,78 +336,114 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, return Error::success(); } -Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, - uint64_t *OffsetPtr, - const DWARFContext &Ctx, - const DWARFUnit *U) { +uint64_t DWARFDebugLine::Prologue::getLength() const { + uint64_t Length = PrologueLength + sizeofTotalLength() + + sizeof(getVersion()) + sizeofPrologueLength(); + if (getVersion() >= 5) + Length += 2; // Address + Segment selector sizes. + return Length; +} + +Error DWARFDebugLine::Prologue::parse( + DWARFDataExtractor DebugLineData, uint64_t *OffsetPtr, + function_ref<void(Error)> RecoverableErrorHandler, const DWARFContext &Ctx, + const DWARFUnit *U) { const uint64_t PrologueOffset = *OffsetPtr; clear(); - TotalLength = DebugLineData.getRelocatedValue(4, OffsetPtr); - if (TotalLength == dwarf::DW_LENGTH_DWARF64) { - FormParams.Format = dwarf::DWARF64; - TotalLength = DebugLineData.getU64(OffsetPtr); - } else if (TotalLength >= dwarf::DW_LENGTH_lo_reserved) { - return createStringError(errc::invalid_argument, + DataExtractor::Cursor Cursor(*OffsetPtr); + std::tie(TotalLength, FormParams.Format) = + DebugLineData.getInitialLength(Cursor); + + DebugLineData = + DWARFDataExtractor(DebugLineData, Cursor.tell() + TotalLength); + FormParams.Version = DebugLineData.getU16(Cursor); + if (Cursor && !versionIsSupported(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. + *OffsetPtr = Cursor.tell(); + return createStringError( + errc::not_supported, "parsing line table prologue at offset 0x%8.8" PRIx64 - " unsupported reserved unit length found of value 0x%8.8" PRIx64, - PrologueOffset, TotalLength); + ": unsupported version %" PRIu16, + PrologueOffset, getVersion()); } - 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()); if (getVersion() >= 5) { - FormParams.AddrSize = DebugLineData.getU8(OffsetPtr); - assert((DebugLineData.getAddressSize() == 0 || + FormParams.AddrSize = DebugLineData.getU8(Cursor); + assert((!Cursor || DebugLineData.getAddressSize() == 0 || DebugLineData.getAddressSize() == getAddressSize()) && "Line table header and data extractor disagree"); - SegSelectorSize = DebugLineData.getU8(OffsetPtr); + SegSelectorSize = DebugLineData.getU8(Cursor); } PrologueLength = - DebugLineData.getRelocatedValue(sizeofPrologueLength(), OffsetPtr); - const uint64_t EndPrologueOffset = PrologueLength + *OffsetPtr; - MinInstLength = DebugLineData.getU8(OffsetPtr); + DebugLineData.getRelocatedValue(Cursor, sizeofPrologueLength()); + const uint64_t EndPrologueOffset = PrologueLength + Cursor.tell(); + DebugLineData = DWARFDataExtractor(DebugLineData, EndPrologueOffset); + MinInstLength = DebugLineData.getU8(Cursor); if (getVersion() >= 4) - MaxOpsPerInst = DebugLineData.getU8(OffsetPtr); - DefaultIsStmt = DebugLineData.getU8(OffsetPtr); - LineBase = DebugLineData.getU8(OffsetPtr); - LineRange = DebugLineData.getU8(OffsetPtr); - OpcodeBase = DebugLineData.getU8(OffsetPtr); - - StandardOpcodeLengths.reserve(OpcodeBase - 1); - for (uint32_t I = 1; I < OpcodeBase; ++I) { - uint8_t OpLen = DebugLineData.getU8(OffsetPtr); - StandardOpcodeLengths.push_back(OpLen); + MaxOpsPerInst = DebugLineData.getU8(Cursor); + DefaultIsStmt = DebugLineData.getU8(Cursor); + LineBase = DebugLineData.getU8(Cursor); + LineRange = DebugLineData.getU8(Cursor); + OpcodeBase = DebugLineData.getU8(Cursor); + + if (Cursor && OpcodeBase == 0) { + // If the opcode base is 0, we cannot read the standard opcode lengths (of + // which there are supposed to be one fewer than the opcode base). Assume + // there are no standard opcodes and continue parsing. + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "parsing line table prologue at offset 0x%8.8" PRIx64 + " found opcode base of 0. Assuming no standard opcodes", + PrologueOffset)); + } else if (Cursor) { + StandardOpcodeLengths.reserve(OpcodeBase - 1); + for (uint32_t I = 1; I < OpcodeBase; ++I) { + uint8_t OpLen = DebugLineData.getU8(Cursor); + StandardOpcodeLengths.push_back(OpLen); + } } - if (getVersion() >= 5) { - if (Error E = - parseV5DirFileTables(DebugLineData, OffsetPtr, FormParams, Ctx, U, - ContentTypes, IncludeDirectories, FileNames)) { - return 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)); - } - } else - parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, - ContentTypes, IncludeDirectories, FileNames); + *OffsetPtr = Cursor.tell(); + // A corrupt file name or directory table does not prevent interpretation of + // the main line program, so check the cursor state now so that its errors can + // be handled separately. + if (!Cursor) + return createStringError( + errc::invalid_argument, + "parsing line table prologue at offset 0x%8.8" PRIx64 ": %s", + PrologueOffset, toString(Cursor.takeError()).c_str()); + + Error E = + getVersion() >= 5 + ? parseV5DirFileTables(DebugLineData, OffsetPtr, FormParams, Ctx, U, + ContentTypes, IncludeDirectories, FileNames) + : parseV2DirFileTables(DebugLineData, OffsetPtr, ContentTypes, + IncludeDirectories, FileNames); + if (E) { + RecoverableErrorHandler(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))); + return Error::success(); + } - 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); + assert(*OffsetPtr <= EndPrologueOffset); + if (*OffsetPtr != EndPrologueOffset) { + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "unknown data in line table prologue at offset 0x%8.8" PRIx64 + ": parsing ended (at offset 0x%8.8" PRIx64 + ") before reaching the prologue end at offset 0x%8.8" PRIx64, + PrologueOffset, *OffsetPtr, EndPrologueOffset)); + } return Error::success(); } @@ -396,10 +471,12 @@ void DWARFDebugLine::Row::reset(bool DefaultIsStmt) { EpilogueBegin = false; } -void DWARFDebugLine::Row::dumpTableHeader(raw_ostream &OS) { - OS << "Address Line Column File ISA Discriminator Flags\n" - << "------------------ ------ ------ ------ --- ------------- " - "-------------\n"; +void DWARFDebugLine::Row::dumpTableHeader(raw_ostream &OS, unsigned Indent) { + OS.indent(Indent) + << "Address Line Column File ISA Discriminator Flags\n"; + OS.indent(Indent) + << "------------------ ------ ------ ------ --- ------------- " + "-------------\n"; } void DWARFDebugLine::Row::dump(raw_ostream &OS) const { @@ -430,7 +507,7 @@ void DWARFDebugLine::LineTable::dump(raw_ostream &OS, if (!Rows.empty()) { OS << '\n'; - Row::dumpTableHeader(OS); + Row::dumpTableHeader(OS, 0); for (const Row &R : Rows) { R.dump(OS); } @@ -447,8 +524,10 @@ void DWARFDebugLine::LineTable::clear() { Sequences.clear(); } -DWARFDebugLine::ParsingState::ParsingState(struct LineTable *LT) - : LineTable(LT) { +DWARFDebugLine::ParsingState::ParsingState( + struct LineTable *LT, uint64_t TableOffset, + function_ref<void(Error)> ErrorHandler) + : LineTable(LT), LineTableOffset(TableOffset), ErrorHandler(ErrorHandler) { resetRowAndSequence(); } @@ -488,7 +567,7 @@ DWARFDebugLine::getLineTable(uint64_t Offset) const { Expected<const DWARFDebugLine::LineTable *> DWARFDebugLine::getOrParseLineTable( DWARFDataExtractor &DebugLineData, uint64_t Offset, const DWARFContext &Ctx, - const DWARFUnit *U, function_ref<void(Error)> RecoverableErrorCallback) { + const DWARFUnit *U, function_ref<void(Error)> RecoverableErrorHandler) { if (!DebugLineData.isValidOffset(Offset)) return createStringError(errc::invalid_argument, "offset 0x%8.8" PRIx64 " is not a valid debug line section offset", @@ -499,32 +578,163 @@ Expected<const DWARFDebugLine::LineTable *> DWARFDebugLine::getOrParseLineTable( LineTable *LT = &Pos.first->second; if (Pos.second) { if (Error Err = - LT->parse(DebugLineData, &Offset, Ctx, U, RecoverableErrorCallback)) + LT->parse(DebugLineData, &Offset, Ctx, U, RecoverableErrorHandler)) return std::move(Err); return LT; } return LT; } +static StringRef getOpcodeName(uint8_t Opcode, uint8_t OpcodeBase) { + assert(Opcode != 0); + if (Opcode < OpcodeBase) + return LNStandardString(Opcode); + return "special"; +} + +uint64_t DWARFDebugLine::ParsingState::advanceAddr(uint64_t OperationAdvance, + uint8_t Opcode, + uint64_t OpcodeOffset) { + StringRef OpcodeName = getOpcodeName(Opcode, LineTable->Prologue.OpcodeBase); + // For versions less than 4, the MaxOpsPerInst member is set to 0, as the + // maximum_operations_per_instruction field wasn't introduced until DWARFv4. + // Don't warn about bad values in this situation. + if (ReportAdvanceAddrProblem && LineTable->Prologue.getVersion() >= 4 && + LineTable->Prologue.MaxOpsPerInst != 1) + ErrorHandler(createStringError( + errc::not_supported, + "line table program at offset 0x%8.8" PRIx64 + " contains a %s opcode at offset 0x%8.8" PRIx64 + ", but the prologue maximum_operations_per_instruction value is %" PRId8 + ", which is unsupported. Assuming a value of 1 instead", + LineTableOffset, OpcodeName.data(), OpcodeOffset, + LineTable->Prologue.MaxOpsPerInst)); + if (ReportAdvanceAddrProblem && LineTable->Prologue.MinInstLength == 0) + ErrorHandler( + createStringError(errc::invalid_argument, + "line table program at offset 0x%8.8" PRIx64 + " contains a %s opcode at offset 0x%8.8" PRIx64 + ", but the prologue minimum_instruction_length value " + "is 0, which prevents any address advancing", + LineTableOffset, OpcodeName.data(), OpcodeOffset)); + ReportAdvanceAddrProblem = false; + uint64_t AddrOffset = OperationAdvance * LineTable->Prologue.MinInstLength; + Row.Address.Address += AddrOffset; + return AddrOffset; +} + +DWARFDebugLine::ParsingState::AddrAndAdjustedOpcode +DWARFDebugLine::ParsingState::advanceAddrForOpcode(uint8_t Opcode, + uint64_t OpcodeOffset) { + assert(Opcode == DW_LNS_const_add_pc || + Opcode >= LineTable->Prologue.OpcodeBase); + if (ReportBadLineRange && LineTable->Prologue.LineRange == 0) { + StringRef OpcodeName = + getOpcodeName(Opcode, LineTable->Prologue.OpcodeBase); + ErrorHandler( + createStringError(errc::not_supported, + "line table program at offset 0x%8.8" PRIx64 + " contains a %s opcode at offset 0x%8.8" PRIx64 + ", but the prologue line_range value is 0. The " + "address and line will not be adjusted", + LineTableOffset, OpcodeName.data(), OpcodeOffset)); + ReportBadLineRange = false; + } + + uint8_t OpcodeValue = Opcode; + if (Opcode == DW_LNS_const_add_pc) + OpcodeValue = 255; + uint8_t AdjustedOpcode = OpcodeValue - LineTable->Prologue.OpcodeBase; + uint64_t OperationAdvance = + LineTable->Prologue.LineRange != 0 + ? AdjustedOpcode / LineTable->Prologue.LineRange + : 0; + uint64_t AddrOffset = advanceAddr(OperationAdvance, Opcode, OpcodeOffset); + return {AddrOffset, AdjustedOpcode}; +} + +DWARFDebugLine::ParsingState::AddrAndLineDelta +DWARFDebugLine::ParsingState::handleSpecialOpcode(uint8_t Opcode, + uint64_t OpcodeOffset) { + // A special opcode value is chosen based on the amount that needs + // to be added to the line and address registers. The maximum line + // increment for a special opcode is the value of the line_base + // field in the header, plus the value of the line_range field, + // minus 1 (line base + line range - 1). If the desired line + // increment is greater than the maximum line increment, a standard + // opcode must be used instead of a special opcode. The "address + // advance" is calculated by dividing the desired address increment + // by the minimum_instruction_length field from the header. The + // special opcode is then calculated using the following formula: + // + // opcode = (desired line increment - line_base) + + // (line_range * address advance) + opcode_base + // + // If the resulting opcode is greater than 255, a standard opcode + // must be used instead. + // + // To decode a special opcode, subtract the opcode_base from the + // opcode itself to give the adjusted opcode. The amount to + // increment the address register is the result of the adjusted + // opcode divided by the line_range multiplied by the + // minimum_instruction_length field from the header. That is: + // + // address increment = (adjusted opcode / line_range) * + // minimum_instruction_length + // + // The amount to increment the line register is the line_base plus + // the result of the adjusted opcode modulo the line_range. That is: + // + // line increment = line_base + (adjusted opcode % line_range) + + DWARFDebugLine::ParsingState::AddrAndAdjustedOpcode AddrAdvanceResult = + advanceAddrForOpcode(Opcode, OpcodeOffset); + int32_t LineOffset = 0; + if (LineTable->Prologue.LineRange != 0) + LineOffset = + LineTable->Prologue.LineBase + + (AddrAdvanceResult.AdjustedOpcode % LineTable->Prologue.LineRange); + Row.Line += LineOffset; + return {AddrAdvanceResult.AddrDelta, LineOffset}; +} + +/// Parse a ULEB128 using the specified \p Cursor. \returns the parsed value on +/// success, or None if \p Cursor is in a failing state. +template <typename T> +static Optional<T> parseULEB128(DWARFDataExtractor &Data, + DataExtractor::Cursor &Cursor) { + T Value = Data.getULEB128(Cursor); + if (Cursor) + return Value; + return None; +} + Error DWARFDebugLine::LineTable::parse( DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, const DWARFContext &Ctx, const DWARFUnit *U, - function_ref<void(Error)> RecoverableErrorCallback, raw_ostream *OS) { + function_ref<void(Error)> RecoverableErrorHandler, raw_ostream *OS, + bool Verbose) { + assert((OS || !Verbose) && "cannot have verbose output without stream"); const uint64_t DebugLineOffset = *OffsetPtr; clear(); - Error PrologueErr = Prologue.parse(DebugLineData, OffsetPtr, Ctx, U); + Error PrologueErr = + Prologue.parse(DebugLineData, OffsetPtr, RecoverableErrorHandler, Ctx, U); if (OS) { - // The presence of OS signals verbose dumping. DIDumpOptions DumpOptions; - DumpOptions.Verbose = true; + DumpOptions.Verbose = Verbose; Prologue.dump(*OS, DumpOptions); } - if (PrologueErr) + if (PrologueErr) { + // Ensure there is a blank line after the prologue to clearly delineate it + // from later dumps. + if (OS) + *OS << "\n"; return PrologueErr; + } uint64_t ProgramLength = Prologue.TotalLength + Prologue.sizeofTotalLength(); if (!DebugLineData.isValidOffsetForDataOfSize(DebugLineOffset, @@ -532,7 +742,7 @@ Error DWARFDebugLine::LineTable::parse( assert(DebugLineData.size() > DebugLineOffset && "prologue parsing should handle invalid offset"); uint64_t BytesRemaining = DebugLineData.size() - DebugLineOffset; - RecoverableErrorCallback( + RecoverableErrorHandler( createStringError(errc::invalid_argument, "line table program with offset 0x%8.8" PRIx64 " has length 0x%8.8" PRIx64 " but only 0x%8.8" PRIx64 @@ -542,41 +752,62 @@ Error DWARFDebugLine::LineTable::parse( ProgramLength = BytesRemaining; } + // Create a DataExtractor which can only see the data up to the end of the + // table, to prevent reading past the end. const uint64_t EndOffset = DebugLineOffset + ProgramLength; + DWARFDataExtractor TableData(DebugLineData, EndOffset); // See if we should tell the data extractor the address size. - if (DebugLineData.getAddressSize() == 0) - DebugLineData.setAddressSize(Prologue.getAddressSize()); + if (TableData.getAddressSize() == 0) + TableData.setAddressSize(Prologue.getAddressSize()); else assert(Prologue.getAddressSize() == 0 || - Prologue.getAddressSize() == DebugLineData.getAddressSize()); + Prologue.getAddressSize() == TableData.getAddressSize()); - ParsingState State(this); + ParsingState State(this, DebugLineOffset, RecoverableErrorHandler); + *OffsetPtr = DebugLineOffset + Prologue.getLength(); + if (OS && *OffsetPtr < EndOffset) { + *OS << '\n'; + Row::dumpTableHeader(*OS, /*Indent=*/Verbose ? 12 : 0); + } while (*OffsetPtr < EndOffset) { - if (OS) + DataExtractor::Cursor Cursor(*OffsetPtr); + + if (Verbose) *OS << format("0x%08.08" PRIx64 ": ", *OffsetPtr); - uint8_t Opcode = DebugLineData.getU8(OffsetPtr); + uint64_t OpcodeOffset = *OffsetPtr; + uint8_t Opcode = TableData.getU8(Cursor); + size_t RowCount = Rows.size(); - if (OS) + if (Cursor && Verbose) *OS << format("%02.02" PRIx8 " ", Opcode); if (Opcode == 0) { // Extended Opcodes always start with a zero opcode followed by // a uleb128 length so you can skip ones you don't know about - uint64_t Len = DebugLineData.getULEB128(OffsetPtr); - uint64_t ExtOffset = *OffsetPtr; + uint64_t Len = TableData.getULEB128(Cursor); + uint64_t ExtOffset = Cursor.tell(); // Tolerate zero-length; assume length is correct and soldier on. if (Len == 0) { - if (OS) + if (Cursor && Verbose) *OS << "Badly formed extended line op (length 0)\n"; + if (!Cursor) { + if (Verbose) + *OS << "\n"; + RecoverableErrorHandler(Cursor.takeError()); + } + *OffsetPtr = Cursor.tell(); continue; } - uint8_t SubOpcode = DebugLineData.getU8(OffsetPtr); - if (OS) + uint8_t SubOpcode = TableData.getU8(Cursor); + // OperandOffset will be the same as ExtOffset, if it was not possible to + // read the SubOpcode. + uint64_t OperandOffset = Cursor.tell(); + if (Verbose) *OS << LNExtendedString(SubOpcode); switch (SubOpcode) { case DW_LNE_end_sequence: @@ -588,11 +819,15 @@ Error DWARFDebugLine::LineTable::parse( // address is that of the byte after the last target machine instruction // of the sequence. State.Row.EndSequence = true; - if (OS) { + // No need to test the Cursor is valid here, since it must be to get + // into this code path - if it were invalid, the default case would be + // followed. + if (Verbose) { *OS << "\n"; OS->indent(12); - State.Row.dump(*OS); } + if (OS) + State.Row.dump(*OS); State.appendRowToMatrix(); State.resetRowAndSequence(); break; @@ -608,25 +843,39 @@ Error DWARFDebugLine::LineTable::parse( // Make sure the extractor knows the address size. If not, infer it // from the size of the operand. { - uint8_t ExtractorAddressSize = DebugLineData.getAddressSize(); - if (ExtractorAddressSize != Len - 1 && ExtractorAddressSize != 0) - RecoverableErrorCallback(createStringError( + uint8_t ExtractorAddressSize = TableData.getAddressSize(); + uint64_t OpcodeAddressSize = Len - 1; + if (ExtractorAddressSize != OpcodeAddressSize && + ExtractorAddressSize != 0) + RecoverableErrorHandler(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)); // Assume that the line table is correct and temporarily override the - // address size. - DebugLineData.setAddressSize(Len - 1); - State.Row.Address.Address = DebugLineData.getRelocatedAddress( - OffsetPtr, &State.Row.Address.SectionIndex); - - // Restore the address size if the extractor already had it. - if (ExtractorAddressSize != 0) - DebugLineData.setAddressSize(ExtractorAddressSize); + // address size. If the size is unsupported, give up trying to read + // the address and continue to the next opcode. + if (OpcodeAddressSize != 1 && OpcodeAddressSize != 2 && + OpcodeAddressSize != 4 && OpcodeAddressSize != 8) { + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "address size 0x%2.2" PRIx64 + " of DW_LNE_set_address opcode at offset 0x%8.8" PRIx64 + " is unsupported", + OpcodeAddressSize, ExtOffset)); + TableData.skip(Cursor, OpcodeAddressSize); + } else { + TableData.setAddressSize(OpcodeAddressSize); + State.Row.Address.Address = TableData.getRelocatedAddress( + Cursor, &State.Row.Address.SectionIndex); + + // Restore the address size if the extractor already had it. + if (ExtractorAddressSize != 0) + TableData.setAddressSize(ExtractorAddressSize); + } - if (OS) + if (Cursor && Verbose) *OS << format(" (0x%16.16" PRIx64 ")", State.Row.Address.Address); } break; @@ -654,14 +903,14 @@ Error DWARFDebugLine::LineTable::parse( // the file register of the state machine. { FileNameEntry FileEntry; - const char *Name = DebugLineData.getCStr(OffsetPtr); + const char *Name = TableData.getCStr(Cursor); FileEntry.Name = DWARFFormValue::createFromPValue(dwarf::DW_FORM_string, Name); - FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); - FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); - FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); + FileEntry.DirIdx = TableData.getULEB128(Cursor); + FileEntry.ModTime = TableData.getULEB128(Cursor); + FileEntry.Length = TableData.getULEB128(Cursor); Prologue.FileNames.push_back(FileEntry); - if (OS) + if (Cursor && Verbose) *OS << " (" << Name << ", dir=" << FileEntry.DirIdx << ", mod_time=" << format("(0x%16.16" PRIx64 ")", FileEntry.ModTime) << ", length=" << FileEntry.Length << ")"; @@ -669,41 +918,63 @@ Error DWARFDebugLine::LineTable::parse( break; case DW_LNE_set_discriminator: - State.Row.Discriminator = DebugLineData.getULEB128(OffsetPtr); - if (OS) + State.Row.Discriminator = TableData.getULEB128(Cursor); + if (Cursor && Verbose) *OS << " (" << State.Row.Discriminator << ")"; break; default: - if (OS) + if (Cursor && Verbose) *OS << format("Unrecognized extended op 0x%02.02" PRIx8, SubOpcode) << format(" length %" PRIx64, Len); // Len doesn't include the zero opcode byte or the length itself, but // it does include the sub_opcode, so we have to adjust for that. - (*OffsetPtr) += Len - 1; + TableData.skip(Cursor, Len - 1); 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); + // Make sure the length as recorded in the table and the standard length + // for the opcode match. If they don't, continue from the end as claimed + // by the table. Similarly, continue from the claimed end in the event of + // a parsing error. + uint64_t End = ExtOffset + Len; + if (Cursor && Cursor.tell() != End) + RecoverableErrorHandler(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, Cursor.tell() - ExtOffset)); + if (!Cursor && Verbose) { + DWARFDataExtractor::Cursor ByteCursor(OperandOffset); + uint8_t Byte = TableData.getU8(ByteCursor); + if (ByteCursor) { + *OS << " (<parsing error>"; + do { + *OS << format(" %2.2" PRIx8, Byte); + Byte = TableData.getU8(ByteCursor); + } while (ByteCursor); + *OS << ")"; + } + + // The only parse failure in this case should be if the end was reached. + // In that case, throw away the error, as the main Cursor's error will + // be sufficient. + consumeError(ByteCursor.takeError()); + } + *OffsetPtr = End; } else if (Opcode < Prologue.OpcodeBase) { - if (OS) + if (Verbose) *OS << LNStandardString(Opcode); switch (Opcode) { // Standard Opcodes case DW_LNS_copy: // Takes no arguments. Append a row to the matrix using the // current values of the state-machine registers. - if (OS) { + if (Verbose) { *OS << "\n"; OS->indent(12); - State.Row.dump(*OS); - *OS << "\n"; } + if (OS) + State.Row.dump(*OS); State.appendRowToMatrix(); break; @@ -711,11 +982,11 @@ Error DWARFDebugLine::LineTable::parse( // Takes a single unsigned LEB128 operand, multiplies it by the // min_inst_length field of the prologue, and adds the // result to the address register of the state machine. - { + if (Optional<uint64_t> Operand = + parseULEB128<uint64_t>(TableData, Cursor)) { uint64_t AddrOffset = - DebugLineData.getULEB128(OffsetPtr) * Prologue.MinInstLength; - State.Row.Address.Address += AddrOffset; - if (OS) + State.advanceAddr(*Operand, Opcode, OpcodeOffset); + if (Verbose) *OS << " (" << AddrOffset << ")"; } break; @@ -723,25 +994,36 @@ Error DWARFDebugLine::LineTable::parse( case DW_LNS_advance_line: // Takes a single signed LEB128 operand and adds that value to // the line register of the state machine. - State.Row.Line += DebugLineData.getSLEB128(OffsetPtr); - if (OS) - *OS << " (" << State.Row.Line << ")"; + { + int64_t LineDelta = TableData.getSLEB128(Cursor); + if (Cursor) { + State.Row.Line += LineDelta; + if (Verbose) + *OS << " (" << State.Row.Line << ")"; + } + } break; case DW_LNS_set_file: // Takes a single unsigned LEB128 operand and stores it in the file // register of the state machine. - State.Row.File = DebugLineData.getULEB128(OffsetPtr); - if (OS) - *OS << " (" << State.Row.File << ")"; + if (Optional<uint16_t> File = + parseULEB128<uint16_t>(TableData, Cursor)) { + State.Row.File = *File; + if (Verbose) + *OS << " (" << State.Row.File << ")"; + } break; case DW_LNS_set_column: // Takes a single unsigned LEB128 operand and stores it in the // column register of the state machine. - State.Row.Column = DebugLineData.getULEB128(OffsetPtr); - if (OS) - *OS << " (" << State.Row.Column << ")"; + if (Optional<uint16_t> Column = + parseULEB128<uint16_t>(TableData, Cursor)) { + State.Row.Column = *Column; + if (Verbose) + *OS << " (" << State.Row.Column << ")"; + } break; case DW_LNS_negate_stmt: @@ -769,13 +1051,10 @@ Error DWARFDebugLine::LineTable::parse( // than twice that range will it need to use both DW_LNS_advance_pc // and a special opcode, requiring three or more bytes. { - uint8_t AdjustOpcode = 255 - Prologue.OpcodeBase; uint64_t AddrOffset = - (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; - State.Row.Address.Address += AddrOffset; - if (OS) - *OS - << format(" (0x%16.16" PRIx64 ")", AddrOffset); + State.advanceAddrForOpcode(Opcode, OpcodeOffset).AddrDelta; + if (Verbose) + *OS << format(" (0x%16.16" PRIx64 ")", AddrOffset); } break; @@ -790,11 +1069,13 @@ Error DWARFDebugLine::LineTable::parse( // requires the use of DW_LNS_advance_pc. Such assemblers, however, // can use DW_LNS_fixed_advance_pc instead, sacrificing compression. { - uint16_t PCOffset = DebugLineData.getRelocatedValue(2, OffsetPtr); - State.Row.Address.Address += PCOffset; - if (OS) - *OS - << format(" (0x%4.4" PRIx16 ")", PCOffset); + uint16_t PCOffset = + TableData.getRelocatedValue(Cursor, 2); + if (Cursor) { + State.Row.Address.Address += PCOffset; + if (Verbose) + *OS << format(" (0x%4.4" PRIx16 ")", PCOffset); + } } break; @@ -812,10 +1093,12 @@ Error DWARFDebugLine::LineTable::parse( case DW_LNS_set_isa: // Takes a single unsigned LEB128 operand and stores it in the - // column register of the state machine. - State.Row.Isa = DebugLineData.getULEB128(OffsetPtr); - if (OS) - *OS << " (" << (uint64_t)State.Row.Isa << ")"; + // ISA register of the state machine. + if (Optional<uint8_t> Isa = parseULEB128<uint8_t>(TableData, Cursor)) { + State.Row.Isa = *Isa; + if (Verbose) + *OS << " (" << (uint64_t)State.Row.Isa << ")"; + } break; default: @@ -824,73 +1107,72 @@ Error DWARFDebugLine::LineTable::parse( // as a multiple of LEB128 operands for each opcode. { assert(Opcode - 1U < Prologue.StandardOpcodeLengths.size()); + if (Verbose) + *OS << "Unrecognized standard opcode"; uint8_t OpcodeLength = Prologue.StandardOpcodeLengths[Opcode - 1]; + std::vector<uint64_t> Operands; for (uint8_t I = 0; I < OpcodeLength; ++I) { - uint64_t Value = DebugLineData.getULEB128(OffsetPtr); - if (OS) - *OS << format("Skipping ULEB128 value: 0x%16.16" PRIx64 ")\n", - Value); + if (Optional<uint64_t> Value = + parseULEB128<uint64_t>(TableData, Cursor)) + Operands.push_back(*Value); + else + break; + } + if (Verbose && !Operands.empty()) { + *OS << " (operands: "; + bool First = true; + for (uint64_t Value : Operands) { + if (!First) + *OS << ", "; + First = false; + *OS << format("0x%16.16" PRIx64, Value); + } + if (Verbose) + *OS << ')'; } } break; } + + *OffsetPtr = Cursor.tell(); } else { - // Special Opcodes - - // A special opcode value is chosen based on the amount that needs - // to be added to the line and address registers. The maximum line - // increment for a special opcode is the value of the line_base - // field in the header, plus the value of the line_range field, - // minus 1 (line base + line range - 1). If the desired line - // increment is greater than the maximum line increment, a standard - // opcode must be used instead of a special opcode. The "address - // advance" is calculated by dividing the desired address increment - // by the minimum_instruction_length field from the header. The - // special opcode is then calculated using the following formula: - // - // opcode = (desired line increment - line_base) + - // (line_range * address advance) + opcode_base - // - // If the resulting opcode is greater than 255, a standard opcode - // must be used instead. - // - // To decode a special opcode, subtract the opcode_base from the - // opcode itself to give the adjusted opcode. The amount to - // increment the address register is the result of the adjusted - // opcode divided by the line_range multiplied by the - // minimum_instruction_length field from the header. That is: - // - // address increment = (adjusted opcode / line_range) * - // minimum_instruction_length - // - // The amount to increment the line register is the line_base plus - // the result of the adjusted opcode modulo the line_range. That is: - // - // line increment = line_base + (adjusted opcode % line_range) - - uint8_t AdjustOpcode = Opcode - Prologue.OpcodeBase; - uint64_t AddrOffset = - (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; - int32_t LineOffset = - Prologue.LineBase + (AdjustOpcode % Prologue.LineRange); - State.Row.Line += LineOffset; - State.Row.Address.Address += AddrOffset; - - if (OS) { - *OS << "address += " << AddrOffset << ", line += " << LineOffset + // Special Opcodes. + ParsingState::AddrAndLineDelta Delta = + State.handleSpecialOpcode(Opcode, OpcodeOffset); + + if (Verbose) { + *OS << "address += " << Delta.Address << ", line += " << Delta.Line << "\n"; OS->indent(12); - State.Row.dump(*OS); } + if (OS) + State.Row.dump(*OS); State.appendRowToMatrix(); + *OffsetPtr = Cursor.tell(); } - if(OS) + + // When a row is added to the matrix, it is also dumped, which includes a + // new line already, so don't add an extra one. + if (Verbose && Rows.size() == RowCount) *OS << "\n"; + + // Most parse failures other than when parsing extended opcodes are due to + // failures to read ULEBs. Bail out of parsing, since we don't know where to + // continue reading from as there is no stated length for such byte + // sequences. Print the final trailing new line if needed before doing so. + if (!Cursor && Opcode != 0) { + if (Verbose) + *OS << "\n"; + return Cursor.takeError(); + } + + if (!Cursor) + RecoverableErrorHandler(Cursor.takeError()); } if (!State.Sequence.Empty) - RecoverableErrorCallback(createStringError( + RecoverableErrorHandler(createStringError( errc::illegal_byte_sequence, "last sequence in debug line table at offset 0x%8.8" PRIx64 " is not terminated", @@ -907,6 +1189,11 @@ Error DWARFDebugLine::LineTable::parse( // rudimentary sequences for address ranges [0x0, 0xsomething). } + // Terminate the table with a final blank line to clearly delineate it from + // later dumps. + if (OS) + *OS << "\n"; + return Error::success(); } @@ -1054,9 +1341,13 @@ bool DWARFDebugLine::Prologue::getFileNameByIndex( if (!Name) return false; StringRef FileName = *Name; - if (Kind != FileLineInfoKind::AbsoluteFilePath || + if (Kind == FileLineInfoKind::RawValue || isPathAbsoluteOnWindowsOrPosix(FileName)) { - Result = FileName; + Result = std::string(FileName); + return true; + } + if (Kind == FileLineInfoKind::BaseNameOnly) { + Result = std::string(llvm::sys::path::filename(FileName)); return true; } @@ -1064,23 +1355,31 @@ bool DWARFDebugLine::Prologue::getFileNameByIndex( StringRef IncludeDir; // Be defensive about the contents of Entry. if (getVersion() >= 5) { - if (Entry.DirIdx < IncludeDirectories.size()) + // DirIdx 0 is the compilation directory, so don't include it for + // relative names. + if ((Entry.DirIdx != 0 || Kind != FileLineInfoKind::RelativeFilePath) && + Entry.DirIdx < IncludeDirectories.size()) IncludeDir = IncludeDirectories[Entry.DirIdx].getAsCString().getValue(); } else { if (0 < Entry.DirIdx && Entry.DirIdx <= IncludeDirectories.size()) IncludeDir = IncludeDirectories[Entry.DirIdx - 1].getAsCString().getValue(); - - // We may still need to append compilation directory of compile unit. - // We know that FileName is not absolute, the only way to have an - // absolute path at this point would be if IncludeDir is absolute. - if (!CompDir.empty() && !isPathAbsoluteOnWindowsOrPosix(IncludeDir)) - sys::path::append(FilePath, Style, CompDir); } + // For absolute paths only, include the compilation directory of compile unit. + // We know that FileName is not absolute, the only way to have an absolute + // path at this point would be if IncludeDir is absolute. + if (Kind == FileLineInfoKind::AbsoluteFilePath && !CompDir.empty() && + !isPathAbsoluteOnWindowsOrPosix(IncludeDir)) + sys::path::append(FilePath, Style, CompDir); + + assert((Kind == FileLineInfoKind::AbsoluteFilePath || + Kind == FileLineInfoKind::RelativeFilePath) && + "invalid FileLineInfo Kind"); + // sys::path::append skips empty strings. sys::path::append(FilePath, Style, IncludeDir, FileName); - Result = FilePath.str(); + Result = std::string(FilePath.str()); return true; } @@ -1131,34 +1430,36 @@ DWARFDebugLine::SectionParser::SectionParser(DWARFDataExtractor &Data, } bool DWARFDebugLine::Prologue::totalLengthIsValid() const { - return TotalLength == dwarf::DW_LENGTH_DWARF64 || - TotalLength < dwarf::DW_LENGTH_lo_reserved; + return TotalLength != 0u; } DWARFDebugLine::LineTable DWARFDebugLine::SectionParser::parseNext( - function_ref<void(Error)> RecoverableErrorCallback, - function_ref<void(Error)> UnrecoverableErrorCallback, raw_ostream *OS) { + function_ref<void(Error)> RecoverableErrorHandler, + function_ref<void(Error)> UnrecoverableErrorHandler, raw_ostream *OS, + bool Verbose) { assert(DebugLineData.isValidOffset(Offset) && "parsing should have terminated"); DWARFUnit *U = prepareToParse(Offset); uint64_t OldOffset = Offset; LineTable LT; if (Error Err = LT.parse(DebugLineData, &Offset, Context, U, - RecoverableErrorCallback, OS)) - UnrecoverableErrorCallback(std::move(Err)); + RecoverableErrorHandler, OS, Verbose)) + UnrecoverableErrorHandler(std::move(Err)); moveToNextTable(OldOffset, LT.Prologue); return LT; } void DWARFDebugLine::SectionParser::skip( - function_ref<void(Error)> ErrorCallback) { + function_ref<void(Error)> RecoverableErrorHandler, + function_ref<void(Error)> UnrecoverableErrorHandler) { 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, + RecoverableErrorHandler, Context, U)) + UnrecoverableErrorHandler(std::move(Err)); moveToNextTable(OldOffset, LT.Prologue); } |