Skip to content
This repository has been archived by the owner on Jul 1, 2024. It is now read-only.

Commit

Permalink
Type system rewrite WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
knah committed Jul 7, 2021
1 parent caa1fd7 commit eeaa230
Show file tree
Hide file tree
Showing 58 changed files with 1,985 additions and 1,044 deletions.
351 changes: 160 additions & 191 deletions AssemblyUnhollower/AssemblyKnownImports.cs

Large diffs are not rendered by default.

20 changes: 8 additions & 12 deletions AssemblyUnhollower/Contexts/AssemblyRewriteContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using UnhollowerBaseLib;

Expand Down Expand Up @@ -28,7 +29,7 @@ public AssemblyRewriteContext(RewriteGlobalContext globalContext, AssemblyDefini
Imports = AssemblyKnownImports.For(newAssembly.MainModule, globalContext);
}

public TypeRewriteContext GetContextForOriginalType(TypeDefinition type) => myOldTypeMap[type];
public TypeRewriteContext GetContextForOriginalType(TypeDefinition type) => myNameTypeMap[type.FullName];
public TypeRewriteContext? TryGetContextForOriginalType(TypeDefinition type) => myOldTypeMap.TryGetValue(type, out var result) ? result : null;
public TypeRewriteContext GetContextForNewType(TypeDefinition type) => myNewTypeMap[type];

Expand All @@ -43,12 +44,15 @@ public void RegisterTypeRewrite(TypeRewriteContext context)
public MethodReference RewriteMethodRef(MethodReference methodRef)
{
var newType = GlobalContext.GetNewTypeForOriginal(methodRef.DeclaringType.Resolve());
if (newType.RewriteSemantic == TypeRewriteContext.TypeRewriteSemantic.UseSystemInterface)
return newType.NewType.Methods.Single(it => it.Name == methodRef.Name);

return newType.GetMethodByOldMethod(methodRef.Resolve()).NewMethod;
}

