mirror of
https://github.com/par274/sharpemu.git
synced 2026-09-01 22:31:17 +08:00
feat: implement cosf, time, ctype tables, tracked heap access, IL2CPP lookup ABI paths (#542)
* fix: add Messenger CRT and AGC compatibility shims * fix: keep Messenger IL2CPP bootstrap on HLE shims * Fix Messenger compatibility ABI handling * Make IL2CPP ABI regression tests portable
This commit is contained in:
@@ -21,6 +21,11 @@ public static class CxaGuardExports
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<ulong, GuardState> _inProgress = new();
|
||||
private static readonly ConcurrentDictionary<ulong, object> _onceGates = new();
|
||||
|
||||
private const int OnceUninitialized = 0;
|
||||
private const int OnceInProgress = 1;
|
||||
private const int OnceComplete = 2;
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "3GPpjQdAMTw",
|
||||
@@ -171,6 +176,167 @@ public static class CxaGuardExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "DiGVep5yB5w",
|
||||
ExportName = "_ZSt13_Execute_onceRSt9once_flagPFiPvS1_PS1_ES1_",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libc")]
|
||||
public static int ExecuteOnce(CpuContext ctx)
|
||||
{
|
||||
var onceAddress = ctx[CpuRegister.Rdi];
|
||||
var callbackAddress = ctx[CpuRegister.Rsi];
|
||||
var parameter = ctx[CpuRegister.Rdx];
|
||||
if (onceAddress == 0 || callbackAddress == 0)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!ctx.TryReadInt32(onceAddress, out var onceValue))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
if (onceValue == OnceComplete)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
var gate = _onceGates.GetOrAdd(onceAddress, static _ => new object());
|
||||
lock (gate)
|
||||
{
|
||||
if (!ctx.TryReadInt32(onceAddress, out onceValue))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
while (onceValue == OnceInProgress)
|
||||
{
|
||||
Monitor.Wait(gate, TimeSpan.FromMilliseconds(1));
|
||||
if (!ctx.TryReadInt32(onceAddress, out onceValue))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
}
|
||||
|
||||
if (onceValue == OnceComplete)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteInt32(onceAddress, OnceInProgress))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
}
|
||||
|
||||
var scheduler = GuestThreadExecution.Scheduler;
|
||||
var callbackSucceeded = false;
|
||||
string? callbackError = null;
|
||||
var allocator = ctx.Memory as IGuestMemoryAllocator;
|
||||
var hasScratchContext = false;
|
||||
ulong scratchContextAddress = 0;
|
||||
try
|
||||
{
|
||||
if (allocator is not null && allocator.TryAllocateGuestMemory(0x10, 0x10, out scratchContextAddress))
|
||||
{
|
||||
hasScratchContext = true;
|
||||
_ = ctx.TryWriteUInt64(scratchContextAddress, 0);
|
||||
_ = ctx.TryWriteUInt64(scratchContextAddress + 8, 0);
|
||||
}
|
||||
|
||||
if (scheduler is null)
|
||||
{
|
||||
callbackError = "guest scheduler unavailable";
|
||||
}
|
||||
else if (scheduler.TryCallGuestFunction(
|
||||
ctx,
|
||||
callbackAddress,
|
||||
onceAddress,
|
||||
parameter,
|
||||
scratchContextAddress,
|
||||
0,
|
||||
0,
|
||||
"std::_Execute_once",
|
||||
out var returnValue,
|
||||
out callbackError))
|
||||
{
|
||||
callbackSucceeded = returnValue != 0;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (hasScratchContext && allocator is not null)
|
||||
{
|
||||
_ = allocator.TryFreeGuestMemory(scratchContextAddress);
|
||||
}
|
||||
}
|
||||
|
||||
lock (gate)
|
||||
{
|
||||
if (!callbackSucceeded)
|
||||
{
|
||||
_ = ctx.TryWriteInt32(onceAddress, OnceUninitialized);
|
||||
Monitor.PulseAll(gate);
|
||||
if (!string.IsNullOrWhiteSpace(callbackError))
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][WARN] std::_Execute_once callback failed: {callbackError}");
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN;
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteInt32(onceAddress, OnceComplete))
|
||||
{
|
||||
_ = ctx.TryWriteInt32(onceAddress, OnceUninitialized);
|
||||
Monitor.PulseAll(gate);
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
Monitor.PulseAll(gate);
|
||||
// A completed once flag no longer needs a host-side gate. Waiters
|
||||
// already holding this gate will observe OnceComplete after the
|
||||
// pulse; future callers take the fast path before looking it up.
|
||||
_ = _onceGates.TryRemove(onceAddress, out _);
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "PsrRUg671K0",
|
||||
ExportName = "__cxa_increment_exception_refcount",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libc")]
|
||||
public static int CxaIncrementExceptionRefcount(CpuContext ctx)
|
||||
{
|
||||
_ = ctx[CpuRegister.Rdi];
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "MQFPAqQPt1s",
|
||||
ExportName = "__cxa_decrement_exception_refcount",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libc")]
|
||||
public static int CxaDecrementExceptionRefcount(CpuContext ctx)
|
||||
{
|
||||
_ = ctx[CpuRegister.Rdi];
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
private static bool TryReadGuardState(CpuContext ctx, ulong guardPtr, out ulong word, out bool initialized, out bool inProgress)
|
||||
{
|
||||
word = 0;
|
||||
|
||||
Reference in New Issue
Block a user