From e493a9976c6ff5aa7852c6c6f512b0ff60e3f5ce Mon Sep 17 00:00:00 2001 From: Timur Iskhodzhanov Date: Thu, 19 Dec 2013 11:37:14 +0000 Subject: Teach the llvm-readobj COFF dumper to dump debug line tables from object files Reviewed at http://llvm-reviews.chandlerc.com/D2425 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@197674 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Support/COFF.h | 7 + .../Inputs/multifile-linetables.obj.coff-2012-i368 | Bin 0 -> 1631 bytes .../multifile-linetables.obj.coff-2012-x86_64 | Bin 0 -> 1799 bytes .../multifunction-linetables.obj.coff-2012-i368 | Bin 0 -> 2155 bytes .../multifunction-linetables.obj.coff-2012-x86_64 | Bin 0 -> 2475 bytes test/tools/llvm-readobj/codeview-linetables.test | 282 +++++++++++++++++++++ tools/llvm-readobj/COFFDumper.cpp | 165 ++++++++++++ tools/llvm-readobj/llvm-readobj.cpp | 4 + tools/llvm-readobj/llvm-readobj.h | 1 + 9 files changed, 459 insertions(+) create mode 100644 test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-i368 create mode 100644 test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-x86_64 create mode 100644 test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-i368 create mode 100644 test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-x86_64 create mode 100644 test/tools/llvm-readobj/codeview-linetables.test diff --git a/include/llvm/Support/COFF.h b/include/llvm/Support/COFF.h index 9cc3989df0..2de01000a7 100644 --- a/include/llvm/Support/COFF.h +++ b/include/llvm/Support/COFF.h @@ -611,6 +611,13 @@ namespace COFF { } }; + enum CodeViewLineTableIdentifiers { + DEBUG_SECTION_MAGIC = 0x4, + DEBUG_LINE_TABLE_SUBSECTION = 0xF2, + DEBUG_STRING_TABLE_SUBSECTION = 0xF3, + DEBUG_INDEX_SUBSECTION = 0xF4 + }; + } // End namespace COFF. } // End namespace llvm. diff --git a/test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-i368 b/test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-i368 new file mode 100644 index 0000000000..1672d3a542 Binary files /dev/null and b/test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-i368 differ diff --git a/test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-x86_64 b/test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-x86_64 new file mode 100644 index 0000000000..30bfe79bc3 Binary files /dev/null and b/test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-x86_64 differ diff --git a/test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-i368 b/test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-i368 new file mode 100644 index 0000000000..a0196ff2d7 Binary files /dev/null and b/test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-i368 differ diff --git a/test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-x86_64 b/test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-x86_64 new file mode 100644 index 0000000000..14f65ab2f6 Binary files /dev/null and b/test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-x86_64 differ diff --git a/test/tools/llvm-readobj/codeview-linetables.test b/test/tools/llvm-readobj/codeview-linetables.test new file mode 100644 index 0000000000..4854d7ac6a --- /dev/null +++ b/test/tools/llvm-readobj/codeview-linetables.test @@ -0,0 +1,282 @@ +RUN: llvm-readobj -s -codeview-linetables %p/Inputs/multifunction-linetables.obj.coff-2012-i368 \ +RUN: | FileCheck %s -check-prefix MFUN32 +RUN: llvm-readobj -s -codeview-linetables %p/Inputs/multifunction-linetables.obj.coff-2012-x86_64 \ +RUN: | FileCheck %s -check-prefix MFUN64 +RUN: llvm-readobj -s -codeview-linetables %p/Inputs/multifile-linetables.obj.coff-2012-i368 \ +RUN: | FileCheck %s -check-prefix MFILE32 +RUN: llvm-readobj -s -codeview-linetables %p/Inputs/multifile-linetables.obj.coff-2012-x86_64 \ +RUN: | FileCheck %s -check-prefix MFILE64 + +MFUN32: CodeViewLineTables [ +MFUN32-NEXT: Magic: 0x4 +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF1 +MFUN32-NEXT: PayloadSize: 0x52 +MFUN32: ] +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF5 +MFUN32-NEXT: PayloadSize: 0x24 +MFUN32: ] +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF1 +MFUN32-NEXT: PayloadSize: 0x4B +MFUN32: ] +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF2 +MFUN32-NEXT: PayloadSize: 0x30 +MFUN32: FunctionName: _x +MFUN32-NEXT: ] +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF5 +MFUN32-NEXT: PayloadSize: 0x24 +MFUN32: ] +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF1 +MFUN32-NEXT: PayloadSize: 0x4B +MFUN32: ] +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF2 +MFUN32-NEXT: PayloadSize: 0x30 +MFUN32: FunctionName: _y +MFUN32-NEXT: ] +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF5 +MFUN32-NEXT: PayloadSize: 0x24 +MFUN32: ] +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF1 +MFUN32-NEXT: PayloadSize: 0x4B +MFUN32: ] +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF2 +MFUN32-NEXT: PayloadSize: 0x40 +MFUN32: FunctionName: _f +MFUN32-NEXT: ] +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF4 +MFUN32-NEXT: PayloadSize: 0x18 +MFUN32: ] +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF3 +MFUN32-NEXT: PayloadSize: 0x46 +MFUN32: ] +MFUN32-NEXT: Subsection [ +MFUN32-NEXT: Type: 0xF1 +MFUN32-NEXT: PayloadSize: 0x8 +MFUN32: ] +MFUN32-NEXT: FunctionLineTable [ +MFUN32-NEXT: FunctionName: _x +MFUN32-NEXT: CodeSize: 0xA +MFUN32-NEXT: FilenameSegment [ +MFUN32-NEXT: Filename: d:\source.c +MFUN32-NEXT: +0x0: 3 +MFUN32-NEXT: +0x3: 4 +MFUN32-NEXT: +0x8: 5 +MFUN32-NEXT: ] +MFUN32-NEXT: ] +MFUN32-NEXT: FunctionLineTable [ +MFUN32-NEXT: FunctionName: _y +MFUN32-NEXT: CodeSize: 0xA +MFUN32-NEXT: FilenameSegment [ +MFUN32-NEXT: Filename: d:\source.c +MFUN32-NEXT: +0x0: 7 +MFUN32-NEXT: +0x3: 8 +MFUN32-NEXT: +0x8: 9 +MFUN32-NEXT: ] +MFUN32-NEXT: ] +MFUN32-NEXT: FunctionLineTable [ +MFUN32-NEXT: FunctionName: _f +MFUN32-NEXT: CodeSize: 0x14 +MFUN32-NEXT: FilenameSegment [ +MFUN32-NEXT: Filename: d:\source.c +MFUN32-NEXT: +0x0: 11 +MFUN32-NEXT: +0x3: 12 +MFUN32-NEXT: +0x8: 13 +MFUN32-NEXT: +0xD: 14 +MFUN32-NEXT: +0x12: 15 +MFUN32-NEXT: ] +MFUN32-NEXT: ] +MFUN32-NEXT: ] + +MFUN64: CodeViewLineTables [ +MFUN64-NEXT: Magic: 0x4 +MFUN64-NEXT: Subsection [ +MFUN64-NEXT: Type: 0xF1 +MFUN64-NEXT: PayloadSize: 0x52 +MFUN64: ] +MFUN64-NEXT: Subsection [ +MFUN64-NEXT: Type: 0xF1 +MFUN64-NEXT: PayloadSize: 0x4B +MFUN64: ] +MFUN64-NEXT: Subsection [ +MFUN64-NEXT: Type: 0xF2 +MFUN64-NEXT: PayloadSize: 0x30 +MFUN64: FunctionName: x +MFUN64-NEXT: ] +MFUN64-NEXT: Subsection [ +MFUN64-NEXT: Type: 0xF1 +MFUN64-NEXT: PayloadSize: 0x4B +MFUN64: ] +MFUN64-NEXT: Subsection [ +MFUN64-NEXT: Type: 0xF2 +MFUN64-NEXT: PayloadSize: 0x30 +MFUN64: FunctionName: y +MFUN64-NEXT: ] +MFUN64-NEXT: Subsection [ +MFUN64-NEXT: Type: 0xF1 +MFUN64-NEXT: PayloadSize: 0x4B +MFUN64: ] +MFUN64-NEXT: Subsection [ +MFUN64-NEXT: Type: 0xF2 +MFUN64-NEXT: PayloadSize: 0x40 +MFUN64: FunctionName: f +MFUN64-NEXT: ] +MFUN64-NEXT: Subsection [ +MFUN64-NEXT: Type: 0xF4 +MFUN64-NEXT: PayloadSize: 0x18 +MFUN64: ] +MFUN64-NEXT: Subsection [ +MFUN64-NEXT: Type: 0xF3 +MFUN64-NEXT: PayloadSize: 0xD +MFUN64: ] +MFUN64-NEXT: Subsection [ +MFUN64-NEXT: Type: 0xF1 +MFUN64-NEXT: PayloadSize: 0x8 +MFUN64: ] +MFUN64-NEXT: FunctionLineTable [ +MFUN64-NEXT: FunctionName: x +MFUN64-NEXT: CodeSize: 0xE +MFUN64-NEXT: FilenameSegment [ +MFUN64-NEXT: Filename: d:\source.c +MFUN64-NEXT: +0x0: 3 +MFUN64-NEXT: +0x4: 4 +MFUN64-NEXT: +0x9: 5 +MFUN64-NEXT: ] +MFUN64-NEXT: ] +MFUN64-NEXT: FunctionLineTable [ +MFUN64-NEXT: FunctionName: y +MFUN64-NEXT: CodeSize: 0xE +MFUN64-NEXT: FilenameSegment [ +MFUN64-NEXT: Filename: d:\source.c +MFUN64-NEXT: +0x0: 7 +MFUN64-NEXT: +0x4: 8 +MFUN64-NEXT: +0x9: 9 +MFUN64-NEXT: ] +MFUN64-NEXT: ] +MFUN64-NEXT: FunctionLineTable [ +MFUN64-NEXT: FunctionName: f +MFUN64-NEXT: CodeSize: 0x18 +MFUN64-NEXT: FilenameSegment [ +MFUN64-NEXT: Filename: d:\source.c +MFUN64-NEXT: +0x0: 11 +MFUN64-NEXT: +0x4: 12 +MFUN64-NEXT: +0x9: 13 +MFUN64-NEXT: +0xE: 14 +MFUN64-NEXT: +0x13: 15 +MFUN64-NEXT: ] +MFUN64-NEXT: ] +MFUN64-NEXT: ] + +MFILE32: CodeViewLineTables [ +MFILE32-NEXT: Magic: 0x4 +MFILE32-NEXT: Subsection [ +MFILE32-NEXT: Type: 0xF1 +MFILE32-NEXT: PayloadSize: 0x51 +MFILE32: ] +MFILE32-NEXT: Subsection [ +MFILE32-NEXT: Type: 0xF5 +MFILE32-NEXT: PayloadSize: 0x24 +MFILE32: ] +MFILE32-NEXT: Subsection [ +MFILE32-NEXT: Type: 0xF1 +MFILE32-NEXT: PayloadSize: 0x4B +MFILE32: ] +MFILE32-NEXT: Subsection [ +MFILE32-NEXT: Type: 0xF2 +MFILE32-NEXT: PayloadSize: 0x64 +MFILE32: FunctionName: _f +MFILE32-NEXT: ] +MFILE32-NEXT: Subsection [ +MFILE32-NEXT: Type: 0xF4 +MFILE32-NEXT: PayloadSize: 0x28 +MFILE32: ] +MFILE32-NEXT: Subsection [ +MFILE32-NEXT: Type: 0xF3 +MFILE32-NEXT: PayloadSize: 0x57 +MFILE32: ] +MFILE32-NEXT: Subsection [ +MFILE32-NEXT: Type: 0xF1 +MFILE32-NEXT: PayloadSize: 0x8 +MFILE32: ] +MFILE32-NEXT: FunctionLineTable [ +MFILE32-NEXT: FunctionName: _f +MFILE32-NEXT: CodeSize: 0x14 +MFILE32-NEXT: FilenameSegment [ +MFILE32-NEXT: Filename: d:\input.c +MFILE32-NEXT: +0x0: 3 +MFILE32-NEXT: ] +MFILE32-NEXT: FilenameSegment [ +MFILE32-NEXT: Filename: d:\one.c +MFILE32-NEXT: +0x3: 1 +MFILE32-NEXT: ] +MFILE32-NEXT: FilenameSegment [ +MFILE32-NEXT: Filename: d:\two.c +MFILE32-NEXT: +0x8: 2 +MFILE32-NEXT: ] +MFILE32-NEXT: FilenameSegment [ +MFILE32-NEXT: Filename: d:\one.c +MFILE32-NEXT: +0xD: 7 +MFILE32-NEXT: +0x12: 8 +MFILE32-NEXT: ] +MFILE32-NEXT: ] +MFILE32-NEXT: ] + +MFILE64: CodeViewLineTables [ +MFILE64-NEXT: Magic: 0x4 +MFILE64-NEXT: Subsection [ +MFILE64-NEXT: Type: 0xF1 +MFILE64-NEXT: PayloadSize: 0x51 +MFILE64: ] +MFILE64-NEXT: Subsection [ +MFILE64-NEXT: Type: 0xF1 +MFILE64-NEXT: PayloadSize: 0x4B +MFILE64: ] +MFILE64-NEXT: Subsection [ +MFILE64-NEXT: Type: 0xF2 +MFILE64-NEXT: PayloadSize: 0x64 +MFILE64: FunctionName: f +MFILE64-NEXT: ] +MFILE64-NEXT: Subsection [ +MFILE64-NEXT: Type: 0xF4 +MFILE64-NEXT: PayloadSize: 0x28 +MFILE64: ] +MFILE64-NEXT: Subsection [ +MFILE64-NEXT: Type: 0xF3 +MFILE64-NEXT: PayloadSize: 0x1E +MFILE64: ] +MFILE64-NEXT: Subsection [ +MFILE64-NEXT: Type: 0xF1 +MFILE64-NEXT: PayloadSize: 0x8 +MFILE64: ] +MFILE64-NEXT: FunctionLineTable [ +MFILE64-NEXT: FunctionName: f +MFILE64-NEXT: CodeSize: 0x18 +MFILE64-NEXT: FilenameSegment [ +MFILE64-NEXT: Filename: d:\input.c +MFILE64-NEXT: +0x0: 3 +MFILE64-NEXT: ] +MFILE64-NEXT: FilenameSegment [ +MFILE64-NEXT: Filename: d:\one.c +MFILE64-NEXT: +0x4: 1 +MFILE64-NEXT: ] +MFILE64-NEXT: FilenameSegment [ +MFILE64-NEXT: Filename: d:\two.c +MFILE64-NEXT: +0x9: 2 +MFILE64-NEXT: ] +MFILE64-NEXT: FilenameSegment [ +MFILE64-NEXT: Filename: d:\one.c +MFILE64-NEXT: +0xE: 7 +MFILE64-NEXT: +0x13: 8 +MFILE64-NEXT: ] +MFILE64-NEXT: ] +MFILE64-NEXT: ] diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index 2f309e30ec..411846b867 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -24,6 +24,8 @@ #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/COFF.h" +#include "llvm/Support/DataExtractor.h" #include "llvm/Support/Format.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/Win64EH.h" @@ -76,6 +78,8 @@ private: void printUnwindCode(const Win64EH::UnwindInfo& UI, ArrayRef UCs); + void printCodeViewLineTables(section_iterator SecI); + void cacheRelocations(); error_code getSectionContents( @@ -648,6 +652,164 @@ void COFFDumper::printFileHeaders() { } } +void COFFDumper::printCodeViewLineTables(section_iterator SecI) { + StringRef Data; + if (error(SecI->getContents(Data))) return; + + SmallVector FunctionNames; + StringMap FunctionLineTables; + StringRef FileIndexToStringOffsetTable; + StringRef StringTable; + + ListScope D(W, "CodeViewLineTables"); + { + DataExtractor DE(Data, true, 4); + uint32_t Offset = 0, + Magic = DE.getU32(&Offset); + W.printHex("Magic", Magic); + if (Magic != COFF::DEBUG_SECTION_MAGIC) { + error(object_error::parse_failed); + return; + } + + bool Finished = false; + while (DE.isValidOffset(Offset) && !Finished) { + // The section consists of a number of subsection in the following format: + // |Type|PayloadSize|Payload...| + uint32_t SubSectionType = DE.getU32(&Offset), + PayloadSize = DE.getU32(&Offset); + ListScope S(W, "Subsection"); + W.printHex("Type", SubSectionType); + W.printHex("PayloadSize", PayloadSize); + if (PayloadSize > Data.size() - Offset) { + error(object_error::parse_failed); + return; + } + + // Print the raw contents to simplify debugging if anything goes wrong + // afterwards. + StringRef Contents = Data.substr(Offset, PayloadSize); + W.printBinaryBlock("Contents", Contents); + + switch (SubSectionType) { + case COFF::DEBUG_LINE_TABLE_SUBSECTION: { + // Holds a PC to file:line table. Some data to parse this subsection is + // stored in the other subsections, so just check sanity and store the + // pointers for deferred processing. + + if (PayloadSize < 12) { + // There should be at least three words to store two function + // relocations and size of the code. + error(object_error::parse_failed); + return; + } + + StringRef FunctionName; + if (error(resolveSymbolName(RelocMap[Obj->getCOFFSection(SecI)], Offset, + FunctionName))) + return; + W.printString("FunctionName", FunctionName); + if (FunctionLineTables.count(FunctionName) != 0) { + // Saw debug info for this function already? + error(object_error::parse_failed); + return; + } + + FunctionLineTables[FunctionName] = Contents; + FunctionNames.push_back(FunctionName); + break; + } + case COFF::DEBUG_STRING_TABLE_SUBSECTION: + if (PayloadSize == 0 || StringTable.data() != 0 || + Contents.back() != '\0') { + // Empty or duplicate or non-null-terminated subsection. + error(object_error::parse_failed); + return; + } + StringTable = Contents; + break; + case COFF::DEBUG_INDEX_SUBSECTION: + // Holds the translation table from file indices + // to offsets in the string table. + + if (PayloadSize == 0 || FileIndexToStringOffsetTable.data() != 0) { + // Empty or duplicate subsection. + error(object_error::parse_failed); + return; + } + FileIndexToStringOffsetTable = Contents; + break; + } + Offset += PayloadSize; + + // Align the reading pointer by 4. + Offset += (-Offset) % 4; + } + } + + // Dump the line tables now that we've read all the subsections and know all + // the required information. + for (unsigned I = 0, E = FunctionNames.size(); I != E; ++I) { + StringRef Name = FunctionNames[I]; + ListScope S(W, "FunctionLineTable"); + W.printString("FunctionName", Name); + + DataExtractor DE(FunctionLineTables[Name], true, 4); + uint32_t Offset = 8; // Skip relocations. + uint32_t FunctionSize = DE.getU32(&Offset); + W.printHex("CodeSize", FunctionSize); + while (DE.isValidOffset(Offset)) { + // For each range of lines with the same filename, we have a segment + // in the line table. The filename string is accessed using double + // indirection to the string table subsection using the index subsection. + uint32_t OffsetInIndex = DE.getU32(&Offset), + SegmentLength = DE.getU32(&Offset), + FullSegmentSize = DE.getU32(&Offset); + if (FullSegmentSize != 12 + 8 * SegmentLength) { + error(object_error::parse_failed); + return; + } + + uint32_t FilenameOffset; + { + DataExtractor SDE(FileIndexToStringOffsetTable, true, 4); + uint32_t OffsetInSDE = OffsetInIndex; + if (!SDE.isValidOffset(OffsetInSDE)) { + error(object_error::parse_failed); + return; + } + FilenameOffset = SDE.getU32(&OffsetInSDE); + } + + if (FilenameOffset == 0 || FilenameOffset + 1 >= StringTable.size() || + StringTable.data()[FilenameOffset - 1] != '\0') { + // Each string in an F3 subsection should be preceded by a null + // character. + error(object_error::parse_failed); + return; + } + + StringRef Filename(StringTable.data() + FilenameOffset); + ListScope S(W, "FilenameSegment"); + W.printString("Filename", Filename); + for (unsigned J = 0; J != SegmentLength && DE.isValidOffset(Offset); + ++J) { + // Then go the (PC, LineNumber) pairs. The line number is stored in the + // least significant 31 bits of the respective word in the table. + uint32_t PC = DE.getU32(&Offset), + LineNumber = DE.getU32(&Offset) & 0x7fffffff; + if (PC >= FunctionSize) { + error(object_error::parse_failed); + return; + } + char Buffer[32]; + format("+0x%X", PC).snprint(Buffer, 32); + W.printNumber(Buffer, LineNumber); + } + } + } +} + void COFFDumper::printSections() { error_code EC; @@ -707,6 +869,9 @@ void COFFDumper::printSections() { } } + if (Name == ".debug$S" && opts::CodeViewLineTables) + printCodeViewLineTables(SecI); + if (opts::SectionData) { StringRef Data; if (error(SecI->getContents(Data))) break; diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index f84a72f461..c09be74dc7 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -128,6 +128,10 @@ namespace opts { // -expand-relocs cl::opt ExpandRelocs("expand-relocs", cl::desc("Expand each shown relocation to multiple lines")); + + // -codeview-linetables + cl::opt CodeViewLineTables("codeview-linetables", + cl::desc("Display CodeView line table information")); } // namespace opts static int ReturnValue = EXIT_SUCCESS; diff --git a/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h index 3f756106c9..f0bba42e91 100644 --- a/tools/llvm-readobj/llvm-readobj.h +++ b/tools/llvm-readobj/llvm-readobj.h @@ -38,6 +38,7 @@ namespace opts { extern llvm::cl::opt DynamicSymbols; extern llvm::cl::opt UnwindInfo; extern llvm::cl::opt ExpandRelocs; + extern llvm::cl::opt CodeViewLineTables; } // namespace opts #define LLVM_READOBJ_ENUM_ENT(ns, enum) \ -- cgit v1.2.3