Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Il2CppInterop.Generator/Passes/Pass16ScanMethodRefs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,17 @@ public static void DoPass(RewriteGlobalContext context, GeneratorOptions options
gameAssemblyPtr = (nint)fileStartPtr;
}

// Method file offsets are relative to the x86-64 image, which is a slice of the file in a universal Mach-O binary
var gameAssemblyLength = accessor.Capacity;
if (MachOUniversalBinary.TryGetSlice(gameAssemblyPtr, gameAssemblyLength, MachOUniversalBinary.CpuTypeX86_64,
out var sliceOffset, out var sliceSize))
{
gameAssemblyPtr += (nint)sliceOffset;
gameAssemblyLength = sliceSize;
}

context.HasGcWbarrierFieldWrite =
FindByteSequence(gameAssemblyPtr, accessor.Capacity, "il2cpp_gc_wbarrier_set_field");
FindByteSequence(gameAssemblyPtr, gameAssemblyLength, "il2cpp_gc_wbarrier_set_field");

if (!Pass15GenerateMemberContexts.HasObfuscatedMethods) return;

Expand Down
69 changes: 69 additions & 0 deletions Il2CppInterop.Generator/Utils/MachOUniversalBinary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
namespace Il2CppInterop.Generator.Utils;

/// <summary>
/// Locates one architecture's image inside a universal (fat) Mach-O binary, such as the
/// GameAssembly.dylib shipped by macOS Unity games built for both x86-64 and arm64.
/// </summary>
internal static class MachOUniversalBinary
{
internal const int CpuTypeX86_64 = 0x01000007;

private const uint FatMagic = 0xCAFEBABE;
private const uint FatMagic64 = 0xCAFEBABF;
private const int FatHeaderSize = 8;
private const int FatArchSize = 20;
private const int FatArch64Size = 32;

/// <summary>
/// Finds the slice for <paramref name="cpuType"/> when <paramref name="basePtr"/> points to a universal binary.
/// Returns false for thin binaries, or when the universal binary has no such slice.
/// </summary>
internal static unsafe bool TryGetSlice(nint basePtr, long length, int cpuType, out long offset, out long size)
{
offset = 0;
size = 0;

var bytes = (byte*)basePtr;
if (length < FatHeaderSize)
return false;

var magic = ReadUInt32BigEndian(bytes);
if (magic != FatMagic && magic != FatMagic64)
return false;

var archSize = magic == FatMagic64 ? FatArch64Size : FatArchSize;
var archCount = ReadUInt32BigEndian(bytes + 4);
if (FatHeaderSize + archCount * (long)archSize > length)
return false;

for (var i = 0; i < archCount; i++)
{
var arch = bytes + FatHeaderSize + i * archSize;
if ((int)ReadUInt32BigEndian(arch) != cpuType)
continue;

// fat_arch: cputype, cpusubtype, offset (32-bit), size (32-bit), align
// fat_arch_64: cputype, cpusubtype, offset (64-bit), size (64-bit), align, reserved
if (magic == FatMagic64)
{
offset = (long)ReadUInt64BigEndian(arch + 8);
size = (long)ReadUInt64BigEndian(arch + 16);
}
else
{
offset = ReadUInt32BigEndian(arch + 8);
size = ReadUInt32BigEndian(arch + 12);
}

return offset >= 0 && size > 0 && offset + size <= length;
}

return false;
}

private static unsafe uint ReadUInt32BigEndian(byte* p) =>
(uint)(p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]);

private static unsafe ulong ReadUInt64BigEndian(byte* p) =>
(ulong)ReadUInt32BigEndian(p) << 32 | ReadUInt32BigEndian(p + 4);
}
Loading