-
-
Notifications
You must be signed in to change notification settings - Fork 303
Expand file tree
/
Copy pathAsmResolverUtils.cs
More file actions
343 lines (289 loc) · 15.5 KB
/
AsmResolverUtils.cs
File metadata and controls
343 lines (289 loc) · 15.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures;
using LibCpp2IL;
using LibCpp2IL.BinaryStructures;
namespace Cpp2IL.Core.Utils.AsmResolver;
public static class AsmResolverUtils
{
private static readonly ConcurrentDictionary<string, TypeDefinition?> CachedTypeDefsByName = new();
private static readonly ConcurrentDictionary<string, TypeSignature?> CachedTypeSignaturesByName = new();
private static readonly ConcurrentDictionary<AssemblyDefinition, ReferenceImporter> ImportersByAssembly = new();
public static readonly ConcurrentDictionary<long, TypeDefinition> TypeDefsByIndex = new();
public static readonly ConcurrentDictionary<long, GenericParameter> GenericParamsByIndexNew = new();
internal static void Reset()
{
CachedTypeDefsByName.Clear();
CachedTypeSignaturesByName.Clear();
ImportersByAssembly.Clear();
TypeDefsByIndex.Clear();
GenericParamsByIndexNew.Clear();
}
public static TypeDefinition GetPrimitiveTypeDef(Il2CppTypeEnum type) =>
type switch
{
Il2CppTypeEnum.IL2CPP_TYPE_OBJECT => TypeDefinitionsAsmResolver.Object,
Il2CppTypeEnum.IL2CPP_TYPE_VOID => TypeDefinitionsAsmResolver.Void,
Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN => TypeDefinitionsAsmResolver.Boolean,
Il2CppTypeEnum.IL2CPP_TYPE_CHAR => TypeDefinitionsAsmResolver.Char,
Il2CppTypeEnum.IL2CPP_TYPE_I1 => TypeDefinitionsAsmResolver.SByte,
Il2CppTypeEnum.IL2CPP_TYPE_U1 => TypeDefinitionsAsmResolver.Byte,
Il2CppTypeEnum.IL2CPP_TYPE_I2 => TypeDefinitionsAsmResolver.Int16,
Il2CppTypeEnum.IL2CPP_TYPE_U2 => TypeDefinitionsAsmResolver.UInt16,
Il2CppTypeEnum.IL2CPP_TYPE_I4 => TypeDefinitionsAsmResolver.Int32,
Il2CppTypeEnum.IL2CPP_TYPE_U4 => TypeDefinitionsAsmResolver.UInt32,
Il2CppTypeEnum.IL2CPP_TYPE_I => TypeDefinitionsAsmResolver.IntPtr,
Il2CppTypeEnum.IL2CPP_TYPE_U => TypeDefinitionsAsmResolver.UIntPtr,
Il2CppTypeEnum.IL2CPP_TYPE_I8 => TypeDefinitionsAsmResolver.Int64,
Il2CppTypeEnum.IL2CPP_TYPE_U8 => TypeDefinitionsAsmResolver.UInt64,
Il2CppTypeEnum.IL2CPP_TYPE_R4 => TypeDefinitionsAsmResolver.Single,
Il2CppTypeEnum.IL2CPP_TYPE_R8 => TypeDefinitionsAsmResolver.Double,
Il2CppTypeEnum.IL2CPP_TYPE_STRING => TypeDefinitionsAsmResolver.String,
Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF => TypeDefinitionsAsmResolver.TypedReference,
Il2CppTypeEnum.IL2CPP_TYPE_IL2CPP_TYPE_INDEX => TypeDefinitionsAsmResolver.Type,
_ => throw new ArgumentException($"Type is not a primitive - {type}", nameof(type))
};
public static TypeSignature GetTypeSignatureFromIl2CppType(ModuleDefinition module, Il2CppType il2CppType)
{
//Module is needed for generic params
if (il2CppType == null)
throw new ArgumentNullException(nameof(il2CppType));
TypeSignature ret;
switch (il2CppType.Type)
{
case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT:
case Il2CppTypeEnum.IL2CPP_TYPE_VOID:
case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN:
case Il2CppTypeEnum.IL2CPP_TYPE_CHAR:
case Il2CppTypeEnum.IL2CPP_TYPE_I1:
case Il2CppTypeEnum.IL2CPP_TYPE_U1:
case Il2CppTypeEnum.IL2CPP_TYPE_I2:
case Il2CppTypeEnum.IL2CPP_TYPE_U2:
case Il2CppTypeEnum.IL2CPP_TYPE_I4:
case Il2CppTypeEnum.IL2CPP_TYPE_U4:
case Il2CppTypeEnum.IL2CPP_TYPE_I:
case Il2CppTypeEnum.IL2CPP_TYPE_U:
case Il2CppTypeEnum.IL2CPP_TYPE_I8:
case Il2CppTypeEnum.IL2CPP_TYPE_U8:
case Il2CppTypeEnum.IL2CPP_TYPE_R4:
case Il2CppTypeEnum.IL2CPP_TYPE_R8:
case Il2CppTypeEnum.IL2CPP_TYPE_STRING:
case Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF:
ret = GetPrimitiveTypeDef(il2CppType.Type)
.ToTypeSignature();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
ret = TypeDefsByIndex[il2CppType.Data.ClassIndex]
.ToTypeSignature();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
ret = GetTypeSignatureFromIl2CppType(module, il2CppType.GetArrayElementType())
.MakeArrayTypeWithLowerBounds(il2CppType.GetArrayRank());
break;
case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
ret = GetTypeSignatureFromIl2CppType(module, il2CppType.GetEncapsulatedType())
.MakeSzArrayType();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_PTR:
ret = GetTypeSignatureFromIl2CppType(module, il2CppType.GetEncapsulatedType())
.MakePointerType();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_VAR: //Generic type parameter
case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: //Generic method parameter
var method = il2CppType.Type == Il2CppTypeEnum.IL2CPP_TYPE_MVAR;
ret = new GenericParameterSignature(module, method ? GenericParameterType.Method : GenericParameterType.Type, il2CppType.GetGenericParameterDef().genericParameterIndexInOwner);
break;
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
{
var genericClass = il2CppType.GetGenericClass();
//Get base type
TypeDefsByIndex.TryGetValue(genericClass.TypeDefinitionIndex, out var typeDefinition);
if (Cpp2IlApi.CurrentAppContext!.MetadataVersion >= 27f) //TODO: we should pass in the app context to this method
{
//V27 - type indexes are pointers now.
var type = LibCpp2IlMain.Binary!.ReadReadableAtVirtualAddress<Il2CppType>((ulong)genericClass.TypeDefinitionIndex);
typeDefinition = GetTypeSignatureFromIl2CppType(module, type).Resolve() ?? throw new Exception("Unable to resolve base type for generic inst");
}
var genericInstanceType = new GenericInstanceTypeSignature(typeDefinition!, typeDefinition!.IsValueType);
//Get generic arguments
var genericArgumentTypes = genericClass.Context.ClassInst.Types;
//Add arguments to generic instance
foreach (var type in genericArgumentTypes)
genericInstanceType.TypeArguments.Add(GetTypeSignatureFromIl2CppType(module, type));
ret = genericInstanceType;
break;
}
default:
throw new("Don't know how to make a type signature from " + il2CppType.Type);
}
if (il2CppType.Byref == 1)
ret = ret.MakeByReferenceType();
return ret;
}
/// <summary>
/// Imports the managed representation of the given il2cpp type using the given importer, and returns said type.
/// <br/><br/>
/// Prefer <see cref="GetTypeSignatureFromIl2CppType"/> where possible, only use this where an actual type reference is needed.
/// Such cases would include generic parameter constraints, base types/interfaces, and event types.
/// </summary>
public static ITypeDefOrRef ImportReferenceFromIl2CppType(ModuleDefinition module, Il2CppType il2CppType)
{
if (il2CppType == null)
throw new ArgumentNullException(nameof(il2CppType));
switch (il2CppType.Type)
{
case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT:
case Il2CppTypeEnum.IL2CPP_TYPE_VOID:
case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN:
case Il2CppTypeEnum.IL2CPP_TYPE_CHAR:
case Il2CppTypeEnum.IL2CPP_TYPE_I1:
case Il2CppTypeEnum.IL2CPP_TYPE_U1:
case Il2CppTypeEnum.IL2CPP_TYPE_I2:
case Il2CppTypeEnum.IL2CPP_TYPE_U2:
case Il2CppTypeEnum.IL2CPP_TYPE_I4:
case Il2CppTypeEnum.IL2CPP_TYPE_U4:
case Il2CppTypeEnum.IL2CPP_TYPE_I:
case Il2CppTypeEnum.IL2CPP_TYPE_U:
case Il2CppTypeEnum.IL2CPP_TYPE_I8:
case Il2CppTypeEnum.IL2CPP_TYPE_U8:
case Il2CppTypeEnum.IL2CPP_TYPE_R4:
case Il2CppTypeEnum.IL2CPP_TYPE_R8:
case Il2CppTypeEnum.IL2CPP_TYPE_STRING:
case Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF:
//This case, and the one below, are faster to go this way rather than delegating to type signature creation, because we can go straight from def -> ref.
return GetPrimitiveTypeDef(il2CppType.Type);
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
return TypeDefsByIndex[il2CppType.Data.ClassIndex];
case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
case Il2CppTypeEnum.IL2CPP_TYPE_PTR:
case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
//For the rest of these, we have to make a type signature first anyway, so just delegate to signature getter
return GetTypeSignatureFromIl2CppType(module, il2CppType).ToTypeDefOrRef();
default:
throw new("Don't know how to import a type reference from an il2cpp type of type " + il2CppType.Type);
}
}
public static TypeDefinition? TryLookupTypeDefKnownNotGeneric(string? name)
{
if (name == null)
return null;
if (TypeDefinitionsAsmResolver.GetPrimitive(name) is { } primitive)
return primitive;
var key = name.ToLower(CultureInfo.InvariantCulture);
if (CachedTypeDefsByName.TryGetValue(key, out var ret))
return ret;
var definedType = Cpp2IlApi.CurrentAppContext!.AllTypes.FirstOrDefault(t => t.Definition != null && string.Equals(t.Definition.FullName, name, StringComparison.OrdinalIgnoreCase));
//Try subclasses
definedType ??= Cpp2IlApi.CurrentAppContext.AllTypes.FirstOrDefault(t =>
{
return t.Definition?.FullName != null
&& t.Definition.FullName.Contains('/')
&& string.Equals(t.Definition.FullName.Replace('/', '.'), name, StringComparison.OrdinalIgnoreCase);
});
ret = definedType?.GetExtraData<TypeDefinition>("AsmResolverType");
if (ret == null)
return null;
CachedTypeDefsByName.TryAdd(key, ret);
return ret;
}
public static TypeSignature? TryLookupTypeSignatureByName(string? name, ReadOnlySpan<string> genericParameterNames = default)
{
if (name == null)
return null;
var key = name.ToLower(CultureInfo.InvariantCulture);
if (genericParameterNames.Length == 0 && CachedTypeSignaturesByName.TryGetValue(key, out var ret))
return ret;
var result = InternalTryLookupTypeSignatureByName(name, genericParameterNames);
if (genericParameterNames.Length == 0)
CachedTypeSignaturesByName.TryAdd(key, result);
return result;
}
private static TypeSignature? InternalTryLookupTypeSignatureByName(string name, ReadOnlySpan<string> genericParameterNames = default)
{
if (TypeDefinitionsAsmResolver.GetPrimitive(name) is { } primitive)
return primitive.ToTypeSignature();
//The only real cases we end up here are:
//From explicit override resolving, because that has to be done by name
//Sometimes in attribute restoration if we come across an object parameter, but this almost always has to be a system or cecil type, or an enum.
//While originally initializing the TypeDefinitions class, which is always a system type
//And during exception helper location, which is always a system type.
//So really the only remapping we should have to handle is during explicit override restoration.
if (name.EndsWith("[]", StringComparison.Ordinal))
{
var without = name[..^2];
var result = InternalTryLookupTypeSignatureByName(without, genericParameterNames);
return result?.MakeSzArrayType();
}
var parsedType = Parse(name);
// Arrays should be handled above
Debug.Assert(parsedType.Suffix is "");
var genericParameterIndex = genericParameterNames.IndexOf(parsedType.BaseType);
if (genericParameterIndex >= 0)
return new GenericParameterSignature(GenericParameterType.Type, genericParameterIndex);
var baseType = TryLookupTypeDefKnownNotGeneric(parsedType.BaseType);
if (baseType == null)
return null;
if (parsedType.GenericArguments.Length == 0)
return baseType.ToTypeSignature();
var typeArguments = new TypeSignature[parsedType.GenericArguments.Length];
for (var i = 0; i < parsedType.GenericArguments.Length; i++)
{
var typeArgument = InternalTryLookupTypeSignatureByName(parsedType.GenericArguments[i], genericParameterNames);
if (typeArgument == null)
return null;
typeArguments[i] = typeArgument;
}
return baseType.MakeGenericInstanceType(typeArguments);
}
private readonly record struct ParsedTypeString(string BaseType, string Suffix, string[] GenericArguments);
private static ParsedTypeString Parse(string name)
{
var firstAngleBracket = name.IndexOf('<');
if (firstAngleBracket < 0)
{
var firstSquareBracket = name.IndexOf('[');
if (firstSquareBracket < 0)
return new ParsedTypeString(name, "", []);
else
return new ParsedTypeString(name[..firstSquareBracket], name[(firstSquareBracket + 1)..], []);
}
var lastAngleBracket = name.LastIndexOf('>');
var genericParams = MiscUtils.GetGenericParams(name[(firstAngleBracket + 1)..(lastAngleBracket)]);
var baseType = $"{name[..firstAngleBracket]}`{genericParams.Length}";
var suffix = name[(lastAngleBracket + 1)..];
return new ParsedTypeString(baseType, suffix, genericParams);
}
public static ReferenceImporter GetImporter(this AssemblyDefinition assemblyDefinition)
{
if (ImportersByAssembly.TryGetValue(assemblyDefinition, out var ret))
return ret;
ImportersByAssembly[assemblyDefinition] = ret = new(assemblyDefinition.Modules[0]);
return ret;
}
public static ITypeDefOrRef ImportTypeIfNeeded(this ReferenceImporter importer, ITypeDefOrRef type)
{
if (type is TypeSpecification spec)
return new TypeSpecification(importer.ImportTypeSignature(spec.Signature!));
return importer.ImportType(type);
}
public static bool IsManagedMethodWithBody(this MethodDefinition managedMethod) =>
managedMethod.Managed && !managedMethod.IsAbstract && !managedMethod.IsPInvokeImpl
&& !managedMethod.IsInternalCall && !managedMethod.IsNative && !managedMethod.IsRuntime;
internal static ArrayTypeSignature MakeArrayTypeWithLowerBounds(this TypeSignature elementType, int rank)
{
var result = new ArrayTypeSignature(elementType, rank);
for (var i = 0; i < rank; i++)
result.Dimensions[i] = new ArrayDimension(null, 0);
return result;
}
}