|
@@ -1,6 +1,8 @@
|
|
using System;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Generic;
|
|
|
|
+using System.ComponentModel;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
+using System.Runtime.InteropServices;
|
|
using MonoMod.Utils;
|
|
using MonoMod.Utils;
|
|
|
|
|
|
namespace BepInEx.IL2CPP.Allocator
|
|
namespace BepInEx.IL2CPP.Allocator
|
|
@@ -59,12 +61,79 @@ namespace BepInEx.IL2CPP.Allocator
|
|
|
|
|
|
protected override IntPtr AllocateChunk(IntPtr hint)
|
|
protected override IntPtr AllocateChunk(IntPtr hint)
|
|
{
|
|
{
|
|
- throw new NotImplementedException();
|
|
|
|
|
|
+ /* From https://github.com/kubo/funchook/blob/master/src/funchook_unix.c#L251-L254:
|
|
|
|
+ * Loop three times just to avoid rare cases such as
|
|
|
|
+ * unused memory region is used between 'get_free_address()'
|
|
|
|
+ * and 'mmap()'.
|
|
|
|
+ */
|
|
|
|
+ const int retryCount = 3;
|
|
|
|
+
|
|
|
|
+ for (var attempt = 0; attempt < retryCount; attempt++)
|
|
|
|
+ {
|
|
|
|
+ var freeAdrresses = GetFreeAddresses(hint);
|
|
|
|
+ // Try to use addr[1] (allocated after original method) first, then try before
|
|
|
|
+ for (int i = freeAdrresses.Length - 1; i >= 0; i--)
|
|
|
|
+ {
|
|
|
|
+ var addr = freeAdrresses[i];
|
|
|
|
+ if (addr == IntPtr.Zero)
|
|
|
|
+ continue;
|
|
|
|
+ var result = Unix.mmap(freeAdrresses[i], (UIntPtr)PAGE_SIZE, Unix.Protection.PROT_READ | Unix.Protection.PROT_WRITE, Unix.MapFlags.MAP_PRIVATE | Unix.MapFlags.MAP_ANONYMOUS, -1, 0);
|
|
|
|
+ if (result == freeAdrresses[i])
|
|
|
|
+ return result;
|
|
|
|
+ if (result == Unix.MAP_FAILED)
|
|
|
|
+ throw new Win32Exception(Marshal.GetLastWin32Error()); // Yes, this should work on unix too
|
|
|
|
+ Unix.munmap(result, (UIntPtr)PAGE_SIZE);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ throw new PageAllocatorException("Failed to allocate memory in unused regions");
|
|
}
|
|
}
|
|
|
|
|
|
protected interface IMemoryMapper : IDisposable
|
|
protected interface IMemoryMapper : IDisposable
|
|
{
|
|
{
|
|
bool FindNextFree(out IntPtr start, out IntPtr end);
|
|
bool FindNextFree(out IntPtr start, out IntPtr end);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ private static class Unix
|
|
|
|
+ {
|
|
|
|
+ public static readonly IntPtr MAP_FAILED = new IntPtr(-1);
|
|
|
|
+
|
|
|
|
+ [DynDllImport("mmap")]
|
|
|
|
+ public static mmapDelegate mmap;
|
|
|
|
+
|
|
|
|
+ [DynDllImport("munmap")]
|
|
|
|
+ public static munmapDelegate munmap;
|
|
|
|
+
|
|
|
|
+ public delegate IntPtr mmapDelegate(IntPtr addr, UIntPtr length, Protection prot, MapFlags flags, int fd, int offset);
|
|
|
|
+
|
|
|
|
+ public delegate int munmapDelegate(IntPtr addr, UIntPtr length);
|
|
|
|
+
|
|
|
|
+ [Flags]
|
|
|
|
+ public enum MapFlags
|
|
|
|
+ {
|
|
|
|
+ MAP_PRIVATE = 0x02,
|
|
|
|
+ MAP_ANONYMOUS = 0x20
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ [Flags]
|
|
|
|
+ public enum Protection
|
|
|
|
+ {
|
|
|
|
+ PROT_READ = 0x1,
|
|
|
|
+ PROT_WRITE = 0x2
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static Unix()
|
|
|
|
+ {
|
|
|
|
+ typeof(Unix).ResolveDynDllImports(new Dictionary<string, List<DynDllMapping>>
|
|
|
|
+ {
|
|
|
|
+ ["libc"] = new List<DynDllMapping>
|
|
|
|
+ {
|
|
|
|
+ "libc.so.6", // Ubuntu glibc
|
|
|
|
+ "libc", // Linux glibc,
|
|
|
|
+ "/usr/lib/libSystem.dylib" // OSX POSIX
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|