diff --git a/include/llvm/Support/COFF.h b/include/llvm/Support/COFF.h index 1bfb7f74433..86698e3c5a2 100644 --- a/include/llvm/Support/COFF.h +++ b/include/llvm/Support/COFF.h @@ -662,9 +662,14 @@ namespace COFF { enum CodeViewLineTableIdentifiers { DEBUG_SECTION_MAGIC = 0x4, + DEBUG_SYMBOL_SUBSECTION = 0xF1, DEBUG_LINE_TABLE_SUBSECTION = 0xF2, DEBUG_STRING_TABLE_SUBSECTION = 0xF3, - DEBUG_INDEX_SUBSECTION = 0xF4 + DEBUG_INDEX_SUBSECTION = 0xF4, + + // Symbol subsections are split into records of different types. + DEBUG_SYMBOL_TYPE_PROC_START = 0x1147, + DEBUG_SYMBOL_TYPE_PROC_END = 0x114F }; inline bool isReservedSectionNumber(int32_t SectionNumber) { diff --git a/test/tools/llvm-readobj/codeview-linetables.test b/test/tools/llvm-readobj/codeview-linetables.test index a1bfca02d89..7b9e743d849 100644 --- a/test/tools/llvm-readobj/codeview-linetables.test +++ b/test/tools/llvm-readobj/codeview-linetables.test @@ -40,6 +40,12 @@ MFUN32: ] MFUN32-NEXT: Subsection [ MFUN32-NEXT: Type: 0xF1 MFUN32-NEXT: PayloadSize: 0x4B +MFUN32: ProcStart { +MFUN32-NEXT: FunctionName: x +MFUN32-NEXT: Section: _x +MFUN32-NEXT: CodeSize: 0xA +MFUN32-NEXT: } +MFUN32-NEXT: ProcEnd MFUN32: ] MFUN32-NEXT: Subsection [ MFUN32-NEXT: Type: 0xF2 @@ -53,6 +59,12 @@ MFUN32: ] MFUN32-NEXT: Subsection [ MFUN32-NEXT: Type: 0xF1 MFUN32-NEXT: PayloadSize: 0x4B +MFUN32: ProcStart { +MFUN32-NEXT: FunctionName: y +MFUN32-NEXT: Section: _y +MFUN32-NEXT: CodeSize: 0xA +MFUN32-NEXT: } +MFUN32-NEXT: ProcEnd MFUN32: ] MFUN32-NEXT: Subsection [ MFUN32-NEXT: Type: 0xF2 @@ -66,6 +78,12 @@ MFUN32: ] MFUN32-NEXT: Subsection [ MFUN32-NEXT: Type: 0xF1 MFUN32-NEXT: PayloadSize: 0x4B +MFUN32: ProcStart { +MFUN32-NEXT: FunctionName: f +MFUN32-NEXT: Section: _f +MFUN32-NEXT: CodeSize: 0x14 +MFUN32-NEXT: } +MFUN32-NEXT: ProcEnd MFUN32: ] MFUN32-NEXT: Subsection [ MFUN32-NEXT: Type: 0xF2 @@ -127,6 +145,12 @@ MFUN64: ] MFUN64-NEXT: Subsection [ MFUN64-NEXT: Type: 0xF1 MFUN64-NEXT: PayloadSize: 0x4B +MFUN64: ProcStart { +MFUN64-NEXT: FunctionName: x +MFUN64-NEXT: Section: x +MFUN64-NEXT: CodeSize: 0xE +MFUN64-NEXT: } +MFUN64-NEXT: ProcEnd MFUN64: ] MFUN64-NEXT: Subsection [ MFUN64-NEXT: Type: 0xF2 @@ -136,6 +160,12 @@ MFUN64-NEXT: ] MFUN64-NEXT: Subsection [ MFUN64-NEXT: Type: 0xF1 MFUN64-NEXT: PayloadSize: 0x4B +MFUN64: ProcStart { +MFUN64-NEXT: FunctionName: y +MFUN64-NEXT: Section: y +MFUN64-NEXT: CodeSize: 0xE +MFUN64-NEXT: } +MFUN64-NEXT: ProcEnd MFUN64: ] MFUN64-NEXT: Subsection [ MFUN64-NEXT: Type: 0xF2 @@ -145,6 +175,12 @@ MFUN64-NEXT: ] MFUN64-NEXT: Subsection [ MFUN64-NEXT: Type: 0xF1 MFUN64-NEXT: PayloadSize: 0x4B +MFUN64: ProcStart { +MFUN64-NEXT: FunctionName: f +MFUN64-NEXT: Section: f +MFUN64-NEXT: CodeSize: 0x18 +MFUN64-NEXT: } +MFUN64-NEXT: ProcEnd MFUN64: ] MFUN64-NEXT: Subsection [ MFUN64-NEXT: Type: 0xF2 @@ -234,6 +270,12 @@ MFILE32: ] MFILE32-NEXT: Subsection [ MFILE32-NEXT: Type: 0xF1 MFILE32-NEXT: PayloadSize: 0x4B +MFILE32: ProcStart { +MFILE32-NEXT: FunctionName: f +MFILE32-NEXT: Section: _f +MFILE32-NEXT: CodeSize: 0x14 +MFILE32-NEXT: } +MFILE32-NEXT: ProcEnd MFILE32: ] MFILE32-NEXT: Subsection [ MFILE32-NEXT: Type: 0xF2 @@ -284,6 +326,12 @@ MFILE64: ] MFILE64-NEXT: Subsection [ MFILE64-NEXT: Type: 0xF1 MFILE64-NEXT: PayloadSize: 0x4B +MFILE64: ProcStart { +MFILE64-NEXT: FunctionName: f +MFILE64-NEXT: Section: f +MFILE64-NEXT: CodeSize: 0x18 +MFILE64-NEXT: } +MFILE64-NEXT: ProcEnd MFILE64: ] MFILE64-NEXT: Subsection [ MFILE64-NEXT: Type: 0xF2 @@ -344,23 +392,33 @@ RUN: | FileCheck %s -check-prefix MCOMDAT RUN: llvm-readobj -s -codeview-linetables %p/Inputs/comdat-function-linetables.obj.coff-2013-i386 \ RUN: | FileCheck %s -check-prefix MCOMDAT +MCOMDAT: ProcStart { +MCOMDAT-NEXT: FunctionName: f +MCOMDAT-NEXT: Section: ?f@@YAHXZ +MCOMDAT-NEXT: CodeSize: 0x7 +MCOMDAT-NEXT: } MCOMDAT: FunctionLineTable [ -MCOMDAT-NEXT: FunctionName: ?f@@YAHXZ -MCOMDAT-NEXT: CodeSize: 0x7 -MCOMDAT-NEXT: FilenameSegment [ -MCOMDAT-NEXT: Filename: c:\src\test.cc -MCOMDAT-NEXT: +0x0: 2 -MCOMDAT-NEXT: +0x3: 3 -MCOMDAT-NEXT: +0x5: 4 -MCOMDAT-NEXT: ] -MCOMDAT-NEXT: ] +MCOMDAT-NEXT: FunctionName: ?f@@YAHXZ +MCOMDAT-NEXT: CodeSize: 0x7 +MCOMDAT-NEXT: FilenameSegment [ +MCOMDAT-NEXT: Filename: c:\src\test.cc +MCOMDAT-NEXT: +0x0: 2 +MCOMDAT-NEXT: +0x3: 3 +MCOMDAT-NEXT: +0x5: 4 +MCOMDAT-NEXT: ] +MCOMDAT-NEXT: ] +MCOMDAT: ProcStart { +MCOMDAT-NEXT: FunctionName: g +MCOMDAT-NEXT: Section: ?g@@YAHXZ +MCOMDAT-NEXT: CodeSize: 0x7 +MCOMDAT-NEXT: } MCOMDAT: FunctionLineTable [ -MCOMDAT-NEXT: FunctionName: ?g@@YAHXZ -MCOMDAT-NEXT: CodeSize: 0x7 -MCOMDAT-NEXT: FilenameSegment [ -MCOMDAT-NEXT: Filename: c:\src\test.cc -MCOMDAT-NEXT: +0x0: 7 -MCOMDAT-NEXT: +0x3: 8 -MCOMDAT-NEXT: +0x5: 9 -MCOMDAT-NEXT: ] -MCOMDAT-NEXT: ] +MCOMDAT-NEXT: FunctionName: ?g@@YAHXZ +MCOMDAT-NEXT: CodeSize: 0x7 +MCOMDAT-NEXT: FilenameSegment [ +MCOMDAT-NEXT: Filename: c:\src\test.cc +MCOMDAT-NEXT: +0x0: 7 +MCOMDAT-NEXT: +0x3: 8 +MCOMDAT-NEXT: +0x5: 9 +MCOMDAT-NEXT: ] +MCOMDAT-NEXT: ] diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index dc280a634dc..6242a790f5a 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -69,6 +69,10 @@ private: void printCodeViewLineTables(const SectionRef &Section); + void printCodeViewSymbolsSubsection(StringRef Subsection, + const SectionRef &Section, + uint32_t Offset); + void cacheRelocations(); std::error_code resolveSymbol(const coff_section *Section, uint64_t Offset, @@ -444,6 +448,7 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) { ListScope D(W, "CodeViewLineTables"); { + // FIXME: Add more offset correctness checks. DataExtractor DE(Data, true, 4); uint32_t Offset = 0, Magic = DE.getU32(&Offset); @@ -473,6 +478,9 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) { W.printBinaryBlock("Contents", Contents); switch (SubSectionType) { + case COFF::DEBUG_SYMBOL_SUBSECTION: + printCodeViewSymbolsSubsection(Contents, Section, Offset); + break; 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 @@ -592,6 +600,80 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) { } } +void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection, + const SectionRef &Section, + uint32_t OffsetInSection) { + if (Subsection.size() == 0) { + error(object_error::parse_failed); + return; + } + DataExtractor DE(Subsection, true, 4); + uint32_t Offset = 0; + + // Function-level subsections have "procedure start" and "procedure end" + // commands that should come in pairs and surround relevant info. + bool InFunctionScope = false; + while (DE.isValidOffset(Offset)) { + // Read subsection segments one by one. + uint16_t Size = DE.getU16(&Offset); + // The section size includes the size of the type identifier. + if (Size < 2 || !DE.isValidOffsetForDataOfSize(Offset, Size)) { + error(object_error::parse_failed); + return; + } + Size -= 2; + uint16_t Type = DE.getU16(&Offset); + switch (Type) { + case COFF::DEBUG_SYMBOL_TYPE_PROC_START: { + DictScope S(W, "ProcStart"); + if (InFunctionScope || Size < 36) { + error(object_error::parse_failed); + return; + } + InFunctionScope = true; + + // We're currently interested in a limited subset of fields in this + // segment, just ignore the rest of the fields for now. + uint8_t Unused[12]; + DE.getU8(&Offset, Unused, 12); + uint32_t CodeSize = DE.getU32(&Offset); + DE.getU8(&Offset, Unused, 12); + StringRef SectionName; + if (error(resolveSymbolName(Obj->getCOFFSection(Section), + OffsetInSection + Offset, SectionName))) + return; + Offset += 4; + DE.getU8(&Offset, Unused, 3); + StringRef FunctionName = DE.getCStr(&Offset); + if (!DE.isValidOffset(Offset)) { + error(object_error::parse_failed); + return; + } + W.printString("FunctionName", FunctionName); + W.printString("Section", SectionName); + W.printHex("CodeSize", CodeSize); + + break; + } + case COFF::DEBUG_SYMBOL_TYPE_PROC_END: { + W.startLine() << "ProcEnd\n"; + if (!InFunctionScope || Size > 0) { + error(object_error::parse_failed); + return; + } + InFunctionScope = false; + break; + } + default: + Offset += Size; + break; + } + } + + if (InFunctionScope) + error(object_error::parse_failed); +} + void COFFDumper::printSections() { ListScope SectionsD(W, "Sections"); int SectionNumber = 0;