/* Copyright (C) 2011 de4dot@gmail.com This file is part of de4dot. de4dot is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. de4dot is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with de4dot. If not, see . */ using System; using System.Collections.Generic; using Mono.Cecil; using de4dot.blocks; namespace de4dot.code.renamer.asmmodules { class Modules : IResolver { bool initializeCalled = false; List modules = new List(); Dictionary modulesDict = new Dictionary(); AssemblyHash assemblyHash = new AssemblyHash(); List allTypes = new List(); List baseTypes = new List(); List nonNestedTypes; public IList TheModules { get { return modules; } } public IEnumerable AllTypes { get { return allTypes; } } public IEnumerable BaseTypes { get { return baseTypes; } } public List NonNestedTypes { get { return nonNestedTypes; } } class AssemblyHash { IDictionary assemblyHash = new Dictionary(StringComparer.Ordinal); public void add(Module module) { ModuleHash moduleHash; var key = getModuleKey(module); if (!assemblyHash.TryGetValue(key, out moduleHash)) assemblyHash[key] = moduleHash = new ModuleHash(); moduleHash.add(module); } string getModuleKey(Module module) { if (module.ModuleDefinition.Assembly != null) return module.ModuleDefinition.Assembly.ToString(); return Utils.getBaseName(module.ModuleDefinition.FullyQualifiedName); } public ModuleHash lookup(string assemblyName) { ModuleHash moduleHash; if (assemblyHash.TryGetValue(assemblyName, out moduleHash)) return moduleHash; return null; } } class ModuleHash { ModulesDict modulesDict = new ModulesDict(); Module mainModule = null; public void add(Module module) { var asm = module.ModuleDefinition.Assembly; if (asm != null && ReferenceEquals(asm.MainModule, module.ModuleDefinition)) { if (mainModule != null) { throw new UserException(string.Format( "Two modules in the same assembly are main modules.\n" + "Is one 32-bit and the other 64-bit?\n" + " Module1: \"{0}\"" + " Module2: \"{1}\"", module.ModuleDefinition.FullyQualifiedName, mainModule.ModuleDefinition.FullyQualifiedName)); } mainModule = module; } modulesDict.add(module); } public Module lookup(string moduleName) { return modulesDict.lookup(moduleName); } public IEnumerable Modules { get { return modulesDict.Modules; } } } class ModulesDict { IDictionary modulesDict = new Dictionary(StringComparer.Ordinal); public void add(Module module) { var moduleName = module.ModuleDefinition.Name; if (lookup(moduleName) != null) throw new ApplicationException(string.Format("Module \"{0}\" was found twice", moduleName)); modulesDict[moduleName] = module; } public Module lookup(string moduleName) { Module module; if (modulesDict.TryGetValue(moduleName, out module)) return module; return null; } public IEnumerable Modules { get { return modulesDict.Values; } } } public bool Empty { get { return modules.Count == 0; } } public void add(Module module) { if (initializeCalled) throw new ApplicationException("initialize() has been called"); Module otherModule; if (modulesDict.TryGetValue(module.ModuleDefinition, out otherModule)) return; modulesDict[module.ModuleDefinition] = module; modules.Add(module); assemblyHash.add(module); } public void initialize() { initializeCalled = true; findAllMemberReferences(); resolveAllRefs(); initAllTypes(); } void findAllMemberReferences() { Log.v("Finding all MemberReferences"); int index = 0; foreach (var module in modules) { if (modules.Count > 1) Log.v("Finding all MemberReferences ({0})", module.Filename); Log.indent(); module.findAllMemberReferences(ref index); Log.deIndent(); } } void resolveAllRefs() { Log.v("Resolving references"); foreach (var module in modules) { if (modules.Count > 1) Log.v("Resolving references ({0})", module.Filename); Log.indent(); module.resolveAllRefs(this); Log.deIndent(); } } void initAllTypes() { foreach (var module in modules) allTypes.AddRange(module.getAllTypes()); var typeToTypeDef = new Dictionary(allTypes.Count); foreach (var typeDef in allTypes) typeToTypeDef[typeDef.TypeDefinition] = typeDef; // Initialize Owner foreach (var typeDef in allTypes) { if (typeDef.TypeDefinition.DeclaringType != null) typeDef.Owner = typeToTypeDef[typeDef.TypeDefinition.DeclaringType]; } // Initialize baseType and derivedTypes foreach (var typeDef in allTypes) { var baseType = typeDef.TypeDefinition.BaseType; if (baseType == null) continue; var baseTypeDef = resolve(baseType) ?? resolveOther(baseType); if (baseTypeDef != null) { typeDef.addBaseType(baseTypeDef, baseType); baseTypeDef.derivedTypes.Add(typeDef); } } // Initialize interfaces foreach (var typeDef in allTypes) { if (typeDef.TypeDefinition.Interfaces == null) continue; foreach (var iface in typeDef.TypeDefinition.Interfaces) { var ifaceTypeDef = resolve(iface) ?? resolveOther(iface); if (ifaceTypeDef != null) typeDef.addInterface(ifaceTypeDef, iface); } } // Find all non-nested types var allTypesDict = new Dictionary(); foreach (var t in allTypes) allTypesDict[t] = true; foreach (var t in allTypes) { foreach (var t2 in t.NestedTypes) allTypesDict.Remove(t2); } nonNestedTypes = new List(allTypesDict.Keys); foreach (var typeDef in allTypes) { if (typeDef.baseType == null || !typeDef.baseType.typeDef.HasModule) baseTypes.Add(typeDef); } } class AssemblyKeyDictionary where T : class { Dictionary dict = new Dictionary(); Dictionary> refs = new Dictionary>(); public T this[TypeReference type] { get { T value; if (tryGetValue(type, out value)) return value; throw new KeyNotFoundException(); } set { var key = new TypeReferenceSameVersionKey(type); dict[key] = value; if (value != null) { var key2 = new TypeReferenceKey(type); List list; if (!refs.TryGetValue(key2, out list)) refs[key2] = list = new List(); list.Add(type); } } } public bool tryGetValue(TypeReference type, out T value) { return dict.TryGetValue(new TypeReferenceSameVersionKey(type), out value); } public bool tryGetSimilarValue(TypeReference type, out T value) { var key2 = new TypeReferenceKey(type); List list; if (!refs.TryGetValue(key2, out list)) { value = default(T); return false; } // Find a type whose version is >= type's version and closest to it. TypeReference foundType = null; var typeAsmName = MemberReferenceHelper.getAssemblyNameReference(type.Scope); AssemblyNameReference foundAsmName = null; foreach (var otherRef in list) { var key = new TypeReferenceSameVersionKey(otherRef); if (!dict.TryGetValue(key, out value)) continue; if (typeAsmName == null) { foundType = otherRef; break; } var otherAsmName = MemberReferenceHelper.getAssemblyNameReference(otherRef.Scope); if (otherAsmName == null) continue; // Check pkt or we could return a type in eg. a SL assembly when it's not a SL app. if (!same(typeAsmName.PublicKeyToken, otherAsmName.PublicKeyToken)) continue; if (typeAsmName.Version > otherAsmName.Version) continue; // old version if (foundType == null) { foundAsmName = otherAsmName; foundType = otherRef; continue; } if (foundAsmName.Version <= otherAsmName.Version) continue; foundAsmName = otherAsmName; foundType = otherRef; } if (foundType != null) { value = dict[new TypeReferenceSameVersionKey(foundType)]; return true; } value = default(T); return false; } bool same(byte[] a, byte[] b) { if (ReferenceEquals(a, b)) return true; if (a == null || b == null) return false; if (a.Length != b.Length) return false; for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) return false; } return true; } } AssemblyKeyDictionary typeToTypeDefDict = new AssemblyKeyDictionary(); ExternalAssemblies externalAssemblies = new ExternalAssemblies(); public TypeDef resolveOther(TypeReference type) { if (type == null) return null; type = type.GetElementType(); TypeDef typeDef; if (typeToTypeDefDict.tryGetValue(type, out typeDef)) return typeDef; var typeDefinition = externalAssemblies.resolve(type); if (typeDefinition == null) { typeToTypeDefDict.tryGetSimilarValue(type, out typeDef); typeToTypeDefDict[type] = typeDef; return typeDef; } if (typeToTypeDefDict.tryGetValue(typeDefinition, out typeDef)) { typeToTypeDefDict[type] = typeDef; return typeDef; } typeToTypeDefDict[type] = null; // In case of a circular reference typeToTypeDefDict[typeDefinition] = null; typeDef = new TypeDef(typeDefinition, null, 0); typeDef.addMembers(); foreach (var iface in typeDef.TypeDefinition.Interfaces) { var ifaceDef = resolveOther(iface); if (ifaceDef == null) continue; typeDef.addInterface(ifaceDef, iface); } var baseDef = resolveOther(typeDef.TypeDefinition.BaseType); if (baseDef != null) typeDef.addBaseType(baseDef, typeDef.TypeDefinition.BaseType); typeToTypeDefDict[type] = typeDef; if (type != typeDefinition) typeToTypeDefDict[typeDefinition] = typeDef; return typeDef; } public MethodNameScopes initializeVirtualMembers() { var scopes = new MethodNameScopes(); foreach (var typeDef in allTypes) typeDef.initializeVirtualMembers(scopes, this); return scopes; } public void onTypesRenamed() { foreach (var module in modules) module.onTypesRenamed(); } public void cleanUp() { externalAssemblies.unloadAll(); foreach (var module in DotNetUtils.typeCaches.invalidateAll()) AssemblyResolver.Instance.removeModule(module); } // Returns null if it's a non-loaded module/assembly IEnumerable findModules(TypeReference type) { var scope = type.Scope; if (scope is AssemblyNameReference) return findModules((AssemblyNameReference)scope); if (scope is ModuleDefinition) { var modules = findModules((ModuleDefinition)scope); if (modules != null) return modules; } if (scope is ModuleReference) { var moduleReference = (ModuleReference)scope; if (moduleReference.Name == type.Module.Name) { var modules = findModules(type.Module); if (modules != null) return modules; } var asm = type.Module.Assembly; if (asm == null) return null; var moduleHash = assemblyHash.lookup(asm.ToString()); if (moduleHash == null) return null; var module = moduleHash.lookup(moduleReference.Name); if (module == null) return null; return new List { module }; } throw new ApplicationException(string.Format("scope is an unsupported type: {0}", scope.GetType())); } IEnumerable findModules(AssemblyNameReference assemblyRef) { var moduleHash = assemblyHash.lookup(assemblyRef.ToString()); if (moduleHash != null) return moduleHash.Modules; return null; } IEnumerable findModules(ModuleDefinition moduleDefinition) { Module module; if (modulesDict.TryGetValue(moduleDefinition, out module)) return new List { module }; return null; } bool isAutoCreatedType(TypeReference typeReference) { return typeReference is ArrayType || typeReference is PointerType || typeReference is FunctionPointerType; } public TypeDef resolve(TypeReference typeReference) { var modules = findModules(typeReference); if (modules == null) return null; foreach (var module in modules) { var rv = module.resolve(typeReference); if (rv != null) return rv; } if (isAutoCreatedType(typeReference)) return null; Log.e("Could not resolve TypeReference {0} ({1:X8}) (from {2} -> {3})", Utils.removeNewlines(typeReference), typeReference.MetadataToken.ToInt32(), typeReference.Module, typeReference.Scope); return null; } public MethodDef resolve(MethodReference methodReference) { if (methodReference.DeclaringType == null) return null; var modules = findModules(methodReference.DeclaringType); if (modules == null) return null; foreach (var module in modules) { var rv = module.resolve(methodReference); if (rv != null) return rv; } if (isAutoCreatedType(methodReference.DeclaringType)) return null; Log.e("Could not resolve MethodReference {0} ({1:X8}) (from {2} -> {3})", Utils.removeNewlines(methodReference), methodReference.MetadataToken.ToInt32(), methodReference.DeclaringType.Module, methodReference.DeclaringType.Scope); return null; } public FieldDef resolve(FieldReference fieldReference) { if (fieldReference.DeclaringType == null) return null; var modules = findModules(fieldReference.DeclaringType); if (modules == null) return null; foreach (var module in modules) { var rv = module.resolve(fieldReference); if (rv != null) return rv; } if (isAutoCreatedType(fieldReference.DeclaringType)) return null; Log.e("Could not resolve FieldReference {0} ({1:X8}) (from {2} -> {3})", Utils.removeNewlines(fieldReference), fieldReference.MetadataToken.ToInt32(), fieldReference.DeclaringType.Module, fieldReference.DeclaringType.Scope); return null; } } }