Because of intervening commits, there may be some other incidental changes.

While well intentioned, the refactoring was just a mess when it came to actually groking this low level memory block shuffling code.
This commit is contained in:
nattthebear 2020-05-15 07:40:28 -04:00 committed by GitHub
parent 1ee38dcac2
commit 4abe3f7932
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 73 additions and 57 deletions

View File

@ -185,20 +185,20 @@ namespace BizHawk.BizInvoke
private void WriteThunk(byte[] data, int placeholderIndex, IntPtr p, int index) private void WriteThunk(byte[] data, int placeholderIndex, IntPtr p, int index)
{ {
_memory.Protect(_memory.AddressRange.Start, _memory.Size, MemoryBlockBase.Protection.RW); _memory.Protect(_memory.Start, _memory.Size, MemoryBlockBase.Protection.RW);
var ss = _memory.GetStream(_memory.AddressRange.Start + BlockSize * (ulong) index, BlockSize, true); var ss = _memory.GetStream(_memory.Start + (ulong)index * BlockSize, BlockSize, true);
ss.Write(data, 0, data.Length); ss.Write(data, 0, data.Length);
for (int i = data.Length; i < BlockSize; i++) for (int i = data.Length; i < BlockSize; i++)
ss.WriteByte(Padding); ss.WriteByte(Padding);
ss.Position = placeholderIndex; ss.Position = placeholderIndex;
var bw = new BinaryWriter(ss); var bw = new BinaryWriter(ss);
bw.Write((long)p); bw.Write((long)p);
_memory.Protect(_memory.AddressRange.Start, _memory.Size, MemoryBlockBase.Protection.RX); _memory.Protect(_memory.Start, _memory.Size, MemoryBlockBase.Protection.RX);
} }
private IntPtr GetThunkAddress(int index) private IntPtr GetThunkAddress(int index)
{ {
return Z.US(_memory.AddressRange.Start + BlockSize * (ulong) index); return Z.US(_memory.Start + (ulong)index * BlockSize);
} }
private void SetLifetime(int index, object lifetime) private void SetLifetime(int index, object lifetime)

View File

@ -37,8 +37,8 @@ namespace BizHawk.BizInvoke
0, 0,
0, 0,
Z.UU(Size), Z.UU(Size),
Z.US(AddressRange.Start) Z.US(Start)
) != Z.US(AddressRange.Start)) ) != Z.US(Start))
{ {
throw new InvalidOperationException($"{nameof(Kernel32.MapViewOfFileEx)}() returned NULL"); throw new InvalidOperationException($"{nameof(Kernel32.MapViewOfFileEx)}() returned NULL");
} }
@ -51,7 +51,7 @@ namespace BizHawk.BizInvoke
{ {
if (!Active) if (!Active)
throw new InvalidOperationException("Not active"); throw new InvalidOperationException("Not active");
if (!Kernel32.UnmapViewOfFile(Z.US(AddressRange.Start))) if (!Kernel32.UnmapViewOfFile(Z.US(Start)))
throw new InvalidOperationException($"{nameof(Kernel32.UnmapViewOfFile)}() returned NULL"); throw new InvalidOperationException($"{nameof(Kernel32.UnmapViewOfFile)}() returned NULL");
Active = false; Active = false;
} }
@ -67,12 +67,12 @@ namespace BizHawk.BizInvoke
// temporarily switch the entire block to `R`: in case some areas are unreadable, we don't want // temporarily switch the entire block to `R`: in case some areas are unreadable, we don't want
// that to complicate things // that to complicate things
Kernel32.MemoryProtection old; Kernel32.MemoryProtection old;
if (!Kernel32.VirtualProtect(Z.UU(AddressRange.Start), Z.UU(Size), Kernel32.MemoryProtection.READONLY, out old)) if (!Kernel32.VirtualProtect(Z.UU(Start), Z.UU(Size), Kernel32.MemoryProtection.READONLY, out old))
throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!"); throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
_snapshot = new byte[Size]; _snapshot = new byte[Size];
var ds = new MemoryStream(_snapshot, true); var ds = new MemoryStream(_snapshot, true);
var ss = GetStream(AddressRange.Start, Size, false); var ss = GetStream(Start, Size, false);
ss.CopyTo(ds); ss.CopyTo(ds);
XorHash = WaterboxUtils.Hash(_snapshot); XorHash = WaterboxUtils.Hash(_snapshot);
@ -86,9 +86,9 @@ namespace BizHawk.BizInvoke
throw new InvalidOperationException("Not active"); throw new InvalidOperationException("Not active");
// temporarily switch the entire block to `R` // temporarily switch the entire block to `R`
Kernel32.MemoryProtection old; Kernel32.MemoryProtection old;
if (!Kernel32.VirtualProtect(Z.UU(AddressRange.Start), Z.UU(Size), Kernel32.MemoryProtection.READONLY, out old)) if (!Kernel32.VirtualProtect(Z.UU(Start), Z.UU(Size), Kernel32.MemoryProtection.READONLY, out old))
throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!"); throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
var ret = WaterboxUtils.Hash(GetStream(AddressRange.Start, Size, false)); var ret = WaterboxUtils.Hash(GetStream(Start, Size, false));
ProtectAll(); ProtectAll();
return ret; return ret;
} }

