Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/AsmResolver.DotNet/DefaultPdbMetadataResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.IO;
using System.IO.Compression;
using System.Linq;
using AsmResolver.DotNet.PortablePdbs;
using AsmResolver.DotNet.PortablePdbs.Serialized;
using AsmResolver.DotNet.Serialized;
using AsmResolver.PE.Debug;

namespace AsmResolver.DotNet;

public class DefaultPdbMetadataResolver : IPdbMetadataResolver
{
public static DefaultPdbMetadataResolver Instance { get; } = new();

public PdbReaderContext? ResolvePortablePdb(SerializedModuleDefinition module)
{
if (module.DotNetDirectory?.Metadata is { } metadata && PortablePdb.TryFromMetadata(metadata, module, out var pdb))
{
return pdb.PdbReaderContext;
}

// System.Reflection.Metadata tries reading from a file first, so we will as well
if (module.FilePath is { } path && PortablePdb.TryFromFile(Path.ChangeExtension(path, ".pdb"), module, out pdb))
{
return pdb.PdbReaderContext;
}

var pdbSection = module.DebugData.Select(dd => dd.Contents).OfType<PortablePdbDataSegment>().FirstOrDefault();
if (pdbSection is not null)
{
var memoryStream = new MemoryStream(pdbSection.CompressedContents.ToArray());
var decompressStream = new DeflateStream(memoryStream, CompressionMode.Decompress);
var pdbData = new byte[pdbSection.UncompressedSize];
var resultStream = new MemoryStream(pdbData);
decompressStream.CopyTo(resultStream);
if (PortablePdb.TryFromBytes(pdbData, module, out pdb))
{
return pdb.PdbReaderContext;
}
}

return null;
}
}
11 changes: 11 additions & 0 deletions src/AsmResolver.DotNet/IPdbMetadataResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using AsmResolver.DotNet.PortablePdbs;
using AsmResolver.DotNet.PortablePdbs.Serialized;
using AsmResolver.DotNet.Serialized;

namespace AsmResolver.DotNet
{
public interface IPdbMetadataResolver
{
PdbReaderContext? ResolvePortablePdb(SerializedModuleDefinition module);
}
}
33 changes: 32 additions & 1 deletion src/AsmResolver.DotNet/MethodDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using AsmResolver.DotNet.Code.Cil;
using AsmResolver.DotNet.Code.Native;
using AsmResolver.DotNet.Collections;
using AsmResolver.DotNet.PortablePdbs;
using AsmResolver.DotNet.Signatures;
using AsmResolver.PE.DotNet.Cil;
using AsmResolver.PE.DotNet.Metadata.Tables;
Expand All @@ -25,7 +26,8 @@ public partial class MethodDefinition :
IHasGenericParameters,
IMemberForwarded,
IHasSecurityDeclaration,
IManagedEntryPoint
IManagedEntryPoint,
IHasCustomDebugInformation
{
private ParameterCollection? _parameters;

Expand Down Expand Up @@ -795,6 +797,25 @@ public partial UnmanagedExportInfo? ExportInfo
set;
}

[LazyProperty]
public partial MethodDebugInformation? MethodDebugInformation
{
get;
set;
}

[LazyProperty]
public partial IList<LocalScope> LocalScopes { get; }

[LazyProperty(OwnerProperty = nameof(MoveNextMethod))]
public partial MethodDefinition? KickoffMethod { get; set; }

[LazyProperty(OwnerProperty = nameof(KickoffMethod))]
public partial MethodDefinition? MoveNextMethod { get; set; }

[LazyProperty]
public partial IList<CustomDebugInformation> CustomDebugInformations { get; }

/// <summary>
/// Creates a new private static constructor for a type that is executed when its declaring type is loaded by the CLR.
/// </summary>
Expand Down Expand Up @@ -1031,6 +1052,16 @@ protected virtual IList<GenericParameter> GetGenericParameters() =>
/// </remarks>
protected virtual UnmanagedExportInfo? GetExportInfo() => null;

protected virtual MethodDebugInformation? GetMethodDebugInformation() => null;

protected virtual IList<LocalScope> GetLocalScopes() => new MemberCollection<MethodDefinition, LocalScope>(this);

protected virtual MethodDefinition? GetKickoffMethod() => null;

protected virtual MethodDefinition? GetMoveNextMethod() => null;

protected virtual IList<CustomDebugInformation> GetCustomDebugInformations() => new MemberCollection<IHasCustomDebugInformation, CustomDebugInformation>(this);

/// <summary>
/// Asserts whether the method's metadata is consistent with its signature.
/// </summary>
Expand Down
12 changes: 12 additions & 0 deletions src/AsmResolver.DotNet/ModuleDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System.Threading;
using AsmResolver.Collections;
using AsmResolver.DotNet.Builder;
using AsmResolver.DotNet.Collections;
using AsmResolver.DotNet.PortablePdbs;
using AsmResolver.DotNet.Serialized;
using AsmResolver.DotNet.Signatures;
using AsmResolver.IO;
Expand Down Expand Up @@ -797,6 +799,12 @@ public partial IManagedEntryPoint? ManagedEntryPoint
set;
}

