using System; using System.Runtime.CompilerServices; using MonoMod.Utils; namespace BepInEx.IL2CPP { /// /// A general purpose page allocator for patching purposes. /// Allows to allocate pages (4k memory chunks) within the 1GB radius of a given address. /// /// Based on https://github.com/kubo/funchook internal abstract class PageAllocator { /// /// Common page size on Unix and Windows (4k). /// Call to will allocate a single page of this size. /// public const int PAGE_SIZE = 0x1000; /// /// Allocation granularity on Windows (but can be reused in other implementations). /// protected const int ALLOCATION_UNIT = 0x100000; protected const int PAGES_PER_UNIT = ALLOCATION_UNIT / PAGE_SIZE; private static PageAllocator instance; /// /// Platform-specific instance of page allocator. /// public static PageAllocator Instance => instance ??= Init(); /// /// Allocates a single page of size near the provided address. /// Attempts to allocate the page within the +-1GB region of the hinted address. /// /// Address near which to attempt to allocate the page. /// Address to the allocated page. public abstract IntPtr Allocate(IntPtr hint); /// /// Frees the page allocated with /// /// public abstract void Free(IntPtr page); [MethodImpl(MethodImplOptions.AggressiveInlining)] protected static long RoundUp(long num, long unit) { return (num + unit - 1) & ~ (unit - 1); } /// /// Checks if the given address is within the relative jump range. /// /// Source address to jump from. /// Destination address to jump to. /// True, if the distance between the addresses is within the relative jump range (usually 1GB), otherwise false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsInRelJmpRange(IntPtr src, IntPtr dst) { long diff = dst.ToInt64() - src.ToInt64(); return int.MinValue <= diff && diff <= int.MaxValue; } private static PageAllocator Init() { if (PlatformHelper.Is(Platform.Windows)) return new WindowsPageAllocator(); if (PlatformHelper.Is(Platform.Unix)) return new UnixPageAllocator(); throw new NotImplementedException(); } } }