PageAllocator.cs 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using MonoMod.Utils;
  4. namespace BepInEx.IL2CPP.Allocator
  5. {
  6. public class PageAllocatorException : Exception
  7. {
  8. public PageAllocatorException(string message) : base(message) {}
  9. }
  10. /// <summary>
  11. /// A general purpose page allocator for patching purposes.
  12. /// Allows to allocate pages (4k memory chunks) within the 1GB radius of a given address.
  13. /// </summary>
  14. /// <remarks>Based on https://github.com/kubo/funchook</remarks>
  15. internal abstract class PageAllocator
  16. {
  17. /// <summary>
  18. /// Common page size on Unix and Windows (4k).
  19. /// Call to <see cref="Allocate" /> will allocate a single page of this size.
  20. /// </summary>
  21. public const int PAGE_SIZE = 0x1000;
  22. /// <summary>
  23. /// Allocation granularity on Windows (but can be reused in other implementations).
  24. /// </summary>
  25. protected const int ALLOCATION_UNIT = 0x100000;
  26. protected const int PAGES_PER_UNIT = ALLOCATION_UNIT / PAGE_SIZE;
  27. private static PageAllocator instance;
  28. /// <summary>
  29. /// Platform-specific instance of page allocator.
  30. /// </summary>
  31. public static PageAllocator Instance => instance ??= Init();
  32. /// <summary>
  33. /// Allocates a single page of size <see cref="PAGE_SIZE" /> near the provided address.
  34. /// Attempts to allocate the page within the +-1GB region of the hinted address.
  35. /// </summary>
  36. /// <param name="hint">Address near which to attempt to allocate the page.</param>
  37. /// <returns>Address to the allocated page.</returns>
  38. public abstract IntPtr Allocate(IntPtr hint);
  39. /// <summary>
  40. /// Frees the page allocated with <see cref="Allocate" />
  41. /// </summary>
  42. /// <param name="page"></param>
  43. public abstract void Free(IntPtr page);
  44. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  45. protected static long RoundUp(long num, long unit)
  46. {
  47. return (num + unit - 1) & ~ (unit - 1);
  48. }
  49. /// <summary>
  50. /// Checks if the given address is within the relative jump range.
  51. /// </summary>
  52. /// <param name="src">Source address to jump from.</param>
  53. /// <param name="dst">Destination address to jump to.</param>
  54. /// <returns>True, if the distance between the addresses is within the relative jump range (usually 1GB), otherwise false.</returns>
  55. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  56. public static bool IsInRelJmpRange(IntPtr src, IntPtr dst)
  57. {
  58. long diff = dst.ToInt64() - src.ToInt64();
  59. return int.MinValue <= diff && diff <= int.MaxValue;
  60. }
  61. private static PageAllocator Init() =>
  62. PlatformHelper.Current switch
  63. {
  64. var v when v.Is(Platform.Windows) => new WindowsPageAllocator(),
  65. var v when v.Is(Platform.Linux) => new LinuxPageAllocator(),
  66. var v when v.Is(Platform.MacOS) => new MacOsPageAllocator(),
  67. _ => throw new NotImplementedException()
  68. };
  69. }
  70. internal static class PlatformExt
  71. {
  72. public static bool Is(this Platform pl, Platform val) => (pl & val) == val;
  73. }
  74. }