aboutsummaryrefslogtreecommitdiff
path: root/COFF/PDB.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'COFF/PDB.cpp')
-rw-r--r--COFF/PDB.cpp553
1 files changed, 439 insertions, 114 deletions
diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp
index 89462da93454..91a9a01db569 100644
--- a/COFF/PDB.cpp
+++ b/COFF/PDB.cpp
@@ -10,37 +10,44 @@
#include "PDB.h"
#include "Chunks.h"
#include "Config.h"
-#include "Error.h"
+#include "Driver.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "Writer.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/DebugInfo/CodeView/CVDebugRecord.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
+#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
+#include "llvm/DebugInfo/CodeView/RecordName.h"
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
-#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/MSF/MSFCommon.h"
#include "llvm/DebugInfo/PDB/GenericError.h"
#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h"
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
#include "llvm/DebugInfo/PDB/PDB.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/Endian.h"
-#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/JamCRC.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"
#include <memory>
@@ -67,16 +74,16 @@ class PDBLinker {
public:
PDBLinker(SymbolTable *Symtab)
: Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc),
- IDTable(Alloc) {}
+ IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) {}
/// Emit the basic PDB structure: initial streams, headers, etc.
- void initialize(const llvm::codeview::DebugInfo *DI);
+ void initialize(const llvm::codeview::DebugInfo &BuildId);
/// 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);
+ void addObjFile(ObjFile *File);
/// Produce a mapping from the type and item indices used in the object
/// file to those in the destination PDB.
@@ -89,13 +96,17 @@ public:
/// If the object does not use a type server PDB (compiled with /Z7), we merge
/// all the type and item records from the .debug$S stream and fill in the
/// caller-provided ObjectIndexMap.
- const CVIndexMap &mergeDebugT(ObjectFile *File, CVIndexMap &ObjectIndexMap);
+ const CVIndexMap &mergeDebugT(ObjFile *File, CVIndexMap &ObjectIndexMap);
- const CVIndexMap &maybeMergeTypeServerPDB(ObjectFile *File,
+ const CVIndexMap &maybeMergeTypeServerPDB(ObjFile *File,
TypeServer2Record &TS);
/// Add the section map and section contributions to the PDB.
- void addSections(ArrayRef<uint8_t> SectionTable);
+ void addSections(ArrayRef<OutputSection *> OutputSections,
+ ArrayRef<uint8_t> SectionTable);
+
+ void addSectionContrib(pdb::DbiModuleDescriptorBuilder &LinkerModule,
+ OutputSection *OS, Chunk *C);
/// Write the PDB to disk.
void commit();
@@ -108,10 +119,16 @@ private:
pdb::PDBFileBuilder Builder;
/// Type records that will go into the PDB TPI stream.
- TypeTableBuilder TypeTable;
+ MergingTypeTableBuilder TypeTable;
/// Item records that will go into the PDB IPI stream.
- TypeTableBuilder IDTable;
+ MergingTypeTableBuilder IDTable;
+
+ /// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH)
+ GlobalTypeTableBuilder GlobalTypeTable;
+
+ /// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH)
+ GlobalTypeTableBuilder GlobalIDTable;
/// PDBs use a single global string table for filenames in the file checksum
/// table.
@@ -126,15 +143,7 @@ private:
};
}
-// Returns a list of all SectionChunks.
-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);
-}
-
-static SectionChunk *findByName(std::vector<SectionChunk *> &Sections,
+static SectionChunk *findByName(ArrayRef<SectionChunk *> Sections,
StringRef Name) {
for (SectionChunk *C : Sections)
if (C->getSectionName() == Name)
@@ -152,21 +161,58 @@ static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> Data,
return Data.slice(4);
}
-static ArrayRef<uint8_t> getDebugSection(ObjectFile *File, StringRef SecName) {
+static ArrayRef<uint8_t> getDebugSection(ObjFile *File, StringRef SecName) {
if (SectionChunk *Sec = findByName(File->getDebugChunks(), SecName))
return consumeDebugMagic(Sec->getContents(), SecName);
return {};
}
+// A COFF .debug$H section is currently a clang extension. This function checks
+// if a .debug$H section is in a format that we expect / understand, so that we
+// can ignore any sections which are coincidentally also named .debug$H but do
+// not contain a format we recognize.
+static bool canUseDebugH(ArrayRef<uint8_t> DebugH) {
+ if (DebugH.size() < sizeof(object::debug_h_header))
+ return false;
+ auto *Header =
+ reinterpret_cast<const object::debug_h_header *>(DebugH.data());
+ DebugH = DebugH.drop_front(sizeof(object::debug_h_header));
+ return Header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
+ Header->Version == 0 &&
+ Header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1) &&
+ (DebugH.size() % 20 == 0);
+}
+
+static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *File) {
+ SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$H");
+ if (!Sec)
+ return llvm::None;
+ ArrayRef<uint8_t> Contents = Sec->getContents();
+ if (!canUseDebugH(Contents))
+ return None;
+ return Contents;
+}
+
+static ArrayRef<GloballyHashedType>
+getHashesFromDebugH(ArrayRef<uint8_t> DebugH) {
+ assert(canUseDebugH(DebugH));
+
+ DebugH = DebugH.drop_front(sizeof(object::debug_h_header));
+ uint32_t Count = DebugH.size() / sizeof(GloballyHashedType);
+ return {reinterpret_cast<const GloballyHashedType *>(DebugH.data()), Count};
+}
+
static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
- TypeTableBuilder &TypeTable) {
+ TypeCollection &TypeTable) {
// Start the TPI or IPI stream header.
TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
- // Flatten the in memory type table.
- TypeTable.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Rec) {
- // FIXME: Hash types.
- TpiBuilder.addTypeRecord(Rec, None);
+ // Flatten the in memory type table and hash each type.
+ TypeTable.ForEachRecord([&](TypeIndex TI, const CVType &Type) {
+ auto Hash = pdb::hashTypeRecord(Type);
+ if (auto E = Hash.takeError())
+ fatal("type hashing error");
+ TpiBuilder.addTypeRecord(Type.RecordData, *Hash);
});
}
@@ -180,11 +226,11 @@ maybeReadTypeServerRecord(CVTypeArray &Types) {
return None;
TypeServer2Record TS;
if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(Type), TS))
- fatal(EC, "error reading type server record");
+ fatal("error reading type server record: " + toString(std::move(EC)));
return std::move(TS);
}
-const CVIndexMap &PDBLinker::mergeDebugT(ObjectFile *File,
+const CVIndexMap &PDBLinker::mergeDebugT(ObjFile *File,
CVIndexMap &ObjectIndexMap) {
ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
if (Data.empty())
@@ -194,7 +240,7 @@ const CVIndexMap &PDBLinker::mergeDebugT(ObjectFile *File,
CVTypeArray Types;
BinaryStreamReader Reader(Stream);
if (auto EC = Reader.readArray(Types, Reader.getLength()))
- fatal(EC, "Reader::readArray failed");
+ fatal("Reader::readArray failed: " + toString(std::move(EC)));
// Look through type servers. If we've already seen this type server, don't
// merge any type information.
@@ -203,17 +249,41 @@ const CVIndexMap &PDBLinker::mergeDebugT(ObjectFile *File,
// This is a /Z7 object. Fill in the temporary, caller-provided
// ObjectIndexMap.
- if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable,
- ObjectIndexMap.TPIMap, Types))
- fatal(Err, "codeview::mergeTypeAndIdRecords failed");
+ if (Config->DebugGHashes) {
+ ArrayRef<GloballyHashedType> Hashes;
+ std::vector<GloballyHashedType> OwnedHashes;
+ if (Optional<ArrayRef<uint8_t>> DebugH = getDebugH(File))
+ Hashes = getHashesFromDebugH(*DebugH);
+ else {
+ OwnedHashes = GloballyHashedType::hashTypes(Types);
+ Hashes = OwnedHashes;
+ }
+
+ if (auto Err = mergeTypeAndIdRecords(GlobalIDTable, GlobalTypeTable,
+ ObjectIndexMap.TPIMap, Types, Hashes))
+ fatal("codeview::mergeTypeAndIdRecords failed: " +
+ toString(std::move(Err)));
+ } else {
+ if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable,
+ ObjectIndexMap.TPIMap, Types))
+ fatal("codeview::mergeTypeAndIdRecords failed: " +
+ toString(std::move(Err)));
+ }
return ObjectIndexMap;
}
static Expected<std::unique_ptr<pdb::NativeSession>>
tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile(
+ TSPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false);
+ if (!MBOrErr)
+ return errorCodeToError(MBOrErr.getError());
+
std::unique_ptr<pdb::IPDBSession> ThisSession;
- if (auto EC =
- pdb::loadDataForPDB(pdb::PDB_ReaderType::Native, TSPath, ThisSession))
+ if (auto EC = pdb::NativeSession::createFromPdb(
+ MemoryBuffer::getMemBuffer(Driver->takeBuffer(std::move(*MBOrErr)),
+ /*RequiresNullTerminator=*/false),
+ ThisSession))
return std::move(EC);
std::unique_ptr<pdb::NativeSession> NS(
@@ -234,7 +304,7 @@ tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) {
return std::move(NS);
}
-const CVIndexMap &PDBLinker::maybeMergeTypeServerPDB(ObjectFile *File,
+const CVIndexMap &PDBLinker::maybeMergeTypeServerPDB(ObjFile *File,
TypeServer2Record &TS) {
// First, check if we already loaded a PDB with this GUID. Return the type
// index mapping if we have it.
@@ -260,23 +330,46 @@ const CVIndexMap &PDBLinker::maybeMergeTypeServerPDB(ObjectFile *File,
ExpectedSession = tryToLoadPDB(TS.getGuid(), Path);
}
if (auto E = ExpectedSession.takeError())
- fatal(E, "Type server PDB was not found");
+ fatal("Type server PDB was not found: " + toString(std::move(E)));
- // Merge TPI first, because the IPI stream will reference type indices.
auto ExpectedTpi = (*ExpectedSession)->getPDBFile().getPDBTpiStream();
if (auto E = ExpectedTpi.takeError())
- fatal(E, "Type server does not have TPI stream");
- if (auto Err = mergeTypeRecords(TypeTable, IndexMap.TPIMap,
- ExpectedTpi->typeArray()))
- fatal(Err, "codeview::mergeTypeRecords failed");
-
- // Merge IPI.
+ fatal("Type server does not have TPI stream: " + toString(std::move(E)));
auto ExpectedIpi = (*ExpectedSession)->getPDBFile().getPDBIpiStream();
if (auto E = ExpectedIpi.takeError())
- fatal(E, "Type server does not have TPI stream");
- if (auto Err = mergeIdRecords(IDTable, IndexMap.TPIMap, IndexMap.IPIMap,
- ExpectedIpi->typeArray()))
- fatal(Err, "codeview::mergeIdRecords failed");
+ fatal("Type server does not have TPI stream: " + toString(std::move(E)));
+
+ if (Config->DebugGHashes) {
+ // PDBs do not actually store global hashes, so when merging a type server
+ // PDB we have to synthesize global hashes. To do this, we first synthesize
+ // global hashes for the TPI stream, since it is independent, then we
+ // synthesize hashes for the IPI stream, using the hashes for the TPI stream
+ // as inputs.
+ auto TpiHashes = GloballyHashedType::hashTypes(ExpectedTpi->typeArray());
+ auto IpiHashes =
+ GloballyHashedType::hashIds(ExpectedIpi->typeArray(), TpiHashes);
+
+ // Merge TPI first, because the IPI stream will reference type indices.
+ if (auto Err = mergeTypeRecords(GlobalTypeTable, IndexMap.TPIMap,
+ ExpectedTpi->typeArray(), TpiHashes))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err)));
+
+ // Merge IPI.
+ if (auto Err =
+ mergeIdRecords(GlobalIDTable, IndexMap.TPIMap, IndexMap.IPIMap,
+ ExpectedIpi->typeArray(), IpiHashes))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(Err)));
+ } else {
+ // Merge TPI first, because the IPI stream will reference type indices.
+ if (auto Err = mergeTypeRecords(TypeTable, IndexMap.TPIMap,
+ ExpectedTpi->typeArray()))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err)));
+
+ // Merge IPI.
+ if (auto Err = mergeIdRecords(IDTable, IndexMap.TPIMap, IndexMap.IPIMap,
+ ExpectedIpi->typeArray()))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(Err)));
+ }
return IndexMap;
}
@@ -290,7 +383,7 @@ static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) {
return true;
}
-static void remapTypesInSymbolRecord(ObjectFile *File,
+static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind,
MutableArrayRef<uint8_t> Contents,
const CVIndexMap &IndexMap,
ArrayRef<TiReference> TypeRefs) {
@@ -301,27 +394,73 @@ static void remapTypesInSymbolRecord(ObjectFile *File,
// This can be an item index or a type index. Choose the appropriate map.
ArrayRef<TypeIndex> TypeOrItemMap = IndexMap.TPIMap;
- if (Ref.Kind == TiRefKind::IndexRef && IndexMap.IsTypeServerMap)
+ bool IsItemIndex = Ref.Kind == TiRefKind::IndexRef;
+ if (IsItemIndex && IndexMap.IsTypeServerMap)
TypeOrItemMap = IndexMap.IPIMap;
MutableArrayRef<TypeIndex> TIs(
reinterpret_cast<TypeIndex *>(Contents.data() + Ref.Offset), Ref.Count);
for (TypeIndex &TI : TIs) {
if (!remapTypeIndex(TI, TypeOrItemMap)) {
+ log("ignoring symbol record of kind 0x" + utohexstr(SymKind) + " in " +
+ File->getName() + " with bad " + (IsItemIndex ? "item" : "type") +
+ " index 0x" + utohexstr(TI.getIndex()));
TI = TypeIndex(SimpleTypeKind::NotTranslated);
- log("ignoring symbol record in " + File->getName() +
- " with bad type index 0x" + utohexstr(TI.getIndex()));
continue;
}
}
}
}
-/// MSVC translates S_PROC_ID_END to S_END.
-uint16_t canonicalizeSymbolKind(SymbolKind Kind) {
- if (Kind == SymbolKind::S_PROC_ID_END)
- return SymbolKind::S_END;
- return Kind;
+static SymbolKind symbolKind(ArrayRef<uint8_t> RecordData) {
+ const RecordPrefix *Prefix =
+ reinterpret_cast<const RecordPrefix *>(RecordData.data());
+ return static_cast<SymbolKind>(uint16_t(Prefix->RecordKind));
+}
+
+/// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32
+static void translateIdSymbols(MutableArrayRef<uint8_t> &RecordData,
+ TypeCollection &IDTable) {
+ RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(RecordData.data());
+
+ SymbolKind Kind = symbolKind(RecordData);
+
+ if (Kind == SymbolKind::S_PROC_ID_END) {
+ Prefix->RecordKind = SymbolKind::S_END;
+ return;
+ }
+
+ // In an object file, GPROC32_ID has an embedded reference which refers to the
+ // single object file type index namespace. This has already been translated
+ // to the PDB file's ID stream index space, but we need to convert this to a
+ // symbol that refers to the type stream index space. So we remap again from
+ // ID index space to type index space.
+ if (Kind == SymbolKind::S_GPROC32_ID || Kind == SymbolKind::S_LPROC32_ID) {
+ SmallVector<TiReference, 1> Refs;
+ auto Content = RecordData.drop_front(sizeof(RecordPrefix));
+ CVSymbol Sym(Kind, RecordData);
+ discoverTypeIndicesInSymbol(Sym, Refs);
+ assert(Refs.size() == 1);
+ assert(Refs.front().Count == 1);
+
+ TypeIndex *TI =
+ reinterpret_cast<TypeIndex *>(Content.data() + Refs[0].Offset);
+ // `TI` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in
+ // the IPI stream, whose `FunctionType` member refers to the TPI stream.
+ // Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and
+ // in both cases we just need the second type index.
+ if (!TI->isSimple() && !TI->isNoneType()) {
+ CVType FuncIdData = IDTable.getType(*TI);
+ SmallVector<TypeIndex, 2> Indices;
+ discoverTypeIndices(FuncIdData, Indices);
+ assert(Indices.size() == 2);
+ *TI = Indices[1];
+ }
+
+ Kind = (Kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32
+ : SymbolKind::S_LPROC32;
+ Prefix->RecordKind = uint16_t(Kind);
+ }
}
/// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned.
@@ -339,10 +478,8 @@ static MutableArrayRef<uint8_t> copySymbolForPdb(const CVSymbol &Sym,
memset(NewData.data() + Sym.length(), 0, Size - Sym.length());
// Update the record prefix length. It should point to the beginning of the
- // next record. MSVC does some canonicalization of the record kind, so we do
- // that as well.
+ // next record.
auto *Prefix = reinterpret_cast<RecordPrefix *>(Mem);
- Prefix->RecordKind = canonicalizeSymbolKind(Sym.kind());
Prefix->RecordLen = Size - 2;
return NewData;
}
@@ -402,7 +539,7 @@ static void scopeStackOpen(SmallVectorImpl<SymbolScope> &Stack,
}
static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack,
- uint32_t CurOffset, ObjectFile *File) {
+ uint32_t CurOffset, ObjFile *File) {
if (Stack.empty()) {
warn("symbol scopes are not balanced in " + File->getName());
return;
@@ -411,8 +548,86 @@ static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack,
S.OpeningRecord->PtrEnd = CurOffset;
}
-static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjectFile *File,
+static bool symbolGoesInModuleStream(const CVSymbol &Sym) {
+ switch (Sym.kind()) {
+ case SymbolKind::S_GDATA32:
+ case SymbolKind::S_CONSTANT:
+ case SymbolKind::S_UDT:
+ // We really should not be seeing S_PROCREF and S_LPROCREF in the first place
+ // since they are synthesized by the linker in response to S_GPROC32 and
+ // S_LPROC32, but if we do see them, don't put them in the module stream I
+ // guess.
+ case SymbolKind::S_PROCREF:
+ case SymbolKind::S_LPROCREF:
+ return false;
+ // S_GDATA32 does not go in the module stream, but S_LDATA32 does.
+ case SymbolKind::S_LDATA32:
+ default:
+ return true;
+ }
+}
+
+static bool symbolGoesInGlobalsStream(const CVSymbol &Sym) {
+ switch (Sym.kind()) {
+ case SymbolKind::S_CONSTANT:
+ case SymbolKind::S_GDATA32:
+ // S_LDATA32 goes in both the module stream and the globals stream.
+ case SymbolKind::S_LDATA32:
+ case SymbolKind::S_GPROC32:
+ case SymbolKind::S_LPROC32:
+ // We really should not be seeing S_PROCREF and S_LPROCREF in the first place
+ // since they are synthesized by the linker in response to S_GPROC32 and
+ // S_LPROC32, but if we do see them, copy them straight through.
+ case SymbolKind::S_PROCREF:
+ case SymbolKind::S_LPROCREF:
+ return true;
+ // FIXME: For now, we drop all S_UDT symbols (i.e. they don't go in the
+ // globals stream or the modules stream). These have special handling which
+ // needs more investigation before we can get right, but by putting them all
+ // into the globals stream WinDbg fails to display local variables of class
+ // types saying that it cannot find the type Foo *. So as a stopgap just to
+ // keep things working, we drop them.
+ case SymbolKind::S_UDT:
+ default:
+ return false;
+ }
+}
+
+static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, ObjFile &File,
+ const CVSymbol &Sym) {
+ switch (Sym.kind()) {
+ case SymbolKind::S_CONSTANT:
+ case SymbolKind::S_UDT:
+ case SymbolKind::S_GDATA32:
+ case SymbolKind::S_LDATA32:
+ case SymbolKind::S_PROCREF:
+ case SymbolKind::S_LPROCREF:
+ Builder.addGlobalSymbol(Sym);
+ break;
+ case SymbolKind::S_GPROC32:
+ case SymbolKind::S_LPROC32: {
+ SymbolRecordKind K = SymbolRecordKind::ProcRefSym;
+ if (Sym.kind() == SymbolKind::S_LPROC32)
+ K = SymbolRecordKind::LocalProcRef;
+ ProcRefSym PS(K);
+ PS.Module = static_cast<uint16_t>(File.ModuleDBI->getModuleIndex());
+ // For some reason, MSVC seems to add one to this value.
+ ++PS.Module;
+ PS.Name = getSymbolName(Sym);
+ PS.SumName = 0;
+ PS.SymOffset = File.ModuleDBI->getNextSymbolOffset();
+ Builder.addGlobalSymbol(PS);
+ break;
+ }
+ default:
+ llvm_unreachable("Invalid symbol kind!");
+ }
+}
+
+static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjFile *File,
+ pdb::GSIStreamBuilder &GsiBuilder,
const CVIndexMap &IndexMap,
+ TypeCollection &IDTable,
BinaryStreamRef SymData) {
// FIXME: Improve error recovery by warning and skipping records when
// possible.
@@ -420,11 +635,11 @@ static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjectFile *File,
BinaryStreamReader Reader(SymData);
ExitOnErr(Reader.readArray(Syms, Reader.getLength()));
SmallVector<SymbolScope, 4> Scopes;
- for (const CVSymbol &Sym : Syms) {
+ for (CVSymbol Sym : Syms) {
// Discover type index references in the record. Skip it if we don't know
// where they are.
SmallVector<TiReference, 32> TypeRefs;
- if (!discoverTypeIndices(Sym, TypeRefs)) {
+ if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) {
log("ignoring unknown symbol record with kind 0x" + utohexstr(Sym.kind()));
continue;
}
@@ -435,17 +650,30 @@ static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjectFile *File,
// Re-map all the type index references.
MutableArrayRef<uint8_t> Contents =
NewData.drop_front(sizeof(RecordPrefix));
- remapTypesInSymbolRecord(File, Contents, IndexMap, TypeRefs);
+ remapTypesInSymbolRecord(File, Sym.kind(), Contents, IndexMap, TypeRefs);
+
+ // An object file may have S_xxx_ID symbols, but these get converted to
+ // "real" symbols in a PDB.
+ translateIdSymbols(NewData, IDTable);
+
+ SymbolKind NewKind = symbolKind(NewData);
// Fill in "Parent" and "End" fields by maintaining a stack of scopes.
- CVSymbol NewSym(Sym.kind(), NewData);
- if (symbolOpensScope(Sym.kind()))
+ CVSymbol NewSym(NewKind, NewData);
+ if (symbolOpensScope(NewKind))
scopeStackOpen(Scopes, File->ModuleDBI->getNextSymbolOffset(), NewSym);
- else if (symbolEndsScope(Sym.kind()))
+ else if (symbolEndsScope(NewKind))
scopeStackClose(Scopes, File->ModuleDBI->getNextSymbolOffset(), File);
+ // Add the symbol to the globals stream if necessary. Do this before adding
+ // the symbol to the module since we may need to get the next symbol offset,
+ // and writing to the module's symbol stream will update that offset.
+ if (symbolGoesInGlobalsStream(NewSym))
+ addGlobalSymbol(GsiBuilder, *File, NewSym);
+
// Add the symbol to the module.
- File->ModuleDBI->addSymbol(NewSym);
+ if (symbolGoesInModuleStream(NewSym))
+ File->ModuleDBI->addSymbol(NewSym);
}
}
@@ -460,7 +688,7 @@ static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc,
".debug$S");
}
-void PDBLinker::addObjectFile(ObjectFile *File) {
+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
// path absolute. If it's an object in an archive, we make the archive path
@@ -511,7 +739,13 @@ void PDBLinker::addObjectFile(ObjectFile *File) {
File->ModuleDBI->addDebugSubsection(SS);
break;
case DebugSubsectionKind::Symbols:
- mergeSymbolRecords(Alloc, File, IndexMap, SS.getRecordData());
+ if (Config->DebugGHashes) {
+ mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
+ GlobalIDTable, SS.getRecordData());
+ } else {
+ mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
+ IDTable, SS.getRecordData());
+ }
break;
default:
// FIXME: Process the rest of the subsections.
@@ -539,45 +773,88 @@ void PDBLinker::addObjectFile(ObjectFile *File) {
}
}
+static PublicSym32 createPublic(Defined *Def) {
+ PublicSym32 Pub(SymbolKind::S_PUB32);
+ Pub.Name = Def->getName();
+ if (auto *D = dyn_cast<DefinedCOFF>(Def)) {
+ if (D->getCOFFSymbol().isFunctionDefinition())
+ Pub.Flags = PublicSymFlags::Function;
+ } else if (isa<DefinedImportThunk>(Def)) {
+ Pub.Flags = PublicSymFlags::Function;
+ }
+
+ OutputSection *OS = Def->getChunk()->getOutputSection();
+ assert(OS && "all publics should be in final image");
+ Pub.Offset = Def->getRVA() - OS->getRVA();
+ Pub.Segment = OS->SectionIndex;
+ return Pub;
+}
+
// 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);
+ for (ObjFile *File : ObjFile::Instances)
+ addObjFile(File);
Builder.getStringTableBuilder().setStrings(PDBStrTab);
- // Construct TPI stream contents.
- addTypeInfo(Builder.getTpiBuilder(), TypeTable);
-
- // Construct IPI stream contents.
- addTypeInfo(Builder.getIpiBuilder(), IDTable);
+ // Construct TPI and IPI stream contents.
+ if (Config->DebugGHashes) {
+ addTypeInfo(Builder.getTpiBuilder(), GlobalTypeTable);
+ addTypeInfo(Builder.getIpiBuilder(), GlobalIDTable);
+ } else {
+ addTypeInfo(Builder.getTpiBuilder(), TypeTable);
+ addTypeInfo(Builder.getIpiBuilder(), IDTable);
+ }
- // Add public and symbol records stream.
+ // Compute the public and global symbols.
+ auto &GsiBuilder = Builder.getGsiBuilder();
+ std::vector<PublicSym32> Publics;
+ Symtab->forEachSymbol([&Publics](Symbol *S) {
+ // Only emit defined, live symbols that have a chunk.
+ auto *Def = dyn_cast<Defined>(S);
+ if (Def && Def->isLive() && Def->getChunk())
+ Publics.push_back(createPublic(Def));
+ });
- // 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();
+ if (!Publics.empty()) {
+ // Sort the public symbols and add them to the stream.
+ std::sort(Publics.begin(), Publics.end(),
+ [](const PublicSym32 &L, const PublicSym32 &R) {
+ return L.Name < R.Name;
+ });
+ for (const PublicSym32 &Pub : Publics)
+ GsiBuilder.addPublicSymbol(Pub);
+ }
}
-static void addLinkerModuleSymbols(StringRef Path,
- pdb::DbiModuleDescriptorBuilder &Mod,
- BumpPtrAllocator &Allocator) {
- codeview::SymbolSerializer Serializer(Allocator, CodeViewContainer::Pdb);
- codeview::ObjNameSym ONS(SymbolRecordKind::ObjNameSym);
- codeview::Compile3Sym CS(SymbolRecordKind::Compile3Sym);
- codeview::EnvBlockSym EBS(SymbolRecordKind::EnvBlockSym);
+static void addCommonLinkerModuleSymbols(StringRef Path,
+ pdb::DbiModuleDescriptorBuilder &Mod,
+ BumpPtrAllocator &Allocator) {
+ ObjNameSym ONS(SymbolRecordKind::ObjNameSym);
+ Compile3Sym CS(SymbolRecordKind::Compile3Sym);
+ EnvBlockSym EBS(SymbolRecordKind::EnvBlockSym);
ONS.Name = "* Linker *";
ONS.Signature = 0;
CS.Machine = Config->is64() ? CPUType::X64 : CPUType::Intel80386;
+ // Interestingly, if we set the string to 0.0.0.0, then when trying to view
+ // local variables WinDbg emits an error that private symbols are not present.
+ // By setting this to a valid MSVC linker version string, local variables are
+ // displayed properly. As such, even though it is not representative of
+ // LLVM's version information, we need this for compatibility.
CS.Flags = CompileSym3Flags::None;
- CS.VersionBackendBuild = 0;
- CS.VersionBackendMajor = 0;
- CS.VersionBackendMinor = 0;
+ CS.VersionBackendBuild = 25019;
+ CS.VersionBackendMajor = 14;
+ CS.VersionBackendMinor = 10;
CS.VersionBackendQFE = 0;
+
+ // MSVC also sets the frontend to 0.0.0.0 since this is specifically for the
+ // linker module (which is by definition a backend), so we don't need to do
+ // anything here. Also, it seems we can use "LLVM Linker" for the linker name
+ // without any problems. Only the backend version has to be hardcoded to a
+ // magic number.
CS.VersionFrontendBuild = 0;
CS.VersionFrontendMajor = 0;
CS.VersionFrontendMinor = 0;
@@ -592,7 +869,9 @@ static void addLinkerModuleSymbols(StringRef Path,
sys::fs::current_path(cwd);
EBS.Fields.push_back(cwd);
EBS.Fields.push_back("exe");
- EBS.Fields.push_back(Config->Argv[0]);
+ SmallString<64> exe = Config->Argv[0];
+ llvm::sys::fs::make_absolute(exe);
+ EBS.Fields.push_back(exe);
EBS.Fields.push_back("pdb");
EBS.Fields.push_back(Path);
EBS.Fields.push_back("cmd");
@@ -605,17 +884,33 @@ static void addLinkerModuleSymbols(StringRef Path,
EBS, Allocator, CodeViewContainer::Pdb));
}
+static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &Mod,
+ OutputSection &OS,
+ BumpPtrAllocator &Allocator) {
+ SectionSym Sym(SymbolRecordKind::SectionSym);
+ Sym.Alignment = 12; // 2^12 = 4KB
+ Sym.Characteristics = OS.getCharacteristics();
+ Sym.Length = OS.getVirtualSize();
+ Sym.Name = OS.getName();
+ Sym.Rva = OS.getRVA();
+ Sym.SectionNumber = OS.SectionIndex;
+ Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ Sym, Allocator, CodeViewContainer::Pdb));
+}
+
// Creates a PDB file.
-void coff::createPDB(SymbolTable *Symtab, ArrayRef<uint8_t> SectionTable,
- const llvm::codeview::DebugInfo *DI) {
+void coff::createPDB(SymbolTable *Symtab,
+ ArrayRef<OutputSection *> OutputSections,
+ ArrayRef<uint8_t> SectionTable,
+ const llvm::codeview::DebugInfo &BuildId) {
PDBLinker PDB(Symtab);
- PDB.initialize(DI);
+ PDB.initialize(BuildId);
PDB.addObjectsToPDB();
- PDB.addSections(SectionTable);
+ PDB.addSections(OutputSections, SectionTable);
PDB.commit();
}
-void PDBLinker::initialize(const llvm::codeview::DebugInfo *DI) {
+void PDBLinker::initialize(const llvm::codeview::DebugInfo &BuildId) {
ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize
// Create streams in MSF for predefined streams, namely
@@ -625,41 +920,71 @@ void PDBLinker::initialize(const llvm::codeview::DebugInfo *DI) {
// Add an Info stream.
auto &InfoBuilder = Builder.getInfoBuilder();
- InfoBuilder.setAge(DI ? DI->PDB70.Age : 0);
+ InfoBuilder.setAge(BuildId.PDB70.Age);
- GUID uuid{};
- if (DI)
- memcpy(&uuid, &DI->PDB70.Signature, sizeof(uuid));
+ GUID uuid;
+ memcpy(&uuid, &BuildId.PDB70.Signature, sizeof(uuid));
InfoBuilder.setGuid(uuid);
InfoBuilder.setSignature(time(nullptr));
InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
// Add an empty DBI stream.
pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
+ DbiBuilder.setAge(BuildId.PDB70.Age);
DbiBuilder.setVersionHeader(pdb::PdbDbiV70);
ExitOnErr(DbiBuilder.addDbgStream(pdb::DbgHeaderType::NewFPO, {}));
}
-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)};
- SectionMap = pdb::DbiStreamBuilder::createSectionMap(Sections);
- DbiBuilder.setSectionMap(SectionMap);
+void PDBLinker::addSectionContrib(pdb::DbiModuleDescriptorBuilder &LinkerModule,
+ OutputSection *OS, Chunk *C) {
+ pdb::SectionContrib SC;
+ memset(&SC, 0, sizeof(SC));
+ SC.ISect = OS->SectionIndex;
+ SC.Off = C->getRVA() - OS->getRVA();
+ SC.Size = C->getSize();
+ if (auto *SecChunk = dyn_cast<SectionChunk>(C)) {
+ SC.Characteristics = SecChunk->Header->Characteristics;
+ SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex();
+ ArrayRef<uint8_t> Contents = SecChunk->getContents();
+ JamCRC CRC(0);
+ ArrayRef<char> CharContents = makeArrayRef(
+ reinterpret_cast<const char *>(Contents.data()), Contents.size());
+ CRC.update(CharContents);
+ SC.DataCrc = CRC.getCRC();
+ } else {
+ SC.Characteristics = OS->getCharacteristics();
+ // FIXME: When we start creating DBI for import libraries, use those here.
+ SC.Imod = LinkerModule.getModuleIndex();
+ }
+ SC.RelocCrc = 0; // FIXME
+ Builder.getDbiBuilder().addSectionContrib(SC);
+}
+void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections,
+ ArrayRef<uint8_t> SectionTable) {
// It's not entirely clear what this is, but the * Linker * module uses it.
+ pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
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);
+ addCommonLinkerModuleSymbols(NativePath, LinkerModule, Alloc);
+
+ // Add section contributions. They must be ordered by ascending RVA.
+ for (OutputSection *OS : OutputSections) {
+ addLinkerModuleSectionSymbol(LinkerModule, *OS, Alloc);
+ for (Chunk *C : OS->getChunks())
+ addSectionContrib(LinkerModule, OS, C);
+ }
+
+ // Add Section Map stream.
+ ArrayRef<object::coff_section> Sections = {
+ (const object::coff_section *)SectionTable.data(),
+ SectionTable.size() / sizeof(object::coff_section)};
+ SectionMap = pdb::DbiStreamBuilder::createSectionMap(Sections);
+ DbiBuilder.setSectionMap(SectionMap);
// Add COFF section header stream.
ExitOnErr(