Index: include/llvm/Support/COFF.h =================================================================== --- include/llvm/Support/COFF.h +++ include/llvm/Support/COFF.h @@ -666,12 +666,19 @@ DEBUG_LINE_TABLE_SUBSECTION = 0xF2, DEBUG_STRING_TABLE_SUBSECTION = 0xF3, DEBUG_INDEX_SUBSECTION = 0xF4, + DEBUG_FPO_SUBSECTION = 0xF5, // Symbol subsections are split into records of different types. DEBUG_SYMBOL_TYPE_PROC_START = 0x1147, DEBUG_SYMBOL_TYPE_PROC_END = 0x114F }; + enum FPOFlags : uint32_t { + FPO_SYSTEM_EXCEPTION_HANDLING = 0x00000001, + FPO_CXX_EXCEPTION_HANDLING = 0x00000002, + FPO_FUNCTION_ENTRY = 0x00000004 + }; + inline bool isReservedSectionNumber(int32_t SectionNumber) { return SectionNumber <= 0; } Index: test/tools/llvm-readobj/codeview-linetables.test =================================================================== --- test/tools/llvm-readobj/codeview-linetables.test +++ test/tools/llvm-readobj/codeview-linetables.test @@ -112,6 +112,22 @@ MFUN32-NEXT: +0x8: 5 MFUN32-NEXT: ] MFUN32-NEXT: ] +MFUN32-NEXT: FunctionFPOTable [ +MFUN32-NEXT: FunctionName: _x +MFUN32-NEXT: FPOSegment [ +MFUN32-NEXT: Start: 0x0 +MFUN32-NEXT: CodeSize: 0xA +MFUN32-NEXT: LocalsSize: 0x0 +MFUN32-NEXT: ParamsSize: 0x0 +MFUN32-NEXT: MaxStackSize: 0x0 +MFUN32-NEXT: ProgramStr: $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = +MFUN32-NEXT: PrologSize: 0x3 +MFUN32-NEXT: SavedRegSize: 0x0 +MFUN32-NEXT: Flags [ (0x4) +MFUN32-NEXT: FPO_FUNCTION_ENTRY (0x4) +MFUN32-NEXT: ] +MFUN32-NEXT: ] +MFUN32-NEXT: ] MFUN32-NEXT: FunctionLineTable [ MFUN32-NEXT: FunctionName: _y MFUN32-NEXT: CodeSize: 0xA @@ -122,6 +138,22 @@ MFUN32-NEXT: +0x8: 9 MFUN32-NEXT: ] MFUN32-NEXT: ] +MFUN32-NEXT: FunctionFPOTable [ +MFUN32-NEXT: FunctionName: _y +MFUN32-NEXT: FPOSegment [ +MFUN32-NEXT: Start: 0x0 +MFUN32-NEXT: CodeSize: 0xA +MFUN32-NEXT: LocalsSize: 0x0 +MFUN32-NEXT: ParamsSize: 0x0 +MFUN32-NEXT: MaxStackSize: 0x0 +MFUN32-NEXT: ProgramStr: $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = +MFUN32-NEXT: PrologSize: 0x3 +MFUN32-NEXT: SavedRegSize: 0x0 +MFUN32-NEXT: Flags [ (0x4) +MFUN32-NEXT: FPO_FUNCTION_ENTRY (0x4) +MFUN32-NEXT: ] +MFUN32-NEXT: ] +MFUN32-NEXT: ] MFUN32-NEXT: FunctionLineTable [ MFUN32-NEXT: FunctionName: _f MFUN32-NEXT: CodeSize: 0x14 @@ -134,6 +166,22 @@ MFUN32-NEXT: +0x12: 15 MFUN32-NEXT: ] MFUN32-NEXT: ] +MFUN32-NEXT: FunctionFPOTable [ +MFUN32-NEXT: FunctionName: _f +MFUN32-NEXT: FPOSegment [ +MFUN32-NEXT: Start: 0x0 +MFUN32-NEXT: CodeSize: 0x14 +MFUN32-NEXT: LocalsSize: 0x0 +MFUN32-NEXT: ParamsSize: 0x0 +MFUN32-NEXT: MaxStackSize: 0x0 +MFUN32-NEXT: ProgramStr: $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = +MFUN32-NEXT: PrologSize: 0x3 +MFUN32-NEXT: SavedRegSize: 0x0 +MFUN32-NEXT: Flags [ (0x4) +MFUN32-NEXT: FPO_FUNCTION_ENTRY (0x4) +MFUN32-NEXT: ] +MFUN32-NEXT: ] +MFUN32-NEXT: ] MFUN32-NEXT: ] MFUN64: CodeViewLineTables [ @@ -315,6 +363,22 @@ MFILE32-NEXT: +0x12: 8 MFILE32-NEXT: ] MFILE32-NEXT: ] +MFILE32-NEXT: FunctionFPOTable [ +MFILE32-NEXT: FunctionName: _f +MFILE32-NEXT: FPOSegment [ +MFILE32-NEXT: Start: 0x0 +MFILE32-NEXT: CodeSize: 0x14 +MFILE32-NEXT: LocalsSize: 0x0 +MFILE32-NEXT: ParamsSize: 0x0 +MFILE32-NEXT: MaxStackSize: 0x0 +MFILE32-NEXT: ProgramStr: $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = +MFILE32-NEXT: PrologSize: 0x3 +MFILE32-NEXT: SavedRegSize: 0x0 +MFILE32-NEXT: Flags [ (0x4) +MFILE32-NEXT: FPO_FUNCTION_ENTRY (0x4) +MFILE32-NEXT: ] +MFILE32-NEXT: ] +MFILE32-NEXT: ] MFILE32-NEXT: ] MFILE64: CodeViewLineTables [ Index: tools/llvm-readobj/COFFDumper.cpp =================================================================== --- tools/llvm-readobj/COFFDumper.cpp +++ tools/llvm-readobj/COFFDumper.cpp @@ -247,6 +247,14 @@ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_WRITE ) }; +static const EnumEntry +FPOFlags[] = { + LLVM_READOBJ_ENUM_ENT(COFF, FPO_SYSTEM_EXCEPTION_HANDLING), + LLVM_READOBJ_ENUM_ENT(COFF, FPO_CXX_EXCEPTION_HANDLING), + LLVM_READOBJ_ENUM_ENT(COFF, FPO_FUNCTION_ENTRY) +}; + + static const EnumEntry ImageSymType[] = { { "Null" , COFF::IMAGE_SYM_TYPE_NULL }, { "Void" , COFF::IMAGE_SYM_TYPE_VOID }, @@ -445,6 +453,7 @@ SmallVector FunctionNames; StringMap FunctionLineTables; + StringMap FunctionFPOTables; ListScope D(W, "CodeViewLineTables"); { @@ -508,6 +517,33 @@ FunctionNames.push_back(FunctionName); break; } + case COFF::DEBUG_FPO_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(Obj->getCOFFSection(Section), Offset, + FunctionName))) + return; + W.printString("FunctionName", FunctionName); + if (FunctionFPOTables.count(FunctionName) != 0) { + // Saw debug info for this function already? + error(object_error::parse_failed); + return; + } + + FunctionFPOTables[FunctionName] = Contents; + break; + } + case COFF::DEBUG_STRING_TABLE_SUBSECTION: if (PayloadSize == 0 || CVStringTable.data() != nullptr || Contents.back() != '\0') { @@ -530,6 +566,7 @@ CVFileIndexToStringOffsetTable = Contents; break; } + Offset += PayloadSize; // Align the reading pointer by 4. @@ -541,60 +578,92 @@ // 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(CVFileIndexToStringOffsetTable, true, 4); - uint32_t OffsetInSDE = OffsetInIndex; - if (!SDE.isValidOffset(OffsetInSDE)) { + if (FunctionLineTables.find(Name) != FunctionLineTables.end()) { + 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; } - FilenameOffset = SDE.getU32(&OffsetInSDE); - } - if (FilenameOffset == 0 || FilenameOffset + 1 >= CVStringTable.size() || - CVStringTable.data()[FilenameOffset - 1] != '\0') { - // Each string in an F3 subsection should be preceded by a null - // character. - error(object_error::parse_failed); - return; - } + uint32_t FilenameOffset; + { + DataExtractor SDE(CVFileIndexToStringOffsetTable, true, 4); + uint32_t OffsetInSDE = OffsetInIndex; + if (!SDE.isValidOffset(OffsetInSDE)) { + error(object_error::parse_failed); + return; + } + FilenameOffset = SDE.getU32(&OffsetInSDE); + } - StringRef Filename(CVStringTable.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) { + if (FilenameOffset == 0 || FilenameOffset + 1 >= CVStringTable.size() || + CVStringTable.data()[FilenameOffset - 1] != '\0') { + // Each string in an F3 subsection should be preceded by a null + // character. error(object_error::parse_failed); return; } - char Buffer[32]; - format("+0x%X", PC).snprint(Buffer, 32); - W.printNumber(Buffer, LineNumber); + + StringRef Filename(CVStringTable.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); + } + } + } + if (FunctionFPOTables.find(Name) != FunctionFPOTables.end()) { + ListScope S(W, "FunctionFPOTable"); + W.printString("FunctionName", Name); + + DataExtractor DE(FunctionFPOTables[Name], true, 4); + uint32_t Offset = 4; // Skip relocations. + + while (DE.isValidOffset(Offset)) { + ListScope S(W, "FPOSegment"); + uint32_t Start = DE.getU32(&Offset); + W.printHex("Start", Start); + uint32_t FunctionSize = DE.getU32(&Offset); + W.printHex("CodeSize", FunctionSize); + uint32_t LocalsSize = DE.getU32(&Offset); + W.printHex("LocalsSize", LocalsSize); + uint32_t ParamsSize = DE.getU32(&Offset); + W.printHex("ParamsSize", ParamsSize); + uint32_t MaxStackSize = DE.getU32(&Offset); + W.printHex("MaxStackSize", MaxStackSize); + uint32_t StrOffset = DE.getU32(&Offset); + StringRef ProgramStr(CVStringTable.data() + StrOffset); + W.printString("ProgramStr", ProgramStr); + uint32_t PrologSize = DE.getU16(&Offset); + W.printHex("PrologSize", PrologSize); + uint32_t SavedRegSize = DE.getU16(&Offset); + W.printHex("SavedRegSize", SavedRegSize); + uint32_t Flags = DE.getU32(&Offset); + W.printFlags("Flags", Flags, makeArrayRef(FPOFlags)); } } }