From a1f85f8bdd809762f16c1c485b5f7a3d938f2b5f Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Wed, 12 Sep 2018 21:02:01 +0000 Subject: [PATCH] [PDB] Emit old fpo data to the PDB file. r342003 added support for emitting FPO data from the DEBUG_S_FRAMEDATA subsection of the .debug$S section to the PDB file. However, that is not the end of the story. FPO can end up in two different destinations in a PDB, each corresponding to a different FPO data source. The case handled by r342003 involves copying data from the DEBUG_S_FRAMEDATA subsection of the .debug$S section to the "New FPO" stream in the PDB, which is then referred to by the DBI stream. The case handled by this patch involves copying records from the .debug$F section of an object file to the "FPO" stream (or perhaps more aptly, the "Old FPO" stream) in the PDB file, which is also referred to by the DBI stream. The formats are largely similar, and the difference is mostly only visible in masm generated object files, such as some of the low-level CRT object files like memcpy. MASM doesn't appear to support writing the DEBUG_S_FRAMEDATA subsection, and instead just writes these records to the .debug$F section. Although clang-cl does not emit a .debug$F section ever, lld still needs to support it so we have good debugging for CRT functions. Differential Revision: https://reviews.llvm.org/D51958 llvm-svn: 342080 --- lld/COFF/PDB.cpp | 295 +++++++++++------- lld/test/COFF/pdb-debug-f.s | 27 ++ .../DebugInfo/PDB/Native/DbiStreamBuilder.h | 7 +- llvm/include/llvm/Object/COFF.h | 4 +- .../DebugInfo/PDB/Native/DbiStreamBuilder.cpp | 28 +- llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp | 81 ++++- llvm/tools/llvm-pdbutil/DumpOutputStyle.h | 2 + 7 files changed, 308 insertions(+), 136 deletions(-) create mode 100644 lld/test/COFF/pdb-debug-f.s diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp index 9a9ad48c26fe..0c77d3927281 100644 --- a/lld/COFF/PDB.cpp +++ b/lld/COFF/PDB.cpp @@ -82,7 +82,11 @@ struct CVIndexMap { bool IsTypeServerMap = false; }; +class DebugSHandler; + class PDBLinker { + friend DebugSHandler; + public: PDBLinker(SymbolTable *Symtab) : Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc), @@ -168,6 +172,50 @@ private: /// Cached to prevent repeated load attempts. std::map MissingTypeServerPDBs; }; + +class DebugSHandler { + PDBLinker &Linker; + + /// The object file whose .debug$S sections we're processing. + ObjFile &File; + + /// The result of merging type indices. + const CVIndexMap &IndexMap; + + /// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by + /// index from other records in the .debug$S section. All of these strings + /// need to be added to the global PDB string table, and all references to + /// these strings need to have their indices re-written to refer to the + /// global PDB string table. + DebugStringTableSubsectionRef CVStrTab; + + /// The DEBUG_S_FILECHKSMS subsection. As above, these are referred to + /// by other records in the .debug$S section and need to be merged into the + /// PDB. + DebugChecksumsSubsectionRef Checksums; + + /// The DEBUG_S_FRAMEDATA subsection(s). There can be more than one of + /// these and they need not appear in any specific order. However, they + /// contain string table references which need to be re-written, so we + /// collect them all here and re-write them after all subsections have been + /// discovered and processed. + std::vector NewFpoFrames; + + /// Pointers to raw memory that we determine have string table references + /// that need to be re-written. We first process all .debug$S subsections + /// to ensure that we can handle subsections written in any order, building + /// up this list as we go. At the end, we use the string table (which must + /// have been discovered by now else it is an error) to re-write these + /// references. + std::vector StringTableReferences; + +public: + DebugSHandler(PDBLinker &Linker, ObjFile &File, const CVIndexMap &IndexMap) + : Linker(Linker), File(File), IndexMap(IndexMap) {} + + void handleDebugS(lld::coff::SectionChunk &DebugS); + void finish(); +}; } static SectionChunk *findByName(ArrayRef Sections, @@ -785,15 +833,14 @@ static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjFile *File, cantFail(std::move(EC)); } -// Allocate memory for a .debug$S section and relocate it. +// Allocate memory for a .debug$S / .debug$F section and relocate it. static ArrayRef relocateDebugChunk(BumpPtrAllocator &Alloc, - SectionChunk *DebugChunk) { - uint8_t *Buffer = Alloc.Allocate(DebugChunk->getSize()); - assert(DebugChunk->OutputSectionOff == 0 && + SectionChunk &DebugChunk) { + uint8_t *Buffer = Alloc.Allocate(DebugChunk.getSize()); + assert(DebugChunk.OutputSectionOff == 0 && "debug sections should not be in output sections"); - DebugChunk->writeTo(Buffer); - return consumeDebugMagic(makeArrayRef(Buffer, DebugChunk->getSize()), - ".debug$S"); + DebugChunk.writeTo(Buffer); + return makeArrayRef(Buffer, DebugChunk.getSize()); } static pdb::SectionContrib createSectionContrib(const Chunk *C, uint32_t Modi) { @@ -836,6 +883,114 @@ translateStringTableIndex(uint32_t ObjIndex, return PdbStrTable.insert(*ExpectedString); } +void DebugSHandler::handleDebugS(lld::coff::SectionChunk &DebugS) { + DebugSubsectionArray Subsections; + + ArrayRef RelocatedDebugContents = consumeDebugMagic( + relocateDebugChunk(Linker.Alloc, DebugS), DebugS.getSectionName()); + + BinaryStreamReader Reader(RelocatedDebugContents, support::little); + ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size())); + + for (const DebugSubsectionRecord &SS : Subsections) { + switch (SS.kind()) { + case DebugSubsectionKind::StringTable: { + assert(!CVStrTab.valid() && + "Encountered multiple string table subsections!"); + ExitOnErr(CVStrTab.initialize(SS.getRecordData())); + break; + } + case DebugSubsectionKind::FileChecksums: + assert(!Checksums.valid() && + "Encountered multiple checksum subsections!"); + ExitOnErr(Checksums.initialize(SS.getRecordData())); + break; + case DebugSubsectionKind::Lines: + // We can add the relocated line table directly to the PDB without + // modification because the file checksum offsets will stay the same. + File.ModuleDBI->addDebugSubsection(SS); + break; + case DebugSubsectionKind::FrameData: { + // We need to re-write string table indices here, so save off all + // frame data subsections until we've processed the entire list of + // subsections so that we can be sure we have the string table. + DebugFrameDataSubsectionRef FDS; + ExitOnErr(FDS.initialize(SS.getRecordData())); + NewFpoFrames.push_back(std::move(FDS)); + break; + } + case DebugSubsectionKind::Symbols: + if (Config->DebugGHashes) { + mergeSymbolRecords(Linker.Alloc, &File, Linker.Builder.getGsiBuilder(), + IndexMap, Linker.GlobalIDTable, + StringTableReferences, SS.getRecordData()); + } else { + mergeSymbolRecords(Linker.Alloc, &File, Linker.Builder.getGsiBuilder(), + IndexMap, Linker.IDTable, StringTableReferences, + SS.getRecordData()); + } + break; + default: + // FIXME: Process the rest of the subsections. + break; + } + } +} + +void DebugSHandler::finish() { + pdb::DbiStreamBuilder &DbiBuilder = Linker.Builder.getDbiBuilder(); + + // We should have seen all debug subsections across the entire object file now + // which means that if a StringTable subsection and Checksums subsection were + // present, now is the time to handle them. + if (!CVStrTab.valid()) { + if (Checksums.valid()) + fatal(".debug$S sections with a checksums subsection must also contain a " + "string table subsection"); + + if (!StringTableReferences.empty()) + warn("No StringTable subsection was encountered, but there are string " + "table references"); + return; + } + + // Rewrite string table indices in the Fpo Data and symbol records to refer to + // the global PDB string table instead of the object file string table. + for (DebugFrameDataSubsectionRef &FDS : NewFpoFrames) { + const uint32_t *Reloc = FDS.getRelocPtr(); + for (codeview::FrameData FD : FDS) { + FD.RvaStart += *Reloc; + FD.FrameFunc = + translateStringTableIndex(FD.FrameFunc, CVStrTab, Linker.PDBStrTab); + DbiBuilder.addNewFpoData(FD); + } + } + + for (ulittle32_t *Ref : StringTableReferences) + *Ref = translateStringTableIndex(*Ref, CVStrTab, Linker.PDBStrTab); + + // Make a new file checksum table that refers to offsets in the PDB-wide + // string table. Generally the string table subsection appears after the + // checksum table, so we have to do this after looping over all the + // subsections. + auto NewChecksums = make_unique(Linker.PDBStrTab); + for (FileChecksumEntry &FC : Checksums) { + SmallString<128> FileName = + ExitOnErr(CVStrTab.getString(FC.FileNameOffset)); + if (!sys::path::is_absolute(FileName) && !Config->PDBSourcePath.empty()) { + SmallString<128> AbsoluteFileName = Config->PDBSourcePath; + sys::path::append(AbsoluteFileName, FileName); + sys::path::native(AbsoluteFileName); + sys::path::remove_dots(AbsoluteFileName, /*remove_dot_dots=*/true); + FileName = std::move(AbsoluteFileName); + } + ExitOnErr(Linker.Builder.getDbiBuilder().addModuleSourceFile( + *File.ModuleDBI, FileName)); + NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum); + } + File.ModuleDBI->addDebugSubsection(std::move(NewChecksums)); +} + void PDBLinker::addObjFile(ObjFile *File) { // Add a module descriptor for every object file. We need to put an absolute // path to the object into the PDB. If this is a plain object, we make its @@ -878,122 +1033,38 @@ void PDBLinker::addObjFile(ObjFile *File) { return; } - const CVIndexMap &IndexMap = *IndexMapResult; - ScopedTimer T(SymbolMergingTimer); - // Now do all live .debug$S sections. - DebugStringTableSubsectionRef CVStrTab; - DebugChecksumsSubsectionRef Checksums; - std::vector StringTableReferences; - std::vector FpoFrames; + DebugSHandler DSH(*this, *File, *IndexMapResult); + // Now do all live .debug$S and .debug$F sections. for (SectionChunk *DebugChunk : File->getDebugChunks()) { - if (!DebugChunk->Live || DebugChunk->getSectionName() != ".debug$S") + if (!DebugChunk->Live || DebugChunk->getSize() == 0) continue; - ArrayRef RelocatedDebugContents = - relocateDebugChunk(Alloc, DebugChunk); - if (RelocatedDebugContents.empty()) + if (DebugChunk->getSectionName() == ".debug$S") { + DSH.handleDebugS(*DebugChunk); continue; + } - DebugSubsectionArray Subsections; - BinaryStreamReader Reader(RelocatedDebugContents, support::little); - ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size())); + if (DebugChunk->getSectionName() == ".debug$F") { + ArrayRef RelocatedDebugContents = + relocateDebugChunk(Alloc, *DebugChunk); - for (const DebugSubsectionRecord &SS : Subsections) { - switch (SS.kind()) { - case DebugSubsectionKind::StringTable: { - assert(!CVStrTab.valid() && - "Encountered multiple string table subsections!"); - ExitOnErr(CVStrTab.initialize(SS.getRecordData())); - break; - } - case DebugSubsectionKind::FileChecksums: - assert(!Checksums.valid() && - "Encountered multiple checksum subsections!"); - ExitOnErr(Checksums.initialize(SS.getRecordData())); - break; - case DebugSubsectionKind::Lines: - // We can add the relocated line table directly to the PDB without - // modification because the file checksum offsets will stay the same. - File->ModuleDBI->addDebugSubsection(SS); - break; - case DebugSubsectionKind::FrameData: { - // We need to re-write string table indices here, so save off all - // frame data subsections until we've processed the entire list of - // subsections so that we can be sure we have the string table. - DebugFrameDataSubsectionRef FDS; - ExitOnErr(FDS.initialize(SS.getRecordData())); - FpoFrames.push_back(std::move(FDS)); - break; - } - case DebugSubsectionKind::Symbols: - if (Config->DebugGHashes) { - mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap, - GlobalIDTable, StringTableReferences, - SS.getRecordData()); - } else { - mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap, - IDTable, StringTableReferences, - SS.getRecordData()); - } - break; - default: - // FIXME: Process the rest of the subsections. - break; - } + FixedStreamArray FpoRecords; + BinaryStreamReader Reader(RelocatedDebugContents, support::little); + uint32_t Count = RelocatedDebugContents.size() / sizeof(object::FpoData); + ExitOnErr(Reader.readArray(FpoRecords, Count)); + + // These are already relocated and don't refer to the string table, so we + // can just copy it. + for (const object::FpoData &FD : FpoRecords) + DbiBuilder.addOldFpoData(FD); + continue; } } - // We should have seen all debug subsections across the entire object file now - // which means that if a StringTable subsection and Checksums subsection were - // present, now is the time to handle them. - if (!CVStrTab.valid()) { - if (Checksums.valid()) - fatal(".debug$S sections with a checksums subsection must also contain a " - "string table subsection"); - - if (!StringTableReferences.empty()) - warn("No StringTable subsection was encountered, but there are string " - "table references"); - return; - } - - // Rewrite string table indices in the Fpo Data and symbol records to refer to - // the global PDB string table instead of the object file string table. - for (DebugFrameDataSubsectionRef &FDS : FpoFrames) { - const uint32_t *Reloc = FDS.getRelocPtr(); - for (codeview::FrameData FD : FDS) { - FD.RvaStart += *Reloc; - FD.FrameFunc = - translateStringTableIndex(FD.FrameFunc, CVStrTab, PDBStrTab); - DbiBuilder.addFrameData(FD); - } - } - - for (ulittle32_t *Ref : StringTableReferences) - *Ref = translateStringTableIndex(*Ref, CVStrTab, PDBStrTab); - - // Make a new file checksum table that refers to offsets in the PDB-wide - // string table. Generally the string table subsection appears after the - // checksum table, so we have to do this after looping over all the - // subsections. - auto NewChecksums = make_unique(PDBStrTab); - for (FileChecksumEntry &FC : Checksums) { - SmallString<128> FileName = ExitOnErr(CVStrTab.getString(FC.FileNameOffset)); - if (!sys::path::is_absolute(FileName) && - !Config->PDBSourcePath.empty()) { - SmallString<128> AbsoluteFileName = Config->PDBSourcePath; - sys::path::append(AbsoluteFileName, FileName); - sys::path::native(AbsoluteFileName); - sys::path::remove_dots(AbsoluteFileName, /*remove_dot_dots=*/true); - FileName = std::move(AbsoluteFileName); - } - ExitOnErr(Builder.getDbiBuilder().addModuleSourceFile(*File->ModuleDBI, - FileName)); - NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum); - } - File->ModuleDBI->addDebugSubsection(std::move(NewChecksums)); + // Do any post-processing now that all .debug$S sections have been processed. + DSH.finish(); } static PublicSym32 createPublic(Defined *Def) { diff --git a/lld/test/COFF/pdb-debug-f.s b/lld/test/COFF/pdb-debug-f.s new file mode 100644 index 000000000000..ffd9e3e54160 --- /dev/null +++ b/lld/test/COFF/pdb-debug-f.s @@ -0,0 +1,27 @@ +# RUN: llvm-mc -triple=i386-pc-win32 -filetype=obj -o %t.obj %s +# RUN: lld-link /subsystem:console /debug /nodefaultlib /entry:foo /out:%t.exe /pdb:%t.pdb %t.obj +# RUN: llvm-pdbutil dump -fpo %t.pdb | FileCheck %s + +# CHECK: Old FPO Data +# CHECK-NEXT: ============================================================ +# CHECK-NEXT: RVA | Code | Locals | Params | Prolog | Saved Regs | Use BP | Has SEH | Frame Type +# CHECK-NEXT: 00001002 | 1 | 2 | 3 | 4 | 0 | false | false | FPO + +.text +_foo: +ret + +.global _foo + +.section .debug$F,"dr" + .long _foo@IMGREL+2 + .long 1 # cbProc + .long 2 # cdwLocals; + .short 3 # cdwParams; + .short 4 # flags + # cbProlog : 8; + # cbRegs : 3; + # fHasSEH : 1; + # fUseBP : 1; + # reserved : 1; + # cbFrame : 2; \ No newline at end of file diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h b/llvm/include/llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h index 826e0c179cc8..b538de576677 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h @@ -33,6 +33,7 @@ class MSFBuilder; } namespace object { struct coff_section; +struct FpoData; } namespace pdb { class DbiStream; @@ -69,7 +70,8 @@ public: void setGlobalsStreamIndex(uint32_t Index); void setPublicsStreamIndex(uint32_t Index); void setSymbolRecordStreamIndex(uint32_t Index); - void addFrameData(const codeview::FrameData &FD); + void addNewFpoData(const codeview::FrameData &FD); + void addOldFpoData(const object::FpoData &Fpo); Expected addModuleInfo(StringRef ModuleName); Error addModuleSourceFile(DbiModuleDescriptorBuilder &Module, StringRef File); @@ -123,7 +125,8 @@ private: std::vector> ModiList; - Optional FrameData; + Optional NewFpoData; + std::vector OldFpoData; StringMap SourceFileNames; diff --git a/llvm/include/llvm/Object/COFF.h b/llvm/include/llvm/Object/COFF.h index 222ade2bdf92..e39a9b2e31c6 100644 --- a/llvm/include/llvm/Object/COFF.h +++ b/llvm/include/llvm/Object/COFF.h @@ -594,6 +594,8 @@ enum class coff_guard_flags : uint32_t { FidTableHasFlags = 0x10000000, // Indicates that fid tables are 5 bytes }; +enum class frame_type : uint16_t { Fpo = 0, Trap = 1, Tss = 2, NonFpo = 3 }; + struct coff_load_config_code_integrity { support::ulittle16_t Flags; support::ulittle16_t Catalog; @@ -1228,7 +1230,7 @@ struct FpoData { bool useBP() const { return (Attributes >> 10) & 1; } // cbFrame: frame pointer - int getFP() const { return Attributes >> 14; } + frame_type getFP() const { return static_cast(Attributes >> 14); } }; } // end namespace object diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp index 9b045f916dbf..094216ea800a 100644 --- a/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp @@ -75,11 +75,15 @@ void DbiStreamBuilder::setPublicsStreamIndex(uint32_t Index) { PublicsStreamIndex = Index; } -void DbiStreamBuilder::addFrameData(const codeview::FrameData &FD) { - if (!FrameData.hasValue()) - FrameData.emplace(false); +void DbiStreamBuilder::addNewFpoData(const codeview::FrameData &FD) { + if (!NewFpoData.hasValue()) + NewFpoData.emplace(false); - FrameData->addFrameData(FD); + NewFpoData->addFrameData(FD); +} + +void DbiStreamBuilder::addOldFpoData(const object::FpoData &FD) { + OldFpoData.push_back(FD); } Error DbiStreamBuilder::addDbgStream(pdb::DbgHeaderType Type, @@ -286,13 +290,23 @@ Error DbiStreamBuilder::finalize() { } Error DbiStreamBuilder::finalizeMsfLayout() { - if (FrameData.hasValue()) { + if (NewFpoData.hasValue()) { DbgStreams[(int)DbgHeaderType::NewFPO].emplace(); DbgStreams[(int)DbgHeaderType::NewFPO]->Size = - FrameData->calculateSerializedSize(); + NewFpoData->calculateSerializedSize(); DbgStreams[(int)DbgHeaderType::NewFPO]->WriteFn = [this](BinaryStreamWriter &Writer) { - return FrameData->commit(Writer); + return NewFpoData->commit(Writer); + }; + } + + if (!OldFpoData.empty()) { + DbgStreams[(int)DbgHeaderType::FPO].emplace(); + DbgStreams[(int)DbgHeaderType::FPO]->Size = + sizeof(object::FpoData) * OldFpoData.size(); + DbgStreams[(int)DbgHeaderType::FPO]->WriteFn = + [this](BinaryStreamWriter &Writer) { + return Writer.writeArray(makeArrayRef(OldFpoData)); }; } diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp index 60f79f05e01a..9869b3ae4d23 100644 --- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -991,22 +991,56 @@ Error DumpOutputStyle::dumpXme() { return Error::success(); } -Error DumpOutputStyle::dumpFpo() { +std::string formatFrameType(object::frame_type FT) { + switch (FT) { + case object::frame_type::Fpo: + return "FPO"; + case object::frame_type::NonFpo: + return "Non-FPO"; + case object::frame_type::Trap: + return "Trap"; + case object::frame_type::Tss: + return "TSS"; + } + return ""; +} + +Error DumpOutputStyle::dumpOldFpo(PDBFile &File) { + printHeader(P, "Old FPO Data"); + + ExitOnError Err("Error dumping old fpo data:"); + auto &Dbi = Err(File.getPDBDbiStream()); + + uint32_t Index = Dbi.getDebugStreamIndex(DbgHeaderType::FPO); + if (Index == kInvalidStreamIndex) { + printStreamNotPresent("FPO"); + return Error::success(); + } + + std::unique_ptr OldFpo = File.createIndexedStream(Index); + BinaryStreamReader Reader(*OldFpo); + FixedStreamArray Records; + Err(Reader.readArray(Records, + Reader.bytesRemaining() / sizeof(object::FpoData))); + + P.printLine(" RVA | Code | Locals | Params | Prolog | Saved Regs | Use " + "BP | Has SEH | Frame Type"); + + for (const object::FpoData &FD : Records) { + P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,6} | {5,10} | {6,6} | " + "{7,7} | {8,9}", + uint32_t(FD.Offset), uint32_t(FD.Size), uint32_t(FD.NumLocals), + uint32_t(FD.NumParams), FD.getPrologSize(), + FD.getNumSavedRegs(), FD.useBP(), FD.hasSEH(), + formatFrameType(FD.getFP())); + } + return Error::success(); +} + +Error DumpOutputStyle::dumpNewFpo(PDBFile &File) { printHeader(P, "New FPO Data"); - if (!File.isPdb()) { - printStreamNotValidForObj(); - return Error::success(); - } - - PDBFile &File = getPdb(); - if (!File.hasPDBDbiStream()) { - printStreamNotPresent("DBI"); - return Error::success(); - } - - ExitOnError Err("Error dumping fpo data:"); - + ExitOnError Err("Error dumping new fpo data:"); auto &Dbi = Err(File.getPDBDbiStream()); uint32_t Index = Dbi.getDebugStreamIndex(DbgHeaderType::NewFPO); @@ -1043,6 +1077,25 @@ Error DumpOutputStyle::dumpFpo() { return Error::success(); } +Error DumpOutputStyle::dumpFpo() { + if (!File.isPdb()) { + printStreamNotValidForObj(); + return Error::success(); + } + + PDBFile &File = getPdb(); + if (!File.hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + if (auto EC = dumpOldFpo(File)) + return EC; + if (auto EC = dumpNewFpo(File)) + return EC; + return Error::success(); +} + Error DumpOutputStyle::dumpStringTableFromPdb() { AutoIndent Indent(P); auto IS = getPdb().getStringTable(); diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.h b/llvm/tools/llvm-pdbutil/DumpOutputStyle.h index 946a30539a8e..9b3a85587bde 100644 --- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.h +++ b/llvm/tools/llvm-pdbutil/DumpOutputStyle.h @@ -86,6 +86,8 @@ private: Error dumpXmi(); Error dumpXme(); Error dumpFpo(); + Error dumpOldFpo(PDBFile &File); + Error dumpNewFpo(PDBFile &File); Error dumpTpiStream(uint32_t StreamIdx); Error dumpTypesFromObjectFile(); Error dumpModules();