[PDB] Put LLD's PDB writing code in a class NFC

Summary:
We've accumulated about five or so data structures that are widely
referenced:
- PDBBuilder
- Type table
- Id table
- PDB string table
- Type server handler

I'm about to rewrite type server handling, and I need a new class in LLD
where I can put its state. By creating a new PDBLinker class, I hope to
put it there next.

Reviewers: ruiu

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D35392

llvm-svn: 307979
This commit is contained in:
Reid Kleckner 2017-07-14 00:14:58 +00:00
parent 23e4df6a59
commit 0faf6d7beb
1 changed files with 166 additions and 121 deletions

View File

@ -53,8 +53,65 @@ using llvm::object::coff_section;
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<TypeIndex> &TypeIndexMap);
/// Add the section map and section contributions to the PDB.
void addSections(ArrayRef<uint8_t> SectionTable);
/// Write the PDB to disk.
void commit();
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;
llvm::SmallString<128> NativePath;
std::vector<pdb::SecMapEntry> SectionMap;
};
}
// Returns a list of all SectionChunks.
static void addSectionContribs(SymbolTable *Symtab, pdb::DbiStreamBuilder &DbiBuilder) {
static void addSectionContribs(SymbolTable *Symtab,
pdb::DbiStreamBuilder &DbiBuilder) {
for (Chunk *C : Symtab->getChunks())
if (auto *SC = dyn_cast<SectionChunk>(C))
DbiBuilder.addSectionContrib(SC->File->ModuleDBI, SC->Header);
@ -96,11 +153,8 @@ static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
});
}
static void mergeDebugT(ObjectFile *File,
TypeTableBuilder &IDTable,
TypeTableBuilder &TypeTable,
SmallVectorImpl<TypeIndex> &TypeIndexMap,
pdb::PDBTypeServerHandler &Handler) {
void PDBLinker::mergeDebugT(ObjectFile *File,
SmallVectorImpl<TypeIndex> &TypeIndexMap) {
ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
if (Data.empty())
return;
@ -109,7 +163,7 @@ static void mergeDebugT(ObjectFile *File,
// 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 +171,7 @@ static void mergeDebugT(ObjectFile *File,
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 +348,105 @@ static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc,
".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;
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, sys::path::Style::windows);
StringRef Name = InArchive ? File->getName() : StringRef(Path);
// PDBs use a single global string table for filenames in the file checksum
// table.
auto PDBStrTab = std::make_shared<DebugStringTableSubsection>();
File->ModuleDBI = &ExitOnErr(Builder.getDbiBuilder().addModuleInfo(Name));
File->ModuleDBI->setObjFileName(Path);
// 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);
// 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<TypeIndex, 128> TypeIndexMap;
mergeDebugT(File, TypeIndexMap);
File->ModuleDBI = &ExitOnErr(Builder.getDbiBuilder().addModuleInfo(Name));
File->ModuleDBI->setObjFileName(Path);
// Now do all live .debug$S sections.
for (SectionChunk *DebugChunk : File->getDebugChunks()) {
if (!DebugChunk->isLive() || DebugChunk->getSectionName() != ".debug$S")
continue;
// 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<TypeIndex, 128> TypeIndexMap;
mergeDebugT(File, IDTable, TypeTable, TypeIndexMap, Handler);
ArrayRef<uint8_t> RelocatedDebugContents =
relocateDebugChunk(Alloc, DebugChunk);
if (RelocatedDebugContents.empty())
continue;
// Now do all line info.
for (SectionChunk *DebugChunk : File->getDebugChunks()) {
if (!DebugChunk->isLive() || DebugChunk->getSectionName() != ".debug$S")
continue;
DebugSubsectionArray Subsections;
BinaryStreamReader Reader(RelocatedDebugContents, support::little);
ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size()));
ArrayRef<uint8_t> 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;
}
}
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<DebugChecksumsSubsection>(*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));
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;
}
}
}
Builder.getStringTableBuilder().setStrings(*PDBStrTab);
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<DebugChecksumsSubsection>(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));
}
}
}
// 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,
@ -428,7 +477,7 @@ static void addLinkerModuleSymbols(StringRef Path,
std::string ArgStr = llvm::join(Args, " ");
EBS.Fields.push_back("cwd");
SmallString<64> cwd;
llvm::sys::fs::current_path(cwd);
sys::fs::current_path(cwd);
EBS.Fields.push_back(cwd);
EBS.Fields.push_back("exe");
EBS.Fields.push_back(Config->Argv[0]);
@ -447,8 +496,14 @@ static void addLinkerModuleSymbols(StringRef Path,
// Creates a PDB file.
void coff::createPDB(SymbolTable *Symtab, ArrayRef<uint8_t> SectionTable,
const llvm::codeview::DebugInfo *DI) {
BumpPtrAllocator Alloc;
pdb::PDBFileBuilder Builder(Alloc);
PDBLinker PDB(Symtab);
PDB.initialize(DI);
PDB.addObjectsToPDB();
PDB.addSections(SectionTable);
PDB.commit();
}
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 +515,6 @@ void coff::createPDB(SymbolTable *Symtab, ArrayRef<uint8_t> SectionTable,
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,32 +526,25 @@ void coff::createPDB(SymbolTable *Symtab, ArrayRef<uint8_t> SectionTable,
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<uint8_t> SectionTable) {
// Add Section Contributions.
pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
addSectionContribs(Symtab, DbiBuilder);
// Add Section Map stream.
ArrayRef<object::coff_section> Sections = {
(const object::coff_section *)SectionTable.data(),
SectionTable.size() / sizeof(object::coff_section)};
std::vector<pdb::SecMapEntry> SectionMap =
pdb::DbiStreamBuilder::createSectionMap(Sections);
SectionMap = pdb::DbiStreamBuilder::createSectionMap(Sections);
DbiBuilder.setSectionMap(SectionMap);
// It's not entirely clear what this is, but the * Linker * module uses it.
NativePath = Config->PDBPath;
sys::fs::make_absolute(NativePath);
sys::path::native(NativePath, sys::path::Style::windows);
uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath);
auto &LinkerModule = ExitOnErr(DbiBuilder.addModuleInfo("* Linker *"));
LinkerModule.setPdbFilePathNI(PdbFilePathNI);
addLinkerModuleSymbols(NativePath, LinkerModule, Alloc);
@ -509,7 +552,9 @@ void coff::createPDB(SymbolTable *Symtab, ArrayRef<uint8_t> SectionTable,
// Add COFF section header stream.
ExitOnErr(
DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable));
}
void PDBLinker::commit() {
// Write to a file.
ExitOnErr(Builder.commit(Config->PDBPath));
}