[LazyProperty]
public partial PortablePdb? PortablePdb { get; set; }

[LazyProperty]
public partial IList<Document> Documents { get; }

/// <summary>
/// Gets the default importer instance for this module.
/// </summary>
Expand Down Expand Up @@ -1237,6 +1245,10 @@ protected virtual IList<CustomAttribute> GetCustomAttributes() =>
/// </remarks>
protected virtual ReferenceImporter GetDefaultImporter() => new(this);

protected virtual PortablePdb? GetPortablePdb() => null;

protected virtual IList<Document> GetDocuments() => new MemberCollection<ModuleDefinition, Document>(this);

/// <summary>
/// Detects the runtime that this module targets.
/// </summary>
Expand Down
36 changes: 36 additions & 0 deletions src/AsmResolver.DotNet/PortablePdbs/CustomDebugInformation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using AsmResolver.Collections;
using AsmResolver.DotNet.PortablePdbs.CustomRecords;
using AsmResolver.PE.DotNet.Metadata.Tables;

namespace AsmResolver.DotNet.PortablePdbs;

public partial class CustomDebugInformation : IMetadataMember, IOwnedCollectionElement<IHasCustomDebugInformation>
{
public CustomDebugInformation(MetadataToken token)
{
MetadataToken = token;
}

public MetadataToken MetadataToken
{
get;
}

[LazyProperty]
public partial IHasCustomDebugInformation? Owner
{
get;
set;
}

[LazyProperty]
public partial CustomDebugRecord? Value
{
get;
set;
}

protected virtual IHasCustomDebugInformation? GetOwner() => null;

protected virtual CustomDebugRecord? GetValue() => null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using AsmResolver.DotNet.PortablePdbs.Serialized;
using AsmResolver.DotNet.Signatures;
using AsmResolver.PE.DotNet.Metadata.Tables;

namespace AsmResolver.DotNet.PortablePdbs.CustomRecords;

public abstract class CustomDebugRecord : ExtendableBlobSignature
{
public abstract Guid Kind
{
get;
}

public abstract bool HasBlob
{
get;
}

public static CustomDebugRecord FromRow(PdbReaderContext context, in CustomDebugInformationRow row)
{
var kind = context.GuidStream!.GetGuidByIndex(row.Kind);
var blobReaderContext = new BlobReaderContext(context.OwningModule.ReaderContext);
var hasBlob = context.BlobStream!.TryGetBlobReaderByIndex(row.Value, out var reader);
if (kind == PrimaryConstructorInformationRecord.KnownKind)
{
return new PrimaryConstructorInformationRecord();
}
else if (kind == EmbeddedSourceRecord.KnownKind)
{
return EmbeddedSourceRecord.FromReader(blobReaderContext, ref reader);
}

if (hasBlob)
{
return new UnknownDebugRecord(kind, reader.ReadToEnd());
}
else
{
return new UnknownDebugRecord(kind, null);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.IO;
using System.IO.Compression;
using AsmResolver.DotNet.Signatures;
using AsmResolver.IO;

namespace AsmResolver.DotNet.PortablePdbs.CustomRecords;

public class EmbeddedSourceRecord : CustomDebugRecord
{
public static Guid KnownKind { get; } = new("0E8A571B-6926-466E-B4AD-8AB04611F5FE");

public override Guid Kind => KnownKind;

public override bool HasBlob => true;

public bool WriteCompressed
{
get;
set;
}

public byte[]? Source { get; set; }

public static EmbeddedSourceRecord FromReader(in BlobReaderContext context, ref BinaryStreamReader reader)
{
var uncompressedSize = reader.ReadInt32();
var data = reader.ReadToEnd();

if (uncompressedSize != 0)
{
using var deflateStream = new DeflateStream(new MemoryStream(data), CompressionMode.Decompress);
data = new byte[uncompressedSize];
using var output = new MemoryStream(data);
deflateStream.CopyTo(output);
}

return new EmbeddedSourceRecord
{
Source = data,
};
}

protected override void WriteContents(in BlobSerializationContext context)
{
throw new NotImplementedException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using AsmResolver.DotNet.Signatures;

namespace AsmResolver.DotNet.PortablePdbs.CustomRecords;

public class PrimaryConstructorInformationRecord : CustomDebugRecord
{
public static Guid KnownKind { get; } = new("9D40ACE1-C703-4D0E-BF41-7243060A8FB5");

public override Guid Kind => KnownKind;

public override bool HasBlob => false;

protected override void WriteContents(in BlobSerializationContext context) => throw new NotImplementedException();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using AsmResolver.DotNet.Signatures;

namespace AsmResolver.DotNet.PortablePdbs.CustomRecords;

public class UnknownDebugRecord : CustomDebugRecord
{
public UnknownDebugRecord(Guid kind, byte[]? data)
{
Kind = kind;
Data = data;
}

public override Guid Kind
{
get;
}

public override bool HasBlob => Data is not null;

public byte[]? Data { get; set; }

protected override void WriteContents(in BlobSerializationContext context)
{
context.Writer.WriteBytes(Data!);
}
}
Loading
Loading