Index: test/tools/llvm-readobj/codeview-linetables.test =================================================================== --- /dev/null +++ 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: ] Index: tools/llvm-readobj/COFFDumper.cpp =================================================================== --- tools/llvm-readobj/COFFDumper.cpp +++ tools/llvm-readobj/COFFDumper.cpp @@ -24,6 +24,7 @@ #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/DataExtractor.h" #include "llvm/Support/Format.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/Win64EH.h" @@ -76,6 +77,8 @@ void printUnwindCode(const Win64EH::UnwindInfo& UI, ArrayRef UCs); + void printCodeViewLineTables(section_iterator SecI); + void cacheRelocations(); error_code getSectionContents( @@ -648,6 +651,135 @@ } } +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; + + W.printHex("Magic", DE.getU32(&Offset)); + + bool Finished = false; + while (DE.isValidOffset(Offset) && !Finished) { + 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; + } + StringRef Contents = Data.substr(Offset, PayloadSize); + W.printBinaryBlock("Contents", Contents); + + switch (SubSectionType) { + case 0xF2: { + // There should be at least three words to store two function + // relocations and size of the code. + if (PayloadSize < 12) { + 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 0xF4: + if (PayloadSize == 0 || FileIndexToStringOffsetTable.data() != 0) { + // Empty or duplicate 0xF4 subsection. + error(object_error::parse_failed); + return; + } + FileIndexToStringOffsetTable = Contents; + break; + case 0xF3: + if (PayloadSize == 0 || StringTable.data() != 0 || + Contents.back() != '\0') { + // Empty or duplicate or non-null-terminated 0xF3 subsection. + error(object_error::parse_failed); + return; + } + StringTable = Contents; + break; + } + Offset += PayloadSize; + + // Align the reading pointer by 4. + Offset += (-Offset) % 4; + } + } + + 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)) { + uint32_t OffsetInF4 = DE.getU32(&Offset), + SegmentLength = DE.getU32(&Offset), + FullSegmentSize = DE.getU32(&Offset); + if (FullSegmentSize != 12 + 8 * SegmentLength) { + error(object_error::parse_failed); + return; + } + + uint32_t OffsetInF3; + { + DataExtractor SDE(FileIndexToStringOffsetTable, true, 4); + uint32_t OffsetInSDE = OffsetInF4; + if (!SDE.isValidOffset(OffsetInSDE)) { + error(object_error::parse_failed); + return; + } + OffsetInF3 = SDE.getU32(&OffsetInSDE); + } + + if (OffsetInF3 == 0 || StringTable.data()[OffsetInF3 - 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() + OffsetInF3); + ListScope S(W, "FilenameSegment"); + W.printString("Filename", Filename); + for (unsigned J = 0; J != SegmentLength; ++J) { + uint32_t PC = DE.getU32(&Offset), + LineNumber = DE.getU32(&Offset) & 0x7fffffff; + char Buffer[32]; + format("+0x%X", PC).snprint(Buffer, 32); + W.printNumber(Buffer, LineNumber); + } + } + } +} + void COFFDumper::printSections() { error_code EC; @@ -707,6 +839,9 @@ } } + if (Name == ".debug$S" && opts::CodeViewLineTables) + printCodeViewLineTables(SecI); + if (opts::SectionData) { StringRef Data; if (error(SecI->getContents(Data))) break; Index: tools/llvm-readobj/llvm-readobj.h =================================================================== --- tools/llvm-readobj/llvm-readobj.h +++ tools/llvm-readobj/llvm-readobj.h @@ -38,6 +38,7 @@ 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) \ Index: tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- tools/llvm-readobj/llvm-readobj.cpp +++ tools/llvm-readobj/llvm-readobj.cpp @@ -128,6 +128,10 @@ // -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;