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);
+ }
+}