diff --git a/src/AsmResolver.Symbols.Pdb/Records/CodeViewSymbol.cs b/src/AsmResolver.Symbols.Pdb/Records/CodeViewSymbol.cs index 5869c3acd..21d6191af 100644 --- a/src/AsmResolver.Symbols.Pdb/Records/CodeViewSymbol.cs +++ b/src/AsmResolver.Symbols.Pdb/Records/CodeViewSymbol.cs @@ -34,6 +34,8 @@ public static CodeViewSymbol FromReader(PdbReaderContext context, ref BinaryStre CodeViewSymbolType.Pub32 => new SerializedPublicSymbol(dataReader), CodeViewSymbolType.Udt => new SerializedUserDefinedTypeSymbol(context, dataReader), CodeViewSymbolType.Constant => new SerializedConstantSymbol(context, dataReader), + CodeViewSymbolType.ProcRef => new SerializedProcedureReferenceSymbol(dataReader, false), + CodeViewSymbolType.LProcRef => new SerializedProcedureReferenceSymbol(dataReader, true), _ => new UnknownSymbol(type, dataReader.ReadToEnd()) }; } diff --git a/src/AsmResolver.Symbols.Pdb/Records/ProcedureReferenceSymbol.cs b/src/AsmResolver.Symbols.Pdb/Records/ProcedureReferenceSymbol.cs new file mode 100644 index 000000000..0ee3f5782 --- /dev/null +++ b/src/AsmResolver.Symbols.Pdb/Records/ProcedureReferenceSymbol.cs @@ -0,0 +1,101 @@ +namespace AsmResolver.Symbols.Pdb.Records; + +/// +/// Represents a procedure reference symbol stored in a PDB symbol stream. +/// +public class ProcedureReferenceSymbol : CodeViewSymbol +{ + private readonly LazyVariable _name; + private readonly bool _local; + + /// + /// Initializes a new empty symbol. + /// + /// If true, this represents a local procedure reference. + protected ProcedureReferenceSymbol(bool local) + { + _name = new LazyVariable(GetName); + _local = local; + } + + /// + /// Creates a new symbol. + /// + /// The checksum of the referenced symbol name. + /// The offset within the segment the symbol starts at. + /// Index of the module that contains this procedure record. + /// The name of the symbol. + /// If true, this represents a local procedure reference. + public ProcedureReferenceSymbol(uint checksum, uint offset, ushort module, Utf8String name, bool local) + { + Checksum = checksum; + Offset = offset; + Module = module; + _name = new LazyVariable(name); + _local = local; + } + + /// + public override CodeViewSymbolType CodeViewSymbolType + { + get + { + return _local ? CodeViewSymbolType.LProcRef : CodeViewSymbolType.ProcRef; + } + } + + /// + /// Is the symbol a Local Procedure Reference? + /// + public bool IsLocal => _local; + + /// + /// Gets the checksum of the referenced symbol name. The checksum used is the + /// one specified in the header of the global symbols stream or static symbols stream. + /// + public uint Checksum + { + get; + set; + } + + /// + /// Gets the offset of the procedure symbol record from the beginning of the + /// $$SYMBOL table for the module. + /// + public uint Offset + { + get; + set; + } + + /// + /// Index of the module that contains this procedure record. + /// + public ushort Module + { + get; + set; + } + + /// + /// Gets or sets the name of the symbol. + /// + public Utf8String Name + { + get => _name.Value; + set => _name.Value = value; + } + + /// + /// Obtains the name of the symbol. + /// + /// The name. + /// + /// This method is called upon initialization of the property. + /// + protected virtual Utf8String GetName() => Utf8String.Empty; + + /// + public override string ToString() => $"{CodeViewSymbolType}: [{Module:X4}:{Offset:X8}] {Name}"; +} diff --git a/src/AsmResolver.Symbols.Pdb/Records/Serialized/SerializedProcedureReferenceSymbol.cs b/src/AsmResolver.Symbols.Pdb/Records/Serialized/SerializedProcedureReferenceSymbol.cs new file mode 100644 index 000000000..e165013c0 --- /dev/null +++ b/src/AsmResolver.Symbols.Pdb/Records/Serialized/SerializedProcedureReferenceSymbol.cs @@ -0,0 +1,27 @@ +using AsmResolver.IO; + +namespace AsmResolver.Symbols.Pdb.Records.Serialized; + +/// +/// Represents a lazily initialized implementation of that is read from a PDB image. +/// +public class SerializedProcedureReferenceSymbol : ProcedureReferenceSymbol +{ + private readonly BinaryStreamReader _nameReader; + + /// + /// Reads a public symbol from the provided input stream. + /// + /// The input stream to read from. + /// If true, this represents a local procedure reference. + public SerializedProcedureReferenceSymbol(BinaryStreamReader reader, bool local) : base(local) + { + Checksum = reader.ReadUInt32(); + Offset = reader.ReadUInt32(); + Module = reader.ReadUInt16(); + _nameReader = reader; + } + + /// + protected override Utf8String GetName() => _nameReader.Fork().ReadUtf8String(); +} diff --git a/test/AsmResolver.Symbols.Pdb.Tests/Records/ProcedureReferenceSymbolTest.cs b/test/AsmResolver.Symbols.Pdb.Tests/Records/ProcedureReferenceSymbolTest.cs new file mode 100644 index 000000000..ae534649b --- /dev/null +++ b/test/AsmResolver.Symbols.Pdb.Tests/Records/ProcedureReferenceSymbolTest.cs @@ -0,0 +1,27 @@ +using System.Linq; +using AsmResolver.Symbols.Pdb.Records; +using Xunit; + +namespace AsmResolver.Symbols.Pdb.Tests.Records; + +public class ProcedureReferenceSymbolTest : IClassFixture +{ + private readonly MockPdbFixture _fixture; + + public ProcedureReferenceSymbolTest(MockPdbFixture fixture) + { + _fixture = fixture; + } + + [Fact] + public void Name() + { + Assert.Equal("DllMain", _fixture.SimplePdb.Symbols.OfType().First(s => !s.IsLocal).Name); + } + + [Fact] + public void LocalName() + { + Assert.Equal("__get_entropy", _fixture.SimplePdb.Symbols.OfType().First(s => s.IsLocal).Name); + } +}