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

Commit

Permalink
Use il2cpp_gc_wbarrier_set_field for field writes if it exists
Browse files Browse the repository at this point in the history
  • Loading branch information
knah committed Jun 8, 2021
1 parent 3f11a31 commit 0ede0f1
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 14 deletions.
6 changes: 6 additions & 0 deletions AssemblyUnhollower/AssemblyKnownImports.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public static AssemblyKnownImports For(ModuleDefinition module, RewriteGlobalCon
public MethodReference Il2CppObjectCast => myIl2CppObjectCast.Value;
public MethodReference Il2CppObjectTryCast => myIl2CppObjectTryCast.Value;
public MethodReference Il2CppResolveICall => myIl2CppResolveICall.Value;
public MethodReference WriteFieldWBarrier => myWriteFieldWBarrier.Value;

private readonly Lazy<MethodReference> myIl2CppObjectToPointer;
private readonly Lazy<MethodReference> myIl2CppObjectToPointerNotNull;
Expand All @@ -77,6 +78,7 @@ public static AssemblyKnownImports For(ModuleDefinition module, RewriteGlobalCon
private readonly Lazy<MethodReference> myIl2CppObjectCast;
private readonly Lazy<MethodReference> myIl2CppObjectTryCast;
private readonly Lazy<MethodReference> myIl2CppResolveICall;
private readonly Lazy<MethodReference> myWriteFieldWBarrier;

private readonly Lazy<MethodReference> myFieldGetOffset;
private readonly Lazy<MethodReference> myFieldStaticGet;
Expand Down Expand Up @@ -184,6 +186,10 @@ public AssemblyKnownImports(ModuleDefinition module, RewriteGlobalContext contex
myIl2CppObjectCast = new Lazy<MethodReference>(() => Module.ImportReference(typeof(Il2CppObjectBase).GetMethod("Cast")));
myIl2CppObjectTryCast = new Lazy<MethodReference>(() => Module.ImportReference(typeof(Il2CppObjectBase).GetMethod("TryCast")));
myIl2CppResolveICall = new Lazy<MethodReference>(() => Module.ImportReference(typeof(IL2CPP).GetMethod(nameof(IL2CPP.ResolveICall))));
myWriteFieldWBarrier = new Lazy<MethodReference>(() =>
Module.ImportReference(myContext.HasGcWbarrierFieldWrite
? typeof(IL2CPP).GetMethod(nameof(IL2CPP.il2cpp_gc_wbarrier_set_field))
: typeof(IL2CPP).GetMethod(nameof(IL2CPP.FieldWriteWbarrierStub))));

myFieldGetOffset = new Lazy<MethodReference>(() => Module.ImportReference(typeof(IL2CPP).GetMethod("il2cpp_field_get_offset")));
myFieldStaticGet = new Lazy<MethodReference>(() => Module.ImportReference(typeof(IL2CPP).GetMethod("il2cpp_field_static_get_value")));
Expand Down
2 changes: 1 addition & 1 deletion AssemblyUnhollower/AssemblyUnhollower.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFrameworks>net4.7.2;net5.0;netstandard2.1</TargetFrameworks>
<Nullable>enable</Nullable>
<Version>0.4.15.0</Version>
<Version>0.4.15.1</Version>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
Expand Down
2 changes: 2 additions & 0 deletions AssemblyUnhollower/Contexts/RewriteGlobalContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public class RewriteGlobalContext : IDisposable

public IEnumerable<AssemblyRewriteContext> Assemblies => myAssemblies.Values;

internal bool HasGcWbarrierFieldWrite { get; set; }

public RewriteGlobalContext(UnhollowerOptions options, IIl2CppMetadataAccess gameAssemblies, IMetadataAccess systemAssemblies, IMetadataAccess unityAssemblies)
{
Options = options;
Expand Down
1 change: 1 addition & 0 deletions AssemblyUnhollower/FieldAccessorGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public static void MakeSetter(FieldDefinition field, FieldRewriteContext fieldCo
else
{
setterBody.EmitObjectToPointer(fieldContext.DeclaringType.OriginalType, fieldContext.DeclaringType.NewType, fieldContext.DeclaringType, 0, false, false, false, out _);
setterBody.Emit(OpCodes.Dup);
setterBody.Emit(OpCodes.Ldsfld, fieldContext.PointerField);
setterBody.Emit(OpCodes.Call, imports.FieldGetOffset);
setterBody.Emit(OpCodes.Add);
Expand Down
35 changes: 30 additions & 5 deletions AssemblyUnhollower/Passes/Pass16ScanMethodRefs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Text;
using AssemblyUnhollower.Contexts;
using AssemblyUnhollower.Extensions;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib.XrefScans;

namespace AssemblyUnhollower.Passes
Expand All @@ -22,11 +24,7 @@ public static void DoPass(RewriteGlobalContext context, UnhollowerOptions option
Pass15GenerateMemberContexts.HasObfuscatedMethods = false;
return;
}
if (!Pass15GenerateMemberContexts.HasObfuscatedMethods) return;

var methodToCallersMap = new ConcurrentDictionary<long, List<XrefInstance>>();
var methodToCalleesMap = new ConcurrentDictionary<long, List<long>>();


using var mappedFile = MemoryMappedFile.CreateFromFile(options.GameAssemblyPath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read);
using var accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);

Expand All @@ -39,6 +37,13 @@ public static void DoPass(RewriteGlobalContext context, UnhollowerOptions option
gameAssemblyPtr = (IntPtr) fileStartPtr;
}

context.HasGcWbarrierFieldWrite = FindByteSequence(gameAssemblyPtr, accessor.Capacity, nameof(IL2CPP.il2cpp_gc_wbarrier_set_field));

if (!Pass15GenerateMemberContexts.HasObfuscatedMethods) return;

var methodToCallersMap = new ConcurrentDictionary<long, List<XrefInstance>>();
var methodToCalleesMap = new ConcurrentDictionary<long, List<long>>();

context.MethodStartAddresses.Sort();

// Scan xrefs
Expand Down Expand Up @@ -96,5 +101,25 @@ void MarkMethodAlive(long address)
MarkMethodAlive(methodRewriteContext.Rva);
}
}

private unsafe static bool FindByteSequence(IntPtr basePtr, long length, string str)
{
byte* bytes = (byte*)basePtr;
var sequence = Encoding.UTF8.GetBytes(str);
for (var i = 0L; i < length; i++)
{
for (var j = 0; j < sequence.Length; j++)
{
if (bytes[i + j] != sequence[j])
goto next;
}

return true;

next: ;
}

return false;
}
}
}
16 changes: 10 additions & 6 deletions AssemblyUnhollower/UtilGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static void EmitLdcI4(this ILProcessor body, int constant)

public static void EmitObjectStore(this ILProcessor body, TypeReference originalType, TypeReference newType, TypeRewriteContext enclosingType, int argumentIndex)
{
// input stack: target address
// input stack: object address, target address
// output: nothing
if (originalType is GenericParameter)
{
Expand All @@ -48,14 +48,15 @@ public static void EmitObjectStore(this ILProcessor body, TypeReference original
{
body.Emit(OpCodes.Ldarg, argumentIndex);
body.Emit(OpCodes.Call, imports.StringToNative);
body.Emit(OpCodes.Stobj, imports.IntPtr);
body.Emit(OpCodes.Call, imports.WriteFieldWBarrier);
} else if (originalType.IsValueType)
{
var typeSpecifics = enclosingType.AssemblyContext.GlobalContext.JudgeSpecificsByOriginalType(originalType);
if (typeSpecifics == TypeRewriteContext.TypeSpecifics.BlittableStruct)
{
body.Emit(OpCodes.Ldarg, argumentIndex);
body.Emit(OpCodes.Stobj, newType);
body.Emit(OpCodes.Pop);
}
else
{
Expand All @@ -68,17 +69,18 @@ public static void EmitObjectStore(this ILProcessor body, TypeReference original
body.Emit(OpCodes.Ldc_I4_0);
body.Emit(OpCodes.Call, imports.ValueSizeGet);
body.Emit(OpCodes.Cpblk);
body.Emit(OpCodes.Pop);
}
} else {
body.Emit(OpCodes.Ldarg, argumentIndex);
body.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointer);
body.Emit(OpCodes.Stobj, imports.IntPtr);
body.Emit(OpCodes.Call, imports.WriteFieldWBarrier);
}
}

private static void EmitObjectStoreGeneric(ILProcessor body, TypeReference originalType, TypeReference newType, TypeRewriteContext enclosingType, int argumentIndex)
{
// input stack: target address
// input stack: object address, target address
// output: nothing

var imports = enclosingType.AssemblyContext.Imports;
Expand Down Expand Up @@ -123,24 +125,26 @@ private static void EmitObjectStoreGeneric(ILProcessor body, TypeReference origi
body.Emit(OpCodes.Conv_U);
body.Emit(OpCodes.Call, imports.ValueSizeGet);
body.Emit(OpCodes.Cpblk);
body.Emit(OpCodes.Pop);
body.Emit(OpCodes.Br_S, finalNop);

body.Append(storePointerNop);
body.Emit(OpCodes.Stind_I);
body.Emit(OpCodes.Call, imports.WriteFieldWBarrier);
body.Emit(OpCodes.Br_S, finalNop);

body.Append(stringNop);
body.Emit(OpCodes.Ldarg, argumentIndex);
body.Emit(OpCodes.Box, newType);
body.Emit(OpCodes.Isinst, imports.String);
body.Emit(OpCodes.Call, imports.StringToNative);
body.Emit(OpCodes.Stind_I);
body.Emit(OpCodes.Call, imports.WriteFieldWBarrier);
body.Emit(OpCodes.Br_S, finalNop);

body.Append(valueTypeNop);
body.Emit(OpCodes.Pop); // pop extra typeof(T)
body.Emit(OpCodes.Ldarg, argumentIndex);
body.Emit(OpCodes.Stobj, newType);
body.Emit(OpCodes.Pop);

body.Append(finalNop);
}
Expand Down
1 change: 1 addition & 0 deletions ReleaseChangelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ Changes:
* Added `Il2CppObjectBase.WasCollected` (contributed by @ds5678 in #37)
* Added basic support for loading custom components (of injected types) from assetbundles (contributed by @ds5678 in #38)
* Added an overload of `Il2CppType.Of` method that doesn't throw exceptions for non-found types (contributed by @ds5678 in #36)
* Field writes now use `il2cpp_gc_wbarrier_set_field` if it exists in GameAssembly

11 changes: 10 additions & 1 deletion UnhollowerBaseLib/IL2CPP.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
Expand Down Expand Up @@ -337,6 +338,14 @@ private static bool TypeHasIl2CppArrayBase(this Type type)
return TypeHasIl2CppArrayBase(type.BaseType);
}

// this is called if there's no actual il2cpp_gc_wbarrier_set_field()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void FieldWriteWbarrierStub(IntPtr obj, IntPtr targetAddress, IntPtr value)
{
// ignore obj
*(IntPtr*)targetAddress = value;
}

// IL2CPP Functions
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void il2cpp_init(IntPtr domain_name);
Expand Down Expand Up @@ -533,7 +542,7 @@ private static bool TypeHasIl2CppArrayBase(this Type type)
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern long il2cpp_gc_get_heap_size();
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void il2cpp_gc_wbarrier_set_field(IntPtr obj, out IntPtr targetAddress, IntPtr gcObj);
public static extern void il2cpp_gc_wbarrier_set_field(IntPtr obj, IntPtr targetAddress, IntPtr gcObj);
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern uint il2cpp_gchandle_new(IntPtr obj, bool pinned);
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
Expand Down
2 changes: 1 addition & 1 deletion UnhollowerBaseLib/UnhollowerBaseLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFrameworks>net4.7.2;netstandard2.1</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<Version>0.4.15.0</Version>
<Version>0.4.15.1</Version>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit 0ede0f1

Please sign in to comment.