// dnlib: See LICENSE.txt for more info using System; using System.IO; using System.Reflection; using System.Threading; using dnlib.Utils; using dnlib.DotNet.MD; using dnlib.DotNet.Writer; using System.Text.RegularExpressions; using dnlib.DotNet.Pdb; using System.Collections.Generic; using System.Diagnostics; namespace dnlib.DotNet { /// /// A high-level representation of a row in the Assembly table /// public abstract class AssemblyDef : IHasCustomAttribute, IHasDeclSecurity, IHasCustomDebugInformation, IAssembly, IListListener, ITypeDefFinder, IDnlibDef { /// /// The row id in its table /// protected uint rid; /// public MDToken MDToken => new MDToken(Table.Assembly, rid); /// public uint Rid { get => rid; set => rid = value; } /// public int HasCustomAttributeTag => 14; /// public int HasDeclSecurityTag => 2; /// /// From column Assembly.HashAlgId /// public AssemblyHashAlgorithm HashAlgorithm { get => hashAlgorithm; set => hashAlgorithm = value; } /// protected AssemblyHashAlgorithm hashAlgorithm; /// /// From columns Assembly.MajorVersion, Assembly.MinorVersion, Assembly.BuildNumber, /// Assembly.RevisionNumber. /// /// If is null public Version Version { get => version; set => version = value ?? throw new ArgumentNullException(nameof(value)); } /// protected Version version; /// /// From column Assembly.Flags /// public AssemblyAttributes Attributes { get => (AssemblyAttributes)attributes; set => attributes = (int)value; } /// Attributes protected int attributes; /// /// From column Assembly.PublicKey /// /// An empty is created if the caller writes null public PublicKey PublicKey { get => publicKey; set => publicKey = value ?? new PublicKey(); } /// protected PublicKey publicKey; /// /// Gets the public key token which is calculated from /// public PublicKeyToken PublicKeyToken => publicKey.Token; /// /// From column Assembly.Name /// public UTF8String Name { get => name; set => name = value; } /// Name protected UTF8String name; /// /// From column Assembly.Locale /// public UTF8String Culture { get => culture; set => culture = value; } /// Name protected UTF8String culture; /// public IList DeclSecurities { get { if (declSecurities is null) InitializeDeclSecurities(); return declSecurities; } } /// protected IList declSecurities; /// Initializes protected virtual void InitializeDeclSecurities() => Interlocked.CompareExchange(ref declSecurities, new List(), null); /// public PublicKeyBase PublicKeyOrToken => publicKey; /// public string FullName => GetFullNameWithPublicKeyToken(); /// public string FullNameToken => GetFullNameWithPublicKeyToken(); /// /// Gets all modules. The first module is always the . /// public IList Modules { get { if (modules is null) InitializeModules(); return modules; } } /// protected LazyList modules; /// Initializes protected virtual void InitializeModules() => Interlocked.CompareExchange(ref modules, new LazyList(this), null); /// /// 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 bool HasCustomAttributes => CustomAttributes.Count > 0; /// public int HasCustomDebugInformationTag => 14; /// 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); /// public bool HasDeclSecurities => DeclSecurities.Count > 0; /// /// true if is not empty /// public bool HasModules => Modules.Count > 0; /// /// Gets the manifest (main) module. This is always the first module in . /// null is returned if is empty. /// public ModuleDef ManifestModule => Modules.Count == 0 ? null : Modules[0]; /// /// Modify property: = /// ( & ) | . /// /// Value to AND /// Value to OR void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) => attributes = (attributes & (int)andMask) | (int)orMask; /// /// Set or clear flags in /// /// true if flags should be set, false if flags should /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, AssemblyAttributes flags) { if (set) attributes |= (int)flags; else attributes &= ~(int)flags; } /// /// Gets/sets the bit /// public bool HasPublicKey { get => ((AssemblyAttributes)attributes & AssemblyAttributes.PublicKey) != 0; set => ModifyAttributes(value, AssemblyAttributes.PublicKey); } /// /// Gets/sets the processor architecture /// public AssemblyAttributes ProcessorArchitecture { get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask; set => ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask); } /// /// Gets/sets the processor architecture /// public AssemblyAttributes ProcessorArchitectureFull { get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_FullMask; set => ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask); } /// /// true if unspecified processor architecture /// public bool IsProcessorArchitectureNone => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None; /// /// true if neutral (PE32) architecture /// public bool IsProcessorArchitectureMSIL => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL; /// /// true if x86 (PE32) architecture /// public bool IsProcessorArchitectureX86 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86; /// /// true if IA-64 (PE32+) architecture /// public bool IsProcessorArchitectureIA64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64; /// /// true if x64 (PE32+) architecture /// public bool IsProcessorArchitectureX64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64; /// /// true if ARM (PE32) architecture /// public bool IsProcessorArchitectureARM => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM; /// /// true if eg. reference assembly (not runnable) /// public bool IsProcessorArchitectureNoPlatform => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform; /// /// Gets/sets the bit /// public bool IsProcessorArchitectureSpecified { get => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Specified) != 0; set => ModifyAttributes(value, AssemblyAttributes.PA_Specified); } /// /// Gets/sets the bit /// public bool EnableJITcompileTracking { get => ((AssemblyAttributes)attributes & AssemblyAttributes.EnableJITcompileTracking) != 0; set => ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking); } /// /// Gets/sets the bit /// public bool DisableJITcompileOptimizer { get => ((AssemblyAttributes)attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0; set => ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer); } /// /// Gets/sets the bit /// public bool IsRetargetable { get => ((AssemblyAttributes)attributes & AssemblyAttributes.Retargetable) != 0; set => ModifyAttributes(value, AssemblyAttributes.Retargetable); } /// /// Gets/sets the content type /// public AssemblyAttributes ContentType { get => (AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask; set => ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask); } /// /// true if content type is Default /// public bool IsContentTypeDefault => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default; /// /// true if content type is WindowsRuntime /// public bool IsContentTypeWindowsRuntime => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime; /// /// Finds a module in this assembly /// /// Name of module /// A instance or null if it wasn't found. public ModuleDef FindModule(UTF8String name) { var modules = Modules; int count = modules.Count; for (int i = 0; i < count; i++) { var module = modules[i]; if (module is null) continue; if (UTF8String.CaseInsensitiveEquals(module.Name, name)) return module; } return null; } /// /// Creates an instance from a file /// /// File name of an existing .NET assembly /// Module context or null /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) public static AssemblyDef Load(string fileName, ModuleContext context) => Load(fileName, new ModuleCreationOptions(context)); /// /// Creates an instance from a file /// /// File name of an existing .NET assembly /// Module creation options or null /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) public static AssemblyDef Load(string fileName, ModuleCreationOptions options = null) { if (fileName is null) throw new ArgumentNullException(nameof(fileName)); ModuleDef module = null; try { module = ModuleDefMD.Load(fileName, options); var asm = module.Assembly; if (asm is null) throw new BadImageFormatException($"{fileName} is only a .NET module, not a .NET assembly. Use ModuleDef.Load()."); return asm; } catch { if (module is not null) module.Dispose(); throw; } } /// /// Creates an instance from a byte[] /// /// Contents of a .NET assembly /// Module context or null /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) public static AssemblyDef Load(byte[] data, ModuleContext context) => Load(data, new ModuleCreationOptions(context)); /// /// Creates an instance from a byte[] /// /// Contents of a .NET assembly /// Module creation options or null /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) public static AssemblyDef Load(byte[] data, ModuleCreationOptions options = null) { if (data is null) throw new ArgumentNullException(nameof(data)); ModuleDef module = null; try { module = ModuleDefMD.Load(data, options); var asm = module.Assembly; if (asm is null) throw new BadImageFormatException($"{module.ToString()} is only a .NET module, not a .NET assembly. Use ModuleDef.Load()."); return asm; } catch { if (module is not null) module.Dispose(); throw; } } /// /// Creates an instance from a memory location /// /// Address of a .NET assembly /// Module context or null /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) public static AssemblyDef Load(IntPtr addr, ModuleContext context) => Load(addr, new ModuleCreationOptions(context)); /// /// Creates an instance from a memory location /// /// Address of a .NET assembly /// Module creation options or null /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) public static AssemblyDef Load(IntPtr addr, ModuleCreationOptions options = null) { if (addr == IntPtr.Zero) throw new ArgumentNullException(nameof(addr)); ModuleDef module = null; try { module = ModuleDefMD.Load(addr, options); var asm = module.Assembly; if (asm is null) throw new BadImageFormatException($"{module.ToString()} (addr: {addr.ToInt64():X8}) is only a .NET module, not a .NET assembly. Use ModuleDef.Load()."); return asm; } catch { if (module is not null) module.Dispose(); throw; } } /// /// Creates an instance from a stream /// /// This will read all bytes from the stream and call . /// It's better to use one of the other Load() methods. /// The stream /// Module context or null /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) public static AssemblyDef Load(Stream stream, ModuleContext context) => Load(stream, new ModuleCreationOptions(context)); /// /// Creates an instance from a stream /// /// This will read all bytes from the stream and call . /// It's better to use one of the other Load() methods. /// The stream /// Module creation options or null /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) public static AssemblyDef Load(Stream stream, ModuleCreationOptions options = null) { if (stream is null) throw new ArgumentNullException(nameof(stream)); ModuleDef module = null; try { module = ModuleDefMD.Load(stream, options); var asm = module.Assembly; if (asm is null) throw new BadImageFormatException($"{module.ToString()} is only a .NET module, not a .NET assembly. Use ModuleDef.Load()."); return asm; } catch { if (module is not null) module.Dispose(); throw; } } /// /// Gets the assembly name with the public key /// public string GetFullNameWithPublicKey() => FullNameFactory.AssemblyFullName(this, false); /// /// Gets the assembly name with the public key token /// public string GetFullNameWithPublicKeyToken() => FullNameFactory.AssemblyFullName(this, true); /// /// 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) { var modules = Modules; int count = modules.Count; for (int i = 0; i < count; i++) { var module = modules[i]; if (module is null) continue; var type = module.Find(fullName, isReflectionName); if (type is not null) return type; } return null; } /// /// 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) { var modules = Modules; int count = modules.Count; for (int i = 0; i < count; i++) { var module = modules[i]; if (module is null) continue; var type = module.Find(typeRef); if (type is not null) return type; } return null; } /// /// Writes the assembly to a file on disk. If the file exists, it will be truncated. /// /// Filename /// Writer options public void Write(string filename, ModuleWriterOptions options = null) => ManifestModule.Write(filename, options); /// /// Writes the assembly to a stream. /// /// Destination stream /// Writer options public void Write(Stream dest, ModuleWriterOptions options = null) => ManifestModule.Write(dest, options); /// /// Checks whether this assembly is a friend assembly of /// /// Target assembly public bool IsFriendAssemblyOf(AssemblyDef targetAsm) { if (targetAsm is null) return false; if (this == targetAsm) return true; // Both must be unsigned or both must be signed according to the // InternalsVisibleToAttribute documentation. if (PublicKeyBase.IsNullOrEmpty2(publicKey) != PublicKeyBase.IsNullOrEmpty2(targetAsm.PublicKey)) return false; foreach (var ca in targetAsm.CustomAttributes.FindAll("System.Runtime.CompilerServices.InternalsVisibleToAttribute")) { if (ca.ConstructorArguments.Count != 1) continue; var arg = ca.ConstructorArguments.Count == 0 ? default : ca.ConstructorArguments[0]; if (arg.Type.GetElementType() != ElementType.String) continue; var asmName = arg.Value as UTF8String; if (UTF8String.IsNull(asmName)) continue; var asmInfo = new AssemblyNameInfo(asmName); if (asmInfo.Name != name) continue; if (!PublicKeyBase.IsNullOrEmpty2(publicKey)) { if (!PublicKey.Equals(asmInfo.PublicKeyOrToken as PublicKey)) continue; } else if (!PublicKeyBase.IsNullOrEmpty2(asmInfo.PublicKeyOrToken)) continue; return true; } return false; } /// /// Adds or updates an existing System.Reflection.AssemblySignatureKeyAttribute /// attribute. This attribute is used in enhanced strong naming with key migration. /// See http://msdn.microsoft.com/en-us/library/hh415055.aspx /// /// Identity public key /// Identity strong name key pair /// Signature public key public void UpdateOrCreateAssemblySignatureKeyAttribute(StrongNamePublicKey identityPubKey, StrongNameKey identityKey, StrongNamePublicKey signaturePubKey) { var manifestModule = ManifestModule; if (manifestModule is null) return; // Remove all existing attributes CustomAttribute ca = null; for (int i = 0; i < CustomAttributes.Count; i++) { var caTmp = CustomAttributes[i]; if (caTmp.TypeFullName != "System.Reflection.AssemblySignatureKeyAttribute") continue; CustomAttributes.RemoveAt(i); i--; if (ca is null) ca = caTmp; } if (IsValidAssemblySignatureKeyAttribute(ca)) ca.NamedArguments.Clear(); else ca = CreateAssemblySignatureKeyAttribute(); var counterSig = StrongNameKey.CreateCounterSignatureAsString(identityPubKey, identityKey, signaturePubKey); ca.ConstructorArguments[0] = new CAArgument(manifestModule.CorLibTypes.String, new UTF8String(signaturePubKey.ToString())); ca.ConstructorArguments[1] = new CAArgument(manifestModule.CorLibTypes.String, new UTF8String(counterSig)); CustomAttributes.Add(ca); } bool IsValidAssemblySignatureKeyAttribute(CustomAttribute ca) { if (dnlib.Settings.IsThreadSafe) return false; if (ca is null) return false; var ctor = ca.Constructor; if (ctor is null) return false; var sig = ctor.MethodSig; if (sig is null || sig.Params.Count != 2) return false; if (sig.Params[0].GetElementType() != ElementType.String) return false; if (sig.Params[1].GetElementType() != ElementType.String) return false; if (ca.ConstructorArguments.Count != 2) return false; return true; } CustomAttribute CreateAssemblySignatureKeyAttribute() { var manifestModule = ManifestModule; var owner = manifestModule.UpdateRowId(new TypeRefUser(manifestModule, "System.Reflection", "AssemblySignatureKeyAttribute", manifestModule.CorLibTypes.AssemblyRef)); var methodSig = MethodSig.CreateInstance(manifestModule.CorLibTypes.Void, manifestModule.CorLibTypes.String, manifestModule.CorLibTypes.String); var ctor = manifestModule.UpdateRowId(new MemberRefUser(manifestModule, MethodDef.InstanceConstructorName, methodSig, owner)); var ca = new CustomAttribute(ctor); ca.ConstructorArguments.Add(new CAArgument(manifestModule.CorLibTypes.String, UTF8String.Empty)); ca.ConstructorArguments.Add(new CAArgument(manifestModule.CorLibTypes.String, UTF8String.Empty)); return ca; } /// /// Gets the original System.Runtime.Versioning.TargetFrameworkAttribute custom attribute information if possible. /// It reads this from the original metadata and doesn't use . /// Returns false if the custom attribute isn't present or if it is invalid. /// /// Framework name /// Version /// Profile /// public virtual bool TryGetOriginalTargetFrameworkAttribute(out string framework, out Version version, out string profile) { framework = null; version = null; profile = null; return false; } /// void IListListener.OnLazyAdd(int index, ref ModuleDef module) { if (module is null) return; #if DEBUG if (module.Assembly is null) throw new InvalidOperationException("Module.Assembly is null"); #endif } /// void IListListener.OnAdd(int index, ModuleDef module) { if (module is null) return; if (module.Assembly is not null) throw new InvalidOperationException("Module already has an assembly. Remove it from that assembly before adding it to this assembly."); module.Assembly = this; } /// void IListListener.OnRemove(int index, ModuleDef module) { if (module is not null) module.Assembly = null; } /// void IListListener.OnResize(int index) { } /// void IListListener.OnClear() { foreach (var module in modules.GetEnumerable_NoLock()) { if (module is not null) module.Assembly = null; } } /// public override string ToString() => FullName; } /// /// An Assembly row created by the user and not present in the original .NET file /// public class AssemblyDefUser : AssemblyDef { /// /// Default constructor /// public AssemblyDefUser() : this(UTF8String.Empty, new Version(0, 0, 0, 0)) { } /// /// Constructor /// /// Simple name /// If any of the args is invalid public AssemblyDefUser(UTF8String name) : this(name, new Version(0, 0, 0, 0), new PublicKey()) { } /// /// Constructor /// /// Simple name /// Version /// If any of the args is invalid public AssemblyDefUser(UTF8String name, Version version) : this(name, version, new PublicKey()) { } /// /// Constructor /// /// Simple name /// Version /// Public key /// If any of the args is invalid public AssemblyDefUser(UTF8String name, Version version, PublicKey publicKey) : this(name, version, publicKey, UTF8String.Empty) { } /// /// Constructor /// /// Simple name /// Version /// Public key /// Locale /// If any of the args is invalid public AssemblyDefUser(UTF8String name, Version version, PublicKey publicKey, UTF8String locale) { if (name is null) throw new ArgumentNullException(nameof(name)); if (locale is null) throw new ArgumentNullException(nameof(locale)); modules = new LazyList(this); this.name = name; this.version = version ?? throw new ArgumentNullException(nameof(version)); this.publicKey = publicKey ?? new PublicKey(); culture = locale; attributes = (int)AssemblyAttributes.None; } /// /// Constructor /// /// Assembly name info /// If is null public AssemblyDefUser(AssemblyName asmName) : this(new AssemblyNameInfo(asmName)) { hashAlgorithm = (AssemblyHashAlgorithm)asmName.HashAlgorithm; attributes = (int)asmName.Flags; } /// /// Constructor /// /// Assembly name info /// If is null public AssemblyDefUser(IAssembly asmName) { if (asmName is null) throw new ArgumentNullException(nameof(asmName)); modules = new LazyList(this); name = asmName.Name; version = asmName.Version ?? new Version(0, 0, 0, 0); publicKey = asmName.PublicKeyOrToken as PublicKey ?? new PublicKey(); culture = asmName.Culture; attributes = (int)AssemblyAttributes.None; hashAlgorithm = AssemblyHashAlgorithm.SHA1; } } /// /// Created from a row in the Assembly table /// sealed class AssemblyDefMD : AssemblyDef, IMDTokenProviderMD { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; /// public uint OrigRid => origRid; /// protected override void InitializeDeclSecurities() { var list = readerModule.Metadata.GetDeclSecurityRidList(Table.Assembly, origRid); var tmp = new LazyList(list.Count, list, (list2, index) => readerModule.ResolveDeclSecurity(list2[index])); Interlocked.CompareExchange(ref declSecurities, tmp, null); } /// protected override void InitializeModules() { var list = readerModule.GetModuleRidList(); var tmp = new LazyList(list.Count + 1, this, list, (list2, index) => { ModuleDef module; if (index == 0) module = readerModule; else module = readerModule.ReadModule(list2[index - 1], this); if (module is null) module = new ModuleDefUser("INVALID", Guid.NewGuid()); module.Assembly = this; return module; }); Interlocked.CompareExchange(ref modules, tmp, null); } /// protected override void InitializeCustomAttributes() { var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Assembly, 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); } /// public override bool TryGetOriginalTargetFrameworkAttribute(out string framework, out Version version, out string profile) { if (!hasInitdTFA) InitializeTargetFrameworkAttribute(); framework = tfaFramework; version = tfaVersion; profile = tfaProfile; return tfaReturnValue; } volatile bool hasInitdTFA; string tfaFramework; Version tfaVersion; string tfaProfile; bool tfaReturnValue; void InitializeTargetFrameworkAttribute() { if (hasInitdTFA) return; var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Assembly, origRid); var gpContext = new GenericParamContext(); for (int i = 0; i < list.Count; i++) { var caRid = list[i]; if (!readerModule.TablesStream.TryReadCustomAttributeRow(caRid, out var caRow)) continue; var caType = readerModule.ResolveCustomAttributeType(caRow.Type, gpContext); if (!TryGetName(caType, out var ns, out var name)) continue; if (ns != nameSystemRuntimeVersioning || name != nameTargetFrameworkAttribute) continue; var ca = CustomAttributeReader.Read(readerModule, caType, caRow.Value, gpContext); if (ca is null || ca.ConstructorArguments.Count != 1) continue; var s = ca.ConstructorArguments[0].Value as UTF8String; if (s is null) continue; if (TryCreateTargetFrameworkInfo(s, out var tmpFramework, out var tmpVersion, out var tmpProfile)) { tfaFramework = tmpFramework; tfaVersion = tmpVersion; tfaProfile = tmpProfile; tfaReturnValue = true; break; } } hasInitdTFA = true; } static readonly UTF8String nameSystemRuntimeVersioning = new UTF8String("System.Runtime.Versioning"); static readonly UTF8String nameTargetFrameworkAttribute = new UTF8String("TargetFrameworkAttribute"); static bool TryGetName(ICustomAttributeType caType, out UTF8String ns, out UTF8String name) { ITypeDefOrRef type; if (caType is MemberRef mr) type = mr.DeclaringType; else type = (caType as MethodDef)?.DeclaringType; if (type is TypeRef tr) { ns = tr.Namespace; name = tr.Name; return true; } if (type is TypeDef td) { ns = td.Namespace; name = td.Name; return true; } ns = null; name = null; return false; } static bool TryCreateTargetFrameworkInfo(string attrString, out string framework, out Version version, out string profile) { framework = null; version = null; profile = null; // See corclr/src/mscorlib/src/System/Runtime/Versioning/BinaryCompatibility.cs var values = attrString.Split(new char[] { ',' }); if (values.Length < 2 || values.Length > 3) return false; var frameworkRes = values[0].Trim(); if (frameworkRes.Length == 0) return false; Version versionRes = null; string profileRes = null; for (int i = 1; i < values.Length; i++) { var kvp = values[i].Split('='); if (kvp.Length != 2) return false; var key = kvp[0].Trim(); var value = kvp[1].Trim(); if (key.Equals("Version", StringComparison.OrdinalIgnoreCase)) { if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase)) value = value.Substring(1); if (!TryParse(value, out versionRes)) return false; versionRes = new Version(versionRes.Major, versionRes.Minor, versionRes.Build == -1 ? 0 : versionRes.Build, 0); } else if (key.Equals("Profile", StringComparison.OrdinalIgnoreCase)) { if (!string.IsNullOrEmpty(value)) profileRes = value; } } if (versionRes is null) return false; framework = frameworkRes; version = versionRes; profile = profileRes; return true; } static int ParseInt32(string s) => int.TryParse(s, out int res) ? res : 0; static bool TryParse(string s, out Version version) { Match m; m = Regex.Match(s, @"^(\d+)\.(\d+)$"); if (m.Groups.Count == 3) { version = new Version(ParseInt32(m.Groups[1].Value), ParseInt32(m.Groups[2].Value)); return true; } m = Regex.Match(s, @"^(\d+)\.(\d+)\.(\d+)$"); if (m.Groups.Count == 4) { version = new Version(ParseInt32(m.Groups[1].Value), ParseInt32(m.Groups[2].Value), ParseInt32(m.Groups[3].Value)); return true; } m = Regex.Match(s, @"^(\d+)\.(\d+)\.(\d+)\.(\d+)$"); if (m.Groups.Count == 5) { version = new Version(ParseInt32(m.Groups[1].Value), ParseInt32(m.Groups[2].Value), ParseInt32(m.Groups[3].Value), ParseInt32(m.Groups[4].Value)); return true; } version = null; return false; } /// /// Constructor /// /// The module which contains this Assembly row /// Row ID /// If is null /// If is invalid public AssemblyDefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.AssemblyTable.IsInvalidRID(rid)) throw new BadImageFormatException($"Assembly rid {rid} does not exist"); #endif origRid = rid; this.rid = rid; this.readerModule = readerModule; if (rid != 1) modules = new LazyList(this); bool b = readerModule.TablesStream.TryReadAssemblyRow(origRid, out var row); Debug.Assert(b); hashAlgorithm = (AssemblyHashAlgorithm)row.HashAlgId; version = new Version(row.MajorVersion, row.MinorVersion, row.BuildNumber, row.RevisionNumber); attributes = (int)row.Flags; name = readerModule.StringsStream.ReadNoNull(row.Name); culture = readerModule.StringsStream.ReadNoNull(row.Locale); publicKey = new PublicKey(readerModule.BlobStream.Read(row.PublicKey)); } } }