PageAllocator.cs 2.6 KB

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