initial commit

This commit is contained in:
ParantezTech
2026-03-11 15:48:28 +03:00
commit 4d73f469bc
92 changed files with 166746 additions and 0 deletions
@@ -0,0 +1,63 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
namespace SharpEmu.Core.Cpu;
public readonly struct CpuControlTransferInfo
{
public CpuControlTransferInfo(
ulong sourceInstructionPointer,
byte opcode,
ulong targetInstructionPointer,
CpuControlTransferKind kind,
bool isIndirect,
ulong rax,
ulong rbx,
ulong rcx,
ulong rdx,
ulong rsi,
ulong rdi,
ulong rsp,
ulong rbp)
{
SourceInstructionPointer = sourceInstructionPointer;
Opcode = opcode;
TargetInstructionPointer = targetInstructionPointer;
Kind = kind;
IsIndirect = isIndirect;
Rax = rax;
Rbx = rbx;
Rcx = rcx;
Rdx = rdx;
Rsi = rsi;
Rdi = rdi;
Rsp = rsp;
Rbp = rbp;
}
public ulong SourceInstructionPointer { get; }
public byte Opcode { get; }
public ulong TargetInstructionPointer { get; }
public CpuControlTransferKind Kind { get; }
public bool IsIndirect { get; }
public ulong Rax { get; }
public ulong Rbx { get; }
public ulong Rcx { get; }
public ulong Rdx { get; }
public ulong Rsi { get; }
public ulong Rdi { get; }
public ulong Rsp { get; }
public ulong Rbp { get; }
}
@@ -0,0 +1,13 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
namespace SharpEmu.Core.Cpu;
public enum CpuControlTransferKind
{
Call = 0,
Jump = 1,
Return = 2,
}
+580
View File
@@ -0,0 +1,580 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Buffers.Binary;
using System.Text;
using SharpEmu.Core.Cpu.Native;
using SharpEmu.Core.Loader;
using SharpEmu.Core.Memory;
using SharpEmu.HLE;
namespace SharpEmu.Core.Cpu;
public sealed class CpuDispatcher : ICpuDispatcher, IDisposable
{
private const ulong StackBaseAddress = 0x7FFF_F000_0000UL;
private const ulong StackSize = 0x0020_0000UL;
private const ulong TlsBaseAddress = 0x7FFE_0000_0000UL;
private const ulong TlsSize = 0x0001_0000UL;
private const ulong BootstrapStubBaseAddress = 0x7FFD_F000_0000UL;
private const ulong BootstrapPayloadBaseAddress = 0x7FFD_E000_0000UL;
private const ulong DynlibFallbackStubBaseAddress = 0x7FFD_D000_0000UL;
private const ulong ReturnToHostStubBaseAddress = 0x7FFD_C000_0000UL;
private const ulong BootstrapRegionSize = 0x0000_1000UL;
private const ulong ReturnToHostStubStride = 0x0100_0000UL;
private const ulong BootstrapPayloadResultOffset = 0x28UL;
private const ulong BootstrapStatusOffset = 0x100UL;
private static readonly byte[] BootstrapStartSignature =
[
0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56,
0x41, 0x55, 0x41, 0x54, 0x53, 0x50, 0x48, 0x89,
];
private readonly IVirtualMemory _virtualMemory;
private readonly IModuleManager _moduleManager;
private INativeCpuBackend? _nativeCpuBackend;
public CpuDispatcher(
IVirtualMemory virtualMemory,
IModuleManager moduleManager,
INativeCpuBackend? nativeCpuBackend = null)
{
_virtualMemory = virtualMemory ?? throw new ArgumentNullException(nameof(virtualMemory));
_moduleManager = moduleManager ?? throw new ArgumentNullException(nameof(moduleManager));
_nativeCpuBackend = nativeCpuBackend;
}
public ulong? LastEntryPoint { get; private set; }
public CpuTrapInfo? LastTrapInfo { get; private set; }
public CpuMemoryFaultInfo? LastMemoryFaultInfo { get; private set; }
public CpuControlTransferInfo? LastControlTransferInfo { get; private set; }
public CpuNotImplementedInfo? LastNotImplementedInfo { get; private set; }
public string? LastImportResolutionTrace { get; private set; }
public string? LastBasicBlockTrace { get; private set; }
public string? LastMilestoneLog { get; private set; }
public string? LastRecentInstructionWindow { get; private set; }
public string? LastRecentControlTransferTrace { get; private set; }
public CpuSessionSummary LastSessionSummary { get; private set; }
public OrbisGen2Result DispatchEntry(
ulong entryPoint,
Generation generation,
IReadOnlyDictionary<ulong, string>? importStubs = null,
IReadOnlyDictionary<string, ulong>? runtimeSymbols = null,
string processImageName = "eboot.bin",
CpuExecutionOptions executionOptions = default)
{
Console.Error.WriteLine("[DISPATCHER] === DispatchEntry START ===");
Console.Error.WriteLine($"[DISPATCHER] entryPoint=0x{entryPoint:X16}, generation={generation}");
try
{
return DispatchEntryCore(entryPoint, generation, importStubs, runtimeSymbols, processImageName, executionOptions);
}
catch (Exception ex)
{
Console.Error.WriteLine($"[DISPATCHER] FATAL EXCEPTION in DispatchEntry: {ex.GetType().Name}: {ex.Message}");
Console.Error.WriteLine($"[DISPATCHER] Stack trace: {ex.StackTrace}");
throw;
}
}
private OrbisGen2Result DispatchEntryCore(
ulong entryPoint,
Generation generation,
IReadOnlyDictionary<ulong, string>? importStubs = null,
IReadOnlyDictionary<string, ulong>? runtimeSymbols = null,
string processImageName = "eboot.bin",
CpuExecutionOptions executionOptions = default)
{
Console.Error.WriteLine("[DISPATCHER] DispatchEntryCore STARTING...");
LastEntryPoint = entryPoint;
LastTrapInfo = null;
LastMemoryFaultInfo = null;
LastControlTransferInfo = null;
LastNotImplementedInfo = null;
LastImportResolutionTrace = null;
LastBasicBlockTrace = null;
LastMilestoneLog = null;
LastRecentInstructionWindow = null;
LastRecentControlTransferTrace = null;
LastSessionSummary = default;
OrbisGen2Result FailEarly(OrbisGen2Result result, CpuExitReason reason = CpuExitReason.UnhandledException)
{
LastSessionSummary = new CpuSessionSummary(
result,
reason,
exitCode: null,
lastGuestRip: entryPoint,
lastStubRip: 0,
totalInstructions: 0,
importsHit: 0,
uniqueNidsHit: 0);
return result;
}
var stackBase = TryMapStackRegion();
if (stackBase == 0)
{
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
var tlsBase = TryMapTlsRegion();
if (tlsBase == 0)
{
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
var trackedMemory = new TrackedCpuMemory(_virtualMemory);
var context = new CpuContext(trackedMemory, generation)
{
Rip = entryPoint,
Rflags = 0x202,
FsBase = tlsBase,
GsBase = tlsBase,
};
var returnToHostStubAddress = TryMapReturnToHostStubRegion();
if (returnToHostStubAddress == 0)
{
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
context[CpuRegister.Rsp] = stackBase + StackSize - sizeof(ulong);
if (!context.TryWriteUInt64(context[CpuRegister.Rsp], returnToHostStubAddress))
{
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
if (!InitializeTls(context, tlsBase))
{
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
var effectiveImportStubs = importStubs is null
? new Dictionary<ulong, string>()
: new Dictionary<ulong, string>(importStubs);
var programExitHandlerStubAddress = TryMapDynlibFallbackStubRegion();
if (programExitHandlerStubAddress == 0)
{
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
if (!InitializeProcessEntryFrame(context, processImageName, programExitHandlerStubAddress))
{
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
if (ShouldInjectBootstrapPayload(entryPoint))
{
if (!TryInstallBootstrapPayload(context, effectiveImportStubs))
{
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
}
var entryFrameDiagnostic = BuildEntryFrameDiagnostic(
entryPoint,
context,
sentinelEnabled: true,
sentinelValue: returnToHostStubAddress,
entryParamsConfigured: true);
if (executionOptions.CpuEngine != CpuExecutionEngine.NativeOnly)
{
LastMilestoneLog = string.Concat(
entryFrameDiagnostic,
Environment.NewLine,
$"CpuEngine: {executionOptions.CpuEngine} (unsupported)");
LastNotImplementedInfo = new CpuNotImplementedInfo(
CpuNotImplementedSource.NativeBackend,
entryPoint,
nid: null,
exportName: "cpu_engine_unsupported",
libraryName: executionOptions.CpuEngine.ToString(),
detail: "Unsupported CPU engine mode.");
return FailEarly(
OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED,
CpuExitReason.NativeBackendUnavailable);
}
LastMilestoneLog = string.Concat(
entryFrameDiagnostic,
Environment.NewLine,
"CpuEngine: native-only");
_nativeCpuBackend ??= new DirectExecutionBackend(_moduleManager);
if (_nativeCpuBackend.TryExecute(
context,
entryPoint,
generation,
effectiveImportStubs,
runtimeSymbols ?? new Dictionary<string, ulong>(StringComparer.Ordinal),
executionOptions,
out var nativeResult))
{
LastSessionSummary = new CpuSessionSummary(
nativeResult,
nativeResult == OrbisGen2Result.ORBIS_GEN2_OK
? CpuExitReason.ReturnedToHost
: CpuExitReason.UnhandledException,
exitCode: null,
lastGuestRip: context.Rip,
lastStubRip: 0,
totalInstructions: 0,
importsHit: 0,
uniqueNidsHit: 0);
return nativeResult;
}
var backendName = string.IsNullOrWhiteSpace(_nativeCpuBackend.BackendName)
? "native-backend"
: _nativeCpuBackend.BackendName;
var backendError = string.IsNullOrWhiteSpace(_nativeCpuBackend.LastError)
? "unknown backend error"
: _nativeCpuBackend.LastError;
LastNotImplementedInfo = new CpuNotImplementedInfo(
CpuNotImplementedSource.NativeBackend,
entryPoint,
nid: null,
exportName: "cpu_engine_native_only",
libraryName: backendName,
detail: backendError);
LastMilestoneLog = string.Concat(
LastMilestoneLog,
Environment.NewLine,
$"CpuEngine native-only failed: {backendError}");
Console.Error.WriteLine($"[DISPATCHER] Native backend FAILED: {backendError}");
return FailEarly(
OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED,
CpuExitReason.NativeBackendUnavailable);
}
private ulong TryMapStackRegion()
{
const ulong stackStride = 0x0100_0000UL;
for (var i = 0; i < 32; i++)
{
var candidateBase = StackBaseAddress - ((ulong)i * stackStride);
try
{
_virtualMemory.Map(
candidateBase,
StackSize,
fileOffset: 0,
fileData: ReadOnlySpan<byte>.Empty,
ProgramHeaderFlags.Read | ProgramHeaderFlags.Write);
return candidateBase;
}
catch (InvalidOperationException)
{
continue;
}
}
return 0;
}
private ulong TryMapTlsRegion()
{
const ulong tlsStride = 0x0100_0000UL;
for (var i = 0; i < 32; i++)
{
var candidateBase = TlsBaseAddress - ((ulong)i * tlsStride);
try
{
_virtualMemory.Map(
candidateBase,
TlsSize,
fileOffset: 0,
fileData: ReadOnlySpan<byte>.Empty,
ProgramHeaderFlags.Read | ProgramHeaderFlags.Write);
return candidateBase;
}
catch (InvalidOperationException)
{
continue;
}
}
return 0;
}
private static bool InitializeTls(CpuContext context, ulong tlsBase)
{
return context.TryWriteUInt64(tlsBase + 0x00, tlsBase) &&
context.TryWriteUInt64(tlsBase + 0x10, tlsBase) &&
context.TryWriteUInt64(tlsBase + 0x28, 0xC0DEC0DECAFEBABEUL);
}
private static bool InitializeProcessEntryFrame(
CpuContext context,
string processImageName,
ulong programExitHandlerAddress)
{
var imageName = string.IsNullOrWhiteSpace(processImageName) ? "eboot.bin" : processImageName;
var encodedNameLength = Encoding.UTF8.GetByteCount(imageName);
Span<byte> argv0Buffer = encodedNameLength + 1 <= 512
? stackalloc byte[encodedNameLength + 1]
: new byte[encodedNameLength + 1];
if (Encoding.UTF8.GetBytes(imageName.AsSpan(), argv0Buffer) != encodedNameLength)
{
return false;
}
argv0Buffer[encodedNameLength] = 0;
var cursor = context[CpuRegister.Rsp];
var argv0Address = AlignDown(cursor - (ulong)argv0Buffer.Length, 16);
if (!context.Memory.TryWrite(argv0Address, argv0Buffer))
{
return false;
}
const ulong entryParamsSize = 0x20;
var entryParamsAddress = AlignDown(argv0Address - entryParamsSize, 16);
if (!TryWriteUInt32(context, entryParamsAddress + 0x00, 1) ||
!TryWriteUInt32(context, entryParamsAddress + 0x04, 0) ||
!context.TryWriteUInt64(entryParamsAddress + 0x08, argv0Address) ||
!context.TryWriteUInt64(entryParamsAddress + 0x10, 0) ||
!context.TryWriteUInt64(entryParamsAddress + 0x18, 0))
{
return false;
}
context[CpuRegister.Rdi] = entryParamsAddress;
context[CpuRegister.Rsi] = programExitHandlerAddress;
context[CpuRegister.Rdx] = 0;
context[CpuRegister.Rcx] = 0;
context[CpuRegister.R8] = 0;
context[CpuRegister.R9] = 0;
return true;
}
private static ulong AlignDown(ulong value, ulong alignment)
{
return value & ~(alignment - 1);
}
private static bool TryWriteUInt32(CpuContext context, ulong address, uint value)
{
Span<byte> buffer = stackalloc byte[sizeof(uint)];
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
return context.Memory.TryWrite(address, buffer);
}
private static string BuildEntryFrameDiagnostic(
ulong entryPoint,
CpuContext context,
bool sentinelEnabled,
ulong sentinelValue,
bool entryParamsConfigured)
{
var initialRsp = context[CpuRegister.Rsp];
var stackTopText = context.TryReadUInt64(initialRsp, out var stackTop)
? $"0x{stackTop:X16}"
: "??";
var sentinelText = sentinelEnabled ? "yes" : "no";
var sentinelValueText = sentinelEnabled ? $"0x{sentinelValue:X16}" : "??";
var entryParamsText = entryParamsConfigured ? "yes" : "no";
return
$"EntryFrame: entry_rip=0x{entryPoint:X16} initial_rsp=0x{initialRsp:X16} [rsp]={stackTopText} sentinel_enabled={sentinelText} sentinel_value={sentinelValueText} entry_params_configured={entryParamsText}";
}
private bool ShouldInjectBootstrapPayload(ulong entryPoint)
{
Span<byte> probe = stackalloc byte[16];
if (!_virtualMemory.TryRead(entryPoint, probe))
{
return false;
}
for (var i = 0; i < BootstrapStartSignature.Length; i++)
{
if (probe[i] != BootstrapStartSignature[i])
{
return false;
}
}
return true;
}
private bool TryInstallBootstrapPayload(CpuContext context, IDictionary<ulong, string> importStubs)
{
var stubAddress = TryMapBootstrapStubRegion();
if (stubAddress == 0)
{
return false;
}
var payloadAddress = TryMapBootstrapPayloadRegion();
if (payloadAddress == 0)
{
return false;
}
var statusAddress = payloadAddress + BootstrapStatusOffset;
if (!TryWriteUInt64(payloadAddress, stubAddress) ||
!TryWriteUInt64(payloadAddress + 0x08, statusAddress) ||
!TryWriteUInt64(payloadAddress + 0x10, statusAddress) ||
!TryWriteUInt64(payloadAddress + 0x18, statusAddress) ||
!TryWriteUInt64(payloadAddress + 0x20, statusAddress) ||
!TryWriteUInt64(payloadAddress + BootstrapPayloadResultOffset, statusAddress) ||
!TryWriteUInt32(statusAddress, 0))
{
return false;
}
importStubs[stubAddress] = RuntimeStubNids.BootstrapBridge;
importStubs[stubAddress + 0x0A] = RuntimeStubNids.BootstrapBridge;
context[CpuRegister.Rdi] = payloadAddress;
return true;
}
private ulong TryMapBootstrapStubRegion()
{
var stubData = new byte[(int)BootstrapRegionSize];
stubData[0] = 0xCC;
stubData[1] = 0xC3;
const ulong stride = 0x0100_0000UL;
for (var i = 0; i < 16; i++)
{
var candidateBase = BootstrapStubBaseAddress - ((ulong)i * stride);
try
{
_virtualMemory.Map(
candidateBase,
BootstrapRegionSize,
fileOffset: 0,
stubData,
ProgramHeaderFlags.Read | ProgramHeaderFlags.Execute);
return candidateBase;
}
catch (InvalidOperationException)
{
continue;
}
}
return 0;
}
private ulong TryMapBootstrapPayloadRegion()
{
const ulong stride = 0x0100_0000UL;
for (var i = 0; i < 16; i++)
{
var candidateBase = BootstrapPayloadBaseAddress - ((ulong)i * stride);
try
{
_virtualMemory.Map(
candidateBase,
BootstrapRegionSize,
fileOffset: 0,
ReadOnlySpan<byte>.Empty,
ProgramHeaderFlags.Read | ProgramHeaderFlags.Write);
return candidateBase;
}
catch (InvalidOperationException)
{
continue;
}
}
return 0;
}
private ulong TryMapDynlibFallbackStubRegion()
{
var stubData = new byte[(int)BootstrapRegionSize];
stubData[0] = 0x31;
stubData[1] = 0xC0;
stubData[2] = 0xC3;
const ulong stride = 0x0100_0000UL;
for (var i = 0; i < 16; i++)
{
var candidateBase = DynlibFallbackStubBaseAddress - ((ulong)i * stride);
try
{
_virtualMemory.Map(
candidateBase,
BootstrapRegionSize,
fileOffset: 0,
stubData,
ProgramHeaderFlags.Read | ProgramHeaderFlags.Execute);
return candidateBase;
}
catch (InvalidOperationException)
{
continue;
}
}
return 0;
}
private ulong TryMapReturnToHostStubRegion()
{
var stubData = new byte[(int)BootstrapRegionSize];
stubData[0] = 0xF4;
stubData[1] = 0xCC;
for (var i = 0; i < 16; i++)
{
var candidateBase = ReturnToHostStubBaseAddress - ((ulong)i * ReturnToHostStubStride);
try
{
_virtualMemory.Map(
candidateBase,
BootstrapRegionSize,
fileOffset: 0,
stubData,
ProgramHeaderFlags.Read | ProgramHeaderFlags.Execute);
return candidateBase;
}
catch (InvalidOperationException)
{
continue;
}
}
return 0;
}
private bool TryWriteUInt64(ulong address, ulong value)
{
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
return _virtualMemory.TryWrite(address, buffer);
}
private bool TryWriteUInt32(ulong address, uint value)
{
Span<byte> buffer = stackalloc byte[sizeof(uint)];
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
return _virtualMemory.TryWrite(address, buffer);
}
public void Dispose()
{
if (_nativeCpuBackend is IDisposable disposableBackend)
{
disposableBackend.Dispose();
}
_nativeCpuBackend = null;
}
}
@@ -0,0 +1,9 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
namespace SharpEmu.Core.Cpu;
public enum CpuExecutionEngine
{
NativeOnly = 0,
}
@@ -0,0 +1,15 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
namespace SharpEmu.Core.Cpu;
public readonly struct CpuExecutionOptions
{
public bool EnableDisasmDiagnostics { get; init; }
public CpuExecutionEngine CpuEngine { get; init; }
public bool StrictDynlibResolution { get; init; }
public int ImportTraceLimit { get; init; }
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
namespace SharpEmu.Core.Cpu;
public enum CpuExitReason
{
Exited = 0,
SentinelReturn = 1,
ReturnedToHost = 2,
Halted = 3,
BudgetExceeded = 4,
CpuTrap = 5,
UnhandledException = 6,
UnhandledSyscall = 7,
NativeBackendUnavailable = 8,
}
@@ -0,0 +1,20 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
namespace SharpEmu.Core.Cpu;
public readonly struct CpuMemoryAccessFailure
{
public CpuMemoryAccessFailure(ulong address, int size, bool isWrite)
{
Address = address;
Size = size;
IsWrite = isWrite;
}
public ulong Address { get; }
public int Size { get; }
public bool IsWrite { get; }
}
@@ -0,0 +1,20 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
namespace SharpEmu.Core.Cpu;
public readonly struct CpuMemoryFaultInfo
{
public CpuMemoryFaultInfo(ulong instructionPointer, byte? opcode, CpuMemoryAccessFailure access)
{
InstructionPointer = instructionPointer;
Opcode = opcode;
Access = access;
}
public ulong InstructionPointer { get; }
public byte? Opcode { get; }
public CpuMemoryAccessFailure Access { get; }
}
@@ -0,0 +1,35 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
namespace SharpEmu.Core.Cpu;
public readonly struct CpuNotImplementedInfo
{
public CpuNotImplementedInfo(
CpuNotImplementedSource source,
ulong instructionPointer,
string? nid,
string? exportName,
string? libraryName,
string? detail)
{
Source = source;
InstructionPointer = instructionPointer;
Nid = nid;
ExportName = exportName;
LibraryName = libraryName;
Detail = detail;
}
public CpuNotImplementedSource Source { get; }
public ulong InstructionPointer { get; }
public string? Nid { get; }
public string? ExportName { get; }
public string? LibraryName { get; }
public string? Detail { get; }
}
@@ -0,0 +1,17 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
namespace SharpEmu.Core.Cpu;
public enum CpuNotImplementedSource
{
Unknown = 0,
InstructionBudget = 1,
KernelDynlibDlsym = 2,
HleExport = 3,
NativeBackend = 4,
}
@@ -0,0 +1,45 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
namespace SharpEmu.Core.Cpu;
public readonly struct CpuSessionSummary
{
public CpuSessionSummary(
OrbisGen2Result result,
CpuExitReason reason,
int? exitCode,
ulong lastGuestRip,
ulong lastStubRip,
int totalInstructions,
int importsHit,
int uniqueNidsHit)
{
Result = result;
Reason = reason;
ExitCode = exitCode;
LastGuestRip = lastGuestRip;
LastStubRip = lastStubRip;
TotalInstructions = totalInstructions;
ImportsHit = importsHit;
UniqueNidsHit = uniqueNidsHit;
}
public OrbisGen2Result Result { get; }
public CpuExitReason Reason { get; }
public int? ExitCode { get; }
public ulong LastGuestRip { get; }
public ulong LastStubRip { get; }
public int TotalInstructions { get; }
public int ImportsHit { get; }
public int UniqueNidsHit { get; }
}
+17
View File
@@ -0,0 +1,17 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
namespace SharpEmu.Core.Cpu;
public readonly struct CpuTrapInfo
{
public CpuTrapInfo(ulong instructionPointer, byte opcode)
{
InstructionPointer = instructionPointer;
Opcode = opcode;
}
public ulong InstructionPointer { get; }
public byte Opcode { get; }
}
@@ -0,0 +1,45 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using Iced.Intel;
namespace SharpEmu.Core.Cpu.Disasm;
public readonly struct DecodedInst
{
public DecodedInst(
ulong rip,
int length,
string text,
string mnemonic,
FlowControl flowControl,
ulong? nearBranchTarget,
ulong? memoryAddress,
byte[] bytes)
{
Rip = rip;
Length = length;
Text = text;
Mnemonic = mnemonic;
FlowControl = flowControl;
NearBranchTarget = nearBranchTarget;
MemoryAddress = memoryAddress;
Bytes = bytes;
}
public ulong Rip { get; }
public int Length { get; }
public string Text { get; }
public string Mnemonic { get; }
public FlowControl FlowControl { get; }
public ulong? NearBranchTarget { get; }
public ulong? MemoryAddress { get; }
public byte[] Bytes { get; }
}
+213
View File
@@ -0,0 +1,213 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using Iced.Intel;
using SharpEmu.Core.Memory;
using SharpEmu.HLE;
namespace SharpEmu.Core.Cpu.Disasm;
public static class IcedDecoder
{
private const int MaxInstructionBytes = 15;
public static bool TryDecode(ulong rip, ReadOnlySpan<byte> codeBytes, out DecodedInst inst)
{
if (codeBytes.IsEmpty)
{
inst = default;
return false;
}
var decodeLength = Math.Min(MaxInstructionBytes, codeBytes.Length);
var decodeBytes = GC.AllocateUninitializedArray<byte>(decodeLength);
codeBytes[..decodeLength].CopyTo(decodeBytes);
try
{
var decoder = Decoder.Create(64, new ByteArrayCodeReader(decodeBytes));
decoder.IP = rip;
decoder.Decode(out var instruction);
if (instruction.Code == Code.INVALID || instruction.Length <= 0)
{
inst = default;
return false;
}
var formatter = new IntelFormatter();
var output = new StringOutput();
formatter.Format(instruction, output);
var effectiveLength = Math.Min(instruction.Length, decodeBytes.Length);
var effectiveBytes = new byte[effectiveLength];
Array.Copy(decodeBytes, 0, effectiveBytes, 0, effectiveLength);
inst = new DecodedInst(
rip,
instruction.Length,
output.ToString(),
instruction.Mnemonic.ToString(),
instruction.FlowControl,
GetNearBranchTarget(in instruction),
GetMemoryAddress(in instruction),
effectiveBytes);
return true;
}
catch
{
inst = default;
return false;
}
}
public static bool TryReadGuestBytes(ICpuMemory memory, ulong rip, int maxLen, out byte[] bytes)
{
ArgumentNullException.ThrowIfNull(memory);
var clampedLength = Math.Clamp(maxLen, 1, MaxInstructionBytes);
var buffer = new byte[clampedLength];
Span<byte> oneByte = stackalloc byte[1];
var readCount = 0;
for (var i = 0; i < clampedLength; i++)
{
if (!memory.TryRead(rip + (ulong)i, oneByte))
{
break;
}
buffer[readCount] = oneByte[0];
readCount++;
}
if (readCount == 0)
{
bytes = Array.Empty<byte>();
return false;
}
if (readCount == clampedLength)
{
bytes = buffer;
return true;
}
bytes = new byte[readCount];
Array.Copy(buffer, bytes, readCount);
return true;
}
public static bool TryReadGuestBytes(IVirtualMemory memory, ulong rip, int maxLen, out byte[] bytes)
{
ArgumentNullException.ThrowIfNull(memory);
var clampedLength = Math.Clamp(maxLen, 1, MaxInstructionBytes);
var buffer = new byte[clampedLength];
Span<byte> oneByte = stackalloc byte[1];
var readCount = 0;
for (var i = 0; i < clampedLength; i++)
{
if (!memory.TryRead(rip + (ulong)i, oneByte))
{
break;
}
buffer[readCount] = oneByte[0];
readCount++;
}
if (readCount == 0)
{
bytes = Array.Empty<byte>();
return false;
}
if (readCount == clampedLength)
{
bytes = buffer;
return true;
}
bytes = new byte[readCount];
Array.Copy(buffer, bytes, readCount);
return true;
}
public static string FormatBytes(ReadOnlySpan<byte> bytes)
{
if (bytes.IsEmpty)
{
return "??";
}
var parts = new string[bytes.Length];
for (var i = 0; i < bytes.Length; i++)
{
parts[i] = bytes[i].ToString("X2");
}
return string.Join(' ', parts);
}
private static ulong? GetNearBranchTarget(in Instruction instruction)
{
for (var opIndex = 0; opIndex < instruction.OpCount; opIndex++)
{
switch (instruction.GetOpKind(opIndex))
{
case OpKind.NearBranch16:
return instruction.NearBranch16;
case OpKind.NearBranch32:
return instruction.NearBranch32;
case OpKind.NearBranch64:
return instruction.NearBranch64;
}
}
return null;
}
private static ulong? GetMemoryAddress(in Instruction instruction)
{
var hasMemoryOperand = false;
for (var opIndex = 0; opIndex < instruction.OpCount; opIndex++)
{
if (IsMemoryOpKind(instruction.GetOpKind(opIndex)))
{
hasMemoryOperand = true;
break;
}
}
if (!hasMemoryOperand)
{
return null;
}
if (instruction.IsIPRelativeMemoryOperand)
{
return instruction.IPRelativeMemoryAddress;
}
if (instruction.MemoryBase == Register.None &&
instruction.MemoryIndex == Register.None &&
instruction.MemoryDisplacement64 != 0)
{
return instruction.MemoryDisplacement64;
}
return null;
}
private static bool IsMemoryOpKind(OpKind opKind)
{
return opKind is
OpKind.Memory or
OpKind.MemorySegSI or
OpKind.MemorySegESI or
OpKind.MemorySegRSI or
OpKind.MemorySegDI or
OpKind.MemorySegEDI or
OpKind.MemorySegRDI or
OpKind.MemoryESDI or
OpKind.MemoryESEDI or
OpKind.MemoryESRDI;
}
}
+37
View File
@@ -0,0 +1,37 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
namespace SharpEmu.Core.Cpu;
public interface ICpuDispatcher
{
CpuTrapInfo? LastTrapInfo { get; }
CpuMemoryFaultInfo? LastMemoryFaultInfo { get; }
CpuControlTransferInfo? LastControlTransferInfo { get; }
CpuNotImplementedInfo? LastNotImplementedInfo { get; }
string? LastImportResolutionTrace { get; }
string? LastBasicBlockTrace { get; }
string? LastMilestoneLog { get; }
string? LastRecentInstructionWindow { get; }
string? LastRecentControlTransferTrace { get; }
CpuSessionSummary LastSessionSummary { get; }
OrbisGen2Result DispatchEntry(
ulong entryPoint,
Generation generation,
IReadOnlyDictionary<ulong, string>? importStubs = null,
IReadOnlyDictionary<string, ulong>? runtimeSymbols = null,
string processImageName = "eboot.bin",
CpuExecutionOptions executionOptions = default);
}
@@ -0,0 +1,9 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
namespace SharpEmu.Core.Cpu;
public interface ITrackedCpuMemory
{
CpuMemoryAccessFailure? LastFailure { get; }
}
@@ -0,0 +1,47 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System;
using System.Runtime.InteropServices;
namespace SharpEmu.Core.Cpu.Native;
public sealed unsafe class CpuPatcher : IDisposable
{
private readonly nint _tlsBaseAddress;
public CpuPatcher(nint tlsBaseAddress)
{
_tlsBaseAddress = tlsBaseAddress;
}
public bool TryPatchInstruction(nint address)
{
return false;
}
public void Dispose()
{
}
internal unsafe class UnsafeCodeReader : Iced.Intel.CodeReader
{
private byte* _start;
private byte* _current;
private int _length;
public UnsafeCodeReader(byte* start, int length)
{
_start = start;
_current = start;
_length = length;
}
public override int ReadByte()
{
if (_current >= _start + _length)
return -1;
return *_current++;
}
}
}
@@ -0,0 +1,264 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using SharpEmu.HLE;
namespace SharpEmu.Core.Cpu.Native;
public sealed partial class DirectExecutionBackend
{
private void RecordRecentImportTrace(string traceLine)
{
_recentImportTrace[_recentImportTraceWriteIndex] = traceLine;
_recentImportTraceWriteIndex = (_recentImportTraceWriteIndex + 1) % _recentImportTrace.Length;
if (_recentImportTraceCount < _recentImportTrace.Length)
{
_recentImportTraceCount++;
}
}
private void DumpRecentImportTrace()
{
if (_recentImportTraceCount == 0)
{
return;
}
Console.Error.WriteLine($"[LOADER][INFO] Recent import calls ({_recentImportTraceCount}):");
int num = (_recentImportTraceWriteIndex - _recentImportTraceCount + _recentImportTrace.Length) % _recentImportTrace.Length;
for (int i = 0; i < _recentImportTraceCount; i++)
{
int num2 = (num + i) % _recentImportTrace.Length;
string text = _recentImportTrace[num2];
if (!string.IsNullOrEmpty(text))
{
Console.Error.WriteLine("[LOADER][INFO] " + text);
}
}
}
private unsafe static List<ulong> ScanSuspiciousResolverPointers(ulong scanStart, ulong scanEnd)
{
if (scanEnd <= scanStart)
{
return new List<ulong>(0);
}
int num = 0;
int num2 = 0;
List<ulong> list = new List<ulong>(16);
ulong num3 = scanStart;
MEMORY_BASIC_INFORMATION64 lpBuffer;
while (num3 < scanEnd && VirtualQuery((void*)num3, out lpBuffer, (nuint)sizeof(MEMORY_BASIC_INFORMATION64)) != 0)
{
ulong baseAddress = lpBuffer.BaseAddress;
ulong num4 = baseAddress + lpBuffer.RegionSize;
if (num4 <= num3)
{
break;
}
ulong value = Math.Max(num3, baseAddress);
ulong num5 = Math.Min(num4, scanEnd);
if (lpBuffer.State == 4096 && IsReadableProtection(lpBuffer.Protect) && !IsExecutableProtection(lpBuffer.Protect))
{
ulong num6 = AlignUp(value, 8uL);
for (ulong num7 = num6; num7 + 8 <= num5; num7 += 8)
{
ulong value2 = *(ulong*)num7;
if (IsUnresolvedSentinel(value2))
{
num++;
list.Add(num7);
if (num2 < 32)
{
Console.Error.WriteLine($"[LOADER][INFO] Suspicious unresolved pointer: slot=0x{num7:X16} value=0x{value2:X16}");
num2++;
}
if (num >= 16384)
{
Console.Error.WriteLine($"[LOADER][WARNING] Suspicious unresolved pointer scan reached cap ({16384}); truncating.");
return list;
}
}
}
}
num3 = num5;
}
if (num != 0)
{
Console.Error.WriteLine($"[LOADER][WARNING] Suspicious unresolved pointer hits: {num}");
}
return list;
}
private void ProbeReturnRip(ulong returnRip, long dispatchIndex)
{
if (_cpuContext == null || returnRip == 0)
{
return;
}
Span<byte> destination = stackalloc byte[128];
if (!_cpuContext.Memory.TryRead(returnRip, destination))
{
Console.Error.WriteLine($"[LOADER][TRACE] Import#{dispatchIndex} return-rip probe: unreadable @0x{returnRip:X16}");
return;
}
string value = BitConverter.ToString(destination.ToArray()).Replace("-", " ");
Console.Error.WriteLine($"[LOADER][TRACE] Import#{dispatchIndex} return-rip bytes @0x{returnRip:X16}: {value}");
if (destination[0] == byte.MaxValue && (destination[1] == 21 || destination[1] == 37))
{
int num = BitConverter.ToInt32(destination.Slice(2, 4));
ulong num2 = returnRip + 6 + (ulong)num;
if (_cpuContext.TryReadUInt64(num2, out var value2))
{
Console.Error.WriteLine($"[LOADER][TRACE] Import#{dispatchIndex} return-rip slot: [0x{num2:X16}] = 0x{value2:X16}");
}
}
if (destination[0] == 72 && destination[1] == 139 && destination[2] == 5)
{
int num3 = BitConverter.ToInt32(destination.Slice(3, 4));
ulong num4 = returnRip + 7 + (ulong)num3;
if (_cpuContext.TryReadUInt64(num4, out var value3))
{
Console.Error.WriteLine($"[LOADER][TRACE] Import#{dispatchIndex} return-rip mov-slot: [0x{num4:X16}] = 0x{value3:X16}");
}
}
for (int i = 0; i + 6 <= destination.Length; i++)
{
if (destination[i] == byte.MaxValue && (destination[i + 1] == 21 || destination[i + 1] == 37))
{
int num5 = BitConverter.ToInt32(destination.Slice(i + 2, 4));
ulong num6 = returnRip + (ulong)i;
ulong num7 = num6 + 6 + (ulong)num5;
if (_cpuContext.TryReadUInt64(num7, out var value4))
{
Console.Error.WriteLine($"[LOADER][TRACE] Import#{dispatchIndex} near-indirect @{num6:X16}: slot=0x{num7:X16} val=0x{value4:X16}");
}
}
}
}
private static bool IsUnresolvedSentinel(ulong value)
{
return value == 65534 || value == 4294967294u || value == 18446744073709551614uL;
}
private static bool IsPlausibleReturnAddress(ulong address)
{
return address >= 12884901888L && address < 17592186044416L && !IsUnresolvedSentinel(address);
}
private static bool TryGetPlausibleReturnFromStack(ulong rsp, out ulong returnRip, out ulong nextRsp)
{
returnRip = 0uL;
nextRsp = rsp;
if (rsp <= 65536 || rsp >= 140737488355328L)
{
return false;
}
ulong num = rsp & 0xFFFFFFFFFFFFFFF8uL;
ulong num2 = ((num >= 8) ? (num - 8) : num);
for (int i = 0; i < 24; i++)
{
ulong num3 = num2 + (ulong)((long)i * 8L);
if (TryReadStackU64(num3, out var value) && IsLikelyReturnAddress(value))
{
returnRip = value;
nextRsp = num3 + 8;
return true;
}
}
for (ulong num4 = 1uL; num4 < 8; num4++)
{
for (int j = 0; j < 24; j++)
{
ulong num5 = rsp + num4 + (ulong)((long)j * 8L);
if (TryReadStackU64(num5, out var value2) && IsLikelyReturnAddress(value2))
{
returnRip = value2;
ulong num6 = num5 + 8;
nextRsp = (num6 + 7) & 0xFFFFFFFFFFFFFFF8uL;
return true;
}
}
}
return false;
}
private unsafe static bool TryReadStackU64(ulong address, out ulong value)
{
value = 0uL;
if (address <= 65536 || address >= 140737488355328L)
{
return false;
}
if (VirtualQuery((void*)address, out var lpBuffer, (nuint)sizeof(MEMORY_BASIC_INFORMATION64)) == 0)
{
return false;
}
ulong num = lpBuffer.BaseAddress + lpBuffer.RegionSize;
if (num < lpBuffer.BaseAddress || address > num - 8)
{
return false;
}
if (lpBuffer.State != 4096 || !IsReadableProtection(lpBuffer.Protect))
{
return false;
}
try
{
value = *(ulong*)address;
return true;
}
catch
{
return false;
}
}
private static bool IsLikelyReturnAddress(ulong address)
{
if (!IsPlausibleReturnAddress(address))
{
return false;
}
return IsExecutableAddress(address);
}
private unsafe static bool IsExecutableAddress(ulong address)
{
if (VirtualQuery((void*)address, out var lpBuffer, (nuint)sizeof(MEMORY_BASIC_INFORMATION64)) == 0)
{
return false;
}
return lpBuffer.State == 4096 && IsExecutableProtection(lpBuffer.Protect);
}
private static ulong AlignUp(ulong value, ulong alignment)
{
if (alignment == 0)
{
return value;
}
ulong num = alignment - 1;
return (value + num) & ~num;
}
private static bool IsReadableProtection(uint protect)
{
if ((protect & 0x100) != 0 || (protect & 1) != 0)
{
return false;
}
return (protect & 0xEE) != 0;
}
private static bool IsExecutableProtection(uint protect)
{
return (protect & 0xF0) != 0;
}
}
@@ -0,0 +1,740 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using SharpEmu.Core.Cpu.Disasm;
using SharpEmu.HLE;
namespace SharpEmu.Core.Cpu.Native;
public sealed partial class DirectExecutionBackend
{
private unsafe void SetupExceptionHandler()
{
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_DISABLE_RAW_HANDLER"), "1", StringComparison.Ordinal))
{
_rawExceptionHandler = (nint)AddVectoredExceptionHandler(1u, RawVectoredHandlerPtrManaged);
Console.Error.WriteLine($"[LOADER][INFO] Raw exception handler installed: 0x{_rawExceptionHandler:X16}");
}
else
{
Console.Error.WriteLine("[LOADER][INFO] Raw exception handler disabled by SHARPEMU_DISABLE_RAW_HANDLER=1");
}
_handlerDelegate = VectoredHandler;
_handlerHandle = GCHandle.Alloc(_handlerDelegate);
_exceptionHandler = (nint)AddVectoredExceptionHandler(1u, Marshal.GetFunctionPointerForDelegate(_handlerDelegate));
Console.Error.WriteLine($"[LOADER][INFO] Exception handler installed: 0x{_exceptionHandler:X16}");
_unhandledFilterDelegate = UnhandledExceptionFilter;
_unhandledFilterHandle = GCHandle.Alloc(_unhandledFilterDelegate);
SetUnhandledExceptionFilter(Marshal.GetFunctionPointerForDelegate(_unhandledFilterDelegate));
}
private unsafe int UnhandledExceptionFilter(void* exceptionInfo)
{
try
{
EXCEPTION_RECORD* exceptionRecord = ((EXCEPTION_POINTERS*)exceptionInfo)->ExceptionRecord;
ulong rip = ReadCtxU64(((EXCEPTION_POINTERS*)exceptionInfo)->ContextRecord, 248);
ulong rsp = ReadCtxU64(((EXCEPTION_POINTERS*)exceptionInfo)->ContextRecord, 152);
Console.Error.WriteLine("[LOADER][FATAL] Unhandled exception filter fired.");
Console.Error.WriteLine($"[LOADER][FATAL] Code: 0x{exceptionRecord->ExceptionCode:X8}");
Console.Error.WriteLine($"[LOADER][FATAL] Exception Address: 0x{(ulong)(nint)exceptionRecord->ExceptionAddress:X16}");
Console.Error.WriteLine($"[LOADER][FATAL] RIP: 0x{rip:X16}");
Console.Error.WriteLine($"[LOADER][FATAL] RSP: 0x{rsp:X16}");
Console.Error.Flush();
}
catch
{
}
return 0;
}
private unsafe int VectoredHandler(void* exceptionInfo)
{
if (_vectoredHandlerDepth > 0)
{
Console.Error.WriteLine("[LOADER][TRACE] Nested VEH exception; passing through.");
Console.Error.Flush();
return 0;
}
_vectoredHandlerDepth++;
try
{
EXCEPTION_RECORD* exceptionRecord = ((EXCEPTION_POINTERS*)exceptionInfo)->ExceptionRecord;
uint exceptionCode = exceptionRecord->ExceptionCode;
uint exceptionFlags = exceptionRecord->ExceptionFlags;
ulong exceptionAddress = (ulong)exceptionRecord->ExceptionAddress;
void* contextRecord = ((EXCEPTION_POINTERS*)exceptionInfo)->ContextRecord;
if (contextRecord == null)
{
Console.Error.WriteLine("[LOADER][FATAL] ContextRecord is null!");
Console.Error.Flush();
return 0;
}
ulong rip = ReadCtxU64(contextRecord, 248);
ulong rsp = ReadCtxU64(contextRecord, 152);
if (exceptionCode == 3221225477u && TryHandleLazyCommittedPage(exceptionRecord))
{
return -1;
}
switch (exceptionCode)
{
case 3221225477u:
LogAccessViolationTrace(exceptionAddress, exceptionRecord);
break;
case 3221226505u:
{
ulong p0 = exceptionRecord->NumberParameters >= 1 ? (*exceptionRecord->ExceptionInformation) : 0;
ulong p1 = exceptionRecord->NumberParameters >= 2 ? exceptionRecord->ExceptionInformation[1] : 0;
Console.Error.WriteLine($"[LOADER][TRACE] VEH_FASTFAIL code=0x{exceptionCode:X8} ex=0x{exceptionAddress:X16} rip=0x{rip:X16} rsp=0x{rsp:X16} p0=0x{p0:X16} p1=0x{p1:X16}");
Console.Error.Flush();
break;
}
}
ulong rax = ReadCtxU64(contextRecord, 120);
ulong rbx = ReadCtxU64(contextRecord, 144);
ulong rcx = ReadCtxU64(contextRecord, 128);
ulong rdx = ReadCtxU64(contextRecord, 136);
ulong rsi = ReadCtxU64(contextRecord, 168);
ulong rdi = ReadCtxU64(contextRecord, 176);
ulong rbp = ReadCtxU64(contextRecord, 160);
ulong r8 = ReadCtxU64(contextRecord, 184);
ulong r9 = ReadCtxU64(contextRecord, 192);
ulong r10 = ReadCtxU64(contextRecord, 200);
ulong r11 = ReadCtxU64(contextRecord, 208);
ulong r12 = ReadCtxU64(contextRecord, 216);
ulong r13 = ReadCtxU64(contextRecord, 224);
ulong r14 = ReadCtxU64(contextRecord, 232);
ulong r15 = ReadCtxU64(contextRecord, 240);
Console.Error.WriteLine("[LOADER][INFO] =========================================");
Console.Error.WriteLine("[LOADER][INFO] NATIVE EXCEPTION CAUGHT!");
Console.Error.WriteLine($"[LOADER][INFO] Code: 0x{exceptionCode:X8}");
Console.Error.WriteLine($"[LOADER][INFO] Exception Address: 0x{exceptionAddress:X16}");
Console.Error.WriteLine($"[LOADER][INFO] RIP: 0x{rip:X16}");
if (TryFormatNearestRuntimeSymbol(rip, out string symbol))
{
Console.Error.WriteLine("[LOADER][INFO] RIP symbol: " + symbol);
}
Console.Error.WriteLine($"[LOADER][INFO] RAX: 0x{rax:X16}");
Console.Error.WriteLine($"[LOADER][INFO] RBX: 0x{rbx:X16}");
Console.Error.WriteLine($"[LOADER][INFO] RCX: 0x{rcx:X16}");
Console.Error.WriteLine($"[LOADER][INFO] RDX: 0x{rdx:X16}");
Console.Error.WriteLine($"[LOADER][INFO] RSI: 0x{rsi:X16}");
Console.Error.WriteLine($"[LOADER][INFO] RDI: 0x{rdi:X16}");
Console.Error.WriteLine($"[LOADER][INFO] RBP: 0x{rbp:X16}");
Console.Error.WriteLine($"[LOADER][INFO] RSP: 0x{rsp:X16}");
Console.Error.WriteLine($"[LOADER][INFO] R8 : 0x{r8:X16}");
Console.Error.WriteLine($"[LOADER][INFO] R9 : 0x{r9:X16}");
Console.Error.WriteLine($"[LOADER][INFO] R10: 0x{r10:X16}");
Console.Error.WriteLine($"[LOADER][INFO] R11: 0x{r11:X16}");
Console.Error.WriteLine($"[LOADER][INFO] R12: 0x{r12:X16}");
Console.Error.WriteLine($"[LOADER][INFO] R13: 0x{r13:X16}");
Console.Error.WriteLine($"[LOADER][INFO] R14: 0x{r14:X16}");
Console.Error.WriteLine($"[LOADER][INFO] R15: 0x{r15:X16}");
Console.Error.WriteLine($"[LOADER][INFO] Flags: 0x{exceptionFlags:X8}");
ulong accessType = 0;
ulong target = 0;
if (exceptionCode == 3221225477u && exceptionRecord->NumberParameters >= 2)
{
accessType = *exceptionRecord->ExceptionInformation;
target = exceptionRecord->ExceptionInformation[1];
string accessText = accessType switch
{
0uL => "read",
1uL => "write",
8uL => "execute",
_ => $"unknown({accessType})"
};
Console.Error.WriteLine("[LOADER][INFO] AV access: " + accessText);
Console.Error.WriteLine($"[LOADER][INFO] AV target: 0x{target:X16}");
if (VirtualQuery((void*)target, out var mbi, (nuint)sizeof(MEMORY_BASIC_INFORMATION64)) != 0)
{
Console.Error.WriteLine($"[LOADER][INFO] AV target region: base=0x{mbi.BaseAddress:X16} size=0x{mbi.RegionSize:X16} state=0x{mbi.State:X08} protect=0x{mbi.Protect:X08}");
}
}
try
{
Console.Error.WriteLine("[LOADER][INFO] Stack qwords (RSP..):");
for (int i = 0; i < 16; i++)
{
ulong stackAddr = rsp + (ulong)(i * 8);
ulong value = (ulong)Marshal.ReadInt64((nint)stackAddr);
Console.Error.WriteLine($"[LOADER][INFO] [rsp+0x{i * 8:X2}] @0x{stackAddr:X16} = 0x{value:X16}");
}
}
catch
{
Console.Error.WriteLine("[LOADER][WARNING] Could not read stack qwords.");
}
try
{
Console.Error.WriteLine("[LOADER][INFO] Frame chain (RBP walk):");
ulong frame = rbp;
for (int i = 0; i < 12; i++)
{
if (frame < 140733193388032L || frame > 140737488355327L)
{
break;
}
ulong next = (ulong)Marshal.ReadInt64((nint)frame);
ulong ret = (ulong)Marshal.ReadInt64((nint)(frame + 8));
string extra = TryFormatNearestRuntimeSymbol(ret, out string retSym) ? $" [{retSym}]" : string.Empty;
Console.Error.WriteLine($"[LOADER][INFO] frame#{i}: rbp=0x{frame:X16} ret=0x{ret:X16}{extra} next=0x{next:X16}");
if (next <= frame)
{
break;
}
frame = next;
}
}
catch
{
Console.Error.WriteLine("[LOADER][WARNING] Could not walk RBP frame chain.");
}
switch (exceptionCode)
{
case 3221225477u:
Console.Error.WriteLine("[LOADER][ERROR] Type: Access Violation");
Console.Error.WriteLine("[LOADER][ERROR] This usually means:");
Console.Error.WriteLine("[LOADER][ERROR] - Guest code called an unmapped import");
Console.Error.WriteLine("[LOADER][ERROR] - Guest code accessed unmapped memory");
Console.Error.WriteLine("[LOADER][ERROR] - Need to implement HLE for this NID");
try
{
byte[] code = new byte[16];
Marshal.Copy((nint)rip, code, 0, code.Length);
Console.Error.WriteLine("[LOADER][INFO] Code at RIP: " + BitConverter.ToString(code).Replace("-", " "));
if (code[0] == 100)
{
Console.Error.WriteLine("[LOADER][ERROR] Detected FS segment prefix - TLS access not patched!");
}
else if (code[0] == 101)
{
Console.Error.WriteLine("[LOADER][ERROR] Detected GS segment prefix - TLS access not patched!");
}
else if (code[0] == 197 || code[0] == 196)
{
Console.Error.WriteLine("[LOADER][INFO] Detected AVX instruction - check CPU support!");
Console.Error.WriteLine($"[LOADER][INFO] RBP: 0x{rbp:X16} (mod 16 = {rbp % 16})");
Console.Error.WriteLine($"[LOADER][INFO] RSP: 0x{rsp:X16} (mod 16 = {rsp % 16})");
}
if (rip > 16)
{
byte[] before = new byte[16];
Marshal.Copy((nint)(rip - 16), before, 0, before.Length);
Console.Error.WriteLine("[LOADER][INFO] Code before RIP: " + BitConverter.ToString(before).Replace("-", " "));
}
if (rip > 32)
{
byte[] window = new byte[64];
Marshal.Copy((nint)(rip - 32), window, 0, window.Length);
Console.Error.WriteLine("[LOADER][INFO] Code window [RIP-0x20..]: " + BitConverter.ToString(window).Replace("-", " "));
}
}
catch
{
Console.Error.WriteLine("[LOADER][ERROR] Could not read code at RIP");
}
DumpRecentImportTrace();
break;
case 2147483651u:
Console.Error.WriteLine("[LOADER][WARNING] Type: Breakpoint (int3)");
Console.Error.WriteLine("[LOADER][WARNING] Unexpected breakpoint in direct-bridge mode");
break;
case 3221225501u:
Console.Error.WriteLine("[LOADER][INFO] Type: Illegal Instruction");
break;
}
Console.Error.WriteLine("[LOADER][INFO] =========================================");
Console.Error.Flush();
return 0;
}
finally
{
_vectoredHandlerDepth--;
}
}
private unsafe void LogAccessViolationTrace(ulong exceptionAddress, EXCEPTION_RECORD* exceptionRecord)
{
ulong accessType = exceptionRecord->NumberParameters >= 1 ? (*exceptionRecord->ExceptionInformation) : 0;
ulong target = exceptionRecord->NumberParameters >= 2 ? exceptionRecord->ExceptionInformation[1] : 0;
if (_lastAvTraceRip == exceptionAddress && _lastAvTraceType == accessType && _lastAvTraceTarget == target)
{
_lastAvTraceRepeatCount++;
if (_lastAvTraceRepeatCount > 4 && _lastAvTraceRepeatCount % 128 != 0)
{
return;
}
Console.Error.WriteLine($"[LOADER][TRACE] VEH_AV repeat#{_lastAvTraceRepeatCount} at 0x{exceptionAddress:X16} type={accessType} target=0x{target:X16}");
Console.Error.Flush();
return;
}
_lastAvTraceRip = exceptionAddress;
_lastAvTraceType = accessType;
_lastAvTraceTarget = target;
_lastAvTraceRepeatCount = 1;
Console.Error.WriteLine($"[LOADER][TRACE] VEH_AV first-chance at 0x{exceptionAddress:X16} type={accessType} target=0x{target:X16}");
Console.Error.Flush();
}
private void DumpGuestInstructionStream(string name, ulong startRip, int maxInstructions)
{
if (_cpuContext == null || startRip < 0x10000 || maxInstructions <= 0)
{
return;
}
Console.Error.WriteLine($"[LOADER][INFO] {name} disasm @0x{startRip:X16}:");
ulong rip = startRip;
for (int i = 0; i < maxInstructions; i++)
{
if (!IcedDecoder.TryReadGuestBytes(_cpuContext.Memory, rip, maxLen: 15, out var bytes) ||
!IcedDecoder.TryDecode(rip, bytes, out var instruction))
{
Console.Error.WriteLine($"[LOADER][INFO] 0x{rip:X16}: <decode-failed>");
break;
}
Console.Error.WriteLine(
$"[LOADER][INFO] 0x{instruction.Rip:X16}: {instruction.Text} bytes={IcedDecoder.FormatBytes(instruction.Bytes)}");
rip += (ulong)instruction.Length;
}
}
private void DumpUnresolvedSentinelWindow(string name, ulong baseAddress, int size)
{
if (baseAddress < 0x10000 || size <= 0)
{
return;
}
ulong scanStart = baseAddress;
ulong scanEnd = baseAddress + (ulong)size;
List<ulong> hits = ScanSuspiciousResolverPointers(scanStart, scanEnd);
if (hits.Count == 0)
{
Console.Error.WriteLine($"[LOADER][INFO] {name} unresolved scan: none");
return;
}
Console.Error.WriteLine($"[LOADER][INFO] {name} unresolved scan hits: {hits.Count}");
for (int i = 0; i < hits.Count && i < 8; i++)
{
ulong slotAddress = hits[i];
if (TryReadQword(slotAddress, out var value))
{
Console.Error.WriteLine($"[LOADER][INFO] hit#{i}: slot=0x{slotAddress:X16} value=0x{value:X16}");
}
}
}
private void DumpSentinelPatternWindow(string name, ulong baseAddress, int size)
{
if (_cpuContext == null || baseAddress < 0x10000 || size <= 0)
{
return;
}
byte[] buffer = new byte[size];
if (!_cpuContext.Memory.TryRead(baseAddress, buffer))
{
Console.Error.WriteLine($"[LOADER][INFO] {name} sentinel-pattern scan: unreadable");
return;
}
List<string> hits = new();
for (int offset = 0; offset + 2 <= buffer.Length; offset++)
{
if (BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset, 2)) == 0xFFFE)
{
hits.Add($"+0x{offset:X}:u16");
}
if (offset + 4 <= buffer.Length &&
BinaryPrimitives.ReadUInt32LittleEndian(buffer.AsSpan(offset, 4)) == 0xFFFFFFFEu)
{
hits.Add($"+0x{offset:X}:u32");
}
if (offset + 8 <= buffer.Length &&
BinaryPrimitives.ReadUInt64LittleEndian(buffer.AsSpan(offset, 8)) == 0xFFFFFFFFFFFFFFFEuL)
{
hits.Add($"+0x{offset:X}:u64");
}
}
if (hits.Count == 0)
{
Console.Error.WriteLine($"[LOADER][INFO] {name} sentinel-pattern scan: none");
return;
}
Console.Error.WriteLine($"[LOADER][INFO] {name} sentinel-pattern hits: {string.Join(", ", hits.GetRange(0, Math.Min(hits.Count, 12)))}");
}
private void DumpReturnTargetCandidates(ulong rsp)
{
if (rsp < 0x10000)
{
return;
}
ulong start = rsp >= 0x10 ? rsp - 0x10 : rsp;
Console.Error.WriteLine($"[LOADER][INFO] Return-target candidates near RSP=0x{rsp:X16}:");
for (int offset = 0; offset <= 0x20; offset++)
{
ulong address = start + (ulong)offset;
try
{
ulong value = (ulong)Marshal.ReadInt64((nint)address);
Console.Error.WriteLine($"[LOADER][INFO] [0x{address:X16}] -> 0x{value:X16}");
}
catch
{
Console.Error.WriteLine($"[LOADER][INFO] [0x{address:X16}] -> <unreadable>");
break;
}
}
}
private void DumpObjectFieldTargets(string name, ulong objectAddress, int[] offsets, int windowSize)
{
if (objectAddress < 0x10000 || offsets.Length == 0)
{
return;
}
foreach (int offset in offsets)
{
ulong slotAddress = objectAddress + (ulong)offset;
if (!TryReadQword(slotAddress, out var target) || target < 0x10000)
{
continue;
}
Console.Error.WriteLine($"[LOADER][INFO] {name}+0x{offset:X2} target = {FormatPointerWithNearestSymbol(target)}");
DumpPointerWindow($"{name}+0x{offset:X2}", target, windowSize);
DumpUnresolvedSentinelWindow($"{name}+0x{offset:X2}", target, 0x80);
}
}
private void DumpSuspiciousGlobalSlots()
{
DumpAbsoluteSlot("callback_slot[0x80293BD08]", 0x000000080293BD08uL);
DumpAbsoluteSlot("callback_arg[0x8030FBBE8]", 0x00000008030FBBE8uL);
DumpAbsoluteSlot("tsc_freq_global[0x8030FD590]", 0x00000008030FD590uL);
DumpAbsoluteSlot("tsc_base_global[0x8030FD598]", 0x00000008030FD598uL);
DumpAbsoluteSlot("plt_got[0x8028F6100]", 0x00000008028F6100uL);
DumpAbsoluteSlot("plt_got[0x8028F6158]", 0x00000008028F6158uL);
DumpAbsoluteSlot("plt_got[0x8028F6160]", 0x00000008028F6160uL);
DumpAbsoluteSlot("plt_got[0x8028F64C0]", 0x00000008028F64C0uL);
DumpAbsoluteSlot("plt_got[0x8028F64C8]", 0x00000008028F64C8uL);
DumpAbsoluteSlot("plt_got[0x8028F6590]", 0x00000008028F6590uL);
DumpAbsoluteSlot("plt_got[0x8028F6708]", 0x00000008028F6708uL);
DumpUnresolvedSentinelWindow("PLT-GOT", 0x00000008028F6100uL, 0x700);
}
private void DumpAbsoluteSlot(string name, ulong slotAddress)
{
if (!TryReadQword(slotAddress, out var value))
{
Console.Error.WriteLine($"[LOADER][INFO] {name} @0x{slotAddress:X16} = <unreadable>");
return;
}
Console.Error.WriteLine($"[LOADER][INFO] {name} @0x{slotAddress:X16} = {FormatPointerWithNearestSymbol(value)}");
}
private void DumpPointerWindow(string name, ulong baseAddress, int size)
{
if (baseAddress < 0x10000 || size <= 0)
{
return;
}
Console.Error.WriteLine($"[LOADER][INFO] {name} window @0x{baseAddress:X16}:");
for (int offset = 0; offset < size; offset += 8)
{
ulong slotAddress = baseAddress + (ulong)offset;
if (!TryReadQword(slotAddress, out var value))
{
Console.Error.WriteLine($"[LOADER][INFO] +0x{offset:X2}: <unreadable>");
break;
}
Console.Error.WriteLine($"[LOADER][INFO] +0x{offset:X2}: {FormatPointerWithNearestSymbol(value)}");
}
}
private unsafe bool TryReadQword(ulong address, out ulong value)
{
value = 0;
if (address < 0x10000)
{
return false;
}
if (VirtualQuery((void*)address, out var mbi, (nuint)sizeof(MEMORY_BASIC_INFORMATION64)) == 0)
{
return false;
}
ulong regionEnd = mbi.BaseAddress + mbi.RegionSize;
if (mbi.State != MEM_COMMIT || !IsReadableProtection(mbi.Protect) || regionEnd <= address || address > regionEnd - 8)
{
return false;
}
try
{
value = (ulong)Marshal.ReadInt64((nint)address);
return true;
}
catch
{
value = 0;
return false;
}
}
private string FormatPointerWithNearestSymbol(ulong value)
{
string text = $"0x{value:X16}";
if (TryFormatNearestRuntimeSymbol(value, out string symbol))
{
text += $" [{symbol}]";
}
return text;
}
private void InitializeRuntimeSymbolIndex(IReadOnlyDictionary<string, ulong> runtimeSymbols)
{
_runtimeSymbolsByName.Clear();
if (runtimeSymbols.Count == 0)
{
_runtimeSymbolsByAddress = Array.Empty<KeyValuePair<string, ulong>>();
return;
}
List<KeyValuePair<string, ulong>> list = new(runtimeSymbols.Count);
foreach (KeyValuePair<string, ulong> runtimeSymbol in runtimeSymbols)
{
if (runtimeSymbol.Value != 0L && !string.IsNullOrWhiteSpace(runtimeSymbol.Key))
{
list.Add(runtimeSymbol);
_runtimeSymbolsByName[runtimeSymbol.Key] = runtimeSymbol.Value;
}
}
list.Sort((a, b) => a.Value.CompareTo(b.Value));
_runtimeSymbolsByAddress = list.ToArray();
}
private bool TryFormatNearestRuntimeSymbol(ulong address, out string text)
{
text = string.Empty;
KeyValuePair<string, ulong>[] runtimeSymbolsByAddress = _runtimeSymbolsByAddress;
if (runtimeSymbolsByAddress.Length == 0)
{
return false;
}
int low = 0;
int high = runtimeSymbolsByAddress.Length - 1;
int best = -1;
while (low <= high)
{
int mid = low + ((high - low) >> 1);
ulong value = runtimeSymbolsByAddress[mid].Value;
if (value <= address)
{
best = mid;
low = mid + 1;
}
else
{
high = mid - 1;
}
}
if (best < 0)
{
return false;
}
KeyValuePair<string, ulong> symbol = runtimeSymbolsByAddress[best];
ulong delta = address - symbol.Value;
text = delta == 0
? $"{symbol.Key} (0x{symbol.Value:X16})"
: $"{symbol.Key}+0x{delta:X} (0x{symbol.Value:X16})";
return true;
}
private unsafe static bool TryHandleLazyCommittedPage(EXCEPTION_RECORD* exceptionRecord)
{
if (exceptionRecord->NumberParameters < 2)
{
return false;
}
ulong accessType = *exceptionRecord->ExceptionInformation;
ulong faultAddress = exceptionRecord->ExceptionInformation[1];
if (accessType == 8 && faultAddress < 4294967296L)
{
return false;
}
if (faultAddress < 65536 || faultAddress >= 140737488355328L)
{
return false;
}
if (VirtualQuery((void*)faultAddress, out var mbi, (nuint)sizeof(MEMORY_BASIC_INFORMATION64)) == 0)
{
return false;
}
ulong pageBase = faultAddress & 0xFFFFFFFFFFFFF000uL;
uint commitProtect = ResolveLazyCommitProtection(accessType, mbi.AllocationProtect);
Console.Error.WriteLine($"[LOADER][TRACE] lazy-query: fault=0x{faultAddress:X16} state=0x{mbi.State:X08} base=0x{mbi.BaseAddress:X16} size=0x{mbi.RegionSize:X16} alloc=0x{mbi.AllocationProtect:X08} prot=0x{mbi.Protect:X08}");
bool committed = false;
ulong committedBase = 0;
ulong committedSize = 0;
if (mbi.State == 65536)
{
ulong largeBase = faultAddress & 0xFFFFFFFFFFE00000uL;
if (TryReserveThenCommit(largeBase, 2097152uL, largeBase, 2097152uL, commitProtect))
{
committed = true;
committedBase = largeBase;
committedSize = 2097152uL;
}
else
{
ulong region64kBase = faultAddress & 0xFFFFFFFFFFFF0000uL;
if (TryReserveThenCommit(region64kBase, 65536uL, region64kBase, 65536uL, commitProtect))
{
committed = true;
committedBase = region64kBase;
committedSize = 65536uL;
}
else if (TryReserveThenCommit(pageBase, 4096uL, pageBase, 4096uL, commitProtect))
{
committed = true;
committedBase = pageBase;
committedSize = 4096uL;
}
}
if (!committed)
{
return false;
}
TryCommitRange(pageBase + 4096, 4096uL, commitProtect);
Console.Error.WriteLine($"[LOADER][TRACE] lazy-reserve-commit: addr=0x{committedBase:X16} size=0x{committedSize:X16} access={accessType} protect=0x{commitProtect:X8}");
return true;
}
if (mbi.State != 8192)
{
return false;
}
ulong largeCommitBase = faultAddress & 0xFFFFFFFFFFE00000uL;
if (TryCommitRange(largeCommitBase, 2097152uL, commitProtect))
{
committed = true;
committedBase = largeCommitBase;
committedSize = 2097152uL;
}
else
{
ulong region64kBase = faultAddress & 0xFFFFFFFFFFFF0000uL;
if (TryCommitRange(region64kBase, 65536uL, commitProtect))
{
committed = true;
committedBase = region64kBase;
committedSize = 65536uL;
}
else if (TryCommitRange(pageBase, 8192uL, commitProtect))
{
committed = true;
committedBase = pageBase;
committedSize = 8192uL;
}
else if (TryCommitRange(pageBase, 4096uL, commitProtect))
{
committed = true;
committedBase = pageBase;
committedSize = 4096uL;
}
}
if (!committed)
{
return false;
}
TryCommitRange(pageBase + 4096, 4096uL, commitProtect);
Console.Error.WriteLine($"[LOADER][TRACE] lazy-commit: addr=0x{committedBase:X16} size=0x{committedSize:X16} access={accessType} protect=0x{commitProtect:X8}");
return true;
static unsafe bool TryCommitRange(ulong baseAddress, ulong length, uint protection)
{
if (length == 0)
{
return false;
}
return VirtualAlloc((void*)baseAddress, (nuint)length, 4096u, protection) != null;
}
static unsafe bool TryReserveRange(ulong baseAddress, ulong length)
{
if (length == 0)
{
return false;
}
return VirtualAlloc((void*)baseAddress, (nuint)length, 8192u, 4u) != null;
}
static bool TryReserveThenCommit(ulong reserveAddress, ulong reserveSize, ulong commitAddress, ulong commitSize, uint protection)
{
if (!TryReserveRange(reserveAddress, reserveSize))
{
return false;
}
return TryCommitRange(commitAddress, commitSize, protection);
}
}
private static uint ResolveLazyCommitProtection(ulong accessType, uint allocationProtect)
{
if (accessType == 8 || (allocationProtect & 0xF0) != 0)
{
return 64u;
}
return 4u;
}
}
@@ -0,0 +1,760 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using SharpEmu.Core.Cpu;
using SharpEmu.HLE;
namespace SharpEmu.Core.Cpu.Native;
public sealed partial class DirectExecutionBackend
{
private static ulong ImportDispatchGatewayManaged(nint backendHandle, int importIndex, nint argPackPtr)
{
try
{
if (!(GCHandle.FromIntPtr(backendHandle).Target is DirectExecutionBackend directExecutionBackend))
{
Console.Error.WriteLine(
$"[LOADER][ERROR] ImportDispatchGatewayManaged: invalid backend handle 0x{backendHandle:X16}");
return 18446744071562199042uL;
}
return directExecutionBackend.DispatchImport(importIndex, argPackPtr);
}
catch (Exception ex)
{
Console.Error.WriteLine(
$"[LOADER][ERROR] ImportDispatchGatewayManaged exception: {ex.GetType().Name}: {ex.Message}");
return 18446744071562199298uL;
}
}
private unsafe static int RawVectoredHandlerManaged(void* exceptionInfo)
{
return TryRecoverUnresolvedSentinel(exceptionInfo);
}
private unsafe static int RawUnhandledFilterManaged(void* exceptionInfo)
{
return TryRecoverUnresolvedSentinel(exceptionInfo);
}
private unsafe static int TryRecoverUnresolvedSentinel(void* exceptionInfo)
{
EXCEPTION_RECORD* exceptionRecord = ((EXCEPTION_POINTERS*)exceptionInfo)->ExceptionRecord;
if (exceptionRecord->ExceptionCode != 3221225477u)
{
return 0;
}
void* contextRecord = ((EXCEPTION_POINTERS*)exceptionInfo)->ContextRecord;
ulong value = ReadCtxU64(contextRecord, 248);
ulong value2 = (ulong)exceptionRecord->ExceptionAddress;
if (!IsUnresolvedSentinel(value) && !IsUnresolvedSentinel(value2))
{
return 0;
}
ulong rsp = ReadCtxU64(contextRecord, 152);
WriteCtxU64(contextRecord, 120, 0uL);
if (TryGetPlausibleReturnFromStack(rsp, out var returnRip, out var nextRsp))
{
WriteCtxU64(contextRecord, 152, nextRsp);
WriteCtxU64(contextRecord, 248, returnRip);
Interlocked.Increment(ref _rawSentinelRecoveries);
return -1;
}
return 0;
}
private unsafe ulong DispatchImport(int importIndex, nint argPackPtr)
{
long num = Interlocked.Increment(ref _importDispatchCount);
MarkExecutionProgress();
if (_cpuContext == null)
{
LastError = "Import dispatch called without active CPU context";
return 18446744071562199298uL;
}
if ((uint)importIndex >= (uint)_importEntries.Length)
{
LastError = $"Import dispatch index out of range: {importIndex}";
return 18446744071562199042uL;
}
ImportStubEntry importStubEntry = _importEntries[importIndex];
int num2 = Volatile.Read(in _rawSentinelRecoveries);
if (num2 != _lastReportedRawSentinelRecoveries)
{
Console.Error.WriteLine($"[LOADER][TRACE] Raw sentinel recoveries: {num2} (last import index={importIndex})");
_lastReportedRawSentinelRecoveries = num2;
}
_cpuContext.Rip = importStubEntry.Address;
_cpuContext[CpuRegister.Rdi] = *(ulong*)argPackPtr;
_cpuContext[CpuRegister.Rsi] = *(ulong*)(argPackPtr + 8);
_cpuContext[CpuRegister.Rdx] = *(ulong*)(argPackPtr + 16);
_cpuContext[CpuRegister.Rcx] = *(ulong*)(argPackPtr + 24);
_cpuContext[CpuRegister.R8] = *(ulong*)(argPackPtr + 32);
_cpuContext[CpuRegister.R9] = *(ulong*)(argPackPtr + 40);
_cpuContext[CpuRegister.Rbx] = *(ulong*)(argPackPtr + 48);
_cpuContext[CpuRegister.Rbp] = *(ulong*)(argPackPtr + 56);
_cpuContext[CpuRegister.R12] = *(ulong*)(argPackPtr + 64);
_cpuContext[CpuRegister.R13] = *(ulong*)(argPackPtr + 72);
_cpuContext[CpuRegister.R14] = *(ulong*)(argPackPtr + 80);
_cpuContext[CpuRegister.R15] = *(ulong*)(argPackPtr + 88);
_cpuContext[CpuRegister.Rsp] = (ulong)argPackPtr + 96uL;
ulong value = _cpuContext[CpuRegister.Rdi];
ulong value2 = _cpuContext[CpuRegister.Rsi];
ulong num3 = _cpuContext[CpuRegister.Rdx];
ulong num4 = _cpuContext[CpuRegister.Rcx];
ulong num5 = _cpuContext[CpuRegister.R8];
ulong num6 = _cpuContext[CpuRegister.R9];
ulong value3 = _cpuContext[CpuRegister.Rbx];
ulong value4 = _cpuContext[CpuRegister.Rbp];
ulong value5 = _cpuContext[CpuRegister.R12];
ulong value6 = _cpuContext[CpuRegister.R13];
ulong value7 = _cpuContext[CpuRegister.R14];
ulong value8 = _cpuContext[CpuRegister.R15];
ulong num7 = *(ulong*)(argPackPtr + 96);
if (!IsLikelyReturnAddress(num7))
{
for (int i = 1; i <= 4; i++)
{
ulong num8 = *(ulong*)(argPackPtr + 96 + i * 8);
if (IsLikelyReturnAddress(num8))
{
*(ulong*)(argPackPtr + 96) = num8;
num7 = num8;
Console.Error.WriteLine($"[LOADER][WARNING] Import#{num}: corrected suspicious return RIP using stack slot +0x{i * 8:X} -> 0x{num7:X16}");
break;
}
}
}
TrackDistinctImportNid(importStubEntry.Nid);
TrackStrlenPrelude(importStubEntry.Nid, num, num7);
bool logBootstrap = string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_BOOTSTRAP"), "1", StringComparison.Ordinal);
if (logBootstrap && string.Equals(importStubEntry.Nid, RuntimeStubNids.BootstrapBridge, StringComparison.Ordinal))
{
string symbolText = "<unreadable>";
if (TryReadAsciiZ(value2, 256, out var sym))
{
symbolText = sym;
}
Console.Error.WriteLine(
$"[LOADER][TRACE] bootstrap_call#{num}: op=0x{value:X16} sym_ptr=0x{value2:X16} sym='{symbolText}' out_ptr=0x{num3:X16} ret=0x{num7:X16}");
}
if (!_forcedGuestExit && ShouldForceGuestExitOnImportLoop(importStubEntry.Nid, num7, num, value, value2) && TryForceGuestExitToHostStub(argPackPtr, num, num7, importStubEntry.Nid))
{
_cpuContext[CpuRegister.Rax] = 1uL;
return 1uL;
}
bool flag0 = ShouldSuppressStrlenTrace(importStubEntry.Nid);
bool flag = num7 >= 2156221920u && num7 <= 2156225024u;
bool flag2 = num7 >= 2156351360u && num7 <= 2156352080u;
bool flag3 = num >= 1020 && num <= 1040;
if (!flag0 && (num <= 128 || (num >= 240 && num <= 400) || (num >= 900 && num <= 1300) || num % 100000 == 0L || (importStubEntry.Nid == "tsvEmnenz48" && (num <= 256 || num % 1000 == 0L)) || (importStubEntry.Nid == "rTXw65xmLIA" && (num <= 256 || num % 128 == 0)) || flag || flag2 || flag3))
{
if (_moduleManager.TryGetExport(importStubEntry.Nid, out ExportedFunction export))
{
Console.Error.WriteLine($"[LOADER][TRACE] Import#{num}: {export.LibraryName}:{export.Name} ({importStubEntry.Nid})");
}
else
{
Console.Error.WriteLine($"[LOADER][TRACE] Import#{num}: {importStubEntry.Nid}");
}
}
if (!flag0)
{
RecordRecentImportTrace($"#{num} nid={importStubEntry.Nid} ret=0x{num7:X16} rdi=0x{_cpuContext[CpuRegister.Rdi]:X16} rsi=0x{_cpuContext[CpuRegister.Rsi]:X16} rdx=0x{_cpuContext[CpuRegister.Rdx]:X16}");
}
if (importStubEntry.Nid == "8zTFvBIAIN8" && num <= 256)
{
Console.Error.WriteLine($"[LOADER][TRACE] memset#{num}: dst=0x{_cpuContext[CpuRegister.Rdi]:X16} val=0x{_cpuContext[CpuRegister.Rsi] & 0xFF:X2} len=0x{_cpuContext[CpuRegister.Rdx]:X16} ret=0x{num7:X16}");
}
if (importStubEntry.Nid == "tsvEmnenz48" && num <= 64)
{
Console.Error.WriteLine($"[LOADER][TRACE] __cxa_atexit#{num}: func=0x{_cpuContext[CpuRegister.Rdi]:X16} arg=0x{_cpuContext[CpuRegister.Rsi]:X16} dso=0x{_cpuContext[CpuRegister.Rdx]:X16} ret=0x{num7:X16}");
}
if (importStubEntry.Nid == "bzQExy189ZI" || importStubEntry.Nid == "8G2LB+A3rzg")
{
Console.Error.WriteLine($"[LOADER][TRACE] {importStubEntry.Nid}#{num}: rdi=0x{_cpuContext[CpuRegister.Rdi]:X16} rsi=0x{_cpuContext[CpuRegister.Rsi]:X16} rdx=0x{_cpuContext[CpuRegister.Rdx]:X16} ret=0x{num7:X16}");
}
if (flag || flag2 || flag3)
{
Console.Error.WriteLine($"[LOADER][TRACE] ImportCtx#{num}: nid={importStubEntry.Nid} ret=0x{num7:X16} rdi=0x{_cpuContext[CpuRegister.Rdi]:X16} rsi=0x{_cpuContext[CpuRegister.Rsi]:X16} rdx=0x{_cpuContext[CpuRegister.Rdx]:X16} rcx=0x{_cpuContext[CpuRegister.Rcx]:X16}");
Console.Error.WriteLine($"[LOADER][TRACE] ImportNV#{num}: rbx=0x{value3:X16} rbp=0x{value4:X16} r12=0x{value5:X16} r13=0x{value6:X16} r14=0x{value7:X16} r15=0x{value8:X16}");
if (flag3)
{
ulong num9 = _cpuContext[CpuRegister.Rsp];
if (_cpuContext.TryReadUInt64(num9, out var value9) && _cpuContext.TryReadUInt64(num9 + 8, out var value10) && _cpuContext.TryReadUInt64(num9 + 16, out var value11) && _cpuContext.TryReadUInt64(num9 + 24, out var value12) && _cpuContext.TryReadUInt64(num9 + 32, out var value13) && _cpuContext.TryReadUInt64(num9 + 40, out var value14) && _cpuContext.TryReadUInt64(num9 + 48, out var value15) && _cpuContext.TryReadUInt64(num9 + 56, out var value16) && _cpuContext.TryReadUInt64(num9 + 64, out var value17))
{
Console.Error.WriteLine($"[LOADER][TRACE] ImportStackHead#{num}: rsp=0x{num9:X16} [0]=0x{value9:X16} [20]=0x{value13:X16} [40]=0x{value17:X16}");
Console.Error.WriteLine($"[LOADER][TRACE] ImportStack#{num}: rsp=0x{num9:X16} [0]=0x{value9:X16} [8]=0x{value10:X16} [10]=0x{value11:X16} [18]=0x{value12:X16} [20]=0x{value13:X16} [28]=0x{value14:X16} [30]=0x{value15:X16} [38]=0x{value16:X16} [40]=0x{value17:X16}");
}
}
if (flag3)
{
Console.Error.Flush();
}
}
if (importStubEntry.Nid == "Ou3iL1abvng")
{
try
{
byte[] array = new byte[64];
Marshal.Copy((nint)(num7 - 32), array, 0, array.Length);
Console.Error.WriteLine($"[LOADER][TRACE] __stack_chk_fail return-site @0x{num7:X16}: {BitConverter.ToString(array).Replace("-", " ")}");
}
catch
{
}
TryBypassStackChkFailTrap(num, num7);
}
if (importStubEntry.Nid == "9rAeANT2tyE" || importStubEntry.Nid == "1j3S3n-tTW4")
{
ulong rspDbg = _cpuContext[CpuRegister.Rsp];
Console.Error.WriteLine(
$"[LOADER][TRACE] ImportDbg#{num}: nid={importStubEntry.Nid} " +
$"ret=0x{num7:X16} rsp=0x{rspDbg:X16} rsp&7=0x{(rspDbg & 7):X} rsp&F=0x{(rspDbg & 0xF):X}");
if (_cpuContext.TryReadUInt64(rspDbg, out var s0))
Console.Error.WriteLine($"[LOADER][TRACE] [rsp+0x00] = 0x{s0:X16}");
if (_cpuContext.TryReadUInt64(rspDbg + 8, out var s8))
Console.Error.WriteLine($"[LOADER][TRACE] [rsp+0x08] = 0x{s8:X16}");
if (_cpuContext.TryReadUInt64(rspDbg + 16, out var s10))
Console.Error.WriteLine($"[LOADER][TRACE] [rsp+0x10] = 0x{s10:X16}");
if (_cpuContext.TryReadUInt64(rspDbg + 24, out var s18))
Console.Error.WriteLine($"[LOADER][TRACE] [rsp+0x18] = 0x{s18:X16}");
ProbeReturnRip(num7, num);
Console.Error.Flush();
}
ProbeReturnRip(0x0000000800EA020Eul, 999001);
ProbeReturnRip(0x0000000800EA0213ul, 999002);
ProbeReturnRip(0x0000000800EA040Aul, 999003);
try
{
OrbisGen2Result orbisGen2Result;
if (string.Equals(importStubEntry.Nid, RuntimeStubNids.BootstrapBridge, StringComparison.Ordinal))
{
orbisGen2Result = DispatchBootstrapBridge();
}
else if (string.Equals(importStubEntry.Nid, RuntimeStubNids.KernelDynlibDlsym, StringComparison.Ordinal))
{
orbisGen2Result = DispatchKernelDynlibDlsym();
}
else
{
orbisGen2Result = _moduleManager.Dispatch(importStubEntry.Nid, _cpuContext);
}
switch (orbisGen2Result)
{
case OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND:
LastError = "Missing HLE export for NID: " + importStubEntry.Nid;
Console.Error.WriteLine($"[LOADER][WARN] Import#{num} unresolved: nid={importStubEntry.Nid} ret=0x{num7:X16}");
if (importStubEntry.Nid == "L-Q3LEjIbgA")
{
string value18 = string.Join(" ", importStubEntry.Nid.Select(delegate (char c)
{
int num10 = c;
return num10.ToString("X2");
}));
Console.Error.WriteLine($"[LOADER][WARN] map_direct nid raw len={importStubEntry.Nid.Length} chars=[{value18}]");
Delegate function;
bool value19 = _moduleManager.TryGetFunction(importStubEntry.Nid, out function);
ExportedFunction export2;
bool value20 = _moduleManager.TryGetExport(importStubEntry.Nid, out export2);
Console.Error.WriteLine($"[LOADER][WARN] map_direct lookup with import nid: function={value19}, export={value20}");
Console.Error.WriteLine(_moduleManager.TryGetExport("L-Q3LEjIbgA", out ExportedFunction export3) ? $"[LOADER][WARN] Canonical map_direct exists as {export3.LibraryName}:{export3.Name}, target={export3.Target}, ctx_target={_cpuContext.TargetGeneration}" : "[LOADER][WARN] Canonical map_direct export lookup also missing");
}
break;
default:
Console.Error.WriteLine($"[LOADER][WARN] Import#{num} result: {orbisGen2Result} ({importStubEntry.Nid})");
break;
case OrbisGen2Result.ORBIS_GEN2_OK:
break;
}
_cpuContext[CpuRegister.Rbx] = value3;
_cpuContext[CpuRegister.Rbp] = value4;
_cpuContext[CpuRegister.R12] = value5;
_cpuContext[CpuRegister.R13] = value6;
_cpuContext[CpuRegister.R14] = value7;
_cpuContext[CpuRegister.R15] = value8;
_cpuContext[CpuRegister.Rdi] = value;
_cpuContext[CpuRegister.Rsi] = value2;
if (flag || flag2 || flag3)
{
Console.Error.WriteLine($"[LOADER][TRACE] ImportRet#{num}: nid={importStubEntry.Nid} result={orbisGen2Result} rax=0x{_cpuContext[CpuRegister.Rax]:X16}");
if (flag3)
{
Console.Error.Flush();
}
}
return _cpuContext[CpuRegister.Rax];
}
catch (Exception ex)
{
LastError = $"HLE dispatch error for {importStubEntry.Nid}: {ex.GetType().Name}: {ex.Message}";
_cpuContext[CpuRegister.Rax] = 18446744071562199298uL;
return 18446744071562199298uL;
}
}
private unsafe bool TryForceGuestExitToHostStub(nint argPackPtr, long dispatchIndex, ulong returnRip, string nid)
{
ulong num = _entryReturnSentinelRip;
if (num < 65536)
{
return false;
}
try
{
*(ulong*)(argPackPtr + 96) = num;
}
catch
{
return false;
}
_forcedGuestExit = true;
LastError = $"Detected repeating import loop at import#{dispatchIndex} ({nid}) and forced guest exit.";
Console.Error.WriteLine($"[LOADER][ERROR] Import-loop guard fired at import#{dispatchIndex}: nid={nid} ret=0x{returnRip:X16} -> host_exit=0x{num:X16}");
DumpRecentImportTrace();
return true;
}
private bool ShouldForceGuestExitOnImportLoop(string nid, ulong returnRip, long dispatchIndex, ulong arg0, ulong arg1)
{
if (dispatchIndex < 1200)
{
return false;
}
if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_DISABLE_IMPORT_LOOP_GUARD"), "1", StringComparison.Ordinal))
{
return false;
}
RecordImportLoopSignature(BuildImportLoopSignature(nid, returnRip, arg0, arg1));
if (!HasRepeatingImportLoopPattern())
{
if (_importLoopPatternHits > 0)
{
_importLoopPatternHits--;
}
return false;
}
_importLoopPatternHits++;
return _importLoopPatternHits >= 6;
}
private ulong BuildImportLoopSignature(string nid, ulong returnRip, ulong arg0, ulong arg1)
{
if (!_importNidHashCache.TryGetValue(nid, out var value))
{
value = StableHash64(nid);
_importNidHashCache[nid] = value;
}
ulong num = returnRip >> 2;
ulong num2 = ((arg0 >> 4) * 11400714819323198485uL) ^ ((arg1 >> 4) * 14029467366897019727uL);
return num ^ value * 11400714819323198485uL ^ num2;
}
private void RecordImportLoopSignature(ulong signature)
{
_importLoopSignatures[_importLoopSignatureWriteIndex] = signature;
_importLoopSignatureWriteIndex = (_importLoopSignatureWriteIndex + 1) % _importLoopSignatures.Length;
if (_importLoopSignatureCount < _importLoopSignatures.Length)
{
_importLoopSignatureCount++;
}
}
private bool HasRepeatingImportLoopPattern()
{
int num = _importLoopSignatureCount;
if (num < 96)
{
return false;
}
int num2 = Math.Min(48, num / 4);
for (int i = 6; i <= num2; i++)
{
if (HasRepeatingImportLoopPattern(i, 4))
{
return true;
}
}
return false;
}
private bool HasRepeatingImportLoopPattern(int period, int repeats)
{
int num = period * repeats;
if (period <= 0 || repeats < 2 || _importLoopSignatureCount < num)
{
return false;
}
for (int i = 0; i < period; i++)
{
ulong importLoopSignatureFromTail = GetImportLoopSignatureFromTail(i);
for (int j = 1; j < repeats; j++)
{
if (GetImportLoopSignatureFromTail(i + j * period) != importLoopSignatureFromTail)
{
return false;
}
}
}
return true;
}
private ulong GetImportLoopSignatureFromTail(int offset)
{
int num = _importLoopSignatureWriteIndex - 1 - offset;
while (num < 0)
{
num += _importLoopSignatures.Length;
}
return _importLoopSignatures[num % _importLoopSignatures.Length];
}
private bool ShouldSuppressStrlenTrace(string nid)
{
return string.Equals(nid, "j4ViWNHEgww", StringComparison.Ordinal) && !_logStrlenImports;
}
private void TrackDistinctImportNid(string nid)
{
if (string.IsNullOrWhiteSpace(nid) || string.Equals(_lastDistinctImportNid, nid, StringComparison.Ordinal))
{
return;
}
_lastDistinctImportNid = nid;
_distinctImportNidHistory[_distinctImportNidHistoryWriteIndex] = nid;
_distinctImportNidHistoryWriteIndex = (_distinctImportNidHistoryWriteIndex + 1) % _distinctImportNidHistory.Length;
if (_distinctImportNidHistoryCount < _distinctImportNidHistory.Length)
{
_distinctImportNidHistoryCount++;
}
}
private void TrackStrlenPrelude(string nid, long dispatchIndex, ulong returnRip)
{
if (!string.Equals(nid, "j4ViWNHEgww", StringComparison.Ordinal))
{
_consecutiveStrlenImports = 0;
_strlenPreludeLogged = false;
return;
}
_consecutiveStrlenImports++;
if (_strlenPreludeLogged || _consecutiveStrlenImports < 24)
{
return;
}
_strlenPreludeLogged = true;
List<string> list = GetRecentDistinctImportPrelude(maxCount: 5, skipNid: "j4ViWNHEgww");
if (list.Count == 0)
{
Console.Error.WriteLine($"[LOADER][WARNING] Import#{dispatchIndex}: detected strlen burst (count={_consecutiveStrlenImports}) ret=0x{returnRip:X16}; no prelude NIDs recorded.");
return;
}
Console.Error.WriteLine($"[LOADER][WARNING] Import#{dispatchIndex}: detected strlen burst (count={_consecutiveStrlenImports}) ret=0x{returnRip:X16}; last5_nids={string.Join(" -> ", list)}");
}
private List<string> GetRecentDistinctImportPrelude(int maxCount, string skipNid)
{
List<string> list = new List<string>(maxCount);
if (maxCount <= 0 || _distinctImportNidHistoryCount == 0)
{
return list;
}
HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
for (int i = 0; i < _distinctImportNidHistoryCount && list.Count < maxCount; i++)
{
int num = _distinctImportNidHistoryWriteIndex - 1 - i;
while (num < 0)
{
num += _distinctImportNidHistory.Length;
}
string text = _distinctImportNidHistory[num % _distinctImportNidHistory.Length];
if (string.IsNullOrWhiteSpace(text) || string.Equals(text, skipNid, StringComparison.Ordinal) || !hashSet.Add(text))
{
continue;
}
if (_moduleManager.TryGetExport(text, out ExportedFunction export))
{
list.Add($"{export.LibraryName}:{export.Name}({text})");
}
else
{
list.Add(text);
}
}
list.Reverse();
return list;
}
private static ulong StableHash64(string text)
{
ulong num = 14695981039346656037uL;
for (int i = 0; i < text.Length; i++)
{
num ^= text[i];
num *= 1099511628211uL;
}
return num;
}
private OrbisGen2Result DispatchKernelDynlibDlsym()
{
if (_cpuContext == null)
{
return OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
ulong symbolNameAddress = _cpuContext[CpuRegister.Rsi];
ulong outputAddress = _cpuContext[CpuRegister.Rdx];
if (!TryReadAsciiZ(symbolNameAddress, 512, out var symbolName))
{
_cpuContext[CpuRegister.Rax] = 18446744073709551615uL;
return OrbisGen2Result.ORBIS_GEN2_OK;
}
if (!TryResolveRuntimeSymbolAddress(symbolName, out var resolvedAddress))
{
_cpuContext[CpuRegister.Rax] = 18446744073709551615uL;
return OrbisGen2Result.ORBIS_GEN2_OK;
}
if (outputAddress == 0L || !TryWriteUInt64Compat(outputAddress, resolvedAddress))
{
_cpuContext[CpuRegister.Rax] = 18446744073709551615uL;
return OrbisGen2Result.ORBIS_GEN2_OK;
}
_cpuContext[CpuRegister.Rax] = 0uL;
return OrbisGen2Result.ORBIS_GEN2_OK;
}
private OrbisGen2Result DispatchBootstrapBridge()
{
if (_cpuContext == null)
{
return OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
ulong bridgeHandle = _cpuContext[CpuRegister.Rdi];
ulong symbolNameAddress = _cpuContext[CpuRegister.Rsi];
ulong outputAddress = _cpuContext[CpuRegister.Rdx];
_ = TryReadAsciiZ(symbolNameAddress, 512, out var symbolName);
OrbisGen2Result result = DispatchKernelDynlibDlsym();
if (result != OrbisGen2Result.ORBIS_GEN2_OK)
{
return result;
}
bool logBootstrap = string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_BOOTSTRAP"), "1", StringComparison.Ordinal);
if (logBootstrap)
{
Console.Error.WriteLine(
$"[LOADER][TRACE] bootstrap_dispatch: handle=0x{bridgeHandle:X16} symbol='{symbolName}' out=0x{outputAddress:X16} rax=0x{_cpuContext[CpuRegister.Rax]:X16}");
}
if (_cpuContext[CpuRegister.Rax] == 0uL)
{
return OrbisGen2Result.ORBIS_GEN2_OK;
}
Console.Error.WriteLine(
$"[LOADER][WARN] bootstrap_bridge unresolved: handle=0x{bridgeHandle:X} symbol='{symbolName}' out=0x{outputAddress:X16}");
return OrbisGen2Result.ORBIS_GEN2_OK;
}
private bool TryResolveRuntimeSymbolAddress(string symbolName, out ulong address)
{
address = 0uL;
if (string.IsNullOrWhiteSpace(symbolName))
{
return false;
}
if (_runtimeSymbolsByName.TryGetValue(symbolName, out var value) && IsRuntimeSymbolAddressUsable(value))
{
address = value;
return true;
}
if (symbolName.StartsWith("_", StringComparison.Ordinal) && _runtimeSymbolsByName.TryGetValue(symbolName[1..], out value) && IsRuntimeSymbolAddressUsable(value))
{
address = value;
return true;
}
if (_runtimeSymbolsByName.TryGetValue("_" + symbolName, out value) && IsRuntimeSymbolAddressUsable(value))
{
address = value;
return true;
}
return false;
}
private static bool IsRuntimeSymbolAddressUsable(ulong value)
{
return value != 0 && !IsUnresolvedSentinel(value);
}
private bool TryReadAsciiZ(ulong address, int maxLength, out string value)
{
value = string.Empty;
if (_cpuContext == null || address == 0L || maxLength <= 0)
{
return false;
}
List<byte> list = new List<byte>(Math.Min(maxLength, 256));
Span<byte> destination = stackalloc byte[1];
for (int i = 0; i < maxLength; i++)
{
if (!TryReadByteCompat(address + (ulong)i, destination))
{
return false;
}
if (destination[0] == 0)
{
value = System.Text.Encoding.ASCII.GetString(list.ToArray());
return true;
}
list.Add(destination[0]);
}
value = System.Text.Encoding.ASCII.GetString(list.ToArray());
return true;
}
private bool TryReadByteCompat(ulong address, Span<byte> destination)
{
if (_cpuContext == null || destination.Length == 0)
{
return false;
}
if (_cpuContext.Memory.TryRead(address, destination))
{
return true;
}
try
{
destination[0] = Marshal.ReadByte((nint)address);
return true;
}
catch
{
return false;
}
}
private bool TryWriteUInt64Compat(ulong address, ulong value)
{
if (_cpuContext == null || address == 0L)
{
return false;
}
if (_cpuContext.TryWriteUInt64(address, value))
{
return true;
}
try
{
Marshal.WriteInt64((nint)address, unchecked((long)value));
return true;
}
catch
{
return false;
}
}
private void TryBypassStackChkFailTrap(long dispatchIndex, ulong returnRip)
{
if (_cpuContext == null || returnRip < 32)
{
return;
}
try
{
byte[] array = new byte[19];
ulong num = returnRip - 23;
Marshal.Copy((nint)num, array, 0, array.Length);
if (array[0] != 117 || array[1] != 16 || array[2] != 72 || array[3] != 137 || array[4] != 216 || array[5] != 72 || array[6] != 131 || array[7] != 196 || array[9] != 91 || array[10] != 65 || array[11] != 92 || array[12] != 65 || array[13] != 94 || array[14] != 65 || array[15] != 95 || array[16] != 93 || array[17] != 195 || array[18] != 232)
{
return;
}
ulong value = returnRip - 21;
ulong address = _cpuContext[CpuRegister.Rsp];
if (_cpuContext.TryWriteUInt64(address, value))
{
if (_stackChkBypassSites.Add(num) && TryPatchStackChkFailBranch(num))
{
Console.Error.WriteLine($"[LOADER][WARNING] Import#{dispatchIndex}: patched stack_chk_fail tail branch at 0x{num:X16} -> NOP NOP");
}
Console.Error.WriteLine($"[LOADER][WARNING] Import#{dispatchIndex}: redirected __stack_chk_fail return to epilogue 0x{value:X16}");
}
}
catch
{
}
}
private unsafe static bool TryPatchStackChkFailBranch(ulong branchAddress)
{
uint flNewProtect = default(uint);
if (!VirtualProtect((void*)branchAddress, 2u, 64u, &flNewProtect))
{
return false;
}
try
{
if (Marshal.ReadByte((nint)branchAddress) != 117)
{
return false;
}
Marshal.WriteByte((nint)branchAddress, 144);
Marshal.WriteByte((nint)(branchAddress + 1), 144);
FlushInstructionCache(GetCurrentProcess(), (void*)branchAddress, 2u);
return true;
}
catch
{
return false;
}
finally
{
VirtualProtect((void*)branchAddress, 2u, flNewProtect, &flNewProtect);
}
}
private unsafe void TryPatchEa020eLookupCall(long dispatchIndex, ulong returnRip)
{
if (_patchedEa020eLookupCall || returnRip != 0x0000000800EA01A6uL)
{
return;
}
const ulong num = 0x0000000800EA020EuL;
nint num2 = unchecked((nint)num);
uint flNewProtect = default(uint);
try
{
if (Marshal.ReadByte(num2) != 232 || !VirtualProtect((void*)num, 5u, 64u, &flNewProtect))
{
return;
}
for (int i = 0; i < 5; i++)
{
Marshal.WriteByte(num2 + i, 144);
}
FlushInstructionCache(GetCurrentProcess(), (void*)num, 5u);
_patchedEa020eLookupCall = true;
Console.Error.WriteLine($"[LOADER][WARNING] Import#{dispatchIndex}: patched hash-lookup call at 0x{num:X16} -> NOP*5");
}
catch
{
}
finally
{
if (flNewProtect != 0)
{
VirtualProtect((void*)num, 5u, flNewProtect, &flNewProtect);
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,22 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
namespace SharpEmu.Core.Cpu.Native;
public interface INativeCpuBackend
{
string BackendName { get; }
string? LastError { get; }
bool TryExecute(
CpuContext context,
ulong entryPoint,
Generation generation,
IReadOnlyDictionary<ulong, string> importStubs,
IReadOnlyDictionary<string, ulong> runtimeSymbols,
CpuExecutionOptions executionOptions,
out OrbisGen2Result result);
}
+275
View File
@@ -0,0 +1,275 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Runtime.InteropServices;
namespace SharpEmu.Core.Cpu.Native;
public static unsafe class JitStubs
{
public const int XsaveBufferSize = 2688;
public const ulong XsaveChkGuard = 0xDeadBeef5533CCAAu;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct JmpWithIndex
{
private fixed byte _code[16];
public static ReadOnlySpan<byte> Template => new byte[]
{
0x68, 0x00, 0x00, 0x00, 0x00, // push <index>
0xE9, 0x00, 0x00, 0x00, 0x00, // jmp <handler>
0x90, 0x90, 0x90, 0x90, 0x90, 0x90 // nop padding
};
public static int Size => 16;
public void Initialize()
{
fixed (byte* code = _code)
{
Template.CopyTo(new Span<byte>(code, 16));
}
}
public void SetIndex(uint index)
{
fixed (byte* code = _code)
{
*(uint*)(code + 1) = index;
}
}
public void SetHandler(void* handler)
{
fixed (byte* code = _code)
{
var funcAddr = (long)handler;
var ripAddr = (long)(code + 10); // After push + jmp
var offset64 = funcAddr - ripAddr;
var offset32 = (uint)(ulong)offset64;
*(uint*)(code + 6) = offset32;
}
}
public byte* GetCodePointer()
{
fixed (byte* code = _code)
{
return code;
}
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Call9
{
private fixed byte _code[9];
public static ReadOnlySpan<byte> Template => new byte[]
{
0xE8, 0x00, 0x00, 0x00, 0x00, // call func
0x48, 0x89, 0xC0, // mov rax,rax
0x90 // nop
};
public static int Size => 9;
public void Initialize()
{
fixed (byte* code = _code)
{
Template.CopyTo(new Span<byte>(code, 9));
}
}
public void SetFunc(void* func)
{
fixed (byte* code = _code)
{
var funcAddr = (long)func;
var ripAddr = (long)(code + 5); // After call instruction
var offset64 = funcAddr - ripAddr;
var offset32 = (uint)(ulong)offset64;
*(uint*)(code + 1) = offset32;
}
}
public byte* GetCodePointer()
{
fixed (byte* code = _code)
{
return code;
}
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct JmpRax
{
private fixed byte _code[12];
public static ReadOnlySpan<byte> Template => new byte[]
{
0x48, 0xB8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // movabs rax,<addr>
0xFF, 0xE0 // jmp rax
};
public static int Size => 12;
public void Initialize()
{
fixed (byte* code = _code)
{
Template.CopyTo(new Span<byte>(code, 12));
}
}
public void SetTarget(void* target)
{
fixed (byte* code = _code)
{
*(ulong*)(code + 2) = (ulong)target;
}
}
public byte* GetCodePointer()
{
fixed (byte* code = _code)
{
return code;
}
}
}
public static class SafeCall
{
public static ReadOnlySpan<byte> Template => new byte[]
{
0x51,
0x52,
0x41, 0x50,
0x41, 0x51,
0x41, 0x52,
0x41, 0x53,
0x57,
0x56,
0x48, 0xBF, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
0x48, 0xBE, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
0x48, 0xB9, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
0xB0, 0x01,
0x86, 0x07,
0x84, 0xC0,
0x75, 0xF8,
0xB8, 0xFF, 0xFF, 0xFF, 0xFF,
0xBA, 0xFF, 0xFF, 0xFF, 0xFF,
0x0F, 0xAE, 0x26,
0x48, 0x83, 0xEC, 0x08,
0xFF, 0xD1,
0x48, 0x83, 0xC4, 0x08,
0x48, 0x89, 0xC1,
0xB8, 0xFF, 0xFF, 0xFF, 0xFF,
0xBA, 0xFF, 0xFF, 0xFF, 0xFF,
0x0F, 0xAE, 0x2E,
0x48, 0x89, 0xC8,
0xC6, 0x07, 0x00,
0x5E,
0x5F,
0x41, 0x5B,
0x41, 0x5A,
0x41, 0x59,
0x41, 0x58,
0x5A,
0x59,
0xC3
};
public static int Size => Template.Length; // 0x6c = 108 bytes
}
public static ReadOnlySpan<byte> TlsAccessPattern => new byte[]
{
0x64, 0x48, 0x8B, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00
};
public static void CreateJmpWithIndex(byte* location, uint index, void* handler)
{
var code = location;
code[0] = 0x68;
*(uint*)(code + 1) = index;
code[5] = 0xE9;
var handlerAddr = (long)handler;
var ripAddr = (long)(code + 10); // After push + jmp
var offset64 = handlerAddr - ripAddr;
*(uint*)(code + 6) = (uint)(ulong)offset64;
for (int i = 10; i < 16; i++)
{
code[i] = 0x90;
}
}
public static void CreateCall9(byte* location, void* func)
{
var code = location;
code[0] = 0xE8;
var funcAddr = (long)func;
var ripAddr = (long)(code + 5);
var offset64 = funcAddr - ripAddr;
*(uint*)(code + 1) = (uint)(ulong)offset64;
code[5] = 0x48;
code[6] = 0x89;
code[7] = 0xC0;
code[8] = 0x90;
}
public static void CreateJmpRax(byte* location, void* target)
{
var code = location;
code[0] = 0x48;
code[1] = 0xB8;
*(ulong*)(code + 2) = (ulong)target;
code[10] = 0xFF;
code[11] = 0xE0;
}
public static List<nint> FindTlsAccessPatterns(byte* start, int length)
{
var results = new List<nint>();
var pattern = TlsAccessPattern;
var end = start + length - pattern.Length;
for (var ptr = start; ptr < end; ptr++)
{
if (MatchesPattern(ptr, pattern))
{
results.Add((nint)ptr);
}
}
return results;
}
private static bool MatchesPattern(byte* ptr, ReadOnlySpan<byte> pattern)
{
for (var i = 0; i < pattern.Length; i++)
{
if (ptr[i] != pattern[i])
return false;
}
return true;
}
}
+222
View File
@@ -0,0 +1,222 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Runtime.InteropServices;
using SharpEmu.HLE;
namespace SharpEmu.Core.Cpu.Native;
public sealed unsafe class StubManager : IDisposable
{
private readonly List<nint> _allocatedStubs = new();
private readonly Dictionary<string, nint> _importHandlers = new();
private readonly Dictionary<ulong, nint> _stubAddresses = new();
private byte* _pltMemory;
private int _pltOffset;
private const int PltMemorySize = 1024 * 1024; // 1MB for stubs
public StubManager()
{
_pltMemory = (byte*)VirtualAlloc(
null,
(nuint)PltMemorySize,
AllocationType.Reserve | AllocationType.Commit,
MemoryProtection.ExecuteReadWrite);
if (_pltMemory == null)
{
throw new OutOfMemoryException("Failed to allocate executable memory for stubs");
}
_pltOffset = 0;
}
public nint CreateImportStub(ulong guestAddress, string nid, ImportHandler handler)
{
if (!_importHandlers.TryGetValue(nid, out var handlerPtr))
{
handlerPtr = CreateHandlerTrampoline(nid, handler);
_importHandlers[nid] = handlerPtr;
}
var stubPtr = _pltMemory + _pltOffset;
var stubIndex = (uint)(_stubAddresses.Count);
JitStubs.CreateJmpWithIndex(stubPtr, stubIndex, (void*)handlerPtr);
_pltOffset += JitStubs.JmpWithIndex.Size;
_stubAddresses[guestAddress] = (nint)stubPtr;
return (nint)stubPtr;
}
public nint CreatePltEntry(uint index, nint gotAddress)
{
var pltPtr = _pltMemory + _pltOffset;
var pltCode = new byte[]
{
0x49, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs r11,<addr>
0x41, 0xFF, 0x73, 0x08, // push QWORD PTR [r11+8]
0x41, 0xFF, 0x63, 0x10, // jmp QWORD PTR [r11+16]
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 // nop padding
};
*(ulong*)(pltCode.AsSpan().Slice(2).GetPinnableReference()) = (ulong)gotAddress;
fixed (byte* src = pltCode)
{
Buffer.MemoryCopy(src, pltPtr, pltCode.Length, pltCode.Length);
}
_pltOffset += pltCode.Length;
return (nint)pltPtr;
}
public nint CreateTlsHandler(delegate*<void> tlsGetAddrFunc)
{
var handlerPtr = _pltMemory + _pltOffset;
JitStubs.CreateCall9(handlerPtr, (void*)tlsGetAddrFunc);
_pltOffset += JitStubs.Call9.Size;
return (nint)handlerPtr;
}
public nint CreateSafeCallTrampoline(delegate*<void> func, byte* regSaveArea, byte* lockVar)
{
var trampolinePtr = _pltMemory + _pltOffset;
var template = JitStubs.SafeCall.Template;
fixed (byte* src = template)
{
Buffer.MemoryCopy(src, trampolinePtr, template.Length, template.Length);
}
*(ulong*)(trampolinePtr + 0x0c + 2) = (ulong)lockVar;
*(ulong*)(trampolinePtr + 0x16 + 2) = (ulong)regSaveArea;
*(ulong*)(trampolinePtr + 0x20 + 2) = (ulong)func;
_pltOffset += template.Length;
return (nint)trampolinePtr;
}
public bool TryGetStubAddress(ulong guestAddress, out nint hostAddress)
{
return _stubAddresses.TryGetValue(guestAddress, out hostAddress);
}
public bool PatchTlsAccess(byte* guestCode, int codeSize, nint tlsHandler)
{
var locations = JitStubs.FindTlsAccessPatterns(guestCode, codeSize);
foreach (var location in locations)
{
JitStubs.CreateCall9((byte*)location, (void*)tlsHandler);
}
return locations.Count > 0;
}
private nint CreateHandlerTrampoline(string nid, ImportHandler handler)
{
var trampolinePtr = _pltMemory + _pltOffset;
var code = new List<byte>();
code.Add(0x57);
code.Add(0x56);
code.Add(0x55);
code.Add(0x53);
code.AddRange(new byte[] { 0x41, 0x54 });
code.AddRange(new byte[] { 0x41, 0x55 });
code.AddRange(new byte[] { 0x41, 0x56 });
code.AddRange(new byte[] { 0x41, 0x57 });
code.AddRange(new byte[] { 0x48, 0x83, 0xEC, 0x28 });
int contextOffset = code.Count;
code.AddRange(new byte[] { 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
int handlerOffset = code.Count;
code.AddRange(new byte[] { 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
code.AddRange(new byte[] { 0xFF, 0xD0 });
code.AddRange(new byte[] { 0x48, 0x83, 0xC4, 0x28 });
code.AddRange(new byte[] { 0x41, 0x5F });
code.AddRange(new byte[] { 0x41, 0x5E });
code.AddRange(new byte[] { 0x41, 0x5D });
code.AddRange(new byte[] { 0x41, 0x5C });
code.Add(0x5B);
code.Add(0x5D);
code.Add(0x5E);
code.Add(0x5F);
code.Add(0xC3);
var codeArray = code.ToArray();
fixed (byte* src = codeArray)
{
Buffer.MemoryCopy(src, trampolinePtr, codeArray.Length, codeArray.Length);
}
*(ulong*)(trampolinePtr + contextOffset + 2) = 0;
var handlerPtr = Marshal.GetFunctionPointerForDelegate(handler);
*(ulong*)(trampolinePtr + handlerOffset + 2) = (ulong)handlerPtr;
_pltOffset += codeArray.Length;
_allocatedStubs.Add((nint)trampolinePtr);
return (nint)trampolinePtr;
}
public void Dispose()
{
if (_pltMemory != null)
{
VirtualFree(_pltMemory, 0, FreeType.Release);
_pltMemory = null;
}
_allocatedStubs.Clear();
_importHandlers.Clear();
_stubAddresses.Clear();
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern void* VirtualAlloc(void* lpAddress, nuint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool VirtualFree(void* lpAddress, nuint dwSize, FreeType dwFreeType);
[Flags]
private enum AllocationType : uint
{
Commit = 0x1000,
Reserve = 0x2000,
}
[Flags]
private enum MemoryProtection : uint
{
ExecuteReadWrite = 0x40,
}
private enum FreeType : uint
{
Release = 0x8000,
}
public delegate void ImportHandler(CpuContext context);
}
+11
View File
@@ -0,0 +1,11 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
namespace SharpEmu.Core.Cpu;
internal static class RuntimeStubNids
{
public const string BootstrapBridge = "__internal_bootstrap_bridge";
public const string KernelDynlibDlsym = "__internal_kernel_dynlib_dlsym";
}
+42
View File
@@ -0,0 +1,42 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
namespace SharpEmu.Core.Cpu;
public sealed class TrackedCpuMemory : ICpuMemory, ITrackedCpuMemory
{
private readonly ICpuMemory _inner;
public TrackedCpuMemory(ICpuMemory inner)
{
_inner = inner ?? throw new ArgumentNullException(nameof(inner));
}
public CpuMemoryAccessFailure? LastFailure { get; private set; }
public ICpuMemory Inner => _inner;
public bool TryRead(ulong virtualAddress, Span<byte> destination)
{
var result = _inner.TryRead(virtualAddress, destination);
if (!result)
{
LastFailure = new CpuMemoryAccessFailure(virtualAddress, destination.Length, isWrite: false);
}
return result;
}
public bool TryWrite(ulong virtualAddress, ReadOnlySpan<byte> source)
{
var result = _inner.TryWrite(virtualAddress, source);
if (!result)
{
LastFailure = new CpuMemoryAccessFailure(virtualAddress, source.Length, isWrite: true);
}
return result;
}
}