// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using System.IO;
using dnlib.DotNet.MD;
using dnlib.IO;
namespace dnlib.DotNet {
///
/// Helps resolve types
///
public interface ISignatureReaderHelper {
///
/// Resolves a
///
/// A TypeDefOrRef coded token
/// Generic parameter context
/// A or null if
/// is invalid
ITypeDefOrRef ResolveTypeDefOrRef(uint codedToken, GenericParamContext gpContext);
///
/// Converts the address of a to a
///
///
/// Address of . This is also known as the
/// method table and has the same value as
/// A or null if not supported
TypeSig ConvertRTInternalAddress(IntPtr address);
}
///
/// Reads signatures from the #Blob stream
///
public struct SignatureReader {
// .NET Core and .NET Framework limit arrays to 32 dimensions. Use a bigger limit
// so it's possible to read some bad MD, but not big enough to allocate a ton of mem.
const uint MaxArrayRank = 64;
readonly ISignatureReaderHelper helper;
readonly ICorLibTypes corLibTypes;
DataReader reader;
readonly GenericParamContext gpContext;
RecursionCounter recursionCounter;
///
/// Reads a signature from the #Blob stream
///
/// Reader module
/// #Blob stream offset of signature
/// A new instance or null if
/// is invalid.
public static CallingConventionSig ReadSig(ModuleDefMD readerModule, uint sig) =>
ReadSig(readerModule, sig, new GenericParamContext());
///
/// Reads a signature from the #Blob stream
///
/// Reader module
/// #Blob stream offset of signature
/// Generic parameter context
/// A new instance or null if
/// is invalid.
public static CallingConventionSig ReadSig(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext) {
try {
var reader = new SignatureReader(readerModule, sig, gpContext);
if (reader.reader.Length == 0)
return null;
var csig = reader.ReadSig();
if (csig is not null)
csig.ExtraData = reader.GetExtraData();
return csig;
}
catch {
return null;
}
}
///
/// Reads a signature
///
/// The module where the signature is located in
/// The signature data
/// A new instance or null if
/// is invalid.
public static CallingConventionSig ReadSig(ModuleDefMD module, byte[] signature) =>
ReadSig(module, module.CorLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), new GenericParamContext());
///
/// Reads a signature
///
/// The module where the signature is located in
/// The signature data
/// Generic parameter context
/// A new instance or null if
/// is invalid.
public static CallingConventionSig ReadSig(ModuleDefMD module, byte[] signature, GenericParamContext gpContext) =>
ReadSig(module, module.CorLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext);
///
/// Reads a signature
///
/// The module where the signature is located in
/// The signature reader
/// A new instance or null if
/// is invalid.
public static CallingConventionSig ReadSig(ModuleDefMD module, DataReader signature) =>
ReadSig(module, module.CorLibTypes, signature, new GenericParamContext());
///
/// Reads a signature
///
/// The module where the signature is located in
/// The signature reader
/// Generic parameter context
/// A new instance or null if
/// is invalid.
public static CallingConventionSig ReadSig(ModuleDefMD module, DataReader signature, GenericParamContext gpContext) =>
ReadSig(module, module.CorLibTypes, signature, gpContext);
///
/// Reads a signature
///
/// Token resolver
/// A instance
/// The signature data
/// A new instance or null if
/// is invalid.
public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature) =>
ReadSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), new GenericParamContext());
///
/// Reads a signature
///
/// Token resolver
/// A instance
/// The signature data
/// Generic parameter context
/// A new instance or null if
/// is invalid.
public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature, GenericParamContext gpContext) =>
ReadSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext);
///
/// Reads a signature
///
/// Token resolver
/// A instance
/// The signature reader
/// A new instance or null if
/// is invalid.
public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature) =>
ReadSig(helper, corLibTypes, signature, new GenericParamContext());
///
/// Reads a signature
///
/// Token resolver
/// A instance
/// The signature reader
/// Generic parameter context
/// A new instance or null if
/// is invalid.
public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature, GenericParamContext gpContext) {
try {
var reader = new SignatureReader(helper, corLibTypes, ref signature, gpContext);
if (reader.reader.Length == 0)
return null;
return reader.ReadSig();
}
catch {
return null;
}
}
///
/// Reads a type signature from the #Blob stream
///
/// Reader module
/// #Blob stream offset of signature
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig) =>
ReadTypeSig(readerModule, sig, new GenericParamContext());
///
/// Reads a type signature from the #Blob stream
///
/// Reader module
/// #Blob stream offset of signature
/// Generic parameter context
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext) {
try {
var reader = new SignatureReader(readerModule, sig, gpContext);
return reader.ReadType();
}
catch {
return null;
}
}
///
/// Reads a type signature from the #Blob stream
///
/// Reader module
/// #Blob stream offset of signature
/// If there's any extra data after the signature, it's saved
/// here, else this will be null
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, out byte[] extraData) =>
ReadTypeSig(readerModule, sig, new GenericParamContext(), out extraData);
///
/// Reads a type signature from the #Blob stream
///
/// Reader module
/// #Blob stream offset of signature
/// Generic parameter context
/// If there's any extra data after the signature, it's saved
/// here, else this will be null
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext, out byte[] extraData) {
try {
var reader = new SignatureReader(readerModule, sig, gpContext);
TypeSig ts;
try {
ts = reader.ReadType();
}
catch (IOException) {
reader.reader.Position = 0;
ts = null;
}
extraData = reader.GetExtraData();
return ts;
}
catch {
extraData = null;
return null;
}
}
///
/// Reads a signature
///
/// The module where the signature is located in
/// The signature data
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ModuleDefMD module, byte[] signature) =>
ReadTypeSig(module, module.CorLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), new GenericParamContext());
///
/// Reads a signature
///
/// The module where the signature is located in
/// The signature data
/// Generic parameter context
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ModuleDefMD module, byte[] signature, GenericParamContext gpContext) =>
ReadTypeSig(module, module.CorLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext);
///
/// Reads a signature
///
/// The module where the signature is located in
/// The signature reader
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ModuleDefMD module, DataReader signature) =>
ReadTypeSig(module, module.CorLibTypes, signature, new GenericParamContext());
///
/// Reads a signature
///
/// The module where the signature is located in
/// The signature reader
/// Generic parameter context
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ModuleDefMD module, DataReader signature, GenericParamContext gpContext) =>
ReadTypeSig(module, module.CorLibTypes, signature, gpContext);
///
/// Reads a signature
///
/// Token resolver
/// A instance
/// The signature data
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature) =>
ReadTypeSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), new GenericParamContext());
///
/// Reads a signature
///
/// Token resolver
/// A instance
/// The signature data
/// Generic parameter context
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature, GenericParamContext gpContext) =>
ReadTypeSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext);
///
/// Reads a signature
///
/// Token resolver
/// A instance
/// The signature reader
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature) =>
ReadTypeSig(helper, corLibTypes, signature, new GenericParamContext());
///
/// Reads a signature
///
/// Token resolver
/// A instance
/// The signature reader
/// Generic parameter context
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature, GenericParamContext gpContext) =>
ReadTypeSig(helper, corLibTypes, signature, gpContext, out var extraData);
///
/// Reads a signature
///
/// Token resolver
/// A instance
/// The signature data
/// Generic parameter context
/// If there's any extra data after the signature, it's saved
/// here, else this will be null
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature, GenericParamContext gpContext, out byte[] extraData) =>
ReadTypeSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext, out extraData);
///
/// Reads a signature
///
/// Token resolver
/// A instance
/// The signature reader
/// Generic parameter context
/// If there's any extra data after the signature, it's saved
/// here, else this will be null
/// A new instance or null if
/// is invalid.
public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature, GenericParamContext gpContext, out byte[] extraData) {
try {
var reader = new SignatureReader(helper, corLibTypes, ref signature, gpContext);
TypeSig ts;
try {
ts = reader.ReadType();
}
catch (IOException) {
reader.reader.Position = 0;
ts = null;
}
extraData = reader.GetExtraData();
return ts;
}
catch {
extraData = null;
return null;
}
}
///
/// Constructor
///
/// Reader module
/// #Blob stream offset of signature
/// Generic parameter context
SignatureReader(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext) {
helper = readerModule;
corLibTypes = readerModule.CorLibTypes;
reader = readerModule.BlobStream.CreateReader(sig);
this.gpContext = gpContext;
recursionCounter = new RecursionCounter();
}
///
/// Constructor
///
/// Token resolver
/// A instance
/// The signature data
/// Generic parameter context
SignatureReader(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, ref DataReader reader, GenericParamContext gpContext) {
this.helper = helper;
this.corLibTypes = corLibTypes;
this.reader = reader;
this.gpContext = gpContext;
recursionCounter = new RecursionCounter();
}
byte[] GetExtraData() {
if (reader.Position == reader.Length)
return null;
return reader.ReadRemainingBytes();
}
///
/// Reads the signature
///
/// A new instance or null if invalid signature
CallingConventionSig ReadSig() {
if (!recursionCounter.Increment())
return null;
CallingConventionSig result;
var callingConvention = (CallingConvention)reader.ReadByte();
switch (callingConvention & CallingConvention.Mask) {
case CallingConvention.Default:
case CallingConvention.C:
case CallingConvention.StdCall:
case CallingConvention.ThisCall:
case CallingConvention.FastCall:
case CallingConvention.VarArg:
case CallingConvention.Unmanaged:
case CallingConvention.NativeVarArg:
result = ReadMethod(callingConvention);
break;
case CallingConvention.Field:
result = ReadField(callingConvention);
break;
case CallingConvention.LocalSig:
result = ReadLocalSig(callingConvention);
break;
case CallingConvention.Property:
result = ReadProperty(callingConvention);
break;
case CallingConvention.GenericInst:
result = ReadGenericInstMethod(callingConvention);
break;
default:
result = null;
break;
}
recursionCounter.Decrement();
return result;
}
///
/// Reads a
///
/// First byte of signature
/// A new instance
FieldSig ReadField(CallingConvention callingConvention) => new FieldSig(callingConvention, ReadType());
///
/// Reads a
///
/// First byte of signature
/// A new instance
MethodSig ReadMethod(CallingConvention callingConvention) => ReadSig(new MethodSig(callingConvention));
///
/// Reads a
///
/// First byte of signature
/// A new instance
PropertySig ReadProperty(CallingConvention callingConvention) => ReadSig(new PropertySig(callingConvention));
T ReadSig(T methodSig) where T : MethodBaseSig {
if (methodSig.Generic) {
if (!reader.TryReadCompressedUInt32(out uint count) || count > 0x10000)
return null;
methodSig.GenParamCount = count;
}
if (!reader.TryReadCompressedUInt32(out uint numParams) || numParams > 0x10000 || numParams > reader.BytesLeft)
return null;
methodSig.RetType = ReadType();
var parameters = methodSig.Params;
for (uint i = 0; i < numParams; i++) {
var type = ReadType();
if (type is SentinelSig) {
if (methodSig.ParamsAfterSentinel is null)
methodSig.ParamsAfterSentinel = parameters = new List((int)(numParams - i));
i--;
}
else
parameters.Add(type);
}
return methodSig;
}
///
/// Reads a
///
/// First byte of signature
/// A new instance
LocalSig ReadLocalSig(CallingConvention callingConvention) {
if (!reader.TryReadCompressedUInt32(out uint count) || count > 0x10000 || count > reader.BytesLeft)
return null;
var sig = new LocalSig(callingConvention, count);
var locals = sig.Locals;
for (uint i = 0; i < count; i++)
locals.Add(ReadType());
return sig;
}
///
/// Reads a
///
/// First byte of signature
/// A new instance
GenericInstMethodSig ReadGenericInstMethod(CallingConvention callingConvention) {
if (!reader.TryReadCompressedUInt32(out uint count) || count > 0x10000 || count > reader.BytesLeft)
return null;
var sig = new GenericInstMethodSig(callingConvention, count);
var args = sig.GenericArguments;
for (uint i = 0; i < count; i++)
args.Add(ReadType());
return sig;
}
///
/// Reads the next type
///
/// true if a TypeSpec is allowed if the next type is a class/value-type
/// A new instance or null if invalid element type
TypeSig ReadType(bool allowTypeSpec = false) {
if (!recursionCounter.Increment())
return null;
uint num, i;
TypeSig nextType, result = null;
switch ((ElementType)reader.ReadByte()) {
case ElementType.Void: result = corLibTypes.Void; break;
case ElementType.Boolean: result = corLibTypes.Boolean; break;
case ElementType.Char: result = corLibTypes.Char; break;
case ElementType.I1: result = corLibTypes.SByte; break;
case ElementType.U1: result = corLibTypes.Byte; break;
case ElementType.I2: result = corLibTypes.Int16; break;
case ElementType.U2: result = corLibTypes.UInt16; break;
case ElementType.I4: result = corLibTypes.Int32; break;
case ElementType.U4: result = corLibTypes.UInt32; break;
case ElementType.I8: result = corLibTypes.Int64; break;
case ElementType.U8: result = corLibTypes.UInt64; break;
case ElementType.R4: result = corLibTypes.Single; break;
case ElementType.R8: result = corLibTypes.Double; break;
case ElementType.String: result = corLibTypes.String; break;
case ElementType.TypedByRef:result = corLibTypes.TypedReference; break;
case ElementType.I: result = corLibTypes.IntPtr; break;
case ElementType.U: result = corLibTypes.UIntPtr; break;
case ElementType.Object: result = corLibTypes.Object; break;
case ElementType.Ptr: result = new PtrSig(ReadType()); break;
case ElementType.ByRef: result = new ByRefSig(ReadType()); break;
case ElementType.ValueType: result = new ValueTypeSig(ReadTypeDefOrRef(allowTypeSpec)); break;
case ElementType.Class: result = new ClassSig(ReadTypeDefOrRef(allowTypeSpec)); break;
case ElementType.FnPtr: result = new FnPtrSig(ReadSig()); break;
case ElementType.SZArray: result = new SZArraySig(ReadType()); break;
case ElementType.CModReqd: result = new CModReqdSig(ReadTypeDefOrRef(true), ReadType()); break;
case ElementType.CModOpt: result = new CModOptSig(ReadTypeDefOrRef(true), ReadType()); break;
case ElementType.Sentinel: result = new SentinelSig(); break;
case ElementType.Pinned: result = new PinnedSig(ReadType()); break;
case ElementType.Var:
if (!reader.TryReadCompressedUInt32(out num))
break;
result = new GenericVar(num, gpContext.Type);
break;
case ElementType.MVar:
if (!reader.TryReadCompressedUInt32(out num))
break;
result = new GenericMVar(num, gpContext.Method);
break;
case ElementType.ValueArray:
nextType = ReadType();
if (!reader.TryReadCompressedUInt32(out num))
break;
result = new ValueArraySig(nextType, num);
break;
case ElementType.Module:
if (!reader.TryReadCompressedUInt32(out num))
break;
result = new ModuleSig(num, ReadType());
break;
case ElementType.GenericInst:
nextType = ReadType();
if (!reader.TryReadCompressedUInt32(out num) || num > 0x10000 || num > reader.BytesLeft)
break;
var genericInstSig = new GenericInstSig(nextType as ClassOrValueTypeSig, num);
var args = genericInstSig.GenericArguments;
for (i = 0; i < num; i++)
args.Add(ReadType());
result = genericInstSig;
break;
case ElementType.Array:
nextType = ReadType();
uint rank;
if (!reader.TryReadCompressedUInt32(out rank))
break;
if (rank > MaxArrayRank)
break;
if (rank == 0) {
result = new ArraySig(nextType, rank);
break;
}
if (!reader.TryReadCompressedUInt32(out num))
break;
if (num > rank)
break;
var sizes = new List((int)num);
for (i = 0; i < num; i++) {
if (!reader.TryReadCompressedUInt32(out uint size))
goto exit;
sizes.Add(size);
}
if (!reader.TryReadCompressedUInt32(out num))
break;
if (num > rank)
break;
var lowerBounds = new List((int)num);
for (i = 0; i < num; i++) {
if (!reader.TryReadCompressedInt32(out int size))
goto exit;
lowerBounds.Add(size);
}
result = new ArraySig(nextType, rank, sizes, lowerBounds);
break;
case ElementType.Internal:
IntPtr address;
if (IntPtr.Size == 4)
address = new IntPtr(reader.ReadInt32());
else
address = new IntPtr(reader.ReadInt64());
result = helper.ConvertRTInternalAddress(address);
break;
case ElementType.End:
case ElementType.R:
default:
result = null;
break;
}
exit:
recursionCounter.Decrement();
return result;
}
ITypeDefOrRef ReadTypeDefOrRef(bool allowTypeSpec) {
if (!reader.TryReadCompressedUInt32(out uint codedToken))
return null;
if (!allowTypeSpec && CodedToken.TypeDefOrRef.Decode2(codedToken).Table == Table.TypeSpec)
return null;
return helper.ResolveTypeDefOrRef(codedToken, default);
}
}
}