public TypeReference RewriteTypeRef(TypeReference? typeRef)
{
if (typeRef == null) return Imports.Il2CppObjectBase;
if (typeRef == null) return Imports.Object;

var sourceModule = NewAssembly.MainModule;

Expand All @@ -58,8 +62,6 @@ public TypeReference RewriteTypeRef(TypeReference? typeRef)
return Imports.Il2CppObjectBase;

var elementType = arrayType.ElementType;
if (elementType.FullName == "System.String")
return Imports.Il2CppStringArray;

var convertedElementType = RewriteTypeRef(elementType);
if (elementType.IsGenericParameter)
Expand Down Expand Up @@ -100,14 +102,8 @@ public TypeReference RewriteTypeRef(TypeReference? typeRef)
if (typeRef.FullName == "System.Void")
return Imports.Void;

if (typeRef.FullName == "System.String")
return Imports.String;

if(typeRef.FullName == "System.Object")
return sourceModule.ImportReference(GlobalContext.GetAssemblyByName("mscorlib").GetTypeByName("System.Object").NewType);

if (typeRef.FullName == "System.Attribute")
return sourceModule.ImportReference(GlobalContext.GetAssemblyByName("mscorlib").GetTypeByName("System.Attribute").NewType);
if (typeRef.FullName == "System.Object")
return Imports.Object;

var originalTypeDef = typeRef.Resolve();
var targetAssembly = GlobalContext.GetNewAssemblyForOriginal(originalTypeDef.Module.Assembly);
Expand Down
96 changes: 66 additions & 30 deletions AssemblyUnhollower/Contexts/MethodRewriteContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,55 +77,91 @@ public void CtorPhase2()
{
UnmangledName = UnmangleMethodName();
UnmangledNameWithSignature = UnmangleMethodNameWithSignature();

// todo: translate explicit overrides based on names perhaps?; dumper doesn't generate those though, so interface implementations can end up scuffed
foreach (var originalMethodOverride in OriginalMethod.Overrides)
{
var specificOverride = DeclaringType.AssemblyContext.NewAssembly.MainModule.ImportReference(DeclaringType.AssemblyContext.RewriteMethodRef(originalMethodOverride));

NewMethod.Overrides.Add(specificOverride);
}

NewMethod.Name = UnmangledName;
NewMethod.ReturnType = DeclaringType.AssemblyContext.RewriteTypeRef(OriginalMethod.ReturnType);

var nonGenericMethodInfoPointerField = new FieldDefinition(
"NativeMethodInfoPtr_" + UnmangledNameWithSignature,
FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly,
DeclaringType.AssemblyContext.Imports.IntPtr);
DeclaringType.NewType.Fields.Add(nonGenericMethodInfoPointerField);

NonGenericMethodInfoPointerField = new FieldReference(nonGenericMethodInfoPointerField.Name,
nonGenericMethodInfoPointerField.FieldType, DeclaringType.SelfSubstitutedRef);

if (OriginalNameObfuscated)
NewMethod.CustomAttributes.Add(
new CustomAttribute(DeclaringType.AssemblyContext.Imports.ObfuscatedNameAttributeCtor)
{
ConstructorArguments = {new CustomAttributeArgument(DeclaringType.AssemblyContext.Imports.String, OriginalMethod.Name)}
});

if (DeclaringType.RewriteSemantic == TypeRewriteContext.TypeRewriteSemantic.Default)
{
var nonGenericMethodInfoPointerField = new FieldDefinition(
"NativeMethodInfoPtr_" + UnmangledNameWithSignature,
FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly,
DeclaringType.AssemblyContext.Imports.IntPtr);
DeclaringType.NewType.Fields.Add(nonGenericMethodInfoPointerField);

NonGenericMethodInfoPointerField = new FieldReference(nonGenericMethodInfoPointerField.Name,
nonGenericMethodInfoPointerField.FieldType, DeclaringType.SelfSubstitutedRef);


if (OriginalMethod.HasGenericParameters)
{
var genericParams = OriginalMethod.GenericParameters;
var genericMethodInfoStoreType = new TypeDefinition("",
"MethodInfoStoreGeneric_" + UnmangledNameWithSignature + "`" + genericParams.Count,
TypeAttributes.NestedPrivate | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit,
DeclaringType.AssemblyContext.Imports.Object);
genericMethodInfoStoreType.DeclaringType = DeclaringType.NewType;
DeclaringType.NewType.NestedTypes.Add(genericMethodInfoStoreType);
GenericInstantiationsStore = genericMethodInfoStoreType;

var selfSubstRef = new GenericInstanceType(genericMethodInfoStoreType);
var selfSubstMethodRef = new GenericInstanceType(genericMethodInfoStoreType);

for (var index = 0; index < genericParams.Count; index++)
{
var oldParameter = genericParams[index];
var genericParameter = new GenericParameter(oldParameter.Name, genericMethodInfoStoreType);
genericMethodInfoStoreType.GenericParameters.Add(genericParameter);
selfSubstRef.GenericArguments.Add(genericParameter);
var newParameter = NewMethod.GenericParameters[index];
selfSubstMethodRef.GenericArguments.Add(newParameter);
}

var pointerField = new FieldDefinition("Pointer", FieldAttributes.Assembly | FieldAttributes.Static,
DeclaringType.AssemblyContext.Imports.IntPtr);
genericMethodInfoStoreType.Fields.Add(pointerField);

GenericInstantiationsStoreSelfSubstRef = DeclaringType.NewType.Module.ImportReference(selfSubstRef);
GenericInstantiationsStoreSelfSubstMethodRef =
DeclaringType.NewType.Module.ImportReference(selfSubstMethodRef);
}
}

if (OriginalMethod.HasGenericParameters)
{
var genericParams = OriginalMethod.GenericParameters;
var genericMethodInfoStoreType = new TypeDefinition("", "MethodInfoStoreGeneric_" + UnmangledNameWithSignature + "`" + genericParams.Count, TypeAttributes.NestedPrivate | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, DeclaringType.AssemblyContext.Imports.Object);
genericMethodInfoStoreType.DeclaringType = DeclaringType.NewType;
DeclaringType.NewType.NestedTypes.Add(genericMethodInfoStoreType);
GenericInstantiationsStore = genericMethodInfoStoreType;

var selfSubstRef = new GenericInstanceType(genericMethodInfoStoreType);
var selfSubstMethodRef = new GenericInstanceType(genericMethodInfoStoreType);

for (var index = 0; index < genericParams.Count; index++)
{
var oldParameter = genericParams[index];
var genericParameter = new GenericParameter(oldParameter.Name, genericMethodInfoStoreType);
genericMethodInfoStoreType.GenericParameters.Add(genericParameter);
selfSubstRef.GenericArguments.Add(genericParameter);
var newParameter = NewMethod.GenericParameters[index];
selfSubstMethodRef.GenericArguments.Add(newParameter);


foreach (var oldConstraint in oldParameter.Constraints)
{
if (oldConstraint.ConstraintType.FullName == "System.ValueType" || oldConstraint.ConstraintType.Resolve()?.IsInterface == true) continue;

if (oldConstraint.ConstraintType.FullName == "System.ValueType" ||
oldConstraint.ConstraintType.Resolve()?.IsInterface == true) continue;

newParameter.Constraints.Add(new GenericParameterConstraint(
DeclaringType.AssemblyContext.RewriteTypeRef(oldConstraint.ConstraintType)));
}
}

var pointerField = new FieldDefinition("Pointer", FieldAttributes.Assembly | FieldAttributes.Static, DeclaringType.AssemblyContext.Imports.IntPtr);
genericMethodInfoStoreType.Fields.Add(pointerField);

GenericInstantiationsStoreSelfSubstRef = DeclaringType.NewType.Module.ImportReference(selfSubstRef);
GenericInstantiationsStoreSelfSubstMethodRef = DeclaringType.NewType.Module.ImportReference(selfSubstMethodRef);
}

DeclaringType.NewType.Methods.Add(NewMethod);
}

Expand Down
19 changes: 17 additions & 2 deletions AssemblyUnhollower/Contexts/RewriteGlobalContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ public class RewriteGlobalContext : IDisposable
public IIl2CppMetadataAccess GameAssemblies { get; }
public IMetadataAccess SystemAssemblies { get; }
public IMetadataAccess UnityAssemblies { get; }

private readonly CecilMetadataAccess.Resolver myNewAssembliesResolver = new();
internal readonly MetadataResolver NewMetadataResolver;

private readonly Dictionary<string, AssemblyRewriteContext> myAssemblies = new Dictionary<string, AssemblyRewriteContext>();
private readonly Dictionary<AssemblyDefinition, AssemblyRewriteContext> myAssembliesByOld = new Dictionary<AssemblyDefinition, AssemblyRewriteContext>();
Expand All @@ -32,6 +35,8 @@ public RewriteGlobalContext(UnhollowerOptions options, IIl2CppMetadataAccess gam
GameAssemblies = gameAssemblies;
SystemAssemblies = systemAssemblies;
UnityAssemblies = unityAssemblies;

NewMetadataResolver = new MetadataResolver(myNewAssembliesResolver);

TargetTypeSystemHandler.Init(systemAssemblies);

Expand All @@ -42,7 +47,7 @@ public RewriteGlobalContext(UnhollowerOptions options, IIl2CppMetadataAccess gam

var newAssembly = AssemblyDefinition.CreateAssembly(
new AssemblyNameDefinition(sourceAssembly.Name.Name.UnSystemify(), sourceAssembly.Name.Version),
sourceAssembly.MainModule.Name.UnSystemify(), sourceAssembly.MainModule.Kind);
sourceAssembly.MainModule.Name.UnSystemify(), new ModuleParameters { Kind = sourceAssembly.MainModule.Kind, MetadataResolver = NewMetadataResolver});

var assemblyRewriteContext = new AssemblyRewriteContext(this, sourceAssembly, newAssembly);
AddAssemblyContext(assemblyName, assemblyRewriteContext);
Expand All @@ -52,13 +57,14 @@ public RewriteGlobalContext(UnhollowerOptions options, IIl2CppMetadataAccess gam
internal void AddAssemblyContext(string assemblyName, AssemblyRewriteContext context)
{
myAssemblies[assemblyName] = context;
myNewAssembliesResolver.Register(context.NewAssembly);
if (context.OriginalAssembly != null)
myAssembliesByOld[context.OriginalAssembly] = context;
}

public AssemblyRewriteContext GetNewAssemblyForOriginal(AssemblyDefinition oldAssembly)
{
return myAssembliesByOld[oldAssembly];
return myAssemblies[oldAssembly.Name.Name];
}

public TypeRewriteContext GetNewTypeForOriginal(TypeDefinition originalType)
Expand Down Expand Up @@ -108,5 +114,14 @@ public void Dispose()
assembly.OriginalAssembly.Dispose();
}
}

public readonly StatisticsStore Statistics = new();

public class StatisticsStore
{
public int SystemInterfaceCandidates;
public int EligibleSystemInterfaces;
public int TokenLessMethods;
}
}
}
78 changes: 58 additions & 20 deletions AssemblyUnhollower/Contexts/TypeRewriteContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using AssemblyUnhollower.Extensions;
using Mono.Cecil;
using UnhollowerBaseLib.Attributes;

