Skip to content

Commit

Permalink
Add name mangling to Methods/MethodInfo/TypeInfo/TypeRef, remove Boxi…
Browse files Browse the repository at this point in the history
…ng from ValueTypes when used as the this parameter, fix crashes when a module has no attributes
  • Loading branch information
LukeFZ committed Dec 6, 2023
1 parent e9434f4 commit 5b1d9c6
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 119 deletions.
2 changes: 1 addition & 1 deletion Il2CppInspector.Common/Cpp/CppDeclarationGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ private CppFnPtrType GenerateMethodDeclaration(MethodBase method, string name, T
} else {
if (declaringType.IsValueType) {
// Methods for structs take the boxed object as the this param
paramList.Add(("this", types.GetType(TypeNamer.GetName(declaringType) + "__Boxed *")));
paramList.Add(("this", types.GetType(TypeNamer.GetName(declaringType) + " *"))); // + "__Boxed *")));
} else {
paramList.Add(("this", AsCType(declaringType)));
}
Expand Down
204 changes: 204 additions & 0 deletions Il2CppInspector.Common/Cpp/MangledNameBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
using System.Diagnostics;
using System.Text;
using Il2CppInspector.Reflection;

namespace Il2CppInspector.Cpp;

// This follows Itanium/GCC mangling specifications.
public class MangledNameBuilder
{
private readonly StringBuilder _sb = new("_Z");

public override string ToString()
=> _sb.ToString();

public static string Method(MethodBase method)
{
var builder = new MangledNameBuilder();
builder.BuildMethod(method);
return builder.ToString();
}

public static string MethodInfo(MethodBase method)
{
var builder = new MangledNameBuilder();
builder.BuildMethod(method, "MethodInfo");
return builder.ToString();
}

public static string TypeInfo(TypeInfo type)
{
var builder = new MangledNameBuilder();
builder.BeginName();
builder.WriteIdentifier("TypeInfo");
builder.WriteTypeName(type);
builder.WriteEnd();
return builder.ToString();
}

public static string TypeRef(TypeInfo type)
{
var builder = new MangledNameBuilder();
builder.BeginName();
builder.WriteIdentifier("TypeRef");
builder.WriteTypeName(type);
builder.WriteEnd();
return builder.ToString();
}

private void BuildMethod(MethodBase method, string prefix = "")
{
/*
* We do not have any CV-qualifiers nor ref-qualifiers,
* so we immediately write the nested name.
*/

BeginName();

if (prefix.Length > 0)
WriteIdentifier(prefix);

WriteTypeName(method.DeclaringType);

switch (method.Name)
{
case ".ctor":
_sb.Append("C1"); // Constructor
break;
case ".cctor":
WriteIdentifier("cctor");
break;
default:
WriteIdentifier(method.Name);
break;
}

var genericParams = method.GetGenericArguments();

WriteGenericParams(genericParams);

WriteEnd(); // End nested name

// Now write the method parameters

if (genericParams.Length > 0 && method is MethodInfo mInfo)
{
// If this is a generic method, the first parameter needs to be the return type
WriteType(mInfo.ReturnType);
}

if (method.DeclaredParameters.Count == 0)
_sb.Append('v');
else
{
foreach (var param in method.DeclaredParameters)
WriteType(param.ParameterType);
}
}

private void WriteTypeName(TypeInfo type)
{
if (type.HasElementType)
type = type.ElementType;

WriteName(type.Namespace);

if (type.DeclaringType != null)
WriteIdentifier(type.DeclaringType.Name);

WriteIdentifier(type.CSharpBaseName);
WriteGenericParams(type.GenericTypeArguments);
}

private void WriteType(TypeInfo type)
{
if (type.FullName == "System.Void")
{
_sb.Append('v');
return;
}

if (type.IsByRef)
_sb.Append('R');

if (type.IsPointer)
_sb.Append('P');

if (type.IsArray)
_sb.Append("A_");

if (type.IsPrimitive && type.Name != "Decimal")
{
if (type.Name is "IntPtr" or "UIntPtr")
_sb.Append("Pv"); // void*
else
{
_sb.Append(type.Name switch
{
"Boolean" => 'b',
"Byte" => 'h',
"SByte" => 'a',
"Int16" => 's',
"UInt16" => 't',
"Int32" => 'i',
"UInt32" => 'j',
"Int64" => 'l',
"UInt64" => 'm',
"Char" => 'w',
"Single" => 'f',
"Double" => 'd',
_ => throw new UnreachableException()
});
}
}
else
{
BeginName();
WriteTypeName(type);
WriteEnd();
}
}

private void WriteGenericParams(TypeInfo[] generics)
{
if (generics.Length > 0)
{
BeginGenerics();

foreach (var arg in generics)
WriteType(arg);

WriteEnd();
}
}

private void WriteIdentifier(string identifier)
{
_sb.Append(identifier.Length);
_sb.Append(identifier);
}

private void WriteName(string name)
{
foreach (var part in name.Split("."))
{
if (part.Length > 0)
WriteIdentifier(part);
}
}

private void BeginName()
{
_sb.Append('N');
}

private void BeginGenerics()
{
_sb.Append('I');
}

private void WriteEnd()
{
_sb.Append('E');
}
}
3 changes: 2 additions & 1 deletion Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) {
attsByToken.Add(token, index);
}

