// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Threading; using dnlib.Utils; using dnlib.DotNet.MD; using dnlib.DotNet.Pdb; using dnlib.DotNet.Writer; using dnlib.PE; using dnlib.Threading; using dnlib.W32Resources; using System.Diagnostics; namespace dnlib.DotNet { /// /// A high-level representation of a row in the Module table /// public abstract class ModuleDef : IHasCustomAttribute, IHasCustomDebugInformation, IResolutionScope, IDisposable, IListListener, IModule, ITypeDefFinder, IDnlibDef, ITokenResolver, ISignatureReaderHelper { /// Default characteristics protected const Characteristics DefaultCharacteristics = Characteristics.ExecutableImage | Characteristics.Bit32Machine; /// Default DLL characteristics protected const DllCharacteristics DefaultDllCharacteristics = DllCharacteristics.TerminalServerAware | DllCharacteristics.NoSeh | DllCharacteristics.NxCompat | DllCharacteristics.DynamicBase; /// /// The row id in its table /// protected uint rid; #if THREAD_SAFE readonly Lock theLock = Lock.Create(); #endif /// /// Initialize this in the ctor /// protected ICorLibTypes corLibTypes; /// /// PDB state /// protected PdbState pdbState; TypeDefFinder typeDefFinder; /// /// Array of last used rid in each table. I.e., next free rid is value + 1 /// protected readonly int[] lastUsedRids = new int[64]; /// Module context protected ModuleContext context; /// public MDToken MDToken => new MDToken(Table.Module, rid); /// public uint Rid { get => rid; set => rid = value; } /// public int HasCustomAttributeTag => 7; /// public int ResolutionScopeTag => 0; /// /// Gets/sets a user value. This is never used by dnlib. This property isn't thread safe. /// public object Tag { get => tag; set => tag = value; } object tag; /// public ScopeType ScopeType => ScopeType.ModuleDef; /// public string ScopeName => FullName; /// /// Gets/sets Module.Generation column /// public ushort Generation { get => generation; set => generation = value; } /// protected ushort generation; /// /// Gets/sets Module.Name column /// public UTF8String Name { get => name; set => name = value; } /// Name protected UTF8String name; /// /// Gets/sets Module.Mvid column /// public Guid? Mvid { get => mvid; set => mvid = value; } /// protected Guid? mvid; /// /// Gets/sets Module.EncId column /// public Guid? EncId { get => encId; set => encId = value; } /// protected Guid? encId; /// /// Gets/sets Module.EncBaseId column /// public Guid? EncBaseId { get => encBaseId; set => encBaseId = value; } /// protected Guid? encBaseId; /// /// Gets all custom attributes /// public CustomAttributeCollection CustomAttributes { get { if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } } /// protected CustomAttributeCollection customAttributes; /// Initializes protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); /// public int HasCustomDebugInformationTag => 7; /// public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; /// /// Gets all custom debug infos /// public IList CustomDebugInfos { get { if (customDebugInfos is null) InitializeCustomDebugInfos(); return customDebugInfos; } } /// protected IList customDebugInfos; /// Initializes protected virtual void InitializeCustomDebugInfos() => Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// Gets the module's assembly. To set this value, add this /// to . /// public AssemblyDef Assembly { get => assembly; internal set => assembly = value; } /// protected AssemblyDef assembly; /// /// Gets a list of all non-nested s. See also /// public IList Types { get { if (types is null) InitializeTypes(); return types; } } /// protected LazyList types; /// Initializes protected virtual void InitializeTypes() => Interlocked.CompareExchange(ref types, new LazyList(this), null); /// /// Gets a list of all s /// public IList ExportedTypes { get { if (exportedTypes is null) InitializeExportedTypes(); return exportedTypes; } } /// protected IList exportedTypes; /// Initializes protected virtual void InitializeExportedTypes() => Interlocked.CompareExchange(ref exportedTypes, new List(), null); /// /// Gets/sets the native entry point. Only one of and /// can be set. You write to one and the other one gets cleared. /// public RVA NativeEntryPoint { get { if (!nativeAndManagedEntryPoint_initialized) InitializeNativeAndManagedEntryPoint(); return nativeEntryPoint; } set { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif nativeEntryPoint = value; managedEntryPoint = null; Cor20HeaderFlags |= ComImageFlags.NativeEntryPoint; nativeAndManagedEntryPoint_initialized = true; #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } } /// /// Gets/sets the managed entry point. Only one of and /// can be set. You write to one and the other one gets cleared. /// public IManagedEntryPoint ManagedEntryPoint { get { if (!nativeAndManagedEntryPoint_initialized) InitializeNativeAndManagedEntryPoint(); return managedEntryPoint; } set { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif nativeEntryPoint = 0; managedEntryPoint = value; Cor20HeaderFlags &= ~ComImageFlags.NativeEntryPoint; nativeAndManagedEntryPoint_initialized = true; #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } } /// protected RVA nativeEntryPoint; /// protected IManagedEntryPoint managedEntryPoint; /// protected bool nativeAndManagedEntryPoint_initialized; void InitializeNativeAndManagedEntryPoint() { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif if (nativeAndManagedEntryPoint_initialized) return; nativeEntryPoint = GetNativeEntryPoint_NoLock(); managedEntryPoint = GetManagedEntryPoint_NoLock(); nativeAndManagedEntryPoint_initialized = true; #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } /// Called to initialize protected virtual RVA GetNativeEntryPoint_NoLock() => 0; /// Called to initialize protected virtual IManagedEntryPoint GetManagedEntryPoint_NoLock() => null; /// public bool HasCustomAttributes => CustomAttributes.Count > 0; /// /// Gets/sets the entry point method /// public MethodDef EntryPoint { get => ManagedEntryPoint as MethodDef; set => ManagedEntryPoint = value; } /// /// true if is non-zero /// public bool IsNativeEntryPointValid => NativeEntryPoint != 0; /// /// true if is non-null /// public bool IsManagedEntryPointValid => ManagedEntryPoint is not null; /// /// true if is non-null /// public bool IsEntryPointValid => EntryPoint is not null; /// /// Gets a list of all s /// public ResourceCollection Resources { get { if (resources is null) InitializeResources(); return resources; } } /// protected ResourceCollection resources; /// Initializes protected virtual void InitializeResources() => Interlocked.CompareExchange(ref resources, new ResourceCollection(), null); /// /// Gets/sets the . This is null if there are no /// vtable fixups. /// public VTableFixups VTableFixups { get { if (!vtableFixups_isInitialized) InitializeVTableFixups(); return vtableFixups; } set { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif vtableFixups = value; vtableFixups_isInitialized = true; #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } } /// protected VTableFixups vtableFixups; /// protected bool vtableFixups_isInitialized; void InitializeVTableFixups() { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif if (vtableFixups_isInitialized) return; vtableFixups = GetVTableFixups_NoLock(); vtableFixups_isInitialized = true; #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } /// Called to initialize protected virtual VTableFixups GetVTableFixups_NoLock() => null; /// /// true if there's at least one in /// public bool HasTypes => Types.Count > 0; /// /// true if there's at least one in /// public bool HasExportedTypes => ExportedTypes.Count > 0; /// /// true if there's at least one in /// public bool HasResources => Resources.Count > 0; /// public string FullName => UTF8String.ToSystemStringOrEmpty(name); /// /// Gets/sets the path of the module or an empty string if it wasn't loaded from disk /// public string Location { get => location; set => location = value; } /// protected string location; /// /// Gets the /// public ICorLibTypes CorLibTypes => corLibTypes; /// /// Gets the instance /// TypeDefFinder TypeDefFinder { get { if (typeDefFinder is null) Interlocked.CompareExchange(ref typeDefFinder, new TypeDefFinder(Types), null); return typeDefFinder; } } /// /// Gets/sets the module context. This is never null. /// public ModuleContext Context { get { if (context is null) Interlocked.CompareExchange(ref context, new ModuleContext(), null); return context; } set => context = value ?? new ModuleContext(); } /// /// If true, the cache is enabled. The cache is used by /// and to find types. ///

