Browse Source

Start work on Unix page allocator

ghorsington 3 years ago
parent
commit
4eacccd93f

+ 1 - 1
BepInEx.IL2CPP/Hook/Allocator/LinuxPageAllocator.cs

@@ -18,7 +18,7 @@ namespace BepInEx.IL2CPP.Allocator
 				throw new NotImplementedException();
 			}
 
-			public bool FindNextFree(ref IntPtr start, ref ulong size)
+			public bool FindNextFree(out IntPtr start, out IntPtr end)
 			{
 				throw new NotImplementedException();
 			}

+ 1 - 1
BepInEx.IL2CPP/Hook/Allocator/MacOsPageAllocator.cs

@@ -16,7 +16,7 @@ namespace BepInEx.IL2CPP.Allocator
 				throw new NotImplementedException();
 			}
 
-			public bool FindNextFree(ref IntPtr start, ref ulong size)
+			public bool FindNextFree(out IntPtr start, out IntPtr end)
 			{
 				throw new NotImplementedException();
 			}

+ 5 - 0
BepInEx.IL2CPP/Hook/Allocator/PageAllocator.cs

@@ -4,6 +4,11 @@ using MonoMod.Utils;
 
 namespace BepInEx.IL2CPP.Allocator
 {
+	public class PageAllocatorException : Exception
+	{
+		public PageAllocatorException(string message) : base(message) {}
+	}
+	
 	/// <summary>
 	///     A general purpose page allocator for patching purposes.
 	///     Allows to allocate pages (4k memory chunks) within the 1GB radius of a given address.

+ 44 - 1
BepInEx.IL2CPP/Hook/Allocator/UnixPageAllocator.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Runtime.CompilerServices;
 using MonoMod.Utils;
 
 namespace BepInEx.IL2CPP.Allocator
@@ -10,6 +11,48 @@ namespace BepInEx.IL2CPP.Allocator
 	internal abstract class UnixPageAllocator : PageAllocator
 	{
 		protected abstract IMemoryMapper OpenMemoryMap();
+
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		private static bool CheckFreeRegionBefore(IntPtr start, IntPtr hint, IntPtr[] result)
+		{
+			if (start.ToInt64() < hint.ToInt64())
+			{
+				var addr = start - PAGE_SIZE;
+				if (hint.ToInt64() - addr.ToInt64() < int.MaxValue)
+					result[0] = addr;
+			}
+			return false;
+		}
+		
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		private static bool CheckFreeRegionAfter(IntPtr end, IntPtr hint, IntPtr[] result)
+		{
+			if (hint.ToInt64() < end.ToInt64())
+			{
+				if (end.ToInt64() - hint.ToInt64() < int.MaxValue)
+					result[1] = end;
+				return true;
+			}
+			return false;
+		}
+			
+		private IntPtr[] GetFreeAddresses(IntPtr hint)
+		{
+			var result = new IntPtr[2];
+			var prevEnd = IntPtr.Zero;
+			using var mapper = OpenMemoryMap();
+			
+			while (mapper.FindNextFree(out var start, out var end))
+			{
+				if ((prevEnd + PAGE_SIZE).ToInt64() <= start.ToInt64())
+					if (CheckFreeRegionBefore(start, hint, result) || CheckFreeRegionAfter(prevEnd, hint, result))
+						return result;
+				prevEnd = end;
+			}
+			if (CheckFreeRegionAfter(prevEnd, hint, result))
+				return result;
+			throw new PageAllocatorException($"Could not find free region near {hint.ToInt64():X8}");
+		}
 		
 		public override IntPtr Allocate(IntPtr hint)
 		{
@@ -23,7 +66,7 @@ namespace BepInEx.IL2CPP.Allocator
 
 		protected interface IMemoryMapper: IDisposable
 		{
-			bool FindNextFree(ref IntPtr start, ref ulong size);
+			bool FindNextFree(out IntPtr start, out IntPtr end);
 		}
 	}
 }