Index: lld/COFF/PDB.cpp =================================================================== --- lld/COFF/PDB.cpp +++ lld/COFF/PDB.cpp @@ -17,6 +17,7 @@ #include "llvm/DebugInfo/CodeView/CVDebugRecord.h" #include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.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" @@ -298,6 +299,7 @@ static void remapTypesInSymbolRecord(ObjFile *File, MutableArrayRef Contents, const CVIndexMap &IndexMap, + const TypeTableBuilder &IDTable, ArrayRef TypeRefs) { for (const TiReference &Ref : TypeRefs) { unsigned ByteSize = Ref.Count * sizeof(TypeIndex); @@ -322,10 +324,53 @@ } } -/// MSVC translates S_PROC_ID_END to S_END. -uint16_t canonicalizeSymbolKind(SymbolKind Kind) { - if (Kind == SymbolKind::S_PROC_ID_END) +static SymbolKind symbolKind(ArrayRef RecordData) { + const RecordPrefix *Prefix = + reinterpret_cast(RecordData.data()); + return static_cast(uint16_t(Prefix->RecordKind)); +} + +/// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32 +static SymbolKind canonicalizeSymbolKind(MutableArrayRef &RecordData, + const TypeTableBuilder &IDTable) { + RecordPrefix *Prefix = reinterpret_cast(RecordData.data()); + + SymbolKind Kind = symbolKind(RecordData); + + if (Kind == SymbolKind::S_PROC_ID_END) { + Prefix->RecordKind = SymbolKind::S_END; return SymbolKind::S_END; + } + + // In an object file, GPROC32_ID has an embedded reference which refers to the + // single object file type index namespace. We translated this to the PDB + // file's index namespace, and since it's an ID record that namespace is + // inside of the ID (IPI) stream. But we need to convert this to a TPI record + // so we remap again from ID namespace to type namespace. + if (Kind == SymbolKind::S_GPROC32_ID || Kind == SymbolKind::S_LPROC32_ID) { + SmallVector Refs; + auto Content = RecordData.drop_front(sizeof(RecordPrefix)); + CVSymbol Sym(Kind, RecordData); + discoverTypeIndices(Sym, Refs); + assert(Refs.size() == 1); + + TypeIndex *TI = + reinterpret_cast(Content.data() + Refs[0].Offset); + // `TI` is the index of a FuncIdRecord which lives in the IPI stream, whose + // `FunctionType` member refers to the TPI stream. + if (!TI->isSimple() && !TI->isNoneType()) { + ArrayRef FuncIdData = IDTable.records()[TI->toArrayIndex()]; + FuncIdRecord FID; + CVType FuncId(TypeLeafKind::LF_FUNC_ID, FuncIdData); + cantFail(TypeDeserializer::deserializeAs(FuncId, FID)); + *TI = FID.FunctionType; + } + + Kind = (Kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32 + : SymbolKind::S_LPROC32; + } + + Prefix->RecordKind = uint16_t(Kind); return Kind; } @@ -344,10 +389,8 @@ 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(Mem); - Prefix->RecordKind = canonicalizeSymbolKind(Sym.kind()); Prefix->RecordLen = Size - 2; return NewData; } @@ -418,6 +461,7 @@ static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjFile *File, const CVIndexMap &IndexMap, + const TypeTableBuilder &IDTable, BinaryStreamRef SymData) { // FIXME: Improve error recovery by warning and skipping records when // possible. @@ -425,7 +469,7 @@ BinaryStreamReader Reader(SymData); ExitOnErr(Reader.readArray(Syms, Reader.getLength())); SmallVector 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 TypeRefs; @@ -440,13 +484,18 @@ // Re-map all the type index references. MutableArrayRef Contents = NewData.drop_front(sizeof(RecordPrefix)); - remapTypesInSymbolRecord(File, Contents, IndexMap, TypeRefs); + remapTypesInSymbolRecord(File, Contents, IndexMap, IDTable, TypeRefs); + SymbolKind NewKind = canonicalizeSymbolKind(NewData, IDTable); + assert(NewKind == symbolKind(NewData)); + assert(NewKind != SymbolKind::S_GPROC32_ID); + assert(NewKind != SymbolKind::S_LPROC32_ID); + assert(NewKind != SymbolKind::S_PROC_ID_END); // 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 module. @@ -516,7 +565,7 @@ File->ModuleDBI->addDebugSubsection(SS); break; case DebugSubsectionKind::Symbols: - mergeSymbolRecords(Alloc, File, IndexMap, SS.getRecordData()); + mergeSymbolRecords(Alloc, File, IndexMap, IDTable, SS.getRecordData()); break; default: // FIXME: Process the rest of the subsections. Index: lld/test/COFF/pdb-comdat.test =================================================================== --- lld/test/COFF/pdb-comdat.test +++ lld/test/COFF/pdb-comdat.test @@ -46,7 +46,7 @@ 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: 120 | S_GPROC32_ID [size = 44] `main` +CHECK: 120 | S_GPROC32 [size = 44] `main` CHECK: parent = 0, end = 196, addr = 0002:0000, code size = 24 CHECK: debug start = 4, debug end = 19, flags = none CHECK: 164 | S_FRAMEPROC [size = 32] @@ -57,7 +57,7 @@ CHECK: 200 | S_GDATA32 [size = 24] `global` CHECK: type = 0x0074 (int), addr = 0000:0000 CHECK: 224 | S_BUILDINFO [size = 8] BuildId = `0x100A` -CHECK: 232 | S_GPROC32_ID [size = 44] `foo` +CHECK: 232 | S_GPROC32 [size = 44] `foo` CHECK: parent = 0, end = 308, addr = 0002:0032, code size = 15 CHECK: debug start = 0, debug end = 14, flags = none CHECK: 276 | S_FRAMEPROC [size = 32] @@ -71,7 +71,7 @@ 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: 120 | S_GPROC32_ID [size = 44] `bar` +CHECK: 120 | S_GPROC32 [size = 44] `bar` CHECK: parent = 0, end = 196, addr = 0002:0048, code size = 14 CHECK: debug start = 4, debug end = 9, flags = none CHECK: 164 | S_FRAMEPROC [size = 32] @@ -82,7 +82,7 @@ CHECK: 200 | S_GDATA32 [size = 24] `global` CHECK: type = 0x0074 (int), addr = 0000:0000 CHECK: 224 | S_BUILDINFO [size = 8] BuildId = `0x100D` -CHECK-NOT: S_GPROC32_ID {{.*}} `foo` +CHECK-NOT: S_GPROC32 {{.*}} `foo` CHECK-LABEL: Mod 0002 | `* Linker *`: Reorder the object files and verify that the other table is selected. Index: lld/test/COFF/pdb-invalid-func-type.yaml =================================================================== --- lld/test/COFF/pdb-invalid-func-type.yaml +++ lld/test/COFF/pdb-invalid-func-type.yaml @@ -7,7 +7,7 @@ # RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s # CHECK: Mod 0000 | `{{.*}}pdb-invalid-func-type.yaml.tmp.obj`: -# CHECK: 4 | S_GPROC32_ID [size = 44] `main` +# CHECK: 4 | S_GPROC32 [size = 44] `main` # CHECK: parent = 0, end = 80, addr = 0001:0000, code size = 3 # CHECK: 48 | S_FRAMEPROC [size = 32] # CHECK: 80 | S_END [size = 4] Index: lld/test/COFF/pdb-procid-remapping.test =================================================================== --- /dev/null +++ lld/test/COFF/pdb-procid-remapping.test @@ -0,0 +1,29 @@ +# RUN: yaml2obj < %p/Inputs/pdb1.yaml > %t1.obj +# RUN: yaml2obj < %p/Inputs/pdb2.yaml > %t2.obj +# RUN: lld-link /debug /pdb:%t.pdb /dll /out:%t.dll /entry:main /nodefaultlib \ +# RUN: %t1.obj %t2.obj + +# RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s + +CHECK: Symbols +CHECK-NEXT: ============================================================ +CHECK-LABEL: Mod 0000 | +CHECK: 92 | S_GPROC32 [size = 44] `main` +CHECK-NEXT: parent = 0, end = 168, addr = 0002:0000, code size = 14 +CHECK-NEXT: type = `0x1004 (int ())`, debug start = 4, debug end = 9, flags = none +CHECK-NEXT: 136 | S_FRAMEPROC [size = 32] +CHECK-NEXT: size = 40, padding size = 0, offset to padding = 0 +CHECK-NEXT: bytes of callee saved registers = 0, exception handler addr = 0000:0000 +CHECK-NEXT: flags = has async eh | opt speed +CHECK-NEXT: 168 | S_END [size = 4] +CHECK-LABEL: Mod 0001 | +CHECK: 92 | S_GPROC32 [size = 44] `foo` +CHECK-NEXT: parent = 0, end = 168, addr = 0002:0016, code size = 6 +CHECK-NEXT: type = `0x1001 (int ())`, debug start = 0, debug end = 5, flags = none +CHECK-NEXT: 136 | S_FRAMEPROC [size = 32] +CHECK-NEXT: size = 0, padding size = 0, offset to padding = 0 +CHECK-NEXT: bytes of callee saved registers = 0, exception handler addr = 0000:0000 +CHECK-NEXT: flags = has async eh | opt speed +CHECK-NEXT: 168 | S_END [size = 4] +CHECK-LABEL: Mod 0002 | +CHECK: 4 | S_OBJNAME [size = 20] sig=0, `* Linker *` Index: lld/test/COFF/pdb-scopes.test =================================================================== --- lld/test/COFF/pdb-scopes.test +++ lld/test/COFF/pdb-scopes.test @@ -34,12 +34,12 @@ RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s CHECK-LABEL: Mod 0000 | `{{.*}}pdb-scopes.test.tmp-a.obj`: -CHECK: 104 | S_GPROC32_ID [size = 44] `g` +CHECK: 104 | S_GPROC32 [size = 44] `g` CHECK: parent = 0, end = 196, addr = 0002:0000, code size = 5 CHECK: debug start = 4, debug end = 4, flags = none CHECK: 180 | S_REGREL32 [size = 16] `x` CHECK: 196 | S_END [size = 4] -CHECK: 200 | S_GPROC32_ID [size = 44] `main` +CHECK: 200 | S_GPROC32 [size = 44] `main` CHECK: parent = 0, end = 384, addr = 0002:0016, code size = 58 CHECK: debug start = 8, debug end = 53, flags = none CHECK: 276 | S_REGREL32 [size = 20] `argc` @@ -56,7 +56,7 @@ CHECK: 384 | S_END [size = 4] CHECK-LABEL: Mod 0001 | `{{.*}}pdb-scopes.test.tmp-b.obj`: -CHECK: 104 | S_GPROC32_ID [size = 44] `f` +CHECK: 104 | S_GPROC32 [size = 44] `f` CHECK: parent = 0, end = 284, addr = 0002:0080, code size = 62 CHECK: debug start = 8, debug end = 57, flags = none CHECK: 180 | S_REGREL32 [size = 16] `x` Index: lld/test/COFF/pdb-symbol-types.yaml =================================================================== --- lld/test/COFF/pdb-symbol-types.yaml +++ lld/test/COFF/pdb-symbol-types.yaml @@ -21,7 +21,7 @@ # 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: 116 | S_GPROC32_ID [size = 44] `main` +# CHECK: 116 | S_GPROC32 [size = 44] `main` # CHECK: parent = 0, end = 192, addr = 0002:0000, code size = 7 # CHECK: debug start = 0, debug end = 6, flags = none # CHECK: 160 | S_FRAMEPROC [size = 32] Index: lld/test/COFF/pdb-type-server-simple.test =================================================================== --- lld/test/COFF/pdb-type-server-simple.test +++ lld/test/COFF/pdb-type-server-simple.test @@ -63,7 +63,7 @@ CHECK: ============================================================ CHECK-LABEL: Mod 0000 | `{{.*}}a.obj`: CHECK: 4 | S_OBJNAME [size = 40] sig=0, `C:\src\llvm-project\build\a.obj` -CHECK: 104 | S_GPROC32_ID [size = 44] `main` +CHECK: 104 | S_GPROC32 [size = 44] `main` CHECK: parent = 0, end = 196, addr = 0002:0000, code size = 27 CHECK: type = {{.*}}, debug start = 4, debug end = 22, flags = none CHECK: 200 | S_UDT [size = 12] `Foo` @@ -75,7 +75,7 @@ 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: 104 | S_GPROC32_ID [size = 44] `g` +CHECK: 104 | S_GPROC32 [size = 44] `g` CHECK: parent = 0, end = 196, addr = 0002:0032, code size = 13 CHECK: type = {{.*}}, debug start = 5, debug end = 12, flags = none CHECK: 148 | S_FRAMEPROC [size = 32] Index: llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp +++ llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp @@ -372,6 +372,9 @@ case SymbolKind::S_LPROC32_DPC_ID: Refs.push_back({TiRefKind::IndexRef, 24, 1}); // LF_FUNC_ID break; + case SymbolKind::S_BPREL32: + Refs.push_back({TiRefKind::TypeRef, 4, 1}); // local variable + break; case SymbolKind::S_UDT: Refs.push_back({TiRefKind::TypeRef, 0, 1}); // UDT break; Index: llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp =================================================================== --- llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -837,6 +837,7 @@ ExitOnError Err("Unexpected error processing symbols: "); + auto &Ids = Err(initializeTypes(StreamIPI)); auto &Types = Err(initializeTypes(StreamTPI)); iterateModules( @@ -852,7 +853,8 @@ SymbolVisitorCallbackPipeline Pipeline; SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); - MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types); + MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, + Types); Pipeline.addCallbackToPipeline(Deserializer); Pipeline.addCallbackToPipeline(Dumper); @@ -936,9 +938,13 @@ auto ExpectedTypes = initializeTypes(StreamTPI); if (!ExpectedTypes) return ExpectedTypes.takeError(); + auto ExpectedIds = initializeTypes(StreamIPI); + if (!ExpectedIds) + return ExpectedIds.takeError(); SymbolVisitorCallbackPipeline Pipeline; SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); - MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, *ExpectedTypes); + MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, *ExpectedIds, + *ExpectedTypes); Pipeline.addCallbackToPipeline(Deserializer); Pipeline.addCallbackToPipeline(Dumper); Index: llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h =================================================================== --- llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h +++ llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h @@ -23,8 +23,9 @@ class MinimalSymbolDumper : public codeview::SymbolVisitorCallbacks { public: MinimalSymbolDumper(LinePrinter &P, bool RecordBytes, + codeview::LazyRandomTypeCollection &Ids, codeview::LazyRandomTypeCollection &Types) - : P(P), Types(Types) {} + : P(P), Ids(Ids), Types(Types) {} Error visitSymbolBegin(codeview::CVSymbol &Record) override; Error visitSymbolBegin(codeview::CVSymbol &Record, uint32_t Offset) override; @@ -37,12 +38,16 @@ #include "llvm/DebugInfo/CodeView/CodeViewSymbols.def" private: + std::string typeOrIdIndex(codeview::TypeIndex TI, bool IsType) const; + std::string typeIndex(codeview::TypeIndex TI) const; + std::string idIndex(codeview::TypeIndex TI) const; LinePrinter &P; + codeview::LazyRandomTypeCollection &Ids; codeview::LazyRandomTypeCollection &Types; }; } // namespace pdb } // namespace llvm #endif \ No newline at end of file Index: llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp =================================================================== --- llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp +++ llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp @@ -389,10 +389,12 @@ return Error::success(); } -std::string MinimalSymbolDumper::typeIndex(TypeIndex TI) const { +std::string MinimalSymbolDumper::typeOrIdIndex(codeview::TypeIndex TI, + bool IsType) const { if (TI.isSimple()) return formatv("{0}", TI).str(); - StringRef Name = Types.getTypeName(TI); + auto &Container = IsType ? Types : Ids; + StringRef Name = Container.getTypeName(TI); if (Name.size() > 32) { Name = Name.take_front(32); return formatv("{0} ({1}...)", TI, Name); @@ -400,6 +402,14 @@ return formatv("{0} ({1})", TI, Name); } +std::string MinimalSymbolDumper::idIndex(codeview::TypeIndex TI) const { + return typeOrIdIndex(TI, false); +} + +std::string MinimalSymbolDumper::typeIndex(TypeIndex TI) const { + return typeOrIdIndex(TI, true); +} + Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, BlockSym &Block) { P.format(" `{0}`", Block.Name); AutoIndent Indent(P, 7); @@ -728,8 +738,19 @@ formatSegmentOffset(Proc.Segment, Proc.CodeOffset), Proc.CodeSize); // FIXME: It seems FunctionType is sometimes an id and sometimes a type. + bool IsType = true; + switch (Proc.getKind()) { + case SymbolRecordKind::GlobalProcIdSym: + case SymbolRecordKind::ProcIdSym: + case SymbolRecordKind::DPCProcIdSym: + IsType = false; + break; + default: + break; + } P.formatLine("type = `{0}`, debug start = {1}, debug end = {2}, flags = {3}", - typeIndex(Proc.FunctionType), Proc.DbgStart, Proc.DbgEnd, + typeOrIdIndex(Proc.FunctionType, IsType), Proc.DbgStart, + Proc.DbgEnd, formatProcSymFlags(P.getIndentLevel() + 9, Proc.Flags)); return Error::success(); }