diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs index 596609c..04b567b 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs @@ -358,12 +358,21 @@ public sealed partial class DirectExecutionBackend } orbisGen2Result = (OrbisGen2Result)returnValue; } - else - { - dispatchResolved = false; - orbisGen2Result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND; - cpuContext[CpuRegister.Rax] = unchecked((ulong)(int)orbisGen2Result); - } + else if (importStubEntry.Export is { } mismatchedExport) + { + dispatchResolved = true; + orbisGen2Result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED; + cpuContext[CpuRegister.Rax] = unchecked((ulong)(int)orbisGen2Result); + Console.Error.WriteLine( + $"[LOADER][WARN] Import#{num} not implemented for generation {cpuContext.TargetGeneration}: " + + $"nid={importStubEntry.Nid} targets={mismatchedExport.Target} ret=0x{num7:X16}"); + } + else + { + dispatchResolved = false; + orbisGen2Result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND; + cpuContext[CpuRegister.Rax] = unchecked((ulong)(int)orbisGen2Result); + } } finally { diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs index b3e56f6..c7a9839 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs @@ -1679,10 +1679,14 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I ptr2[num++] = 65; ptr2[num++] = 95; ptr2[num++] = 195; - uint num2 = default(uint); - VirtualProtect(ptr, 192u, 32u, &num2); - FlushInstructionCache(GetCurrentProcess(), ptr, 192u); - return (nint)ptr; + uint num2 = default(uint); + if (!VirtualProtect(ptr, 192u, 32u, &num2)) + { + Console.Error.WriteLine($"[LOADER][ERROR] VirtualProtect failed for import dispatch stub at 0x{(nint)ptr:X16}"); + return 0; + } + FlushInstructionCache(GetCurrentProcess(), ptr, 192u); + return (nint)ptr; } catch { @@ -1763,7 +1767,11 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I tlsHandlerAddress[num++] = 195; _tlsPatchStubOffset = (num + 15) & ~15; uint num2 = default(uint); - VirtualProtect((void*)_tlsHandlerAddress, TlsHandlerRegionSize, 32u, &num2); + if (!VirtualProtect((void*)_tlsHandlerAddress, TlsHandlerRegionSize, 32u, &num2)) + { + Console.Error.WriteLine($"[LOADER][ERROR] VirtualProtect failed for TLS handler at 0x{_tlsHandlerAddress:X16}"); + return; + } FlushInstructionCache(GetCurrentProcess(), (void*)_tlsHandlerAddress, TlsHandlerRegionSize); Console.Error.WriteLine($"[LOADER][INFO] TLS handler at 0x{_tlsHandlerAddress:X16}"); } @@ -1784,7 +1792,11 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I ptr2[i] = 144; } uint num = default(uint); - VirtualProtect(ptr, 4096u, 32u, &num); + if (!VirtualProtect(ptr, 4096u, 32u, &num)) + { + Console.Error.WriteLine($"[LOADER][ERROR] VirtualProtect failed for unresolved return stub at 0x{(nint)ptr:X16}"); + return 0; + } FlushInstructionCache(GetCurrentProcess(), ptr, 16u); return (nint)ptr; } @@ -1831,7 +1843,11 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I EmitByte(code, ref offset, 0xC3); uint oldProtect = default; - VirtualProtect(ptr, stubSize, 32u, &oldProtect); + if (!VirtualProtect(ptr, stubSize, 32u, &oldProtect)) + { + Console.Error.WriteLine($"[LOADER][ERROR] VirtualProtect failed for guest return stub at 0x{(nint)ptr:X16}"); + return 0; + } FlushInstructionCache(GetCurrentProcess(), ptr, (nuint)offset); return (nint)ptr; } @@ -1923,7 +1939,11 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I *(int*)(code + guestRestoreJump) = restoreOffset - (guestRestoreJump + sizeof(int)); uint oldProtect = default; - VirtualProtect(ptr, stubSize, 32u, &oldProtect); + if (!VirtualProtect(ptr, stubSize, 32u, &oldProtect)) + { + Console.Error.WriteLine($"[LOADER][ERROR] VirtualProtect failed for exception handler trampoline at 0x{(nint)ptr:X16}"); + return 0; + } FlushInstructionCache(GetCurrentProcess(), ptr, (nuint)offset); return (nint)ptr; } @@ -2254,7 +2274,11 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I ptr[num2++] = 144; } uint flNewProtect = default(uint); - VirtualProtect((void*)num, 32u, 32u, &flNewProtect); + if (!VirtualProtect((void*)num, 32u, 32u, &flNewProtect)) + { + Console.Error.WriteLine($"[LOADER][ERROR] VirtualProtect failed for TLS store helper at 0x{num:X16}"); + return 0; + } FlushInstructionCache(GetCurrentProcess(), (void*)num, 32u); return num; } @@ -3591,7 +3615,11 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I return GuestNativeCallExitReason.Exception; } uint oldProtect = default(uint); - VirtualProtect(ptr, stubSize, 64u, &oldProtect); + if (!VirtualProtect(ptr, stubSize, 64u, &oldProtect)) + { + reason = $"VirtualProtect failed for guest thread entry stub at 0x{(nint)ptr:X16}"; + return GuestNativeCallExitReason.Exception; + } FlushInstructionCache(GetCurrentProcess(), ptr, stubSize); TlsSetValue(_hostRspSlotTlsIndex, (nint)hostRspSlot); ActiveGuestThreadYieldRequested = false; @@ -3728,7 +3756,11 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I return GuestNativeCallExitReason.Exception; } uint oldProtect = default(uint); - VirtualProtect(ptr, stubSize, 64u, &oldProtect); + if (!VirtualProtect(ptr, stubSize, 64u, &oldProtect)) + { + reason = $"VirtualProtect failed for guest continuation stub at 0x{(nint)ptr:X16}"; + return GuestNativeCallExitReason.Exception; + } FlushInstructionCache(GetCurrentProcess(), ptr, stubSize); TlsSetValue(_hostRspSlotTlsIndex, (nint)hostRspSlot); ActiveGuestThreadYieldRequested = false; @@ -3988,7 +4020,12 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I return false; } uint num5 = default(uint); - VirtualProtect(ptr, stubSize, 64u, &num5); + if (!VirtualProtect(ptr, stubSize, 64u, &num5)) + { + LastError = $"VirtualProtect failed for guest entry stub at 0x{(nint)ptr:X16}"; + result = OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT; + return false; + } FlushInstructionCache(GetCurrentProcess(), ptr, stubSize); if (_hostRspSlotStorage != 0) { diff --git a/src/SharpEmu.Core/Memory/PhysicalVirtualMemory.cs b/src/SharpEmu.Core/Memory/PhysicalVirtualMemory.cs index 7d7b4b2..0a15ae1 100644 --- a/src/SharpEmu.Core/Memory/PhysicalVirtualMemory.cs +++ b/src/SharpEmu.Core/Memory/PhysicalVirtualMemory.cs @@ -55,7 +55,11 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA private static extern nuint VirtualQuery(void* lpAddress, out MemoryBasicInformation64 lpBuffer, nuint dwLength); [DllImport("kernel32.dll")] - private static extern void FlushInstructionCache(void* hProcess, void* lpBaseAddress, nuint dwSize); + private static extern void* GetCurrentProcess(); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool FlushInstructionCache(void* hProcess, void* lpBaseAddress, nuint dwSize); public bool TryAllocateAtExact(ulong desiredAddress, ulong size, bool executable, out ulong actualAddress) { @@ -451,7 +455,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA if ((flags & ProgramHeaderFlags.Execute) != 0) { - FlushInstructionCache(null, (void*)address, (nuint)size); + FlushInstructionCache(GetCurrentProcess(), (void*)address, (nuint)size); } } @@ -699,7 +703,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA VirtualProtect(destPtr, (nuint)source.Length, oldProtect, out _); if (IsExecutableProtection(oldProtect)) { - FlushInstructionCache(null, destPtr, (nuint)source.Length); + FlushInstructionCache(GetCurrentProcess(), destPtr, (nuint)source.Length); } } diff --git a/src/SharpEmu.HLE/ModuleManager.cs b/src/SharpEmu.HLE/ModuleManager.cs index a899634..aaf34b6 100644 --- a/src/SharpEmu.HLE/ModuleManager.cs +++ b/src/SharpEmu.HLE/ModuleManager.cs @@ -47,6 +47,7 @@ public sealed class ModuleManager : IModuleManager var handler = CreateHandler(type, method, instances); if (!_dispatchTable.TryAdd(exportInfo.Value.Nid, handler)) { + Console.Error.WriteLine($"[HLE] Duplicate NID '{exportInfo.Value.Nid}' ({exportInfo.Value.ExportName}) — already registered, skipping."); continue; } @@ -105,6 +106,7 @@ public sealed class ModuleManager : IModuleManager if (!_dispatchTable.TryGetValue(nid, out var function) || !_exportTable.TryGetValue(nid, out var export)) { + Console.Error.WriteLine($"[HLE] NID '{nid}' not found in dispatch table."); context[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND); result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND; return false; @@ -112,11 +114,13 @@ public sealed class ModuleManager : IModuleManager if ((export.Target & context.TargetGeneration) == 0) { - context[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND); - result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND; + Console.Error.WriteLine($"[HLE] NID '{nid}' ({export.Name}) found but not implemented for generation {context.TargetGeneration} (targets: {export.Target})."); + context[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED); + result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED; return false; } + context.ClearRaxWriteFlag(); int ret = ((SysAbiFunction)function).Invoke(context); diff --git a/src/SharpEmu.Libs/Agc/AgcExports.cs b/src/SharpEmu.Libs/Agc/AgcExports.cs index 1ce98b6..d1de0dc 100644 --- a/src/SharpEmu.Libs/Agc/AgcExports.cs +++ b/src/SharpEmu.Libs/Agc/AgcExports.cs @@ -58,6 +58,8 @@ public static class AgcExports private const uint SpiShaderPgmHiEs = 0xC9; private const uint SpiShaderPgmLoLs = 0x148; private const uint SpiShaderPgmHiLs = 0x149; + private const uint SpiShaderPgmLoGs = 0x8A; + private const uint SpiShaderPgmHiGs = 0x8B; private const uint SpiPsInputEna = 0x1B3; private const uint SpiPsInputAddr = 0x1B4; private const uint ComputePgmLo = 0x20C; @@ -115,6 +117,7 @@ public static class AgcExports private const uint RegisterDefaultsVersion7 = 7; private const uint RegisterDefaultsVersion8 = 8; private const uint RegisterDefaultsVersion10 = 10; + private const uint RegisterDefaultsVersion13 = 13; private const int RegisterDefaultsSize = 0x40; private const int RegisterDefaultBlockSize = 16 * 8; @@ -5081,6 +5084,7 @@ public static class AgcExports 0 => ComputePgmLo, 1 => SpiShaderPgmLoPs, 2 or 6 => SpiShaderPgmLoEs, + 4 => SpiShaderPgmLoGs, 7 => SpiShaderPgmLoLs, _ => 0u, }; @@ -5089,6 +5093,7 @@ public static class AgcExports 0 => ComputePgmHi, 1 => SpiShaderPgmHiPs, 2 or 6 => SpiShaderPgmHiEs, + 4 => SpiShaderPgmHiGs, 7 => SpiShaderPgmHiLs, _ => 0u, }; @@ -5105,7 +5110,7 @@ public static class AgcExports } private static bool IsEsGeometryShaderType(byte shaderType) => - shaderType is 2 or 6; + shaderType is 2 or 4 or 6; private static int SetIndirectPatchAddress(CpuContext ctx, string registerSpace) { @@ -5252,7 +5257,8 @@ public static class AgcExports return version is RegisterDefaultsVersion7 or RegisterDefaultsVersion8 or - RegisterDefaultsVersion10; + RegisterDefaultsVersion10 or + RegisterDefaultsVersion13; } private static bool TryGetRegisterDefaultsAllocation( diff --git a/src/SharpEmu.Libs/Kernel/KernelExports.cs b/src/SharpEmu.Libs/Kernel/KernelExports.cs index 2edb2e8..cba3c7e 100644 --- a/src/SharpEmu.Libs/Kernel/KernelExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelExports.cs @@ -8,7 +8,6 @@ namespace SharpEmu.Libs.Kernel; public static class KernelExports { - private static int _nextFileDescriptor = 2; private static readonly object _cxaGate = new(); private static readonly List _cxaDestructors = new(); private static readonly object _coredumpGate = new(); diff --git a/src/SharpEmu.Libs/Kernel/KernelPthreadCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelPthreadCompatExports.cs index 12f1089..f9b00a0 100644 --- a/src/SharpEmu.Libs/Kernel/KernelPthreadCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelPthreadCompatExports.cs @@ -329,6 +329,12 @@ public static class KernelPthreadCompatExports Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")] public static int PosixPthreadCondSignal(CpuContext ctx) => PthreadCondSignalCore(ctx, ctx[CpuRegister.Rdi], broadcast: false); + [SysAbiExport( + Nid = "27bAgiJmOh0", + ExportName = "pthread_cond_timedwait", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libKernel")] + public static int PosixPthreadCondTimedwait(CpuContext ctx) => PthreadCondWaitCore(ctx, ctx[CpuRegister.Rdi], ctx[CpuRegister.Rsi], timed: true, timeoutUsec: unchecked((uint)ctx[CpuRegister.Rdx])); [SysAbiExport( Nid = "m5-2bsNfv7s",