namespace AssemblyUnhollower.Contexts
{
Expand All @@ -9,6 +11,9 @@ public class TypeRewriteContext
public readonly AssemblyRewriteContext AssemblyContext;
public readonly TypeDefinition OriginalType;
public readonly TypeDefinition NewType;
public readonly int Il2CppToken;

public readonly TypeRewriteSemantic RewriteSemantic;

public readonly bool OriginalNameWasObfuscated;

Expand All @@ -24,29 +29,63 @@ public class TypeRewriteContext
public IEnumerable<FieldRewriteContext> Fields => myFieldContexts.Values;
public IEnumerable<MethodRewriteContext> Methods => myMethodContexts.Values;

public TypeRewriteContext(AssemblyRewriteContext assemblyContext, TypeDefinition originalType, TypeDefinition newType)
public TypeRewriteContext(AssemblyRewriteContext assemblyContext, TypeDefinition originalType, TypeDefinition newType, TypeRewriteSemantic semantic)
{
AssemblyContext = assemblyContext ?? throw new ArgumentNullException(nameof(assemblyContext));
OriginalType = originalType;
NewType = newType ?? throw new ArgumentNullException(nameof(newType));
RewriteSemantic = semantic;

if (OriginalType == null) return;

OriginalNameWasObfuscated = OriginalType.Name != NewType.Name;
if (OriginalNameWasObfuscated)
NewType.CustomAttributes.Add(new CustomAttribute(assemblyContext.Imports.ObfuscatedNameAttributeCtor)
{ConstructorArguments = {new CustomAttributeArgument(assemblyContext.Imports.String, originalType.FullName)}});
if (semantic != TypeRewriteSemantic.Unstripped)
{
Il2CppToken = originalType.ExtractToken();

OriginalNameWasObfuscated = OriginalType.Name != NewType.Name;
if (OriginalNameWasObfuscated)
NewType.CustomAttributes.Add(
new CustomAttribute(assemblyContext.Imports.ObfuscatedNameAttributeCtor)
{
ConstructorArguments = {new CustomAttributeArgument(assemblyContext.Imports.String, originalType.FullName)}
});
}

if (semantic == TypeRewriteSemantic.Default || semantic == TypeRewriteSemantic.Interface)
{
NewType.CustomAttributes.Add(new CustomAttribute(assemblyContext.Imports.NativeTypeTokenAttributeCtor)
{
Fields =
{
new CustomAttributeNamedArgument(nameof(NativeTypeTokenAttribute.AssemblyName),
new CustomAttributeArgument(assemblyContext.Imports.String, originalType.Module.Assembly.Name.Name)),

new CustomAttributeNamedArgument(nameof(NativeTypeTokenAttribute.Token),
new CustomAttributeArgument(assemblyContext.Imports.UInt, Il2CppToken))
}
});
}
if (!OriginalType.IsValueType)
ComputedTypeSpecifics = TypeSpecifics.ReferenceType;
else if (OriginalType.IsEnum)
ComputedTypeSpecifics = TypeSpecifics.BlittableStruct;
else if (OriginalType.HasGenericParameters)
ComputedTypeSpecifics = TypeSpecifics.NonBlittableStruct; // not reference type, covered by first if
else if (OriginalType.IsValueType && OriginalType.HasGenericParameters)
ComputedTypeSpecifics = TypeSpecifics.NonBlittableStruct;
}

public void AddMembers()
{
foreach (var originalTypeMethod in OriginalType.Methods)
{
if (originalTypeMethod.Name == ".cctor") continue;
if (originalTypeMethod.Name == ".ctor" && originalTypeMethod.Parameters.Count == 1 &&
originalTypeMethod.Parameters[0].ParameterType.FullName == "System.IntPtr") continue;

var methodRewriteContext = new MethodRewriteContext(this, originalTypeMethod);
myMethodContexts[originalTypeMethod] = methodRewriteContext;
myMethodContextsByName[originalTypeMethod.Name] = methodRewriteContext;
}

if (RewriteSemantic != TypeRewriteSemantic.Default) return;

if (NewType.HasGenericParameters)
{
var genericInstanceType = new GenericInstanceType(NewType);
Expand All @@ -70,23 +109,13 @@ public void AddMembers()
NewType.Module.ImportReference(genericTypeRef));
}

// enums are filled in Pass22GenerateEnums
if (OriginalType.IsEnum) return;

var renamedFieldCounts = new Dictionary<string, int>();

foreach (var originalTypeField in OriginalType.Fields)
myFieldContexts[originalTypeField] = new FieldRewriteContext(this, originalTypeField, renamedFieldCounts);

foreach (var originalTypeMethod in OriginalType.Methods)
{
if (originalTypeMethod.Name == ".cctor") continue;
if (originalTypeMethod.Name == ".ctor" && originalTypeMethod.Parameters.Count == 1 &&
originalTypeMethod.Parameters[0].ParameterType.FullName == "System.IntPtr") continue;

var methodRewriteContext = new MethodRewriteContext(this, originalTypeMethod);
myMethodContexts[originalTypeMethod] = methodRewriteContext;
myMethodContextsByName[originalTypeMethod.Name] = methodRewriteContext;
}
}

public FieldRewriteContext GetFieldByOldField(FieldDefinition field) => myFieldContexts[field];
Expand Down Expand Up @@ -141,5 +170,14 @@ public enum TypeSpecifics
BlittableStruct,
NonBlittableStruct
}

public enum TypeRewriteSemantic
{
Default,
Interface,
UseSystemInterface,
UseSystemValueType,
Unstripped
}
}
}
Loading

0 comments on commit eeaa230

Please sign in to comment.