diff --git a/src/BizHawk.BizInvoke/IMemoryBlockPal.cs b/src/BizHawk.BizInvoke/IMemoryBlockPal.cs
new file mode 100644
index 0000000000..daebd225a0
--- /dev/null
+++ b/src/BizHawk.BizInvoke/IMemoryBlockPal.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace BizHawk.BizInvoke
+{
+ ///
+ /// Platform abstraction layer over mmap like functionality
+ ///
+ public interface IMemoryBlockPal : IDisposable
+ {
+ ///
+ /// Map in the memory area at the predetermined address
+ ///
+ void PalActivate();
+ ///
+ /// Unmap the memory area
+ ///
+ void PalDeactivate();
+ ///
+ /// Change protection on some addresses, guaranteed to be page aligned and in the memory area
+ ///
+ void PalProtect(ulong start, ulong size, MemoryBlock.Protection prot);
+ }
+}
diff --git a/src/BizHawk.BizInvoke/MemoryBlock.cs b/src/BizHawk.BizInvoke/MemoryBlock.cs
index 28d6ab8eea..61be1c4eaa 100644
--- a/src/BizHawk.BizInvoke/MemoryBlock.cs
+++ b/src/BizHawk.BizInvoke/MemoryBlock.cs
@@ -5,11 +5,11 @@ using BizHawk.Common;
namespace BizHawk.BizInvoke
{
- public abstract class MemoryBlock : IDisposable
+ public class MemoryBlock : IDisposable
{
/// allocate bytes starting at a particular address
/// is not aligned or is 0
- protected MemoryBlock(ulong start, ulong size)
+ public MemoryBlock(ulong start, ulong size)
{
if (!WaterboxUtils.Aligned(start))
throw new ArgumentOutOfRangeException(nameof(start), start, "start address must be aligned");
@@ -19,8 +19,14 @@ namespace BizHawk.BizInvoke
Size = WaterboxUtils.AlignUp(size);
EndExclusive = Start + Size;
_pageData = new Protection[GetPage(EndExclusive - 1) + 1];
+
+ _pal = OSTailoredCode.IsUnixHost
+ ? (IMemoryBlockPal)new MemoryBlockUnixPal(Start, Size)
+ : new MemoryBlockWindowsPal(Start, Size);
}
+ private IMemoryBlockPal _pal;
+
/// stores last set memory protection value for each page
protected readonly Protection[] _pageData;
@@ -74,40 +80,111 @@ namespace BizHawk.BizInvoke
}
/// activate the memory block, swapping it in at the pre-specified address
- public abstract void Activate();
+ /// is or failed to map file view
+ public void Activate()
+ {
+ if (Active)
+ throw new InvalidOperationException("Already active");
+ _pal.PalActivate();
+ ProtectAll();
+ Active = true;
+ }
/// deactivate the memory block, removing it from RAM but leaving it immediately available to swap back in
- public abstract void Deactivate();
+ /// is or failed to unmap file view
+ public void Deactivate()
+ {
+ if (!Active)
+ throw new InvalidOperationException("Not active");
+ _pal.PalDeactivate();
+ Active = false;
+ }
/// take a hash of the current full contents of the block, including unreadable areas
- public abstract byte[] FullHash();
+ /// is or failed to make memory read-only
+ public byte[] FullHash()
+ {
+ if (!Active)
+ throw new InvalidOperationException("Not active");
+ // temporarily switch the entire block to `R`
+ _pal.PalProtect(Start, Size, Protection.R);
+ var ret = WaterboxUtils.Hash(GetStream(Start, Size, false));
+ ProtectAll();
+ return ret;
+ }
- /// set r/w/x protection on a portion of memory. rounded to encompassing pages
- public abstract void Protect(ulong start, ulong length, Protection prot);
+
+ /// set r/w/x protection on a portion of memory. rounded to encompassing pagesfailed to protect memory
+ public void Protect(ulong start, ulong length, Protection prot)
+ {
+ if (length == 0)
+ return;
+ int pstart = GetPage(start);
+ int pend = GetPage(start + length - 1);
+
+ for (int i = pstart; i <= pend; i++)
+ _pageData[i] = prot; // also store the value for later use
+
+ if (Active) // it's legal to Protect() if we're not active; the information is just saved for the next activation
+ {
+ var computedStart = WaterboxUtils.AlignDown(start);
+ var computedEnd = WaterboxUtils.AlignUp(start + length);
+ var computedLength = computedEnd - computedStart;
+
+ _pal.PalProtect(computedStart, computedLength, prot);
+ }
+ }
/// restore all recorded protections
- protected abstract void ProtectAll();
+ private void ProtectAll()
+ {
+ int ps = 0;
+ for (int i = 0; i < _pageData.Length; i++)
+ {
+ if (i == _pageData.Length - 1 || _pageData[i] != _pageData[i + 1])
+ {
+ ulong zstart = GetStartAddr(ps);
+ ulong zend = GetStartAddr(i + 1);
+ _pal.PalProtect(zstart, zend - zstart, _pageData[i]);
+ ps = i + 1;
+ }
+ }
+ }
/// take a snapshot of the entire memory block's contents, for use in
- public abstract void SaveXorSnapshot();
+ /// snapshot already taken, is , or failed to make memory read-only
+ public void SaveXorSnapshot()
+ {
+ if (_snapshot != null)
+ throw new InvalidOperationException("Snapshot already taken");
+ if (!Active)
+ throw new InvalidOperationException("Not active");
- public abstract void Dispose(bool disposing);
+ // temporarily switch the entire block to `R`: in case some areas are unreadable, we don't want
+ // that to complicate things
+ _pal.PalProtect(Start, Size, Protection.R);
+
+ _snapshot = new byte[Size];
+ var ds = new MemoryStream(_snapshot, true);
+ var ss = GetStream(Start, Size, false);
+ ss.CopyTo(ds);
+ XorHash = WaterboxUtils.Hash(_snapshot);
+
+ ProtectAll();
+ }
public void Dispose()
{
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- ~MemoryBlock()
- {
- Dispose(false);
+ if (_pal != null)
+ {
+ _pal.Dispose();
+ _pal = null;
+ }
}
/// allocate bytes starting at a particular address
- public static MemoryBlock Create(ulong start, ulong size) => OSTailoredCode.IsUnixHost
- ? (MemoryBlock) new MemoryBlockUnix(start, size)
- : new MemoryBlockWindows(start, size);
+ public static MemoryBlock Create(ulong start, ulong size) => new MemoryBlock(start, size);
/// allocate bytes at any address
public static MemoryBlock Create(ulong size) => Create(0, size);
diff --git a/src/BizHawk.BizInvoke/MemoryBlockUnix.cs b/src/BizHawk.BizInvoke/MemoryBlockUnix.cs
deleted file mode 100644
index 36ad113680..0000000000
--- a/src/BizHawk.BizInvoke/MemoryBlockUnix.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-using System;
-using System.IO;
-
-using static BizHawk.BizInvoke.POSIXLibC;
-
-namespace BizHawk.BizInvoke
-{
- public sealed class MemoryBlockUnix : MemoryBlock
- {
- /// handle returned by
- private int _fd;
-
- ///
- /// failed to get file descriptor (never thrown as is thrown first)
- /// always
- public MemoryBlockUnix(ulong start, ulong size) : base(start, size)
- {
- throw new NotImplementedException($"{nameof(MemoryBlockUnix)} ctor");
- #if false
- _fd = memfd_create("MemoryBlockUnix", 0);
- if (_fd == -1) throw new InvalidOperationException($"{nameof(memfd_create)}() returned -1");
- #endif
- }
-
- /// is or failed to map memory
- public override void Activate()
- {
- if (Active) throw new InvalidOperationException("Already active");
-
- var ptr = mmap(Z.US(Start), Z.UU(Size), MemoryProtection.Read | MemoryProtection.Write | MemoryProtection.Execute, 16, _fd, IntPtr.Zero);
- if (ptr != Z.US(Start)) throw new InvalidOperationException($"{nameof(mmap)}() returned NULL or the wrong pointer");
-
- ProtectAll();
- Active = true;
- }
-
- /// is or failed to unmap memory
- public override void Deactivate()
- {
- if (!Active) throw new InvalidOperationException("Not active");
-
- var exitCode = munmap(Z.US(Start), Z.UU(Size));
- if (exitCode != 0) throw new InvalidOperationException($"{nameof(munmap)}() returned {exitCode}");
-
- Active = false;
- }
-
- /// is or failed to make memory read-only
- public override byte[] FullHash()
- {
- if (!Active) throw new InvalidOperationException("Not active");
-
- // temporarily switch the entire block to `R`
- var exitCode = mprotect(Z.US(Start), Z.UU(Size), MemoryProtection.Read);
- if (exitCode != 0) throw new InvalidOperationException($"{nameof(mprotect)}() returned {exitCode}!");
-
- var ret = WaterboxUtils.Hash(GetStream(Start, Size, false));
- ProtectAll();
- return ret;
- }
-
- /// failed to protect memory
- public override void Protect(ulong start, ulong length, Protection prot)
- {
- if (length == 0) return;
-
- var pstart = GetPage(start);
- var pend = GetPage(start + length - 1);
- for (var i = pstart; i <= pend; i++) _pageData[i] = prot; // also store the value for later use
- if (!Active) return; // it's legal to call this method if we're not active; the information is just saved for the next activation
-
- var computedStart = WaterboxUtils.AlignDown(start);
- var protEnum = prot.ToMemoryProtection();
- var exitCode = mprotect(
- Z.US(computedStart),
- Z.UU(WaterboxUtils.AlignUp(start + length) - computedStart),
- protEnum
- );
- if (exitCode != 0) throw new InvalidOperationException($"{nameof(mprotect)}() returned {exitCode}!");
- }
-
- protected override void ProtectAll()
- {
- var ps = 0;
- for (var i = 0; i < _pageData.Length; i++)
- {
- if (i == _pageData.Length - 1 || _pageData[i] != _pageData[i + 1])
- {
- var protEnum = _pageData[i].ToMemoryProtection();
- var zstart = GetStartAddr(ps);
- var exitCode = mprotect(
- Z.US(zstart),
- Z.UU(GetStartAddr(i + 1) - zstart),
- protEnum
- );
- if (exitCode != 0) throw new InvalidOperationException($"{nameof(mprotect)}() returned {exitCode}!");
-
- ps = i + 1;
- }
- }
- }
-
- /// snapshot already taken, is , or failed to make memory read-only
- public override void SaveXorSnapshot()
- {
- if (_snapshot != null) throw new InvalidOperationException("Snapshot already taken");
- if (!Active) throw new InvalidOperationException("Not active");
-
- // temporarily switch the entire block to `R`: in case some areas are unreadable, we don't want that to complicate things
- var exitCode = mprotect(Z.US(Start), Z.UU(Size), MemoryProtection.Read);
- if (exitCode != 0) throw new InvalidOperationException($"{nameof(mprotect)}() returned {exitCode}!");
-
- _snapshot = new byte[Size];
- GetStream(Start, Size, false).CopyTo(new MemoryStream(_snapshot, true));
- XorHash = WaterboxUtils.Hash(_snapshot);
- ProtectAll();
- }
-
- public override void Dispose(bool disposing)
- {
- if (_fd == 0) return;
-
- if (Active) Deactivate();
- close(_fd);
- _fd = -1;
- }
-
- ~MemoryBlockUnix()
- {
- Dispose(false);
- }
- }
-}
diff --git a/src/BizHawk.BizInvoke/MemoryBlockUnixPal.cs b/src/BizHawk.BizInvoke/MemoryBlockUnixPal.cs
new file mode 100644
index 0000000000..81cd7f2700
--- /dev/null
+++ b/src/BizHawk.BizInvoke/MemoryBlockUnixPal.cs
@@ -0,0 +1,72 @@
+using System;
+using static BizHawk.BizInvoke.MemoryBlock;
+using static BizHawk.BizInvoke.POSIXLibC;
+
+namespace BizHawk.BizInvoke
+{
+ public sealed class MemoryBlockUnixPal : IMemoryBlockPal
+ {
+ /// handle returned by
+ private int _fd = -1;
+ private ulong _start;
+ private ulong _size;
+
+ ///
+ /// Reserve bytes to later be swapped in, but do not map them
+ ///
+ /// eventual mapped address
+ ///
+ /// failed to get file descriptor (never thrown as is thrown first)
+ /// always
+ public MemoryBlockUnixPal(ulong start, ulong size)
+ {
+ _start = start;
+ _size = size;
+ throw new NotImplementedException($"{nameof(MemoryBlockUnixPal)} ctor");
+ #if false
+ _fd = memfd_create("MemoryBlockUnix", 0);
+ if (_fd == -1)
+ throw new InvalidOperationException($"{nameof(memfd_create)}() returned -1");
+ #endif
+ }
+
+ public void Dispose()
+ {
+ if (_fd == -1)
+ return;
+ close(_fd);
+ _fd = -1;
+ GC.SuppressFinalize(this);
+ }
+
+ ~MemoryBlockUnixPal()
+ {
+ Dispose();
+ }
+
+ public void PalActivate()
+ {
+ var ptr = mmap(Z.US(_start), Z.UU(_size), MemoryProtection.Read | MemoryProtection.Write | MemoryProtection.Execute, 16, _fd, IntPtr.Zero);
+ if (ptr != Z.US(_start))
+ throw new InvalidOperationException($"{nameof(mmap)}() returned NULL or the wrong pointer");
+ }
+
+ public void PalDeactivate()
+ {
+ var exitCode = munmap(Z.US(_start), Z.UU(_size));
+ if (exitCode != 0)
+ throw new InvalidOperationException($"{nameof(munmap)}() returned {exitCode}");
+ }
+
+ public void PalProtect(ulong start, ulong size, Protection prot)
+ {
+ var exitCode = mprotect(
+ Z.US(start),
+ Z.UU(size),
+ prot.ToMemoryProtection()
+ );
+ if (exitCode != 0)
+ throw new InvalidOperationException($"{nameof(mprotect)}() returned {exitCode}!");
+ }
+ }
+}
diff --git a/src/BizHawk.BizInvoke/MemoryBlockWindows.cs b/src/BizHawk.BizInvoke/MemoryBlockWindows.cs
deleted file mode 100644
index ba2f6a3591..0000000000
--- a/src/BizHawk.BizInvoke/MemoryBlockWindows.cs
+++ /dev/null
@@ -1,238 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.IO;
-
-namespace BizHawk.BizInvoke
-{
- public sealed class MemoryBlockWindows : MemoryBlock
- {
- ///
- /// handle returned by CreateFileMapping
- ///
- private IntPtr _handle;
-
- ///
- /// failed to create file mapping
- public MemoryBlockWindows(ulong start, ulong size) : base(start, size)
- {
- _handle = Kernel32.CreateFileMapping(
- Kernel32.INVALID_HANDLE_VALUE,
- IntPtr.Zero,
- Kernel32.FileMapProtection.PageExecuteReadWrite | Kernel32.FileMapProtection.SectionCommit,
- (uint)(Size >> 32),
- (uint)Size,
- null
- );
- if (_handle == IntPtr.Zero) throw new InvalidOperationException($"{nameof(Kernel32.CreateFileMapping)}() returned NULL");
- }
-
- /// is or failed to map file view
- public override void Activate()
- {
- if (Active)
- throw new InvalidOperationException("Already active");
- if (Kernel32.MapViewOfFileEx(
- _handle,
- Kernel32.FileMapAccessType.Read | Kernel32.FileMapAccessType.Write | Kernel32.FileMapAccessType.Execute,
- 0,
- 0,
- Z.UU(Size),
- Z.US(Start)
- ) != Z.US(Start))
- {
- throw new InvalidOperationException($"{nameof(Kernel32.MapViewOfFileEx)}() returned NULL");
- }
- ProtectAll();
- Active = true;
- }
-
- /// is or failed to unmap file view
- public override void Deactivate()
- {
- if (!Active)
- throw new InvalidOperationException("Not active");
- if (!Kernel32.UnmapViewOfFile(Z.US(Start)))
- throw new InvalidOperationException($"{nameof(Kernel32.UnmapViewOfFile)}() returned NULL");
- Active = false;
- }
-
- /// snapshot already taken, is , or failed to make memory read-only
- public override void SaveXorSnapshot()
- {
- if (_snapshot != null)
- throw new InvalidOperationException("Snapshot already taken");
- if (!Active)
- throw new InvalidOperationException("Not active");
-
- // temporarily switch the entire block to `R`: in case some areas are unreadable, we don't want
- // that to complicate things
- Kernel32.MemoryProtection old;
- if (!Kernel32.VirtualProtect(Z.UU(Start), Z.UU(Size), Kernel32.MemoryProtection.READONLY, out old))
- throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
-
- _snapshot = new byte[Size];
- var ds = new MemoryStream(_snapshot, true);
- var ss = GetStream(Start, Size, false);
- ss.CopyTo(ds);
- XorHash = WaterboxUtils.Hash(_snapshot);
-
- ProtectAll();
- }
-
- /// is or failed to make memory read-only
- public override byte[] FullHash()
- {
- if (!Active)
- throw new InvalidOperationException("Not active");
- // temporarily switch the entire block to `R`
- Kernel32.MemoryProtection old;
- if (!Kernel32.VirtualProtect(Z.UU(Start), Z.UU(Size), Kernel32.MemoryProtection.READONLY, out old))
- throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
- var ret = WaterboxUtils.Hash(GetStream(Start, Size, false));
- ProtectAll();
- return ret;
- }
-
- private static Kernel32.MemoryProtection GetKernelMemoryProtectionValue(Protection prot)
- {
- Kernel32.MemoryProtection p;
- switch (prot)
- {
- case Protection.None: p = Kernel32.MemoryProtection.NOACCESS; break;
- case Protection.R: p = Kernel32.MemoryProtection.READONLY; break;
- case Protection.RW: p = Kernel32.MemoryProtection.READWRITE; break;
- case Protection.RX: p = Kernel32.MemoryProtection.EXECUTE_READ; break;
- default: throw new ArgumentOutOfRangeException(nameof(prot));
- }
- return p;
- }
-
- protected override void ProtectAll()
- {
- int ps = 0;
- for (int i = 0; i < _pageData.Length; i++)
- {
- if (i == _pageData.Length - 1 || _pageData[i] != _pageData[i + 1])
- {
- var p = GetKernelMemoryProtectionValue(_pageData[i]);
- ulong zstart = GetStartAddr(ps);
- ulong zend = GetStartAddr(i + 1);
- Kernel32.MemoryProtection old;
- if (!Kernel32.VirtualProtect(Z.UU(zstart), Z.UU(zend - zstart), p, out old))
- throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
- ps = i + 1;
- }
- }
- }
-
- /// failed to protect memory
- public override void Protect(ulong start, ulong length, Protection prot)
- {
- if (length == 0)
- return;
- int pstart = GetPage(start);
- int pend = GetPage(start + length - 1);
-
- var p = GetKernelMemoryProtectionValue(prot);
- for (int i = pstart; i <= pend; i++)
- _pageData[i] = prot; // also store the value for later use
-
- if (Active) // it's legal to Protect() if we're not active; the information is just saved for the next activation
- {
- var computedStart = WaterboxUtils.AlignDown(start);
- var computedEnd = WaterboxUtils.AlignUp(start + length);
- var computedLength = computedEnd - computedStart;
-
- Kernel32.MemoryProtection old;
- if (!Kernel32.VirtualProtect(Z.UU(computedStart),
- Z.UU(computedLength), p, out old))
- throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
- }
- }
-
- public override void Dispose(bool disposing)
- {
- if (_handle != IntPtr.Zero)
- {
- if (Active)
- Deactivate();
- Kernel32.CloseHandle(_handle);
- _handle = IntPtr.Zero;
- }
- }
-
- ~MemoryBlockWindows()
- {
- Dispose(false);
- }
-
- private static class Kernel32
- {
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern bool VirtualProtect(UIntPtr lpAddress, UIntPtr dwSize,
- MemoryProtection flNewProtect, out MemoryProtection lpflOldProtect);
-
- [Flags]
- public enum MemoryProtection : uint
- {
- EXECUTE = 0x10,
- EXECUTE_READ = 0x20,
- EXECUTE_READWRITE = 0x40,
- EXECUTE_WRITECOPY = 0x80,
- NOACCESS = 0x01,
- READONLY = 0x02,
- READWRITE = 0x04,
- WRITECOPY = 0x08,
- GUARD_Modifierflag = 0x100,
- NOCACHE_Modifierflag = 0x200,
- WRITECOMBINE_Modifierflag = 0x400
- }
-
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern IntPtr CreateFileMapping(
- IntPtr hFile,
- IntPtr lpFileMappingAttributes,
- FileMapProtection flProtect,
- uint dwMaximumSizeHigh,
- uint dwMaximumSizeLow,
- string lpName);
-
- [Flags]
- public enum FileMapProtection : uint
- {
- PageReadonly = 0x02,
- PageReadWrite = 0x04,
- PageWriteCopy = 0x08,
- PageExecuteRead = 0x20,
- PageExecuteReadWrite = 0x40,
- SectionCommit = 0x8000000,
- SectionImage = 0x1000000,
- SectionNoCache = 0x10000000,
- SectionReserve = 0x4000000,
- }
-
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern bool CloseHandle(IntPtr hObject);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
-
- [DllImport("kernel32.dll")]
- public static extern IntPtr MapViewOfFileEx(IntPtr hFileMappingObject,
- FileMapAccessType dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow,
- UIntPtr dwNumberOfBytesToMap, IntPtr lpBaseAddress);
-
- [Flags]
- public enum FileMapAccessType : uint
- {
- Copy = 0x01,
- Write = 0x02,
- Read = 0x04,
- AllAccess = 0x08,
- Execute = 0x20,
- }
-
- public static readonly IntPtr INVALID_HANDLE_VALUE = Z.US(0xffffffffffffffff);
- }
- }
-}
diff --git a/src/BizHawk.BizInvoke/MemoryBlockWindowsPal.cs b/src/BizHawk.BizInvoke/MemoryBlockWindowsPal.cs
new file mode 100644
index 0000000000..894f98a76a
--- /dev/null
+++ b/src/BizHawk.BizInvoke/MemoryBlockWindowsPal.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Runtime.InteropServices;
+using static BizHawk.BizInvoke.MemoryBlock;
+
+namespace BizHawk.BizInvoke
+{
+ internal sealed class MemoryBlockWindowsPal : IMemoryBlockPal
+ {
+ ///
+ /// handle returned by CreateFileMapping
+ ///
+ private IntPtr _handle;
+ private ulong _start;
+ private ulong _size;
+
+ ///
+ /// Reserve bytes to later be swapped in, but do not map them
+ ///
+ /// eventual mapped address
+ ///
+ public MemoryBlockWindowsPal(ulong start, ulong size)
+ {
+ _start = start;
+ _size = size;
+ _handle = Kernel32.CreateFileMapping(
+ Kernel32.INVALID_HANDLE_VALUE,
+ IntPtr.Zero,
+ Kernel32.FileMapProtection.PageExecuteReadWrite | Kernel32.FileMapProtection.SectionCommit,
+ (uint)(_size >> 32),
+ (uint)_size,
+ null
+ );
+ if (_handle == IntPtr.Zero)
+ {
+ throw new InvalidOperationException($"{nameof(Kernel32.CreateFileMapping)}() returned NULL");
+ }
+ }
+
+ public void PalActivate()
+ {
+ if (Kernel32.MapViewOfFileEx(
+ _handle,
+ Kernel32.FileMapAccessType.Read | Kernel32.FileMapAccessType.Write | Kernel32.FileMapAccessType.Execute,
+ 0,
+ 0,
+ Z.UU(_size),
+ Z.US(_start)
+ ) != Z.US(_start))
+ {
+ throw new InvalidOperationException($"{nameof(Kernel32.MapViewOfFileEx)}() returned NULL");
+ }
+ }
+
+ public void PalDeactivate()
+ {
+ if (!Kernel32.UnmapViewOfFile(Z.US(_start)))
+ throw new InvalidOperationException($"{nameof(Kernel32.UnmapViewOfFile)}() returned NULL");
+ }
+
+ public void PalProtect(ulong start, ulong size, Protection prot)
+ {
+ if (!Kernel32.VirtualProtect(Z.UU(start), Z.UU(size), GetKernelMemoryProtectionValue(prot), out var old))
+ throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
+ }
+
+ private static Kernel32.MemoryProtection GetKernelMemoryProtectionValue(Protection prot)
+ {
+ Kernel32.MemoryProtection p;
+ switch (prot)
+ {
+ case Protection.None: p = Kernel32.MemoryProtection.NOACCESS; break;
+ case Protection.R: p = Kernel32.MemoryProtection.READONLY; break;
+ case Protection.RW: p = Kernel32.MemoryProtection.READWRITE; break;
+ case Protection.RX: p = Kernel32.MemoryProtection.EXECUTE_READ; break;
+ default: throw new ArgumentOutOfRangeException(nameof(prot));
+ }
+ return p;
+ }
+
+ public void Dispose()
+ {
+ if (_handle != IntPtr.Zero)
+ {
+ Kernel32.CloseHandle(_handle);
+ _handle = IntPtr.Zero;
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ ~MemoryBlockWindowsPal()
+ {
+ Dispose();
+ }
+
+ private static class Kernel32
+ {
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool VirtualProtect(UIntPtr lpAddress, UIntPtr dwSize,
+ MemoryProtection flNewProtect, out MemoryProtection lpflOldProtect);
+
+ [Flags]
+ public enum MemoryProtection : uint
+ {
+ EXECUTE = 0x10,
+ EXECUTE_READ = 0x20,
+ EXECUTE_READWRITE = 0x40,
+ EXECUTE_WRITECOPY = 0x80,
+ NOACCESS = 0x01,
+ READONLY = 0x02,
+ READWRITE = 0x04,
+ WRITECOPY = 0x08,
+ GUARD_Modifierflag = 0x100,
+ NOCACHE_Modifierflag = 0x200,
+ WRITECOMBINE_Modifierflag = 0x400
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr CreateFileMapping(
+ IntPtr hFile,
+ IntPtr lpFileMappingAttributes,
+ FileMapProtection flProtect,
+ uint dwMaximumSizeHigh,
+ uint dwMaximumSizeLow,
+ string lpName);
+
+ [Flags]
+ public enum FileMapProtection : uint
+ {
+ PageReadonly = 0x02,
+ PageReadWrite = 0x04,
+ PageWriteCopy = 0x08,
+ PageExecuteRead = 0x20,
+ PageExecuteReadWrite = 0x40,
+ SectionCommit = 0x8000000,
+ SectionImage = 0x1000000,
+ SectionNoCache = 0x10000000,
+ SectionReserve = 0x4000000,
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool CloseHandle(IntPtr hObject);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr MapViewOfFileEx(IntPtr hFileMappingObject,
+ FileMapAccessType dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow,
+ UIntPtr dwNumberOfBytesToMap, IntPtr lpBaseAddress);
+
+ [Flags]
+ public enum FileMapAccessType : uint
+ {
+ Copy = 0x01,
+ Write = 0x02,
+ Read = 0x04,
+ AllAccess = 0x08,
+ Execute = 0x20,
+ }
+
+ public static readonly IntPtr INVALID_HANDLE_VALUE = Z.US(0xffffffffffffffff);
+ }
+ }
+}