Add enough changes so that snes9x now boots. This includes stubs for a bunch of syscalls, a few mmglue changes, and C++ global constructor support
This commit is contained in:
parent
7739b9dc80
commit
2665510502
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace BizHawk.Common
|
namespace BizHawk.Common
|
||||||
{
|
{
|
||||||
|
@ -29,4 +30,32 @@ namespace BizHawk.Common
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// compose multiple ImportResolvers, where subsequent ones takes precedence over earlier ones
|
||||||
|
/// </summary>
|
||||||
|
public class PatchImportResolver : IImportResolver
|
||||||
|
{
|
||||||
|
private readonly List<IImportResolver> _resolvers = new List<IImportResolver>();
|
||||||
|
|
||||||
|
public PatchImportResolver(params IImportResolver[] rr)
|
||||||
|
{
|
||||||
|
Add(rr);
|
||||||
|
}
|
||||||
|
public void Add(params IImportResolver[] rr)
|
||||||
|
{
|
||||||
|
_resolvers.AddRange(rr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntPtr Resolve(string entryPoint)
|
||||||
|
{
|
||||||
|
for (int i = _resolvers.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var ret = _resolvers[i].Resolve(entryPoint);
|
||||||
|
if (ret != IntPtr.Zero)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,12 +19,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X
|
||||||
ServiceProvider = new BasicServiceProvider(this);
|
ServiceProvider = new BasicServiceProvider(this);
|
||||||
CoreComm = comm;
|
CoreComm = comm;
|
||||||
|
|
||||||
_exe = new PeRunner(comm.CoreFileProvider.DllPath(), "snes9x.exe", 20 * 1024 * 1024, 65536, 65536);
|
_exe = new PeRunner(new PeRunnerOptions
|
||||||
|
{
|
||||||
|
Path = comm.CoreFileProvider.DllPath(),
|
||||||
|
Filename = "snes9x.exe",
|
||||||
|
NormalHeapSizeKB = 32 * 1024,
|
||||||
|
SealedHeapSizeKB = 32 * 1024,
|
||||||
|
InvisibleHeapSizeKB = 32 * 1024,
|
||||||
|
SpecialHeapSizeKB = 1024
|
||||||
|
});
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_core = BizInvoker.GetInvoker<LibSnes9x>(_exe, _exe);
|
_core = BizInvoker.GetInvoker<LibSnes9x>(_exe, _exe);
|
||||||
//Console.WriteLine(_exe.Resolve("biz_init"));
|
|
||||||
//System.Diagnostics.Debugger.Break();
|
|
||||||
if (!_core.biz_init())
|
if (!_core.biz_init())
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Init() failed");
|
throw new InvalidOperationException("Init() failed");
|
||||||
|
|
|
@ -42,7 +42,15 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_elf = new PeRunner(comm.CoreFileProvider.DllPath(), "gpgx.exe", 8 * 1024 * 1024, 36 * 1024 * 1024, 4 * 1024 * 1024);
|
_elf = new PeRunner(new PeRunnerOptions
|
||||||
|
{
|
||||||
|
Path = comm.CoreFileProvider.DllPath(),
|
||||||
|
Filename = "gpgx.exe",
|
||||||
|
NormalHeapSizeKB = 8 * 1024,
|
||||||
|
SealedHeapSizeKB = 36 * 1024,
|
||||||
|
InvisibleHeapSizeKB = 4 * 1024,
|
||||||
|
SpecialHeapSizeKB = 64
|
||||||
|
});
|
||||||
|
|
||||||
if (_elf.ShouldMonitor)
|
if (_elf.ShouldMonitor)
|
||||||
Core = BizInvoker.GetInvoker<LibGPGX>(_elf, _elf);
|
Core = BizInvoker.GetInvoker<LibGPGX>(_elf, _elf);
|
||||||
|
|
|
@ -11,6 +11,41 @@ using System.Text;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Waterbox
|
namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
|
public class PeRunnerOptions
|
||||||
|
{
|
||||||
|
// string directory, string filename, ulong heapsize, ulong sealedheapsize, ulong invisibleheapsize
|
||||||
|
/// <summary>
|
||||||
|
/// path which the main executable and all associated libraries should be found
|
||||||
|
/// </summary>
|
||||||
|
public string Path { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// filename of the main executable; expected to be in Path
|
||||||
|
/// </summary>
|
||||||
|
public string Filename { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// how large the normal heap should be. it services sbrk calls
|
||||||
|
/// </summary>
|
||||||
|
public uint NormalHeapSizeKB { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// how large the sealed heap should be. it services special allocations that become readonly after init
|
||||||
|
/// </summary>
|
||||||
|
public uint SealedHeapSizeKB { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// how large the invisible heap should be. it services special allocations which are not savestated
|
||||||
|
/// </summary>
|
||||||
|
public uint InvisibleHeapSizeKB { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// how large the special heap should be. it is savestated, and contains ??
|
||||||
|
/// </summary>
|
||||||
|
public uint SpecialHeapSizeKB { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public class PeRunner : Swappable, IImportResolver, IBinaryStateable
|
public class PeRunner : Swappable, IImportResolver, IBinaryStateable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -41,12 +76,23 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
|
||||||
public IntPtr EntryPoint { get; private set; }
|
public IntPtr EntryPoint { get; private set; }
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
/// <summary>
|
||||||
|
/// for midipix-built PEs, pointer to the construtors to run during init
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr CtorList { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// for midipix-build PEs, pointer to the destructors to run during fini
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr DtorList { get; private set; }
|
||||||
|
|
||||||
|
/*[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||||
private delegate bool DllEntry(IntPtr instance, int reason, IntPtr reserved);
|
private delegate bool DllEntry(IntPtr instance, int reason, IntPtr reserved);
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||||
private delegate void ExeEntry();
|
private delegate void ExeEntry();*/
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate void GlobalCtor();
|
||||||
|
|
||||||
public bool RunDllEntry()
|
/*public bool RunDllEntry()
|
||||||
{
|
{
|
||||||
var entryThunk = (DllEntry)Marshal.GetDelegateForFunctionPointer(EntryPoint, typeof(DllEntry));
|
var entryThunk = (DllEntry)Marshal.GetDelegateForFunctionPointer(EntryPoint, typeof(DllEntry));
|
||||||
return entryThunk(Z.US(Start), 1, IntPtr.Zero); // DLL_PROCESS_ATTACH
|
return entryThunk(Z.US(Start), 1, IntPtr.Zero); // DLL_PROCESS_ATTACH
|
||||||
|
@ -55,6 +101,32 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
var entryThunk = (ExeEntry)Marshal.GetDelegateForFunctionPointer(EntryPoint, typeof(ExeEntry));
|
var entryThunk = (ExeEntry)Marshal.GetDelegateForFunctionPointer(EntryPoint, typeof(ExeEntry));
|
||||||
entryThunk();
|
entryThunk();
|
||||||
|
}*/
|
||||||
|
public unsafe void RunGlobalCtors()
|
||||||
|
{
|
||||||
|
int did = 0;
|
||||||
|
if (CtorList != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
IntPtr* p = (IntPtr*)CtorList;
|
||||||
|
IntPtr f;
|
||||||
|
while ((f = *++p) != IntPtr.Zero) // skip 0th dummy pointer
|
||||||
|
{
|
||||||
|
var ctorThunk = (GlobalCtor)Marshal.GetDelegateForFunctionPointer(f, typeof(GlobalCtor));
|
||||||
|
//Console.WriteLine(f);
|
||||||
|
//System.Diagnostics.Debugger.Break();
|
||||||
|
ctorThunk();
|
||||||
|
did++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (did > 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Did {did} global ctors for {ModuleName}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Warn: no global ctors for {ModuleName}; possibly no C++?");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PeWrapper(string moduleName, byte[] fileData, ulong destAddress)
|
public PeWrapper(string moduleName, byte[] fileData, ulong destAddress)
|
||||||
|
@ -127,6 +199,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply relocations
|
// apply relocations
|
||||||
|
var n32 = 0;
|
||||||
|
var n64 = 0;
|
||||||
foreach (var rel in _pe.ImageRelocationDirectory)
|
foreach (var rel in _pe.ImageRelocationDirectory)
|
||||||
{
|
{
|
||||||
foreach (var to in rel.TypeOffsets)
|
foreach (var to in rel.TypeOffsets)
|
||||||
|
@ -145,6 +219,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
uint val = BitConverter.ToUInt32(tmp, 0);
|
uint val = BitConverter.ToUInt32(tmp, 0);
|
||||||
tmp = BitConverter.GetBytes((uint)(val + LoadOffset));
|
tmp = BitConverter.GetBytes((uint)(val + LoadOffset));
|
||||||
Marshal.Copy(tmp, 0, Z.US(address), 4);
|
Marshal.Copy(tmp, 0, Z.US(address), 4);
|
||||||
|
n32++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,11 +230,19 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
long val = BitConverter.ToInt64(tmp, 0);
|
long val = BitConverter.ToInt64(tmp, 0);
|
||||||
tmp = BitConverter.GetBytes(val + LoadOffset);
|
tmp = BitConverter.GetBytes(val + LoadOffset);
|
||||||
Marshal.Copy(tmp, 0, Z.US(address), 8);
|
Marshal.Copy(tmp, 0, Z.US(address), 8);
|
||||||
|
n64++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (IntPtr.Size == 8 && n32 > 0)
|
||||||
|
{
|
||||||
|
// check mcmodel, etc
|
||||||
|
throw new InvalidOperationException("32 bit relocations found in 64 bit dll! This will fail.");
|
||||||
|
}
|
||||||
|
Console.WriteLine($"Processed {n32} 32 bit and {n64} 64 bit relocations");
|
||||||
|
|
||||||
ProtectMemory();
|
ProtectMemory();
|
||||||
|
|
||||||
// publish exports
|
// publish exports
|
||||||
|
@ -184,6 +267,15 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
module.Add(import.Name, Z.US(Start + import.Thunk));
|
module.Add(import.Name, Z.US(Start + import.Thunk));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var midipix = _pe.ImageSectionHeaders.Where(s => s.Name.SequenceEqual(Encoding.ASCII.GetBytes(".midipix")))
|
||||||
|
.SingleOrDefault();
|
||||||
|
if (midipix != null)
|
||||||
|
{
|
||||||
|
var dataOffset = midipix.PointerToRawData;
|
||||||
|
CtorList = Z.SS(BitConverter.ToInt64(_fileData, (int)(dataOffset + 0x30)) + LoadOffset);
|
||||||
|
DtorList = Z.SS(BitConverter.ToInt64(_fileData, (int)(dataOffset + 0x38)) + LoadOffset);
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine($"Mounted `{ModuleName}` @{Start:x16}");
|
Console.WriteLine($"Mounted `{ModuleName}` @{Start:x16}");
|
||||||
foreach (var s in _pe.ImageSectionHeaders.OrderBy(s => s.VirtualAddress))
|
foreach (var s in _pe.ImageSectionHeaders.OrderBy(s => s.VirtualAddress))
|
||||||
{
|
{
|
||||||
|
@ -293,6 +385,12 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private class Psx
|
private class Psx
|
||||||
{
|
{
|
||||||
|
private static IntPtr BEXLAND;
|
||||||
|
static Psx()
|
||||||
|
{
|
||||||
|
BEXLAND = Marshal.AllocHGlobal(16);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly PeRunner _parent;
|
private readonly PeRunner _parent;
|
||||||
private readonly List<Delegate> _traps = new List<Delegate>();
|
private readonly List<Delegate> _traps = new List<Delegate>();
|
||||||
|
|
||||||
|
@ -326,9 +424,15 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
if (ptr == IntPtr.Zero)
|
if (ptr == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
var s = string.Format("Trapped on unimplemented function {0}:{1}", moduleName, e);
|
var s = string.Format("Trapped on unimplemented function {0}:{1}", moduleName, e);
|
||||||
Action del = () => { throw new InvalidOperationException(s); };
|
Action del = () =>
|
||||||
|
{
|
||||||
|
Console.WriteLine(s);
|
||||||
|
System.Diagnostics.Debugger.Break(); // do not remove this until all unwindings are fixed
|
||||||
|
throw new InvalidOperationException(s);
|
||||||
|
};
|
||||||
_traps.Add(del);
|
_traps.Add(del);
|
||||||
ptr = Marshal.GetFunctionPointerForDelegate(del);
|
ptr = Marshal.GetFunctionPointerForDelegate(del);
|
||||||
|
//ptr = BEXLAND;
|
||||||
}
|
}
|
||||||
return ptr;
|
return ptr;
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
@ -349,8 +453,13 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
}, _ldsoVtable);
|
}, _ldsoVtable);
|
||||||
PopulateVtable("__syscalls", new[] // psx
|
PopulateVtable("__syscalls", new[] // psx
|
||||||
{
|
{
|
||||||
"start_main", "convert_thread", "unmapself", "log_output"
|
"start_main", "convert_thread", "unmapself", "log_output", "pthread_self"
|
||||||
}, _psxVtable);
|
}, _psxVtable);
|
||||||
|
/*unsafe
|
||||||
|
{
|
||||||
|
var ptr = (IntPtr*)_psxVtable;
|
||||||
|
Console.WriteLine("AWESOMES: " + ptr[0]);
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private IntPtr _syscallVtable;
|
private IntPtr _syscallVtable;
|
||||||
|
@ -377,7 +486,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
|
||||||
context.SyscallVtable = _syscallVtable = AllocVtable(340);
|
context.SyscallVtable = _syscallVtable = AllocVtable(340);
|
||||||
context.LdsoVtable = _ldsoVtable = AllocVtable(7);
|
context.LdsoVtable = _ldsoVtable = AllocVtable(7);
|
||||||
context.PsxVtable = _psxVtable = AllocVtable(4);
|
context.PsxVtable = _psxVtable = AllocVtable(5);
|
||||||
|
|
||||||
ReloadVtables();
|
ReloadVtables();
|
||||||
|
|
||||||
|
@ -439,6 +548,15 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IntPtr _pthreadSelf;
|
||||||
|
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
// as the inits are done in a defined order with a defined memory map,
|
||||||
|
// we don't need to savestate _pthreadSelf
|
||||||
|
_pthreadSelf = Z.US(_parent._specheap.Allocate(65536, 1));
|
||||||
|
}
|
||||||
|
|
||||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "log_output")]
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "log_output")]
|
||||||
public void DebugPuts(IntPtr s)
|
public void DebugPuts(IntPtr s)
|
||||||
{
|
{
|
||||||
|
@ -479,14 +597,215 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// aka __psx_init_frame
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n16")]
|
||||||
// in midipix, this just sets up SEH and does not do anything that start_main does in MUSL normally
|
public int IoCtl(int fd, ulong req)
|
||||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "start_main")]
|
|
||||||
public int StartMain(IntPtr u0, int u1, IntPtr u2, IntPtr main)
|
|
||||||
{
|
{
|
||||||
// since we don't really need main, we can blow up here
|
return 0; // sure it worked, honest
|
||||||
throw new EndOfMainException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct Iovec
|
||||||
|
{
|
||||||
|
public IntPtr Base;
|
||||||
|
public ulong Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n0")]
|
||||||
|
public long Read(int fd, IntPtr buff, ulong count)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n1")]
|
||||||
|
public long Write(int fd, IntPtr buff, ulong count)
|
||||||
|
{
|
||||||
|
return (long)count;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n19")]
|
||||||
|
public unsafe long Readv(int fd, Iovec* iov, int iovcnt)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n20")]
|
||||||
|
public unsafe long Writev(int fd, Iovec* iov, int iovcnt)
|
||||||
|
{
|
||||||
|
long ret = 0;
|
||||||
|
for (int i = 0; i < iovcnt; i++)
|
||||||
|
{
|
||||||
|
ret += (long)iov[i].Length;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n2")]
|
||||||
|
public int Open(string path, int flags, int mode)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n3")]
|
||||||
|
public int Close(int fd)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n4")]
|
||||||
|
public int Stat(string path, IntPtr statbuf)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n5")]
|
||||||
|
public int Fstat(int fd, IntPtr statbuf)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
//private delegate int LibcStartMain(IntPtr main, int argc, IntPtr argv);
|
||||||
|
|
||||||
|
bool _firstTime = true;
|
||||||
|
|
||||||
|
// aka __psx_init_frame (it's used elsewhere for thread setup)
|
||||||
|
// in midipix, this just sets up a SEH frame and then calls musl's start_main
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "start_main")]
|
||||||
|
public unsafe int StartMain(IntPtr main, int argc, IntPtr argv, IntPtr libc_start_main)
|
||||||
|
{
|
||||||
|
//var del = (LibcStartMain)Marshal.GetDelegateForFunctionPointer(libc_start_main, typeof(LibcStartMain));
|
||||||
|
// this will init, and then call user main, and then call exit()
|
||||||
|
//del(main, argc, argv);
|
||||||
|
//int* foobar = stackalloc int[128];
|
||||||
|
|
||||||
|
|
||||||
|
// if we return from this, psx will then halt, so break out
|
||||||
|
//if (_firstTime)
|
||||||
|
//{
|
||||||
|
//_firstTime = false;
|
||||||
|
throw new EndOfMainException();
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// return 0;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "pthread_self")]
|
||||||
|
public IntPtr PthreadSelf()
|
||||||
|
{
|
||||||
|
return _pthreadSelf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*[BizExport(CallingConvention.Cdecl, EntryPoint = "convert_thread")]
|
||||||
|
public void ConvertThread()
|
||||||
|
{
|
||||||
|
|
||||||
|
}*/
|
||||||
|
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n218")]
|
||||||
|
public long SetTidAddress(IntPtr address)
|
||||||
|
{
|
||||||
|
return 8675309;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public class TimeSpec
|
||||||
|
{
|
||||||
|
public long Seconds;
|
||||||
|
public long NanoSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n228")]
|
||||||
|
public int SysClockGetTime(int which, [In,Out] TimeSpec time)
|
||||||
|
{
|
||||||
|
time.Seconds = 1495889068;
|
||||||
|
time.NanoSeconds = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// libc.so functions that we want to redirect
|
||||||
|
/// </summary>
|
||||||
|
private class LibcPatch
|
||||||
|
{
|
||||||
|
private readonly PeRunner _parent;
|
||||||
|
public LibcPatch(PeRunner parent)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private bool _didOnce = false;
|
||||||
|
private readonly Dictionary<uint, IntPtr> _specificKeys = new Dictionary<uint, IntPtr>();
|
||||||
|
private uint _nextSpecificKey = 401;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void PthreadCallback();
|
||||||
|
|
||||||
|
// pthread stuff:
|
||||||
|
// since we don't allow multiple threads (for now), this is all pretty simple
|
||||||
|
/*
|
||||||
|
// int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "pthread_key_create")]
|
||||||
|
public int PthreadKeyCreate(ref uint key, PthreadCallback destructor)
|
||||||
|
{
|
||||||
|
key = _nextSpecificKey++;
|
||||||
|
_specificKeys.Add(key, IntPtr.Zero);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// int pthread_key_delete(pthread_key_t key);
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "pthread_key_delete")]
|
||||||
|
public int PthreadKeyDelete(uint key)
|
||||||
|
{
|
||||||
|
_specificKeys.Remove(key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// int pthread_setspecific(pthread_key_t key, const void *value);
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "pthread_setspecific")]
|
||||||
|
public int PthreadSetSpecific(uint key, IntPtr value)
|
||||||
|
{
|
||||||
|
_specificKeys[key] = value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// void *pthread_getspecific(pthread_key_t key);
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "pthread_getspecific")]
|
||||||
|
public IntPtr PthreadGetSpecific(uint key)
|
||||||
|
{
|
||||||
|
IntPtr ret;
|
||||||
|
_specificKeys.TryGetValue(key, out ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// int pthread_once(pthread_once_t* once_control, void (*init_routine)(void));
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "pthread_once")]
|
||||||
|
public int PthreadOnce(IntPtr control, PthreadCallback init)
|
||||||
|
{
|
||||||
|
if (!_didOnce)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debugger.Break();
|
||||||
|
_didOnce = true;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t* attr);
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "pthread_mutex_init")]
|
||||||
|
public int PthreadMutexInit(IntPtr mutex, IntPtr attr) { return 0; }
|
||||||
|
// int pthread_mutex_destroy(pthread_mutex_t* mutex);
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "pthread_mutex_destroy")]
|
||||||
|
public int PthreadMutexDestroy(IntPtr mutex) { return 0; }
|
||||||
|
|
||||||
|
// int pthread_mutex_lock(pthread_mutex_t* mutex);
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "pthread_mutex_lock")]
|
||||||
|
public int PthreadMutexLock(IntPtr mutex) { return 0; }
|
||||||
|
// int pthread_mutex_trylock(pthread_mutex_t* mutex);
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "pthread_mutex_trylock")]
|
||||||
|
public int PthreadMutexTryLock(IntPtr mutex) { return 0; }
|
||||||
|
// int pthread_mutex_unlock(pthread_mutex_t* mutex);
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "pthread_mutex_unlock")]
|
||||||
|
public int PthreadMutexUnlock(IntPtr mutex) { return 0; }*/
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// usual starting address for the executable
|
// usual starting address for the executable
|
||||||
|
@ -522,6 +841,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Heap _invisibleheap;
|
private Heap _invisibleheap;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// extra savestated heap
|
||||||
|
/// </summary>
|
||||||
|
private Heap _specheap;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// all loaded PE files
|
/// all loaded PE files
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -545,13 +869,29 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
private Psx _psx;
|
private Psx _psx;
|
||||||
private Emu _emu;
|
private Emu _emu;
|
||||||
private Syscalls _syscalls;
|
private Syscalls _syscalls;
|
||||||
|
private LibcPatch _libcpatch;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// timestamp of creation acts as a sort of "object id" in the savestate
|
/// timestamp of creation acts as a sort of "object id" in the savestate
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly long _createstamp = WaterboxUtils.Timestamp();
|
private readonly long _createstamp = WaterboxUtils.Timestamp();
|
||||||
|
|
||||||
public PeRunner(string directory, string filename, ulong heapsize, ulong sealedheapsize, ulong invisibleheapsize)
|
private Heap CreateHeapHelper(uint sizeKB, string name, bool saveStated)
|
||||||
|
{
|
||||||
|
var heap = new Heap(_nextStart, sizeKB * 1024, name);
|
||||||
|
heap.Memory.Activate();
|
||||||
|
ComputeNextStart(sizeKB * 1024);
|
||||||
|
AddMemoryBlock(heap.Memory);
|
||||||
|
if (saveStated)
|
||||||
|
_savestateComponents.Add(heap);
|
||||||
|
_disposeList.Add(heap);
|
||||||
|
return heap;
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate void LibcEntryRoutineD(IntPtr appMain, IntPtr psxInit, int options);
|
||||||
|
|
||||||
|
public PeRunner(PeRunnerOptions opt)
|
||||||
{
|
{
|
||||||
Initialize(_nextStart);
|
Initialize(_nextStart);
|
||||||
Enter();
|
Enter();
|
||||||
|
@ -567,14 +907,14 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
|
||||||
// load and connect all modules, starting with the executable
|
// load and connect all modules, starting with the executable
|
||||||
var todoModules = new Queue<string>();
|
var todoModules = new Queue<string>();
|
||||||
todoModules.Enqueue(filename);
|
todoModules.Enqueue(opt.Filename);
|
||||||
|
|
||||||
while (todoModules.Count > 0)
|
while (todoModules.Count > 0)
|
||||||
{
|
{
|
||||||
var moduleName = todoModules.Dequeue();
|
var moduleName = todoModules.Dequeue();
|
||||||
if (!_exports.ContainsKey(moduleName))
|
if (!_exports.ContainsKey(moduleName))
|
||||||
{
|
{
|
||||||
var module = new PeWrapper(moduleName, File.ReadAllBytes(Path.Combine(directory, moduleName)), _nextStart);
|
var module = new PeWrapper(moduleName, File.ReadAllBytes(Path.Combine(opt.Path, moduleName)), _nextStart);
|
||||||
ComputeNextStart(module.Size);
|
ComputeNextStart(module.Size);
|
||||||
AddMemoryBlock(module.Memory);
|
AddMemoryBlock(module.Memory);
|
||||||
_savestateComponents.Add(module);
|
_savestateComponents.Add(module);
|
||||||
|
@ -589,42 +929,49 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_libcpatch = new LibcPatch(this);
|
||||||
|
_exports["libc.so"] = new PatchImportResolver(_exports["libc.so"], BizExvoker.GetExvoker(_libcpatch));
|
||||||
|
|
||||||
ConnectAllImports();
|
ConnectAllImports();
|
||||||
|
|
||||||
// load all heaps
|
// load all heaps
|
||||||
_heap = new Heap(_nextStart, heapsize, "brk-heap");
|
_heap = CreateHeapHelper(opt.NormalHeapSizeKB, "brk-heap", true);
|
||||||
_heap.Memory.Activate();
|
_sealedheap = CreateHeapHelper(opt.SealedHeapSizeKB, "sealed-heap", true);
|
||||||
ComputeNextStart(heapsize);
|
_invisibleheap = CreateHeapHelper(opt.InvisibleHeapSizeKB, "invisible-heap", false);
|
||||||
AddMemoryBlock(_heap.Memory);
|
_specheap = CreateHeapHelper(opt.SpecialHeapSizeKB, "special-heap", true);
|
||||||
_savestateComponents.Add(_heap);
|
|
||||||
_disposeList.Add(_heap);
|
|
||||||
|
|
||||||
_sealedheap = new Heap(_nextStart, sealedheapsize, "sealed-heap");
|
_syscalls.Init();
|
||||||
_sealedheap.Memory.Activate();
|
|
||||||
ComputeNextStart(sealedheapsize);
|
|
||||||
AddMemoryBlock(_sealedheap.Memory);
|
|
||||||
_savestateComponents.Add(_sealedheap);
|
|
||||||
_disposeList.Add(_sealedheap);
|
|
||||||
|
|
||||||
_invisibleheap = new Heap(_nextStart, invisibleheapsize, "invisible-heap");
|
//System.Diagnostics.Debugger.Break();
|
||||||
_invisibleheap.Memory.Activate();
|
|
||||||
ComputeNextStart(invisibleheapsize);
|
|
||||||
AddMemoryBlock(_invisibleheap.Memory);
|
|
||||||
_savestateComponents.Add(_invisibleheap);
|
|
||||||
_disposeList.Add(_invisibleheap);
|
|
||||||
|
|
||||||
try
|
// run unmanaged init code
|
||||||
|
var libcEnter = _exports["libc.so"].SafeResolve("__libc_entry_routine");
|
||||||
|
var psxInit = _exports["libpsxscl.so"].SafeResolve("__psx_init");
|
||||||
|
|
||||||
|
var del = (LibcEntryRoutineD)Marshal.GetDelegateForFunctionPointer(libcEnter, typeof(LibcEntryRoutineD));
|
||||||
|
// the current mmglue code doesn't use the main pointer at all, and this just returns
|
||||||
|
del(IntPtr.Zero, psxInit, 0);
|
||||||
|
|
||||||
|
foreach (var m in _modules)
|
||||||
|
{
|
||||||
|
m.RunGlobalCtors();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*try
|
||||||
{
|
{
|
||||||
_modules[0].RunExeEntry();
|
_modules[0].RunExeEntry();
|
||||||
throw new InvalidOperationException("main() returned!");
|
//throw new InvalidOperationException("main() returned!");
|
||||||
}
|
}
|
||||||
catch (EndOfMainException)
|
catch //(EndOfMainException)
|
||||||
{ }
|
{
|
||||||
|
}
|
||||||
|
_modules[0].RunGlobalCtors();
|
||||||
foreach (var m in _modules.Skip(1))
|
foreach (var m in _modules.Skip(1))
|
||||||
{
|
{
|
||||||
if (!m.RunDllEntry())
|
if (!m.RunDllEntry())
|
||||||
throw new InvalidOperationException("DllMain() returned false");
|
throw new InvalidOperationException("DllMain() returned false");
|
||||||
}
|
m.RunGlobalCtors();
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -711,6 +1058,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
_heap = null;
|
_heap = null;
|
||||||
_sealedheap = null;
|
_sealedheap = null;
|
||||||
_invisibleheap = null;
|
_invisibleheap = null;
|
||||||
|
_specheap = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ extern "C" {
|
||||||
// mark an entry point or callback pointer
|
// mark an entry point or callback pointer
|
||||||
#define ECL_ENTRY
|
#define ECL_ENTRY
|
||||||
// mark a visible symbol
|
// mark a visible symbol
|
||||||
#define ECL_EXPORT
|
#define ECL_EXPORT __attribute__((visibility("default")))
|
||||||
|
|
||||||
// allocate memory from the "sealed" pool. this memory can never be freed,
|
// allocate memory from the "sealed" pool. this memory can never be freed,
|
||||||
// and can only be allocated or written to during the init phase. after that, the host
|
// and can only be allocated or written to during the init phase. after that, the host
|
||||||
|
|
|
@ -1,25 +1,12 @@
|
||||||
This is the experimental "waterbox" project for bizhawk.
|
This is the experimental "waterbox" project for bizhawk.
|
||||||
Build has been tested only on a recent Debian, but many Linuxes will probably work. amd64 is the only supported platform.
|
|
||||||
|
|
||||||
libc: This is pdclib, with the jam makesystem butchered and replaced by a custom Makefile, and some things removed.
|
libc.so, libgcc_s.so, libstdc++.so.6, and the waterbox executables are built with a modified verion of the midipix project.
|
||||||
libm from musl is added. sjlj from newlib is added.
|
The makefiles for the cores only support the cross compilation setup (build from whatever linux box you built midipix
|
||||||
|
from).
|
||||||
|
|
||||||
gpgx: This is more or less our current gpgx core. Not much has been changed.
|
The mmglue portion of the midipix project is modified; get the correct version from <insert git repo here>.
|
||||||
|
|
||||||
|
gpgx: This is a modified version of our gpgx core
|
||||||
|
snes9x: Based off of snes9x 1.54.
|
||||||
|
|
||||||
|
|
||||||
To build:
|
|
||||||
|
|
||||||
cd libc
|
|
||||||
make
|
|
||||||
cd ../gpgx
|
|
||||||
make
|
|
||||||
|
|
||||||
Copy gpgx.elf to Bizhawk's output64\dll folder.
|
|
||||||
|
|
||||||
Everything is still very much WIP.
|
|
||||||
Notes:
|
|
||||||
1. Remember ms-abi vs systemv!
|
|
||||||
2. gpgx codeblocks project isn't for building.
|
|
||||||
3. SJLJ might be busted.
|
|
||||||
4. VA_ARGS is probably busted.
|
|
||||||
5. STDIO isn't hooked up yet.
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3a358804d34b9ed5b4bedd9266bcebf255d72cfe
|
Subproject commit 849eeee8c8dc9c8dc0283720a091377eb92ee3a8
|
Loading…
Reference in New Issue