Index: lld/COFF/PDB.cpp =================================================================== --- lld/COFF/PDB.cpp +++ lld/COFF/PDB.cpp @@ -18,8 +18,8 @@ #include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" #include "llvm/DebugInfo/CodeView/DebugSubsectionVisitor.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" -#include "llvm/DebugInfo/CodeView/SymbolDumper.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" @@ -115,6 +115,99 @@ fatal(Err, "codeview::mergeTypeStreams failed"); } +static bool remapTypeIndex(TypeIndex &TI, ArrayRef TypeIndexMap) { + if (TI.isSimple()) + return true; + if (TI.toArrayIndex() >= TypeIndexMap.size()) + return false; + TI = TypeIndexMap[TI.toArrayIndex()]; + return true; +} + +static bool remapTypesInSymbolRecord(ObjectFile *File, + MutableArrayRef Contents, + ArrayRef TypeIndexMap, + ArrayRef TypeRefs) { + for (const TiReference &Ref : TypeRefs) { + unsigned ByteSize = Ref.Count * sizeof(TypeIndex); + if (Contents.size() < Ref.Offset + ByteSize) { + log("ignoring short symbol record"); + return false; + } + auto IndexBytes = Contents.slice(Ref.Offset, ByteSize); + MutableArrayRef TIs( + reinterpret_cast(IndexBytes.data()), Ref.Count); + for (TypeIndex &TI : TIs) + if (!remapTypeIndex(TI, TypeIndexMap)) { + log("ignoring symbol record in " + File->getName() + + " with bad type index 0x" + utohexstr(TI.getIndex())); + return false; + } + } + return true; +} + +/// Canonicalize symbol record kinds for the PDB. +uint16_t canonicalizeKind(SymbolKind Kind) { + switch (Kind) { + case SymbolKind::S_PROC_ID_END: + return SymbolKind::S_END; + default: + break; + } + return Kind; +} + +/// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned. +/// The object file may not be aligned. Zero out the padding bytes. +static MutableArrayRef copySymbolForPdb(const CVSymbol &Sym, + BumpPtrAllocator &Alloc) { + size_t Size = alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb)); + assert(Size >= 4 && "record too short"); + assert(Size <= MaxRecordLength && "record too long"); + void *Mem = Alloc.Allocate(Size, 4); + MutableArrayRef NewData(reinterpret_cast(Mem), Size); + memcpy(NewData.data(), Sym.data().data(), Sym.length()); + memset(NewData.data() + Sym.length(), 0, Size - Sym.length()); + auto *Prefix = reinterpret_cast(Mem); + Prefix->RecordKind = canonicalizeKind(Sym.kind()); + Prefix->RecordLen = Size - 2; + return NewData; +} + +static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjectFile *File, + ArrayRef TypeIndexMap, + BinaryStreamRef SymData) { + // FIXME: Improve error recovery by warning and skipping records when + // possible. + CVSymbolArray Syms; + BinaryStreamReader Reader(SymData); + ExitOnErr(Reader.readArray(Syms, Reader.getLength())); + for (const CVSymbol &Sym : Syms) { + // Discover type index references in the record. Skip it if we don't know + // where they are. + SmallVector TypeRefs; + if (!discoverTypeIndices(Sym, TypeRefs)) { + log("ignoring unknown symbol record with kind 0x" + utohexstr(Sym.kind())); + continue; + } + + // Copy the symbol record so we can mutate it. + MutableArrayRef NewData = copySymbolForPdb(Sym, Alloc); + + // Re-map all the type index references. + MutableArrayRef Contents = + NewData.drop_front(sizeof(RecordPrefix)); + if (!remapTypesInSymbolRecord(File, Contents, TypeIndexMap, TypeRefs)) + continue; + + // FIXME: Fill in 'pEnd' fields by maintaining a stack of addresses. + + // Add the symbol to the module. + File->ModuleDBI->addSymbol(CVSymbol(Sym.kind(), NewData)); + } +} + // Allocate memory for a .debug$S section and relocate it. static ArrayRef relocateDebugChunk(BumpPtrAllocator &Alloc, SectionChunk *DebugChunk) { @@ -191,6 +284,9 @@ // 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; Index: lld/COFF/Writer.cpp =================================================================== --- lld/COFF/Writer.cpp +++ lld/COFF/Writer.cpp @@ -213,13 +213,13 @@ uint64_t Defined::getSecrel() { if (auto *D = dyn_cast(this)) return getRVA() - D->getChunk()->getOutputSection()->getRVA(); - fatal("SECREL relocation points to a non-regular symbol"); + fatal("SECREL relocation points to non-regular symbol: " + getName()); } uint64_t Defined::getSectionIndex() { if (auto *D = dyn_cast(this)) return D->getChunk()->getOutputSection()->SectionIndex; - fatal("SECTION relocation points to a non-regular symbol"); + fatal("SECTION relocation points to non-regular symbol: " + getName()); } bool Defined::isExecutable() { Index: lld/test/COFF/pdb-comdat.test =================================================================== --- lld/test/COFF/pdb-comdat.test +++ lld/test/COFF/pdb-comdat.test @@ -26,7 +26,7 @@ RUN: yaml2obj %S/Inputs/pdb_comdat_main.yaml -o pdb_comdat_main.obj RUN: yaml2obj %S/Inputs/pdb_comdat_bar.yaml -o pdb_comdat_bar.obj RUN: lld-link pdb_comdat_main.obj pdb_comdat_bar.obj -out:t.exe -debug -pdb:t.pdb -nodefaultlib -entry:main -RUN: llvm-pdbutil raw -l t.pdb | FileCheck %s +RUN: llvm-pdbutil raw -l -symbols t.pdb | FileCheck %s CHECK: Lines CHECK: ============================================================ @@ -38,6 +38,54 @@ CHECK-NOT: c:\src\llvm-project\build\foo.h CHECK-LABEL: Mod 0002 | `* Linker *`: +CHECK: Symbols +CHECK: ============================================================ +CHECK-LABEL: Mod 0000 | `{{.*}}pdb_comdat_main.obj`: +CHECK: - S_OBJNAME [size = 56] sig=0, `C:\src\llvm-project\build\pdb_comdat_main.obj` +CHECK: - S_COMPILE3 [size = 60] +CHECK: machine = intel x86-x64, Ver = Microsoft (R) Optimizing Compiler, language = c +CHECK: frontend = 19.0.24215.1, backend = 19.0.24215.1 +CHECK: flags = security checks | hot patchable +CHECK: - S_GPROC32_ID [size = 44] `main` + FIXME: We need to fill in "end". +CHECK: parent = 0, addr = 0002:0000, code size = 24, end = 0 +CHECK: debug start = 4, debug end = 19, flags = none +CHECK: - S_FRAMEPROC [size = 32] +CHECK: size = 40, padding size = 0, offset to padding = 0 +CHECK: bytes of callee saved registers = 0, exception handler addr = 0000:0000 +CHECK: flags = has async eh | opt speed +CHECK: - S_END [size = 4] +CHECK: - S_GDATA32 [size = 24] `global` +CHECK: type = 0x0074 (int), addr = 0000:0000 +CHECK: - S_BUILDINFO [size = 8] BuildId = `4106` +CHECK: - S_GPROC32_ID [size = 44] `foo` +CHECK: parent = 0, addr = 0002:0032, code size = 15, end = 0 +CHECK: debug start = 0, debug end = 14, flags = none +CHECK: - S_FRAMEPROC [size = 32] +CHECK: size = 0, padding size = 0, offset to padding = 0 +CHECK: bytes of callee saved registers = 0, exception handler addr = 0000:0000 +CHECK: flags = marked inline | has async eh | opt speed +CHECK: - S_END [size = 4] +CHECK-LABEL: Mod 0001 | `{{.*}}pdb_comdat_bar.obj`: +CHECK: - S_OBJNAME [size = 56] sig=0, `C:\src\llvm-project\build\pdb_comdat_bar.obj` +CHECK: - S_COMPILE3 [size = 60] +CHECK: machine = intel x86-x64, Ver = Microsoft (R) Optimizing Compiler, language = c +CHECK: frontend = 19.0.24215.1, backend = 19.0.24215.1 +CHECK: flags = security checks | hot patchable +CHECK: - S_GPROC32_ID [size = 44] `bar` +CHECK: parent = 0, addr = 0002:0048, code size = 14, end = 0 +CHECK: debug start = 4, debug end = 9, flags = none +CHECK: - S_FRAMEPROC [size = 32] +CHECK: size = 40, padding size = 0, offset to padding = 0 +CHECK: bytes of callee saved registers = 0, exception handler addr = 0000:0000 +CHECK: flags = has async eh | opt speed +CHECK: - S_END [size = 4] +CHECK: - S_GDATA32 [size = 24] `global` +CHECK: type = 0x0074 (int), addr = 0000:0000 +CHECK: - S_BUILDINFO [size = 8] BuildId = `4109` +CHECK-NOT: - S_GPROC32_ID {{.*}} `foo` +CHECK-LABEL: Mod 0002 | `* Linker *`: + Reorder the object files and verify that the other table is selected. RUN: lld-link pdb_comdat_bar.obj pdb_comdat_main.obj -out:t.exe -debug -pdb:t.pdb -nodefaultlib -entry:main Index: llvm/include/llvm/DebugInfo/CodeView/TypeIndexDiscovery.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/TypeIndexDiscovery.h +++ llvm/include/llvm/DebugInfo/CodeView/TypeIndexDiscovery.h @@ -11,6 +11,7 @@ #define LLVM_DEBUGINFO_CODEVIEW_TYPEINDEXDISCOVERY_H #include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/Support/Error.h" @@ -27,6 +28,11 @@ SmallVectorImpl &Refs); void discoverTypeIndices(const CVType &Type, SmallVectorImpl &Refs); + +/// Discover type indices in symbol records. Returns false if this is an unknown +/// record. +bool discoverTypeIndices(const CVSymbol &Symbol, + SmallVectorImpl &Refs); } } Index: llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp +++ llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp @@ -357,6 +357,82 @@ } } +static bool discoverTypeIndices(ArrayRef Content, SymbolKind Kind, + SmallVectorImpl &Refs) { + uint32_t Count; + // FIXME: In the future it would be nice if we could avoid hardcoding these + // values. One idea is to define some structures representing these types + // that would allow the use of offsetof(). + switch (Kind) { + case SymbolKind::S_GPROC32: + case SymbolKind::S_LPROC32: + case SymbolKind::S_GPROC32_ID: + case SymbolKind::S_LPROC32_ID: + case SymbolKind::S_LPROC32_DPC: + case SymbolKind::S_LPROC32_DPC_ID: + Refs.push_back({TiRefKind::IndexRef, 24, 1}); // LF_FUNC_ID + break; + case SymbolKind::S_UDT: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); // UDT + break; + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); // Type + break; + case SymbolKind::S_BUILDINFO: + Refs.push_back({TiRefKind::IndexRef, 0, 1}); // Compile flags + break; + case SymbolKind::S_LOCAL: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); // Type + break; + case SymbolKind::S_CONSTANT: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); // Type + break; + case SymbolKind::S_REGREL32: + Refs.push_back({TiRefKind::TypeRef, 4, 1}); // Type + break; + case SymbolKind::S_CALLSITEINFO: + Refs.push_back({TiRefKind::TypeRef, 8, 1}); // Call signature + break; + case SymbolKind::S_CALLERS: + case SymbolKind::S_CALLEES: + // The record is a count followed by an array of type indices. + Count = *reinterpret_cast(Content.data()); + Refs.push_back({TiRefKind::IndexRef, 4, Count}); // Callees + break; + case SymbolKind::S_INLINESITE: + Refs.push_back({TiRefKind::IndexRef, 8, 1}); // ID of inlinee + break; + + // Defranges don't have types, just registers and code offsets. + case SymbolKind::S_DEFRANGE_REGISTER: + case SymbolKind::S_DEFRANGE_REGISTER_REL: + case SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL: + case SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: + case SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER: + case SymbolKind::S_DEFRANGE_SUBFIELD: + break; + + // No type refernces. + case SymbolKind::S_LABEL32: + case SymbolKind::S_OBJNAME: + case SymbolKind::S_COMPILE: + case SymbolKind::S_COMPILE2: + case SymbolKind::S_COMPILE3: + case SymbolKind::S_BLOCK32: + case SymbolKind::S_FRAMEPROC: + break; + // Scope ending symbols. + case SymbolKind::S_END: + case SymbolKind::S_INLINESITE_END: + case SymbolKind::S_PROC_ID_END: + break; + default: + return false; // Unknown symbol. + } + return true; +} + void llvm::codeview::discoverTypeIndices(const CVType &Type, SmallVectorImpl &Refs) { ::discoverTypeIndices(Type.content(), Type.kind(), Refs); @@ -369,3 +445,9 @@ TypeLeafKind K = static_cast(uint16_t(P->RecordKind)); ::discoverTypeIndices(RecordData.drop_front(sizeof(RecordPrefix)), K, Refs); } + +bool llvm::codeview::discoverTypeIndices(const CVSymbol &Sym, + SmallVectorImpl &Refs) { + SymbolKind K = Sym.kind(); + return ::discoverTypeIndices(Sym.content(), K, Refs); +} Index: llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp =================================================================== --- llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp +++ llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp @@ -10,6 +10,7 @@ #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/SymbolSerializer.h" #include "llvm/Support/Allocator.h" #include "gmock/gmock.h" @@ -26,6 +27,7 @@ Refs.clear(); TTB = make_unique(Storage); FLRB = make_unique(*TTB); + Symbols.clear(); } void TearDown() override { @@ -37,7 +39,19 @@ template bool checkTypeReferences(uint32_t RecordIndex, Indices &&... TIs) const { EXPECT_EQ(sizeof...(Indices), countRefs(RecordIndex)); - return checkTypeReferencesImpl(RecordIndex, std::forward(TIs)...); + + // Choose between type or symbol records. The checking code doesn't care + // which we have. + std::vector> CVRecords; + if (Symbols.empty()) { + CVRecords = TTB->records(); + } else { + for (const CVSymbol &S : Symbols) + CVRecords.push_back(S.data()); + } + + return checkTypeReferencesImpl(RecordIndex, CVRecords, + std::forward(TIs)...); } template void writeFieldList(T &&... MemberRecords) { @@ -54,6 +68,13 @@ discoverAllTypeIndices(); } + template void writeSymbolRecords(T &&... Records) { + writeSymbolRecordsImpl(std::forward(Records)...); + ASSERT_EQ(sizeof...(T), Symbols.size()); + discoverTypeIndicesInSymbols(); + } + + std::unique_ptr TTB; private: @@ -83,18 +104,20 @@ } template - bool checkTypeReferencesImpl(uint32_t RecordIndex) const { + bool checkTypeReferencesImpl(uint32_t RecordIndex, + ArrayRef> CVRecords) const { return true; } template - bool checkTypeReferencesImpl(uint32_t RecordIndex, TypeIndex TI, - Indices &&... Rest) const { - ArrayRef Record = TTB->records()[RecordIndex]; + bool checkTypeReferencesImpl(uint32_t RecordIndex, + ArrayRef> CVRecords, + TypeIndex TI, Indices &&... Rest) const { + ArrayRef Record = CVRecords[RecordIndex]; bool Success = checkOneTypeReference(RecordIndex, Record, TI); EXPECT_TRUE(Success); - return Success & - checkTypeReferencesImpl(RecordIndex, std::forward(Rest)...); + return Success & checkTypeReferencesImpl(RecordIndex, CVRecords, + std::forward(Rest)...); } void discoverAllTypeIndices() { @@ -105,6 +128,12 @@ } } + void discoverTypeIndicesInSymbols() { + Refs.resize(Symbols.size()); + for (uint32_t I = 0; I < Symbols.size(); ++I) + discoverTypeIndices(Symbols[I], Refs[I]); + } + // Helper function to write out a field list record with the given list // of member records. void writeFieldListImpl() {} @@ -124,8 +153,19 @@ writeTypeRecordsImpl(std::forward(Records)...); } + // Helper function to write out a list of symbol records. + void writeSymbolRecordsImpl() {} + + template + void writeSymbolRecordsImpl(RecType &&Record, Rest &&... Records) { + Symbols.push_back(SymbolSerializer::writeOneSymbol(Record, Storage, + CodeViewContainer::Pdb)); + writeSymbolRecordsImpl(std::forward(Records)...); + } + std::vector> Refs; std::unique_ptr FLRB; + std::vector Symbols; BumpPtrAllocator Storage; }; @@ -492,4 +532,35 @@ OneMethod.T1, OneMethod.T2, OneMethod.T3, OneMethod.T4, NestedType.Type, StaticDataMember.Type, VirtualBaseClass.BaseType, VirtualBaseClass.VBPtrType, VFPtr.Type, Continuation.ContinuationIndex); -} \ No newline at end of file +} + +TEST_F(TypeIndexIteratorTest, ProcSym) { + ProcSym GS(SymbolRecordKind::GlobalProcSym); + GS.FunctionType = TypeIndex(0x40); + ProcSym LS(SymbolRecordKind::ProcSym); + LS.FunctionType = TypeIndex(0x41); + writeSymbolRecords(GS, LS); + checkTypeReferences(0, GS.FunctionType); + checkTypeReferences(1, LS.FunctionType); +} + +TEST_F(TypeIndexIteratorTest, DataSym) { + DataSym DS(SymbolRecordKind::GlobalData); + DS.Type = TypeIndex(0x40); + writeSymbolRecords(DS); + checkTypeReferences(0, DS.Type); +} + +TEST_F(TypeIndexIteratorTest, CallerSym) { + CallerSym Callees(SymbolRecordKind::CalleeSym); + Callees.Indices.push_back(TypeIndex(1)); + Callees.Indices.push_back(TypeIndex(2)); + Callees.Indices.push_back(TypeIndex(3)); + CallerSym Callers(SymbolRecordKind::CallerSym); + Callers.Indices.push_back(TypeIndex(4)); + Callers.Indices.push_back(TypeIndex(5)); + Callers.Indices.push_back(TypeIndex(6)); + writeSymbolRecords(Callees, Callers); + checkTypeReferences(0, TypeIndex(1), TypeIndex(2), TypeIndex(3)); + checkTypeReferences(1, TypeIndex(4), TypeIndex(5), TypeIndex(6)); +}