This commit is contained in:
nattthebear 2017-05-20 12:24:15 -04:00
parent 9e4462008c
commit 6053cd6e35
4 changed files with 274 additions and 10 deletions

View File

@ -1,8 +1,40 @@
namespace BizHawk.Common
using System;
namespace BizHawk.Common
{
public interface IMonitor
{
void Enter();
void Exit();
}
public static class MonitorExtensions
{
public static IDisposable EnterExit(this IMonitor m)
{
var ret = new EnterExitWrapper(m);
m.Enter();
return ret;
}
private class EnterExitWrapper : IDisposable
{
private readonly IMonitor _m;
private bool _disposed = false;
public EnterExitWrapper(IMonitor m)
{
_m = m;
}
public void Dispose()
{
if (!_disposed)
{
_m.Exit();
_disposed = true;
}
}
}
}
}

View File

@ -12,10 +12,11 @@ using System.Security.Cryptography;
using System.IO;
using System.Collections.Concurrent;
using System.Threading;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Waterbox
{
public sealed class ElfRunner : Swappable, IImportResolver, IDisposable
public sealed class ElfRunner : Swappable, IImportResolver, IBinaryStateable, IDisposable
{
// TODO: a lot of things only work with our elves and aren't fully generalized

View File

@ -1,4 +1,5 @@
using System;
using BizHawk.Emulation.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -9,7 +10,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// <summary>
/// a simple grow-only fixed max size heap
/// </summary>
internal sealed class Heap : IDisposable
internal sealed class Heap : IBinaryStateable, IDisposable
{
public MemoryBlock Memory { get; private set; }
/// <summary>

View File

@ -1,18 +1,18 @@
using BizHawk.Common;
using BizHawk.Emulation.Common;
using PeNet;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace BizHawk.Emulation.Cores.Waterbox
{
public class PeRunner
public class PeRunner : Swappable, IImportResolver, IBinaryStateable
{
private static readonly ulong CanonicalStart = 0x0000036f00000000;
public class PeWrapper : IImportResolver
public class PeWrapper : IImportResolver, IBinaryStateable, IDisposable
{
public Dictionary<int, IntPtr> ExportsByOrdinal { get; } = new Dictionary<int, IntPtr>();
/// <summary>
@ -26,6 +26,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
private readonly byte[] _fileData;
private readonly PeFile _pe;
private readonly byte[] _fileHash;
public ulong Size { get; }
public ulong Start { get; private set; }
@ -47,6 +48,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
{
throw new InvalidOperationException("Image not Big Enough");
}
_fileHash = WaterboxUtils.Hash(fileData);
}
/// <summary>
@ -54,6 +57,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// </summary>
public void FinishMount()
{
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.R);
foreach (var s in _pe.ImageSectionHeaders)
{
ulong start = Start + s.VirtualAddress;
@ -90,7 +95,6 @@ namespace BizHawk.Emulation.Cores.Waterbox
ulong length = _pe.ImageNtHeaders.OptionalHeader.SizeOfHeaders;
Memory.Protect(Start, length, MemoryBlock.Protection.RW);
Marshal.Copy(_fileData, 0, Z.US(Start), (int)length);
Memory.Protect(Start, length, MemoryBlock.Protection.R);
}
// copy sections
@ -99,7 +103,6 @@ namespace BizHawk.Emulation.Cores.Waterbox
ulong start = Start + s.VirtualAddress;
ulong length = s.VirtualSize;
Memory.Protect(start, length, MemoryBlock.Protection.RW);
Marshal.Copy(_fileData, (int)s.PointerToRawData, Z.US(start), (int)s.SizeOfRawData);
WaterboxUtils.ZeroMemory(Z.US(start + s.SizeOfRawData), (long)(length - s.SizeOfRawData));
}
@ -181,6 +184,233 @@ namespace BizHawk.Emulation.Cores.Waterbox
}
}
}
private bool _disposed = false;
public void Dispose()
{
if (!_disposed)
{
Memory.Dispose();
Memory = null;
_disposed = true;
}
}
const ulong MAGIC = 0x420cccb1a2e17420;
public void SaveStateBinary(BinaryWriter bw)
{
bw.Write(MAGIC);
bw.Write(_fileHash);
bw.Write(Start);
foreach (var s in _pe.ImageSectionHeaders)
{
if ((s.Characteristics & (uint)Constants.SectionFlags.IMAGE_SCN_MEM_WRITE) == 0)
continue;
ulong start = Start + s.VirtualAddress;
ulong length = s.VirtualSize;
var ms = Memory.GetStream(start, length, false);
bw.Write(length);
ms.CopyTo(bw.BaseStream);
}
}
public void LoadStateBinary(BinaryReader br)
{
if (br.ReadUInt64() != MAGIC)
throw new InvalidOperationException("Magic not magic enough!");
if (!br.ReadBytes(_fileHash.Length).SequenceEqual(_fileHash))
throw new InvalidOperationException("Elf changed disguise!");
if (br.ReadUInt64() != Start)
throw new InvalidOperationException("Trickys elves moved on you!");
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.RW);
foreach (var s in _pe.ImageSectionHeaders)
{
if ((s.Characteristics & (uint)Constants.SectionFlags.IMAGE_SCN_MEM_WRITE) == 0)
continue;
ulong start = Start + s.VirtualAddress;
ulong length = s.VirtualSize;
if (br.ReadUInt64() != length)
throw new InvalidOperationException("Unexpected section size for " + s.Name);
var ms = Memory.GetStream(start, length, true);
WaterboxUtils.CopySome(br.BaseStream, ms, (long)length);
}
FinishMount();
}
}
// usual starting address for the executable
private static readonly ulong CanonicalStart = 0x0000036f00000000;
/// <summary>
/// the next place where we can put a module or heap
/// </summary>
private ulong _nextStart = CanonicalStart;
/// <summary>
/// increment _nextStart after adding a module
/// </summary>
private void ComputeNextStart(ulong size)
{
_nextStart += size;
// align to 1MB, then increment 16MB
_nextStart = ((_nextStart - 1) | 0xfffff) + 0x1000001;
}
/// <summary>
/// standard malloc() heap
/// </summary>
private Heap _heap;
/// <summary>
/// sealed heap (writable only during init)
/// </summary>
private Heap _sealedheap;
/// <summary>
/// invisible heap (not savestated, use with care)
/// </summary>
private Heap _invisibleheap;
private readonly List<PeWrapper> _modules = new List<PeWrapper>();
private readonly List<IDisposable> _disposeList = new List<IDisposable>();
private readonly List<IBinaryStateable> _savestateComponents = new List<IBinaryStateable>();
public PeRunner(string directory, string filename, ulong heapsize, ulong sealedheapsize, ulong invisibleheapsize)
{
Enter();
try
{
// load and connect all modules, starting with the executable
var todoModules = new Queue<string>();
todoModules.Enqueue(filename);
var loadedModules = new Dictionary<string, IImportResolver>();
while (todoModules.Count > 0)
{
var moduleName = todoModules.Dequeue();
if (!loadedModules.ContainsKey(moduleName))
{
var module = new PeWrapper(moduleName, File.ReadAllBytes(Path.Combine(directory, moduleName)));
module.Mount(_nextStart);
ComputeNextStart(module.Size);
AddMemoryBlock(module.Memory);
_savestateComponents.Add(module);
_disposeList.Add(module);
loadedModules.Add(moduleName, module);
_modules.Add(module);
foreach (var name in module.ImportsByModule.Keys)
{
todoModules.Enqueue(name);
}
}
}
foreach (var module in _modules)
{
foreach (var name in module.ImportsByModule.Keys)
{
module.ConnectImports(name, loadedModules[name]);
}
}
foreach (var module in _modules)
{
module.FinishMount();
}
// load all heaps
_heap = new Heap(_nextStart, heapsize, "brk-heap");
_heap.Memory.Activate();
ComputeNextStart(heapsize);
AddMemoryBlock(_heap.Memory);
_savestateComponents.Add(_heap);
_disposeList.Add(_heap);
_sealedheap = new Heap(_nextStart, sealedheapsize, "sealed-heap");
_sealedheap.Memory.Activate();
ComputeNextStart(sealedheapsize);
AddMemoryBlock(_sealedheap.Memory);
_savestateComponents.Add(_sealedheap);
_disposeList.Add(_sealedheap);
_invisibleheap = new Heap(_nextStart, invisibleheapsize, "invisible-heap");
_invisibleheap.Memory.Activate();
ComputeNextStart(invisibleheapsize);
AddMemoryBlock(_invisibleheap.Memory);
_savestateComponents.Add(_invisibleheap);
_disposeList.Add(_invisibleheap);
}
catch
{
Dispose();
throw;
}
finally
{
Exit();
}
}
public IntPtr Resolve(string entryPoint)
{
// modules[0] is always the main module
return _modules[0].Resolve(entryPoint);
}
public void SaveStateBinary(BinaryWriter bw)
{
using (this.EnterExit())
{
bw.Write(_savestateComponents.Count);
foreach (var c in _savestateComponents)
{
c.SaveStateBinary(bw);
}
}
}
public void LoadStateBinary(BinaryReader br)
{
if (br.ReadInt32() != _savestateComponents.Count)
throw new InvalidOperationException("Internal savestate error");
using (this.EnterExit())
{
foreach (var c in _savestateComponents)
{
c.LoadStateBinary(br);
}
}
}
private bool _disposed = false;
public void Dispose()
{
if (!_disposed)
{
foreach (var d in _disposeList)
d.Dispose();
_disposeList.Clear();
PurgeMemoryBlocks();
_modules.Clear();
_heap = null;
_sealedheap = null;
_invisibleheap = null;
_disposed = true;
}
}
}
}