View File

@ -1,7 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using BizHawk.Common; using BizHawk.Common;
namespace BizHawk.BizInvoke namespace BizHawk.BizInvoke
@ -12,22 +11,28 @@ namespace BizHawk.BizInvoke
/// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> is not aligned or <paramref name="size"/> is <c>0</c></exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> is not aligned or <paramref name="size"/> is <c>0</c></exception>
protected MemoryBlockBase(ulong start, ulong size) protected MemoryBlockBase(ulong start, ulong size)
{ {
if (!WaterboxUtils.Aligned(start)) throw new ArgumentOutOfRangeException(nameof(start), start, "start address must be aligned"); if (!WaterboxUtils.Aligned(start))
if (size == 0) throw new ArgumentOutOfRangeException(nameof(size), size, "cannot create 0-length block"); throw new ArgumentOutOfRangeException(nameof(start), start, "start address must be aligned");
if (size == 0)
throw new ArgumentOutOfRangeException(nameof(size), size, "cannot create 0-length block");
Start = start;
Size = WaterboxUtils.AlignUp(size); Size = WaterboxUtils.AlignUp(size);
AddressRange = start.RangeToExclusive(start + Size); End = Start + Size;
_pageData = new Protection[1 + GetPage(AddressRange.EndInclusive)]; _pageData = new Protection[GetPage(End - 1) + 1];
} }
/// <summary>stores last set memory protection value for each page</summary> /// <summary>stores last set memory protection value for each page</summary>
protected readonly Protection[] _pageData; protected readonly Protection[] _pageData;
/// <summary>valid address range of the memory block</summary> /// <summary>ending address of the memory block; equal to <see cref="Start"/> + <see cref="Size"/></summary>
public readonly Range<ulong> AddressRange; public readonly ulong End;
/// <summary>total size of the memory block</summary> /// <summary>total size of the memory block</summary>
public readonly ulong Size; public readonly ulong Size;
/// <summary>starting address of the memory block</summary>
public readonly ulong Start;
/// <summary>snapshot for XOR buffer</summary> /// <summary>snapshot for XOR buffer</summary>
protected byte[] _snapshot; protected byte[] _snapshot;
@ -37,31 +42,35 @@ namespace BizHawk.BizInvoke
public byte[] XorHash { get; protected set; } public byte[] XorHash { get; protected set; }
/// <summary>get a page index within the block</summary> /// <summary>get a page index within the block</summary>
protected int GetPage(ulong addr) => AddressRange.Contains(addr) protected int GetPage(ulong addr)
? (int) ((addr - AddressRange.Start) >> WaterboxUtils.PageShift) {
: throw new ArgumentOutOfRangeException(nameof(addr), addr, "invalid address"); if (addr < Start || End <= addr) throw new ArgumentOutOfRangeException();
return (int) ((addr - Start) >> WaterboxUtils.PageShift);
}
/// <summary>get a start address for a page index within the block</summary> /// <summary>get a start address for a page index within the block</summary>
protected ulong GetStartAddr(int page) => AddressRange.Start + ((ulong) page << WaterboxUtils.PageShift); protected ulong GetStartAddr(int page) => ((ulong) page << WaterboxUtils.PageShift) + Start;
/// <summary>Get a stream that can be used to read or write from part of the block. Does not check for or change <see cref="Protect"/>!</summary> /// <summary>Get a stream that can be used to read or write from part of the block. Does not check for or change <see cref="Protect"/>!</summary>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> or end (= <paramref name="start"/> + <paramref name="length"/>) are outside <see cref="AddressRange"/></exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> or end (= <paramref name="start"/> + <paramref name="length"/>) are outside <see cref="AddressRange"/></exception>
public Stream GetStream(ulong start, ulong length, bool writer) public Stream GetStream(ulong start, ulong length, bool writer)
{ {
if (start < AddressRange.Start) throw new ArgumentOutOfRangeException(nameof(start), start, "invalid address"); if (start < Start)
if (AddressRange.EndInclusive < start + length - 1) throw new ArgumentOutOfRangeException(nameof(length), length, "requested length implies invalid end address"); throw new ArgumentOutOfRangeException(nameof(start));
if (End < start + length)
throw new ArgumentOutOfRangeException(nameof(length));
return new MemoryViewStream(!writer, writer, (long) start, (long) length, this); return new MemoryViewStream(!writer, writer, (long) start, (long) length, this);
} }
/// <summary>get a stream that can be used to read or write from part of the block. both reads and writes will be XORed against an earlier recorded snapshot</summary> /// <summary>get a stream that can be used to read or write from part of the block. both reads and writes will be XORed against an earlier recorded snapshot</summary>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> or end (= <paramref name="start"/> + <paramref name="length"/>) are outside <see cref="AddressRange"/></exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> or end (= <paramref name="start"/> + <paramref name="length"/>) are outside bounds of memory block (<see cref="Start"/> and <see cref="End"/>)</exception>
/// <exception cref="InvalidOperationException">no snapshot taken (haven't called <see cref="SaveXorSnapshot"/>)</exception> /// <exception cref="InvalidOperationException">no snapshot taken (haven't called <see cref="SaveXorSnapshot"/>)</exception>
public Stream GetXorStream(ulong start, ulong length, bool writer) public Stream GetXorStream(ulong start, ulong length, bool writer)
{ {
if (start < AddressRange.Start) throw new ArgumentOutOfRangeException(nameof(start), start, "invalid address"); if (start < Start) throw new ArgumentOutOfRangeException(nameof(start));
if (AddressRange.EndInclusive < start + length - 1) throw new ArgumentOutOfRangeException(nameof(length), length, "requested length implies invalid end address"); if (End < start + length) throw new ArgumentOutOfRangeException(nameof(length));
if (_snapshot == null) throw new InvalidOperationException("No snapshot taken!"); if (_snapshot == null) throw new InvalidOperationException("No snapshot taken!");
return new MemoryViewXorStream(!writer, writer, (long) start, (long) length, this, _snapshot, (long) (start - AddressRange.Start)); return new MemoryViewXorStream(!writer, writer, (long) start, (long) length, this, _snapshot, (long) (start - Start));
} }
/// <summary>activate the memory block, swapping it in at the pre-specified address</summary> /// <summary>activate the memory block, swapping it in at the pre-specified address</summary>
@ -142,7 +151,8 @@ namespace BizHawk.BizInvoke
private void EnsureNotDisposed() private void EnsureNotDisposed()
{ {
if (_owner.AddressRange.Start == 0) throw new ObjectDisposedException(nameof(MemoryBlockBase)); //TODO bug? if (_owner.Start == 0)
throw new ObjectDisposedException(nameof(MemoryBlockBase));
} }
public override void Flush() {} public override void Flush() {}

