Index: lld/COFF/PDB.cpp =================================================================== --- lld/COFF/PDB.cpp +++ lld/COFF/PDB.cpp @@ -53,6 +53,55 @@ static ExitOnError ExitOnErr; +namespace { +class PDBLinker { +public: + PDBLinker(SymbolTable *Symtab) + : Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc), + IDTable(Alloc) {} + + /// Emit the basic PDB structure: initial streams, headers, etc. + void initialize(const llvm::codeview::DebugInfo *DI); + + /// Link CodeView from each object file in the symbol table into the PDB. + void addObjectsToPDB(); + + /// Link CodeView from a single object file into the PDB. + void addObjectFile(ObjectFile *File); + + /// Merge the type information from the .debug$T section in the given object + /// file. Produce a mapping from object file type indices to type or + /// item indices in the final PDB. + void mergeDebugT(ObjectFile *File, SmallVectorImpl &TypeIndexMap); + + /// Add the section map and section contributions to the PDB. + void addSections(ArrayRef SectionTable); + +private: + BumpPtrAllocator Alloc; + + SymbolTable *Symtab; + + pdb::PDBFileBuilder Builder; + + /// Type records that will go into the PDB TPI stream. + TypeTableBuilder TypeTable; + + /// Item records that will go into the PDB IPI stream. + TypeTableBuilder IDTable; + + /// PDBs use a single global string table for filenames in the file checksum + /// table. + DebugStringTableSubsection PDBStrTab; + + // Follow type servers. If the same type server is encountered more than once + // for this instance of `PDBTypeServerHandler` (for example if many object + // files reference the same TypeServer), the types from the TypeServer will + // only be visited once. + pdb::PDBTypeServerHandler TSHandler; +}; +} + // Returns a list of all SectionChunks. static void addSectionContribs(SymbolTable *Symtab, pdb::DbiStreamBuilder &DbiBuilder) { for (Chunk *C : Symtab->getChunks()) @@ -96,11 +145,8 @@ }); } -static void mergeDebugT(ObjectFile *File, - TypeTableBuilder &IDTable, - TypeTableBuilder &TypeTable, - SmallVectorImpl &TypeIndexMap, - pdb::PDBTypeServerHandler &Handler) { +void PDBLinker::mergeDebugT(ObjectFile *File, + SmallVectorImpl &TypeIndexMap) { ArrayRef Data = getDebugSection(File, ".debug$T"); if (Data.empty()) return; @@ -109,7 +155,7 @@ // archive, look next to the archive path. StringRef LocalPath = !File->ParentName.empty() ? File->ParentName : File->getName(); - Handler.addSearchPath(sys::path::parent_path(LocalPath)); + TSHandler.addSearchPath(sys::path::parent_path(LocalPath)); BinaryByteStream Stream(Data, support::little); CVTypeArray Types; @@ -117,7 +163,7 @@ if (auto EC = Reader.readArray(Types, Reader.getLength())) fatal(EC, "Reader::readArray failed"); if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable, TypeIndexMap, - &Handler, Types)) + &TSHandler, Types)) fatal(Err, "codeview::mergeTypeStreams failed"); } @@ -294,110 +340,105 @@ ".debug$S"); } -// Add all object files to the PDB. Merge .debug$T sections into IpiData and -// TpiData. -static void addObjectsToPDB(BumpPtrAllocator &Alloc, SymbolTable *Symtab, - pdb::PDBFileBuilder &Builder, - TypeTableBuilder &TypeTable, - TypeTableBuilder &IDTable) { - // Follow type servers. If the same type server is encountered more than - // once for this instance of `PDBTypeServerHandler` (for example if many - // object files reference the same TypeServer), the types from the - // TypeServer will only be visited once. - pdb::PDBTypeServerHandler Handler; - - // PDBs use a single global string table for filenames in the file checksum - // table. - auto PDBStrTab = std::make_shared(); - - // Visit all .debug$T sections to add them to Builder. - for (ObjectFile *File : Symtab->ObjectFiles) { - // 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 - // path absolute. If it's an object in an archive, we make the archive path - // absolute. - bool InArchive = !File->ParentName.empty(); - SmallString<128> Path = InArchive ? File->ParentName : File->getName(); - sys::fs::make_absolute(Path); - sys::path::native(Path, llvm::sys::path::Style::windows); - StringRef Name = InArchive ? File->getName() : StringRef(Path); - - File->ModuleDBI = &ExitOnErr(Builder.getDbiBuilder().addModuleInfo(Name)); - File->ModuleDBI->setObjFileName(Path); - - // Before we can process symbol substreams from .debug$S, we need to process - // type information, file checksums, and the string table. Add type info to - // the PDB first, so that we can get the map from object file type and item - // indices to PDB type and item indices. - SmallVector TypeIndexMap; - mergeDebugT(File, IDTable, TypeTable, TypeIndexMap, Handler); - - // Now do all line info. - for (SectionChunk *DebugChunk : File->getDebugChunks()) { - if (!DebugChunk->isLive() || DebugChunk->getSectionName() != ".debug$S") - continue; +void PDBLinker::addObjectFile(ObjectFile *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 + // path absolute. If it's an object in an archive, we make the archive path + // absolute. + bool InArchive = !File->ParentName.empty(); + SmallString<128> Path = InArchive ? File->ParentName : File->getName(); + sys::fs::make_absolute(Path); + sys::path::native(Path, llvm::sys::path::Style::windows); + StringRef Name = InArchive ? File->getName() : StringRef(Path); + + File->ModuleDBI = &ExitOnErr(Builder.getDbiBuilder().addModuleInfo(Name)); + File->ModuleDBI->setObjFileName(Path); + + // Before we can process symbol substreams from .debug$S, we need to process + // type information, file checksums, and the string table. Add type info to + // the PDB first, so that we can get the map from object file type and item + // indices to PDB type and item indices. + SmallVector TypeIndexMap; + mergeDebugT(File, TypeIndexMap); + + // Now do all live .debug$S sections. + for (SectionChunk *DebugChunk : File->getDebugChunks()) { + if (!DebugChunk->isLive() || DebugChunk->getSectionName() != ".debug$S") + continue; - ArrayRef RelocatedDebugContents = - relocateDebugChunk(Alloc, DebugChunk); - if (RelocatedDebugContents.empty()) - continue; + ArrayRef RelocatedDebugContents = + relocateDebugChunk(Alloc, DebugChunk); + if (RelocatedDebugContents.empty()) + continue; - DebugSubsectionArray Subsections; - BinaryStreamReader Reader(RelocatedDebugContents, support::little); - ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size())); - - DebugStringTableSubsectionRef CVStrTab; - DebugChecksumsSubsectionRef Checksums; - for (const DebugSubsectionRecord &SS : Subsections) { - switch (SS.kind()) { - case DebugSubsectionKind::StringTable: - ExitOnErr(CVStrTab.initialize(SS.getRecordData())); - break; - case DebugSubsectionKind::FileChecksums: - 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::Symbols: - mergeSymbolRecords(Alloc, File, TypeIndexMap, SS.getRecordData()); - break; - default: - // FIXME: Process the rest of the subsections. - break; - } + DebugSubsectionArray Subsections; + BinaryStreamReader Reader(RelocatedDebugContents, support::little); + ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size())); + + DebugStringTableSubsectionRef CVStrTab; + DebugChecksumsSubsectionRef Checksums; + for (const DebugSubsectionRecord &SS : Subsections) { + switch (SS.kind()) { + case DebugSubsectionKind::StringTable: + ExitOnErr(CVStrTab.initialize(SS.getRecordData())); + break; + case DebugSubsectionKind::FileChecksums: + 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::Symbols: + mergeSymbolRecords(Alloc, File, TypeIndexMap, SS.getRecordData()); + break; + default: + // FIXME: Process the rest of the subsections. + break; } + } - if (Checksums.valid()) { - // 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. - if (!CVStrTab.valid()) - fatal(".debug$S sections must have both a string table subsection " - "and a checksum subsection table or neither"); - auto NewChecksums = - make_unique(*PDBStrTab); - for (FileChecksumEntry &FC : Checksums) { - StringRef FileName = ExitOnErr(CVStrTab.getString(FC.FileNameOffset)); - ExitOnErr(Builder.getDbiBuilder().addModuleSourceFile( - *File->ModuleDBI, FileName)); - NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum); - } - File->ModuleDBI->addDebugSubsection(std::move(NewChecksums)); + if (Checksums.valid()) { + // 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. + if (!CVStrTab.valid()) + fatal(".debug$S sections must have both a string table subsection " + "and a checksum subsection table or neither"); + auto NewChecksums = make_unique(PDBStrTab); + for (FileChecksumEntry &FC : Checksums) { + StringRef FileName = ExitOnErr(CVStrTab.getString(FC.FileNameOffset)); + ExitOnErr(Builder.getDbiBuilder().addModuleSourceFile(*File->ModuleDBI, + FileName)); + NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum); } + File->ModuleDBI->addDebugSubsection(std::move(NewChecksums)); } } +} - Builder.getStringTableBuilder().setStrings(*PDBStrTab); +// Add all object files to the PDB. Merge .debug$T sections into IpiData and +// TpiData. +void PDBLinker::addObjectsToPDB() { + for (ObjectFile *File : Symtab->ObjectFiles) + addObjectFile(File); + + Builder.getStringTableBuilder().setStrings(PDBStrTab); // Construct TPI stream contents. addTypeInfo(Builder.getTpiBuilder(), TypeTable); // Construct IPI stream contents. addTypeInfo(Builder.getIpiBuilder(), IDTable); + + // Add public and symbol records stream. + + // For now we don't actually write any thing useful to the publics stream, but + // the act of "getting" it also creates it lazily so that we write an empty + // stream. + (void)Builder.getPublicsBuilder(); } static void addLinkerModuleSymbols(StringRef Path, @@ -447,8 +488,13 @@ // Creates a PDB file. void coff::createPDB(SymbolTable *Symtab, ArrayRef SectionTable, const llvm::codeview::DebugInfo *DI) { - BumpPtrAllocator Alloc; - pdb::PDBFileBuilder Builder(Alloc); + PDBLinker PDB(Symtab); + PDB.initialize(DI); + PDB.addObjectsToPDB(); + PDB.addSections(SectionTable); +} + +void PDBLinker::initialize(const llvm::codeview::DebugInfo *DI) { ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize // Create streams in MSF for predefined streams, namely @@ -460,11 +506,6 @@ auto &InfoBuilder = Builder.getInfoBuilder(); InfoBuilder.setAge(DI ? DI->PDB70.Age : 0); - llvm::SmallString<128> NativePath(Config->PDBPath.begin(), - Config->PDBPath.end()); - llvm::sys::fs::make_absolute(NativePath); - llvm::sys::path::native(NativePath, llvm::sys::path::Style::windows); - pdb::PDB_UniqueId uuid{}; if (DI) memcpy(&uuid, &DI->PDB70.Signature, sizeof(uuid)); @@ -476,22 +517,11 @@ pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); DbiBuilder.setVersionHeader(pdb::PdbDbiV70); ExitOnErr(DbiBuilder.addDbgStream(pdb::DbgHeaderType::NewFPO, {})); +} - // It's not entirely clear what this is, but the * Linker * module uses it. - uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath); - - TypeTableBuilder TypeTable(BAlloc); - TypeTableBuilder IDTable(BAlloc); - addObjectsToPDB(Alloc, Symtab, Builder, TypeTable, IDTable); - - // Add public and symbol records stream. - - // For now we don't actually write any thing useful to the publics stream, but - // the act of "getting" it also creates it lazily so that we write an empty - // stream. - (void)Builder.getPublicsBuilder(); - +void PDBLinker::addSections(ArrayRef SectionTable) { // Add Section Contributions. + pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); addSectionContribs(Symtab, DbiBuilder); // Add Section Map stream. @@ -502,6 +532,12 @@ pdb::DbiStreamBuilder::createSectionMap(Sections); DbiBuilder.setSectionMap(SectionMap); + // It's not entirely clear what this is, but the * Linker * module uses it. + llvm::SmallString<128> NativePath(Config->PDBPath.begin(), + Config->PDBPath.end()); + llvm::sys::fs::make_absolute(NativePath); + llvm::sys::path::native(NativePath, llvm::sys::path::Style::windows); + uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath); auto &LinkerModule = ExitOnErr(DbiBuilder.addModuleInfo("* Linker *")); LinkerModule.setPdbFilePathNI(PdbFilePathNI); addLinkerModuleSymbols(NativePath, LinkerModule, Alloc);