/// IMPORTANT: Only enable the cache if this module's types keep their exact /// name, namespace, and declaring type and if no type is either added or /// removed from or from any type that is reachable from the /// top-level types in (i.e., any type owned by this module). /// This is disabled by default. When disabled, all calls to /// and will result in a slow O(n) (linear) search. ///
/// public bool EnableTypeDefFindCache { get => TypeDefFinder.IsCacheEnabled; set => TypeDefFinder.IsCacheEnabled = value; } /// /// true if this is the manifest (main) module /// public bool IsManifestModule { get { var asm = assembly; return asm is not null && asm.ManifestModule == this; } } /// /// Gets the global (aka. <Module>) type or null if there are no types /// public TypeDef GlobalType => Types.Count == 0 ? null : Types[0]; /// /// true if it's the core library module, false if it's not the core library module, /// and null if it's not known. /// public bool? IsCoreLibraryModule { get; set; } /// /// Gets/sets the Win32 resources /// public Win32Resources Win32Resources { get { if (!win32Resources_isInitialized) InitializeWin32Resources(); return win32Resources; } set { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif win32Resources = value; win32Resources_isInitialized = true; #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } } /// protected Win32Resources win32Resources; /// protected bool win32Resources_isInitialized; void InitializeWin32Resources() { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif if (win32Resources_isInitialized) return; win32Resources = GetWin32Resources_NoLock(); win32Resources_isInitialized = true; #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } /// Called to initialize protected virtual Win32Resources GetWin32Resources_NoLock() => null; /// /// Gets the . This is null if no PDB file /// has been loaded or if no PDB file could be found. /// public PdbState PdbState => pdbState; /// /// Module kind /// public ModuleKind Kind { get; set; } /// /// Gets/sets the characteristics (from PE file header) /// public Characteristics Characteristics { get; set; } /// /// Gets/sets the DLL characteristics (from PE optional header) /// public DllCharacteristics DllCharacteristics { get; set; } /// /// Gets/sets the runtime version which is stored in the metadata header. /// See . /// /// Not thread safe public string RuntimeVersion { get => runtimeVersion; set { if (runtimeVersion != value) { runtimeVersion = value; cachedWinMDStatus = null; runtimeVersionWinMD = null; winMDVersion = null; } } } string runtimeVersion; /// /// Gets the WinMD status /// /// Not thread safe public WinMDStatus WinMDStatus { get { var cval = cachedWinMDStatus; if (cval is not null) return cval.Value; cachedWinMDStatus = cval = CalculateWinMDStatus(RuntimeVersion); return cval.Value; } } WinMDStatus? cachedWinMDStatus; /// /// true if this is a WinMD file /// public bool IsWinMD => WinMDStatus != WinMDStatus.None; /// /// true if this is a managed WinMD file /// public bool IsManagedWinMD => WinMDStatus == WinMDStatus.Managed; /// /// true if this is a pure (non-managed) WinMD file /// public bool IsPureWinMD => WinMDStatus == WinMDStatus.Pure; /// /// Gets the CLR runtime version of the managed WinMD file or null if none. This is /// similar to for normal non-WinMD files. /// /// Not thread safe public string RuntimeVersionWinMD { get { var rtver = runtimeVersionWinMD; if (rtver is not null) return rtver; runtimeVersionWinMD = rtver = CalculateRuntimeVersionWinMD(RuntimeVersion); return rtver; } } string runtimeVersionWinMD; /// /// Gets the WinMD version or null if none /// /// Not thread safe public string WinMDVersion { get { var ver = winMDVersion; if (ver is not null) return ver; winMDVersion = ver = CalculateWinMDVersion(RuntimeVersion); return ver; } } string winMDVersion; static WinMDStatus CalculateWinMDStatus(string version) { if (version is null) return WinMDStatus.None; if (!version.StartsWith("WindowsRuntime ", StringComparison.Ordinal)) return WinMDStatus.None; return version.IndexOf(';') < 0 ? WinMDStatus.Pure : WinMDStatus.Managed; } static string CalculateRuntimeVersionWinMD(string version) { // Original parser code: // CoreCLR file: src/md/winmd/adapter.cpp // Func: WinMDAdapter::Create(IMDCommon *pRawMDCommon, /*[out]*/ WinMDAdapter **ppAdapter) if (version is null) return null; if (!version.StartsWith("WindowsRuntime ", StringComparison.Ordinal)) return null; int index = version.IndexOf(';'); if (index < 0) return null; var s = version.Substring(index + 1); if (s.StartsWith("CLR", StringComparison.OrdinalIgnoreCase)) s = s.Substring(3); s = s.TrimStart(' '); return s; } static string CalculateWinMDVersion(string version) { if (version is null) return null; if (!version.StartsWith("WindowsRuntime ", StringComparison.Ordinal)) return null; int index = version.IndexOf(';'); if (index < 0) return version; return version.Substring(0, index); } /// /// true if is the CLR v1.0 string (only the major /// and minor version numbers are checked) /// public bool IsClr10 { get { var ver = RuntimeVersion ?? string.Empty; return ver.StartsWith(MDHeaderRuntimeVersion.MS_CLR_10_PREFIX) || ver.StartsWith(MDHeaderRuntimeVersion.MS_CLR_10_PREFIX_X86RETAIL) || ver == MDHeaderRuntimeVersion.MS_CLR_10_RETAIL || ver == MDHeaderRuntimeVersion.MS_CLR_10_COMPLUS; } } /// /// true if is the CLR v1.0 string /// public bool IsClr10Exactly => RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10 || RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10_X86RETAIL || RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10_RETAIL || RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10_COMPLUS; /// /// true if is the CLR v1.1 string (only the major /// and minor version numbers are checked) /// public bool IsClr11 => (RuntimeVersion ?? string.Empty).StartsWith(MDHeaderRuntimeVersion.MS_CLR_11_PREFIX); /// /// true if is the CLR v1.1 string /// public bool IsClr11Exactly => RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_11; /// /// true if is the CLR v1.0 or v1.1 string (only the /// major and minor version numbers are checked) /// public bool IsClr1x => IsClr10 || IsClr11; /// /// true if is the CLR v1.0 or v1.1 string /// public bool IsClr1xExactly => IsClr10Exactly || IsClr11Exactly; /// /// true if is the CLR v2.0 string (only the major /// and minor version numbers are checked) /// public bool IsClr20 => (RuntimeVersion ?? string.Empty).StartsWith(MDHeaderRuntimeVersion.MS_CLR_20_PREFIX); /// /// true if is the CLR v2.0 string /// public bool IsClr20Exactly => RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_20; /// /// true if is the CLR v4.0 string (only the major /// and minor version numbers are checked) /// public bool IsClr40 => (RuntimeVersion ?? string.Empty).StartsWith(MDHeaderRuntimeVersion.MS_CLR_40_PREFIX); /// /// true if is the CLR v4.0 string /// public bool IsClr40Exactly => RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_40; /// /// true if is the ECMA 2002 string /// public bool IsEcma2002 => RuntimeVersion == MDHeaderRuntimeVersion.ECMA_2002; /// /// true if is the ECMA 2005 string /// public bool IsEcma2005 => RuntimeVersion == MDHeaderRuntimeVersion.ECMA_2005; /// /// Gets/sets the (from PE header) /// public Machine Machine { get; set; } /// /// true if is , , ... /// public bool IsI386 => Machine.IsI386(); /// /// true if is /// public bool IsIA64 => Machine == Machine.IA64; /// /// true if is , , ... /// public bool IsAMD64 => Machine.IsAMD64(); /// /// true if is , , ... /// public bool IsARM => Machine.IsARMNT(); /// /// true if is , , ... /// public bool IsARM64 => Machine.IsARM64(); /// /// true if is s390x, , ... /// public bool IsS390x => Machine.IsS390x(); /// /// Gets/sets the (from .NET header) /// public ComImageFlags Cor20HeaderFlags { get => (ComImageFlags)cor20HeaderFlags; set => cor20HeaderFlags = (int)value; } /// protected int cor20HeaderFlags; /// /// Gets/sets the runtime version number in the COR20 header. The major version is /// in the high 16 bits. The minor version is in the low 16 bits. This is normally 2.5 /// (0x00020005), but if it's .NET Framework 1.x, it should be 2.0 (0x00020000). If this is /// null, the default value will be used when saving the module (2.0 if CLR 1.x, /// and 2.5 if not CLR 1.x). /// public uint? Cor20HeaderRuntimeVersion { get; set; } /// /// Gets the tables header version. The major version is in the upper 8 bits and the /// minor version is in the lower 8 bits. .NET Framework 1.0/1.1 use version 1.0 (0x0100) and /// .NET Framework 2.x and later use version 2.0 (0x0200). 1.0 has no support for generics, /// 1.1 has support for generics (GenericParam rows have an extra Kind column), /// and 2.0 has support for generics (GenericParam rows have the standard 4 columns). /// No other version is supported. If this is null, the default version is /// used (1.0 if .NET Framework 1.x, else 2.0). /// public ushort? TablesHeaderVersion { get; set; } /// /// Set or clear flags in /// /// true if flags should be set, false if flags should /// be cleared /// Flags to set or clear void ModifyComImageFlags(bool set, ComImageFlags flags) { #if THREAD_SAFE int origVal, newVal; do { origVal = cor20HeaderFlags; if (set) newVal = origVal | (int)flags; else newVal = origVal & ~(int)flags; } while (Interlocked.CompareExchange(ref cor20HeaderFlags, newVal, origVal) != origVal); #else if (set) cor20HeaderFlags |= (int)flags; else cor20HeaderFlags &= ~(int)flags; #endif } /// /// Gets/sets the bit /// public bool IsILOnly { get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.ILOnly) != 0; set => ModifyComImageFlags(value, ComImageFlags.ILOnly); } /// /// Gets/sets the bit /// public bool Is32BitRequired { get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.Bit32Required) != 0; set => ModifyComImageFlags(value, ComImageFlags.Bit32Required); } /// /// Gets/sets the bit /// public bool IsStrongNameSigned { get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.StrongNameSigned) != 0; set => ModifyComImageFlags(value, ComImageFlags.StrongNameSigned); } /// /// Gets/sets the bit /// public bool HasNativeEntryPoint { get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.NativeEntryPoint) != 0; set => ModifyComImageFlags(value, ComImageFlags.NativeEntryPoint); } /// /// Gets/sets the bit /// public bool Is32BitPreferred { get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.Bit32Preferred) != 0; set => ModifyComImageFlags(value, ComImageFlags.Bit32Preferred); } /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Dispose method /// /// true if called by protected virtual void Dispose(bool disposing) { if (!disposing) return; var tdf = typeDefFinder; if (tdf is not null) { tdf.Dispose(); typeDefFinder = null; } pdbState?.Dispose(); pdbState = null; } /// /// Gets all the types (including nested types) present in this module /// public IEnumerable GetTypes() => AllTypesHelper.Types(Types); /// /// Adds as a non-nested type. If it's already nested, its /// will be set to null. /// /// The to insert public void AddAsNonNestedType(TypeDef typeDef) { if (typeDef is null) return; typeDef.DeclaringType = null; Types.Add(typeDef); } /// /// Updates the rid to the next free rid available. It's only updated if /// the original rid is 0. /// /// IMDTokenProvider /// The row that should be updated /// Returns the input public T UpdateRowId(T tableRow) where T : IMDTokenProvider { if (tableRow != null && tableRow.Rid == 0) tableRow.Rid = GetNextFreeRid(tableRow.MDToken.Table); return tableRow; } /// /// Updates the rid to the next free rid available. /// /// IMDTokenProvider /// The row that should be updated /// Returns the input public T ForceUpdateRowId(T tableRow) where T : IMDTokenProvider { if (tableRow != null) tableRow.Rid = GetNextFreeRid(tableRow.MDToken.Table); return tableRow; } uint GetNextFreeRid(Table table) { var lastUsedRids = this.lastUsedRids; if ((uint)table >= lastUsedRids.Length) return 0; return (uint)Interlocked.Increment(ref lastUsedRids[(int)table]) & 0x00FFFFFF; } /// /// Imports a as a /// /// The type /// The imported type or null if is invalid public ITypeDefOrRef Import(Type type) => new Importer(this).Import(type); /// /// Imports a as a /// /// The type /// The imported type or null if is invalid public TypeSig ImportAsTypeSig(Type type) => new Importer(this).ImportAsTypeSig(type); /// /// Imports a as a /// /// The field /// The imported field or null if is invalid /// or if we failed to import the field public MemberRef Import(FieldInfo fieldInfo) => (MemberRef)new Importer(this).Import(fieldInfo); /// /// Imports a as a . This will be either /// a or a . /// /// The method /// The imported method or null if is invalid /// or if we failed to import the method public IMethod Import(MethodBase methodBase) => new Importer(this).Import(methodBase); /// /// Imports a /// /// The type /// The imported type or null public IType Import(IType type) => new Importer(this).Import(type); /// /// Imports a as a /// /// The type /// The imported type or null public TypeRef Import(TypeDef type) => (TypeRef)new Importer(this).Import(type); /// /// Imports a /// /// The type /// The imported type or null public TypeRef Import(TypeRef type) => (TypeRef)new Importer(this).Import(type); /// /// Imports a /// /// The type /// The imported type or null public TypeSpec Import(TypeSpec type) => new Importer(this).Import(type); /// /// Imports a /// /// The type /// The imported type or null public TypeSig Import(TypeSig type) => new Importer(this).Import(type); /// /// Imports a /// /// The field /// The imported type or null if is invalid public MemberRef Import(IField field) => (MemberRef)new Importer(this).Import(field); /// /// Imports a as a /// /// The field /// The imported type or null if is invalid public MemberRef Import(FieldDef field) => (MemberRef)new Importer(this).Import(field); /// /// Imports a /// /// The method /// The imported method or null if is invalid public IMethod Import(IMethod method) => new Importer(this).Import(method); /// /// Imports a as a /// /// The method /// The imported method or null if is invalid public MemberRef Import(MethodDef method) => (MemberRef)new Importer(this).Import(method); /// /// Imports a /// /// The method /// The imported method or null if is invalid public MethodSpec Import(MethodSpec method) => new Importer(this).Import(method); /// /// Imports a /// /// The member ref /// The imported member ref or null if is invalid public MemberRef Import(MemberRef memberRef) => new Importer(this).Import(memberRef); /// /// Writes the module to a file on disk. If the file exists, it will be overwritten. /// /// Filename public void Write(string filename) => Write(filename, null); /// /// Writes the module to a file on disk. If the file exists, it will be overwritten. /// /// Filename /// Writer options public void Write(string filename, ModuleWriterOptions options) { var writer = new ModuleWriter(this, options ?? new ModuleWriterOptions(this)); writer.Write(filename); } /// /// Writes the module to a stream. /// /// Destination stream public void Write(Stream dest) => Write(dest, null); /// /// Writes the module to a stream. /// /// Destination stream /// Writer options public void Write(Stream dest, ModuleWriterOptions options) { var writer = new ModuleWriter(this, options ?? new ModuleWriterOptions(this)); writer.Write(dest); } /// /// Resets the cache which can be enabled by setting /// to true. Use this method if the cache is /// enabled but some of the types have been modified (eg. removed, added, renamed). /// public void ResetTypeDefFindCache() => TypeDefFinder.ResetCache(); /// /// Finds a /// /// Type /// Name /// Language ID /// The or null if none found public ResourceData FindWin32ResourceData(ResourceName type, ResourceName name, ResourceName langId) => Win32Resources?.Find(type, name, langId); /// /// Creates a new /// /// PDB file kind public void CreatePdbState(PdbFileKind pdbFileKind) => SetPdbState(new PdbState(this, pdbFileKind)); /// /// Sets a /// /// New public void SetPdbState(PdbState pdbState) { if (pdbState is null) throw new ArgumentNullException(nameof(pdbState)); var orig = Interlocked.CompareExchange(ref this.pdbState, pdbState, null); if (orig is not null) throw new InvalidOperationException("PDB file has already been initialized"); } uint GetCor20RuntimeVersion() { var rtVer = Cor20HeaderRuntimeVersion; if (rtVer is not null) return rtVer.Value; return IsClr1x ? 0x00020000U : 0x00020005; } /// /// Returns the size of a pointer. Assumes it's 32-bit if pointer size is unknown or /// if it can be 32-bit or 64-bit. /// /// Size of a pointer (4 or 8) public int GetPointerSize() => GetPointerSize(4); /// /// Returns the size of a pointer /// /// Default pointer size if it's not known or if it /// can be 32-bit or 64-bit /// Size of a pointer (4 or 8) public int GetPointerSize(int defaultPointerSize) => GetPointerSize(defaultPointerSize, defaultPointerSize); /// /// Returns the size of a pointer /// /// Default pointer size /// Pointer size if it's prefer-32-bit (should usually be 4) /// public int GetPointerSize(int defaultPointerSize, int prefer32bitPointerSize) { var machine = Machine; if (machine.Is64Bit()) return 8; if (!machine.IsI386()) return 4; // Machine is I386 so it's either x86 or platform neutral // If the runtime version is < 2.5, then it's always loaded as a 32-bit process. if (GetCor20RuntimeVersion() < 0x00020005) return 4; // If it's a 32-bit PE header, and ILOnly is cleared, it's always loaded as a // 32-bit process. var flags = (ComImageFlags)cor20HeaderFlags; if ((flags & ComImageFlags.ILOnly) == 0) return 4; // 32-bit Preferred flag is new in .NET Framework 4.5. See CorHdr.h in Windows SDK for more info switch (flags & (ComImageFlags.Bit32Required | ComImageFlags.Bit32Preferred)) { case 0: // Machine and ILOnly flag should be checked break; case ComImageFlags.Bit32Preferred: // Illegal break; case ComImageFlags.Bit32Required: // x86 image (32-bit process) return 4; case ComImageFlags.Bit32Required | ComImageFlags.Bit32Preferred: // Platform neutral but prefers to be 32-bit return prefer32bitPointerSize; } return defaultPointerSize; } /// void IListListener.OnLazyAdd(int index, ref TypeDef value) { #if DEBUG if (value.DeclaringType is not null) throw new InvalidOperationException("Added type's DeclaringType is not null"); #endif value.Module2 = this; } /// void IListListener.OnAdd(int index, TypeDef value) { if (value.DeclaringType is not null) throw new InvalidOperationException("Nested type is already owned by another type. Set DeclaringType to null first."); if (value.Module is not null) throw new InvalidOperationException("Type is already owned by another module. Remove it from that module's type list."); value.Module2 = this; } /// void IListListener.OnRemove(int index, TypeDef value) => value.Module2 = null; /// void IListListener.OnResize(int index) { } /// void IListListener.OnClear() { foreach (var type in types.GetEnumerable_NoLock()) type.Module2 = null; } /// /// Finds a . For speed, enable /// if possible (read the documentation first). /// /// Full name of the type (no assembly information) /// true if it's a reflection name, and nested /// type names are separated by a + character. If false, nested type names /// are separated by a / character. /// An existing or null if it wasn't found. public TypeDef Find(string fullName, bool isReflectionName) => TypeDefFinder.Find(fullName, isReflectionName); /// /// Finds a . Its scope (i.e., module or assembly) is ignored when /// looking up the type. For speed, enable if possible /// (read the documentation first). /// /// The type ref /// An existing or null if it wasn't found. public TypeDef Find(TypeRef typeRef) => TypeDefFinder.Find(typeRef); /// /// Finds a /// /// The type /// A or null if it wasn't found public TypeDef Find(ITypeDefOrRef typeRef) { if (typeRef is TypeDef td) return td.Module == this ? td : null; if (typeRef is TypeRef tr) return Find(tr); var ts = typeRef as TypeSpec; if (ts is null) return null; var sig = ts.TypeSig as TypeDefOrRefSig; if (sig is null) return null; td = sig.TypeDef; if (td is not null) return td.Module == this ? td : null; tr = sig.TypeRef; if (tr is not null) return Find(tr); return null; } /// /// Creates a new instance. There should normally only be one /// instance shared by all s. /// /// A new instance public static ModuleContext CreateModuleContext() { var ctx = new ModuleContext(); var asmRes = new AssemblyResolver(ctx); var res = new Resolver(asmRes); ctx.AssemblyResolver = asmRes; ctx.Resolver = res; asmRes.DefaultModuleContext = ctx; return ctx; } /// /// Load everything in this module. All types, fields, asm refs, etc are loaded, all their /// properties are read to make sure everything is cached. /// /// Cancellation token or null public virtual void LoadEverything(ICancellationToken cancellationToken = null) => ModuleLoader.LoadAll(this, cancellationToken); /// public override string ToString() => FullName; /// /// Resolves a token /// /// The metadata token /// A or null if is invalid public IMDTokenProvider ResolveToken(MDToken mdToken) => ResolveToken(mdToken.Raw, new GenericParamContext()); /// /// Resolves a token /// /// The metadata token /// Generic parameter context /// A or null if is invalid public IMDTokenProvider ResolveToken(MDToken mdToken, GenericParamContext gpContext) => ResolveToken(mdToken.Raw, gpContext); /// /// Resolves a token /// /// The metadata token /// A or null if is invalid public IMDTokenProvider ResolveToken(int token) => ResolveToken((uint)token, new GenericParamContext()); /// /// Resolves a token /// /// The metadata token /// Generic parameter context /// A or null if is invalid public IMDTokenProvider ResolveToken(int token, GenericParamContext gpContext) => ResolveToken((uint)token, gpContext); /// /// Resolves a token /// /// The metadata token /// A or null if is invalid public IMDTokenProvider ResolveToken(uint token) => ResolveToken(token, new GenericParamContext()); /// /// Resolves a token /// /// The metadata token /// Generic parameter context /// A or null if is invalid public virtual IMDTokenProvider ResolveToken(uint token, GenericParamContext gpContext) => null; /// /// Gets all s /// public IEnumerable GetAssemblyRefs() { for (uint rid = 1; ; rid++) { var asmRef = ResolveToken(new MDToken(Table.AssemblyRef, rid).Raw) as AssemblyRef; if (asmRef is null) break; yield return asmRef; } } /// /// Gets all s /// public IEnumerable GetModuleRefs() { for (uint rid = 1; ; rid++) { var modRef = ResolveToken(new MDToken(Table.ModuleRef, rid).Raw) as ModuleRef; if (modRef is null) break; yield return modRef; } } /// /// Gets all s. s with generic parameters /// aren't cached and a new copy is always returned. /// public IEnumerable GetMemberRefs() => GetMemberRefs(new GenericParamContext()); /// /// Gets all s. s with generic parameters /// aren't cached and a new copy is always returned. /// /// Generic parameter context public IEnumerable GetMemberRefs(GenericParamContext gpContext) { for (uint rid = 1; ; rid++) { var mr = ResolveToken(new MDToken(Table.MemberRef, rid).Raw, gpContext) as MemberRef; if (mr is null) break; yield return mr; } } /// /// Gets all s /// public IEnumerable GetTypeRefs() { for (uint rid = 1; ; rid++) { var mr = ResolveToken(new MDToken(Table.TypeRef, rid).Raw) as TypeRef; if (mr is null) break; yield return mr; } } /// /// Finds an assembly reference by name. If there's more than one, pick the one with /// the greatest version number. /// /// Simple name of assembly (eg. "mscorlib") /// The found or null if there's no such /// assembly reference. public AssemblyRef GetAssemblyRef(UTF8String simpleName) { AssemblyRef found = null; foreach (var asmRef in GetAssemblyRefs()) { if (asmRef.Name != simpleName) continue; if (IsGreaterAssemblyRefVersion(found, asmRef)) found = asmRef; } return found; } /// /// Compare asm refs' version /// /// First asm ref /// New asm ref /// protected static bool IsGreaterAssemblyRefVersion(AssemblyRef found, AssemblyRef newOne) { if (found is null) return true; var foundVer = found.Version; var newVer = newOne.Version; return foundVer is null || (newVer is not null && newVer >= foundVer); } ITypeDefOrRef ISignatureReaderHelper.ResolveTypeDefOrRef(uint codedToken, GenericParamContext gpContext) { if (!CodedToken.TypeDefOrRef.Decode(codedToken, out uint token)) return null; return ResolveToken(token) as ITypeDefOrRef; } TypeSig ISignatureReaderHelper.ConvertRTInternalAddress(IntPtr address) => null; } /// /// A Module row created by the user and not present in the original .NET file /// public class ModuleDefUser : ModuleDef { /// /// Default constructor /// public ModuleDefUser() : this(null, null) { } /// /// Constructor /// /// is initialized to a random /// Module nam public ModuleDefUser(UTF8String name) : this(name, Guid.NewGuid()) { } /// /// Constructor /// /// Module name /// Module version ID public ModuleDefUser(UTF8String name, Guid? mvid) : this(name, mvid, null) { } /// /// Constructor /// /// Module name /// Module version ID /// Corlib assembly ref or null public ModuleDefUser(UTF8String name, Guid? mvid, AssemblyRef corLibAssemblyRef) { Kind = ModuleKind.Windows; Characteristics = DefaultCharacteristics; DllCharacteristics = DefaultDllCharacteristics; RuntimeVersion = MDHeaderRuntimeVersion.MS_CLR_20; Machine = Machine.I386; cor20HeaderFlags = (int)ComImageFlags.ILOnly; Cor20HeaderRuntimeVersion = 0x00020005; // .NET Framework 2.0 or later should use 2.5 TablesHeaderVersion = 0x0200; // .NET Framework 2.0 or later should use 2.0 types = new LazyList(this); exportedTypes = new LazyList(); resources = new ResourceCollection(); corLibTypes = new CorLibTypes(this, corLibAssemblyRef); types = new LazyList(this); this.name = name; this.mvid = mvid; types.Add(CreateModuleType()); UpdateRowId(this); } TypeDef CreateModuleType() { var type = UpdateRowId(new TypeDefUser(UTF8String.Empty, "", null)); type.Attributes = TypeAttributes.NotPublic | TypeAttributes.AutoLayout | TypeAttributes.Class | TypeAttributes.AnsiClass; return type; } } /// /// Created from a row in the Module table /// public class ModuleDefMD2 : ModuleDef, IMDTokenProviderMD { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; /// public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Module, origRid); var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } /// protected override void InitializeCustomDebugInfos() { var list = new List(); readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); Interlocked.CompareExchange(ref customDebugInfos, list, null); } /// protected override RVA GetNativeEntryPoint_NoLock() => readerModule.GetNativeEntryPoint(); /// protected override IManagedEntryPoint GetManagedEntryPoint_NoLock() => readerModule.GetManagedEntryPoint(); /// /// Constructor /// /// The module which contains this Module row /// Row ID /// If is null /// If is invalid internal ModuleDefMD2(ModuleDefMD readerModule, uint rid) { if (rid == 1 && readerModule is null) readerModule = (ModuleDefMD)this; #if DEBUG if (readerModule is null) throw new ArgumentNullException("readerModule"); if (rid != 1 && readerModule.TablesStream.ModuleTable.IsInvalidRID(rid)) throw new BadImageFormatException($"Module rid {rid} does not exist"); #endif origRid = rid; this.rid = rid; this.readerModule = readerModule; if (rid != 1) { Kind = ModuleKind.Windows; Characteristics = DefaultCharacteristics; DllCharacteristics = DefaultDllCharacteristics; RuntimeVersion = MDHeaderRuntimeVersion.MS_CLR_20; Machine = Machine.I386; cor20HeaderFlags = (int)ComImageFlags.ILOnly; Cor20HeaderRuntimeVersion = 0x00020005; // .NET Framework 2.0 or later should use 2.5 TablesHeaderVersion = 0x0200; // .NET Framework 2.0 or later should use 2.0 corLibTypes = new CorLibTypes(this); location = string.Empty; InitializeFromRawRow(); } } /// /// Initialize fields from the raw Module row /// protected void InitializeFromRawRow() { bool b = readerModule.TablesStream.TryReadModuleRow(origRid, out var row); Debug.Assert(b); generation = row.Generation; mvid = readerModule.GuidStream.Read(row.Mvid); encId = readerModule.GuidStream.Read(row.EncId); encBaseId = readerModule.GuidStream.Read(row.EncBaseId); name = readerModule.StringsStream.ReadNoNull(row.Name); if (origRid == 1) assembly = readerModule.ResolveAssembly(origRid); } } }