View File

@ -27,8 +27,8 @@ namespace BizHawk.BizInvoke
{ {
if (Active) throw new InvalidOperationException("Already active"); if (Active) throw new InvalidOperationException("Already active");
var ptr = mmap(Z.US(AddressRange.Start), Z.UU(Size), MemoryProtection.Read | MemoryProtection.Write | MemoryProtection.Execute, 16, _fd, IntPtr.Zero); var ptr = mmap(Z.US(Start), Z.UU(Size), MemoryProtection.Read | MemoryProtection.Write | MemoryProtection.Execute, 16, _fd, IntPtr.Zero);
if (ptr != Z.US(AddressRange.Start)) throw new InvalidOperationException($"{nameof(mmap)}() returned NULL or the wrong pointer"); if (ptr != Z.US(Start)) throw new InvalidOperationException($"{nameof(mmap)}() returned NULL or the wrong pointer");
ProtectAll(); ProtectAll();
Active = true; Active = true;
@ -39,7 +39,7 @@ namespace BizHawk.BizInvoke
{ {
if (!Active) throw new InvalidOperationException("Not active"); if (!Active) throw new InvalidOperationException("Not active");
var exitCode = munmap(Z.US(AddressRange.Start), Z.UU(Size)); var exitCode = munmap(Z.US(Start), Z.UU(Size));
if (exitCode != 0) throw new InvalidOperationException($"{nameof(munmap)}() returned {exitCode}"); if (exitCode != 0) throw new InvalidOperationException($"{nameof(munmap)}() returned {exitCode}");
Active = false; Active = false;
@ -51,10 +51,10 @@ namespace BizHawk.BizInvoke
if (!Active) throw new InvalidOperationException("Not active"); if (!Active) throw new InvalidOperationException("Not active");
// temporarily switch the entire block to `R` // temporarily switch the entire block to `R`
var exitCode = mprotect(Z.US(AddressRange.Start), Z.UU(Size), MemoryProtection.Read); var exitCode = mprotect(Z.US(Start), Z.UU(Size), MemoryProtection.Read);
if (exitCode != 0) throw new InvalidOperationException($"{nameof(mprotect)}() returned {exitCode}!"); if (exitCode != 0) throw new InvalidOperationException($"{nameof(mprotect)}() returned {exitCode}!");
var ret = WaterboxUtils.Hash(GetStream(AddressRange.Start, Size, false)); var ret = WaterboxUtils.Hash(GetStream(Start, Size, false));
ProtectAll(); ProtectAll();
return ret; return ret;
} }
@ -107,11 +107,11 @@ namespace BizHawk.BizInvoke
if (!Active) throw new InvalidOperationException("Not active"); 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 // 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(AddressRange.Start), Z.UU(Size), MemoryProtection.Read); var exitCode = mprotect(Z.US(Start), Z.UU(Size), MemoryProtection.Read);
if (exitCode != 0) throw new InvalidOperationException($"{nameof(mprotect)}() returned {exitCode}!"); if (exitCode != 0) throw new InvalidOperationException($"{nameof(mprotect)}() returned {exitCode}!");
_snapshot = new byte[Size]; _snapshot = new byte[Size];
GetStream(AddressRange.Start, Size, false).CopyTo(new MemoryStream(_snapshot, true)); GetStream(Start, Size, false).CopyTo(new MemoryStream(_snapshot, true));
XorHash = WaterboxUtils.Hash(_snapshot); XorHash = WaterboxUtils.Hash(_snapshot);
ProtectAll(); ProtectAll();
} }