AttributeIndicesByToken.Add(image.customAttributeStart, attsByToken);
if (attsByToken.Count > 0)
AttributeIndicesByToken.Add(image.customAttributeStart, attsByToken);
}
}

Expand Down
95 changes: 50 additions & 45 deletions Il2CppInspector.Common/Model/AppMethod.cs
Original file line number Diff line number Diff line change
@@ -1,45 +1,50 @@
/*
Copyright 2020-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty
All rights reserved.
*/

using Il2CppInspector.Cpp;
using Il2CppInspector.Reflection;

namespace Il2CppInspector.Model
{
// Class that represents a composite IL/C++ method
public class AppMethod
{
// The logical group this method is part of
// This is purely for querying methods in related groups and has no bearing on the code
public string Group { get; set; }

// The corresponding C++ function pointer type
public CppFnPtrType CppFnPtrType { get; internal set; }

// The corresponding .NET method
public MethodBase Method { get; internal set; }

// The VA of the MethodInfo* (VA of the pointer to the MethodInfo) object which defines this method
// Methods not referenced by the binary will be 0xffffffff_ffffffff
public ulong MethodInfoPtrAddress { get; internal set; }

// The VA of the method code itself
// Generic method definitions do not have a code address but may have a reference above
public ulong MethodCodeAddress => Method.VirtualAddress?.Start ?? 0xffffffff_ffffffff;

// Helpers
public bool HasMethodInfo => MethodInfoPtrAddress != 0xffffffff_ffffffff;
public bool HasCompiledCode => Method.VirtualAddress.HasValue && Method.VirtualAddress.Value.Start != 0;

public AppMethod(MethodBase method, CppFnPtrType cppMethod, ulong methodInfoPtr = 0xffffffff_ffffffff) {
Method = method;
CppFnPtrType = cppMethod;
MethodInfoPtrAddress = methodInfoPtr;
}

public override string ToString() => CppFnPtrType.ToSignatureString();
}
}
/*
Copyright 2020-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty
Copyright 2023 LukeFZ - https://github.com/LukeFZ
All rights reserved.
*/

using System.Diagnostics;
using Il2CppInspector.Cpp;
using Il2CppInspector.Reflection;
using System.Text;