View File

@ -61,8 +61,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
{ {
throw new InvalidOperationException($"Failed to allocate {size} bytes from heap {Name}"); throw new InvalidOperationException($"Failed to allocate {size} bytes from heap {Name}");
} }
ulong ret = Memory.AddressRange.Start + allocstart; ulong ret = Memory.Start + allocstart;
Memory.Protect(Memory.AddressRange.Start + Used, newused - Used, MemoryBlockBase.Protection.RW); Memory.Protect(Memory.Start + Used, newused - Used, MemoryBlockBase.Protection.RW);
Used = newused; Used = newused;
Console.WriteLine($"Allocated {size} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)"); Console.WriteLine($"Allocated {size} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)");
return ret; return ret;
@ -72,8 +72,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
{ {
if (!Sealed) if (!Sealed)
{ {
Memory.Protect(Memory.AddressRange.Start, Used, MemoryBlockBase.Protection.R); Memory.Protect(Memory.Start, Used, MemoryBlockBase.Protection.R);
_hash = WaterboxUtils.Hash(Memory.GetStream(Memory.AddressRange.Start, Used, false)); _hash = WaterboxUtils.Hash(Memory.GetStream(Memory.Start, Used, false));
Sealed = true; Sealed = true;
} }
else else
@ -89,7 +89,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
if (!Sealed) if (!Sealed)
{ {
bw.Write(Memory.XorHash); bw.Write(Memory.XorHash);
var ms = Memory.GetXorStream(Memory.AddressRange.Start, WaterboxUtils.AlignUp(Used), false); var ms = Memory.GetXorStream(Memory.Start, WaterboxUtils.AlignUp(Used), false);
ms.CopyTo(bw.BaseStream); ms.CopyTo(bw.BaseStream);
} }
else else
@ -116,9 +116,9 @@ namespace BizHawk.Emulation.Cores.Waterbox
} }
var usedAligned = WaterboxUtils.AlignUp(used); var usedAligned = WaterboxUtils.AlignUp(used);
Memory.Protect(Memory.AddressRange.Start, Memory.Size, MemoryBlockBase.Protection.None); Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.None);
Memory.Protect(Memory.AddressRange.Start, used, MemoryBlockBase.Protection.RW); Memory.Protect(Memory.Start, used, MemoryBlockBase.Protection.RW);
var ms = Memory.GetXorStream(Memory.AddressRange.Start, usedAligned, true); var ms = Memory.GetXorStream(Memory.Start, usedAligned, true);
WaterboxUtils.CopySome(br.BaseStream, ms, (long)usedAligned); WaterboxUtils.CopySome(br.BaseStream, ms, (long)usedAligned);
Used = used; Used = used;
} }

View File

@ -27,12 +27,18 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// <summary> /// <summary>
/// get a page index within the block /// get a page index within the block
/// </summary> /// </summary>
private int GetPage(ulong addr) => (int) ((addr - Memory.AddressRange.Start) >> WaterboxUtils.PageShift); private int GetPage(ulong addr)
{
return (int)((addr - Memory.Start) >> WaterboxUtils.PageShift);
}
/// <summary> /// <summary>
/// get a start address for a page index within the block /// get a start address for a page index within the block
/// </summary> /// </summary>
private ulong GetStartAddr(int page) => Memory.AddressRange.Start + ((ulong) page << WaterboxUtils.PageShift); private ulong GetStartAddr(int page)
{
return ((ulong)page << WaterboxUtils.PageShift) + Memory.Start;
}
private const MemoryBlockBase.Protection FREE = (MemoryBlockBase.Protection)255; private const MemoryBlockBase.Protection FREE = (MemoryBlockBase.Protection)255;
@ -172,7 +178,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
{ {
// TODO: what is the expected behavior when everything requested for remap is allocated, // TODO: what is the expected behavior when everything requested for remap is allocated,
// but with different protections? // but with different protections?
if (oldSize == 0 || newSize == 0 || start < Memory.AddressRange.Start || Memory.AddressRange.EndInclusive < start + oldSize - 1) if (start < Memory.Start || start + oldSize > Memory.End || oldSize == 0 || newSize == 0)
return 0; return 0;
var oldStartPage = GetPage(start); var oldStartPage = GetPage(start);
@ -237,7 +243,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
public bool Protect(ulong start, ulong size, MemoryBlockBase.Protection prot) public bool Protect(ulong start, ulong size, MemoryBlockBase.Protection prot)
{ {
if (size == 0 || start < Memory.AddressRange.Start || Memory.AddressRange.EndInclusive < start + size - 1) if (start < Memory.Start || start + size > Memory.End || size == 0)
return false; return false;
var startPage = GetPage(start); var startPage = GetPage(start);
@ -268,8 +274,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
bw.Write(Memory.XorHash); bw.Write(Memory.XorHash);
bw.Write(_pagesAsBytes); bw.Write(_pagesAsBytes);
Memory.Protect(Memory.AddressRange.Start, Memory.Size, MemoryBlockBase.Protection.R); Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.R);
var srcs = Memory.GetXorStream(Memory.AddressRange.Start, Memory.Size, false); var srcs = Memory.GetXorStream(Memory.Start, Memory.Size, false);
for (int i = 0, addr = 0; i < _pages.Length; i++, addr += WaterboxUtils.PageSize) for (int i = 0, addr = 0; i < _pages.Length; i++, addr += WaterboxUtils.PageSize)
{ {
if (_pages[i] != FREE) if (_pages[i] != FREE)
@ -299,8 +305,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
throw new InvalidOperationException("Unexpected error reading!"); throw new InvalidOperationException("Unexpected error reading!");
Used = 0; Used = 0;
Memory.Protect(Memory.AddressRange.Start, Memory.Size, MemoryBlockBase.Protection.RW); Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.RW);
var dsts = Memory.GetXorStream(Memory.AddressRange.Start, Memory.Size, true); var dsts = Memory.GetXorStream(Memory.Start, Memory.Size, true);
for (int i = 0, addr = 0; i < _pages.Length; i++, addr += WaterboxUtils.PageSize) for (int i = 0, addr = 0; i < _pages.Length; i++, addr += WaterboxUtils.PageSize)
{ {
if (_pages[i] != FREE) if (_pages[i] != FREE)

View File

@ -430,13 +430,13 @@ namespace BizHawk.Emulation.Cores.Waterbox
{ {
var heap = _parent._heap; var heap = _parent._heap;
var start = heap.Memory.AddressRange.Start; var start = heap.Memory.Start;
var end = start + heap.Used; var end = start + heap.Used;
var max = heap.Memory.AddressRange.EndInclusive; var max = heap.Memory.End;
var p = (ulong)_p; var p = (ulong)_p;
if (p < start || max + 1 < p) //TODO bug? if (p < start || p > max)
{ {
// failure: return current break // failure: return current break
return Z.UU(end); return Z.UU(end);

View File

@ -296,7 +296,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// </summary> /// </summary>
private void ProtectMemory() private void ProtectMemory()
{ {
Memory.Protect(Memory.AddressRange.Start, Memory.Size, MemoryBlockBase.Protection.R); Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.R);
foreach (var s in _sections) foreach (var s in _sections)
{ {
@ -441,7 +441,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
// unlikely to get this far if the previous checks pssed // unlikely to get this far if the previous checks pssed
throw new InvalidOperationException("Trickys elves moved on you!"); throw new InvalidOperationException("Trickys elves moved on you!");
Memory.Protect(Memory.AddressRange.Start, Memory.Size, MemoryBlockBase.Protection.RW); Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.RW);
foreach (var s in _sections) foreach (var s in _sections)
{ {