namespace Il2CppInspector.Model
{
// Class that represents a composite IL/C++ method
public class AppMethod
{
// The logical group this method is part of
// This is purely for querying methods in related groups and has no bearing on the code
public string Group { get; set; }

// The corresponding C++ function pointer type
public CppFnPtrType CppFnPtrType { get; internal set; }

// The corresponding .NET method
public MethodBase Method { get; internal set; }

// The VA of the MethodInfo* (VA of the pointer to the MethodInfo) object which defines this method
// Methods not referenced by the binary will be 0xffffffff_ffffffff
public ulong MethodInfoPtrAddress { get; internal set; }

// The VA of the method code itself
// Generic method definitions do not have a code address but may have a reference above
public ulong MethodCodeAddress => Method.VirtualAddress?.Start ?? 0xffffffff_ffffffff;

// Helpers
public bool HasMethodInfo => MethodInfoPtrAddress != 0xffffffff_ffffffff;
public bool HasCompiledCode => Method.VirtualAddress.HasValue && Method.VirtualAddress.Value.Start != 0;

public AppMethod(MethodBase method, CppFnPtrType cppMethod, ulong methodInfoPtr = 0xffffffff_ffffffff) {
Method = method;
CppFnPtrType = cppMethod;
MethodInfoPtrAddress = methodInfoPtr;
}

public override string ToString() => CppFnPtrType.ToSignatureString();

public string ToMangledString() => MangledNameBuilder.Method(Method);
public string ToMangledMethodInfoString() => MangledNameBuilder.MethodInfo(Method);
}
}
19 changes: 6 additions & 13 deletions Il2CppInspector.Common/Model/AppModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ public class AppModel : IEnumerable<CppType>
// For il2cpp < 19, the key is the string literal ordinal instead of the address
public Dictionary<ulong, string> Strings { get; } = [];

public Dictionary<ulong, (string Name, string Value)> Fields { get; } = [];
public Dictionary<ulong, (string Name, string Value)> FieldRvas { get; } = [];
public Dictionary<ulong, (FieldInfo Field, string Value)> Fields { get; } = [];
public Dictionary<ulong, (FieldInfo Field, string Value)> FieldRvas { get; } = [];

public bool StringIndexesAreOrdinals => Package.Version < 19;

Expand Down Expand Up @@ -254,20 +254,16 @@ public AppModel Build(UnityVersion unityVersion = null, CppCompilerType compiler
var fieldType = TypeModel.GetMetadataUsageType(usage);
var field = fieldType.DeclaredFields.First(f => f.Index == fieldType.Definition.fieldStart + fieldRef.fieldIndex);

var name = usage.Type == MetadataUsageType.FieldInfo
? $"{fieldType.Name}.{field.Name}".ToCIdentifier()
: $"{fieldType.Name}.{field.Name}_FieldRva".ToCIdentifier();

var value = field.HasFieldRVA
? Convert.ToHexString(Package.Metadata.ReadBytes(
(long) field.DefaultValueMetadataAddress, field.FieldType.Sizes.nativeSize))
: "";


if (usage.Type == MetadataUsageType.FieldInfo)
Fields[usage.VirtualAddress] = (name, value);
Fields[usage.VirtualAddress] = (field, value);
else
FieldRvas[usage.VirtualAddress] = (name, value);
FieldRvas[usage.VirtualAddress] = (field, value);

break;
}
Expand Down Expand Up @@ -345,11 +341,8 @@ private void AddTypes(List<(TypeInfo ilType, CppComplexType valueType, CppComple
// Get the address map for the model
// This takes a while to construct so we only build it if requested
private AddressMap addressMap;
public AddressMap GetAddressMap() {
if (addressMap == null)
addressMap = new AddressMap(this);
return addressMap;
}
public AddressMap GetAddressMap()
=> addressMap ??= new AddressMap(this);

// Get the byte offset in Il2CppClass for this app's Unity version to the vtable
public int GetVTableOffset() => CppTypeCollection.GetComplexType("Il2CppClass")["vtable"].OffsetBytes;
Expand Down
Loading

0 comments on commit 5b1d9c6

Please sign in to comment.