mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-25 20:28:48 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5256ab8dec |
@@ -723,6 +723,7 @@ public sealed partial class DirectExecutionBackend
|
|||||||
"Vo5V8KAwCmk" or // sceSystemServiceHideSplashScreen
|
"Vo5V8KAwCmk" or // sceSystemServiceHideSplashScreen
|
||||||
"TywrFKCoLGY" or // sceSaveDataInitialize3
|
"TywrFKCoLGY" or // sceSaveDataInitialize3
|
||||||
"dyIhnXq-0SM" or // sceSaveDataDirNameSearch
|
"dyIhnXq-0SM" or // sceSaveDataDirNameSearch
|
||||||
|
"ZP4e7rlzOUk" or // sceSaveDataMount3
|
||||||
"ERKzksauAJA" or // sceSaveDataDialogGetStatus
|
"ERKzksauAJA" or // sceSaveDataDialogGetStatus
|
||||||
"KK3Bdg1RWK0" or // sceSaveDataDialogUpdateStatus
|
"KK3Bdg1RWK0" or // sceSaveDataDialogUpdateStatus
|
||||||
"en7gNVnh878" or // sceSaveDataDialogIsReadyToDisplay
|
"en7gNVnh878" or // sceSaveDataDialogIsReadyToDisplay
|
||||||
|
|||||||
@@ -355,6 +355,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
|
|
||||||
public GuestThreadRunState State { get; set; }
|
public GuestThreadRunState State { get; set; }
|
||||||
|
|
||||||
|
public ulong ExitValue { get; set; }
|
||||||
|
|
||||||
public string? BlockReason { get; set; }
|
public string? BlockReason { get; set; }
|
||||||
|
|
||||||
public bool HasBlockedContinuation { get; set; }
|
public bool HasBlockedContinuation { get; set; }
|
||||||
@@ -1266,7 +1268,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
libraryName.IndexOf("Kernel", StringComparison.OrdinalIgnoreCase) >= 0;
|
libraryName.IndexOf("Kernel", StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool PreferLleForLibcExport(string exportName)
|
private bool PreferLleForLibcExport(string exportName)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(exportName))
|
if (string.IsNullOrWhiteSpace(exportName))
|
||||||
{
|
{
|
||||||
@@ -1287,6 +1289,10 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (IsLibcAllocatorExport(exportName))
|
||||||
|
{
|
||||||
|
return CanUseLleLibcAllocatorFamily();
|
||||||
|
}
|
||||||
if (string.Equals(value, "0", StringComparison.Ordinal))
|
if (string.Equals(value, "0", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -1298,6 +1304,51 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
return IsSafeLleLibcExport(exportName);
|
return IsSafeLleLibcExport(exportName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool CanUseLleLibcAllocatorFamily()
|
||||||
|
{
|
||||||
|
return HasUsableLleLibcExport("gQX+4GDQjpM", "malloc") &&
|
||||||
|
HasUsableLleLibcExport("tIhsqj0qsFE", "free") &&
|
||||||
|
HasUsableLleLibcExport("2X5agFjKxMc", "calloc") &&
|
||||||
|
HasUsableLleLibcExport("Y7aJ1uydPMo", "realloc") &&
|
||||||
|
HasUsableLleLibcExport("Ujf3KzMvRmI", "memalign") &&
|
||||||
|
HasUsableLleLibcExport("2Btkg8k24Zg", "aligned_alloc") &&
|
||||||
|
HasUsableLleLibcExport("cVSk9y8URbc", "posix_memalign");
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasUsableLleLibcExport(string nid, string exportName)
|
||||||
|
{
|
||||||
|
if (TryResolveRuntimeSymbolAddress(nid, out var address) && IsDirectImportTargetUsable(address))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var candidate in EnumerateRuntimeSymbolCandidates(exportName))
|
||||||
|
{
|
||||||
|
if (TryResolveRuntimeSymbolAddress(candidate, out address) && IsDirectImportTargetUsable(address))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsLibcAllocatorExport(string exportName)
|
||||||
|
{
|
||||||
|
return exportName switch
|
||||||
|
{
|
||||||
|
"malloc" or
|
||||||
|
"free" or
|
||||||
|
"calloc" or
|
||||||
|
"realloc" or
|
||||||
|
"memalign" or
|
||||||
|
"aligned_alloc" or
|
||||||
|
"posix_memalign" or
|
||||||
|
"malloc_usable_size" => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static bool IsSafeLleLibcExport(string exportName)
|
private static bool IsSafeLleLibcExport(string exportName)
|
||||||
{
|
{
|
||||||
return exportName switch
|
return exportName switch
|
||||||
@@ -2406,6 +2457,69 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
|
|
||||||
public bool SupportsGuestContextTransfer => true;
|
public bool SupportsGuestContextTransfer => true;
|
||||||
|
|
||||||
|
public bool TryJoinThread(
|
||||||
|
CpuContext callerContext,
|
||||||
|
ulong threadHandle,
|
||||||
|
out ulong returnValue,
|
||||||
|
out string? error)
|
||||||
|
{
|
||||||
|
returnValue = 0;
|
||||||
|
error = null;
|
||||||
|
if (threadHandle == 0)
|
||||||
|
{
|
||||||
|
error = "thread handle is zero";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threadHandle == GuestThreadExecution.CurrentGuestThreadHandle)
|
||||||
|
{
|
||||||
|
error = "thread cannot join itself";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!ActiveForcedGuestExit)
|
||||||
|
{
|
||||||
|
Thread? hostThread;
|
||||||
|
lock (_guestThreadGate)
|
||||||
|
{
|
||||||
|
if (!_guestThreads.TryGetValue(threadHandle, out var thread))
|
||||||
|
{
|
||||||
|
error = $"unknown guest thread 0x{threadHandle:X16}";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread.State == GuestThreadRunState.Exited)
|
||||||
|
{
|
||||||
|
returnValue = thread.ExitValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread.State == GuestThreadRunState.Faulted)
|
||||||
|
{
|
||||||
|
error =
|
||||||
|
$"guest thread 0x{threadHandle:X16} faulted: " +
|
||||||
|
(thread.BlockReason ?? "unknown error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hostThread = thread.HostThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostThread is not null &&
|
||||||
|
!ReferenceEquals(hostThread, Thread.CurrentThread))
|
||||||
|
{
|
||||||
|
hostThread.Join(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Thread.Sleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error = "guest execution stopped while joining thread";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public void Pump(CpuContext callerContext, string reason)
|
public void Pump(CpuContext callerContext, string reason)
|
||||||
{
|
{
|
||||||
_ = callerContext;
|
_ = callerContext;
|
||||||
@@ -3254,6 +3368,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
switch (exitReason)
|
switch (exitReason)
|
||||||
{
|
{
|
||||||
case GuestNativeCallExitReason.Returned:
|
case GuestNativeCallExitReason.Returned:
|
||||||
|
thread.ExitValue = thread.Context[CpuRegister.Rax];
|
||||||
thread.State = GuestThreadRunState.Exited;
|
thread.State = GuestThreadRunState.Exited;
|
||||||
break;
|
break;
|
||||||
case GuestNativeCallExitReason.Blocked:
|
case GuestNativeCallExitReason.Blocked:
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ public interface IGuestThreadScheduler
|
|||||||
|
|
||||||
bool TryStartThread(CpuContext creatorContext, GuestThreadStartRequest request, out string? error);
|
bool TryStartThread(CpuContext creatorContext, GuestThreadStartRequest request, out string? error);
|
||||||
|
|
||||||
|
bool TryJoinThread(
|
||||||
|
CpuContext callerContext,
|
||||||
|
ulong threadHandle,
|
||||||
|
out ulong returnValue,
|
||||||
|
out string? error);
|
||||||
|
|
||||||
void Pump(CpuContext callerContext, string reason);
|
void Pump(CpuContext callerContext, string reason);
|
||||||
|
|
||||||
int WakeBlockedThreads(string wakeKey, int maxCount = int.MaxValue);
|
int WakeBlockedThreads(string wakeKey, int maxCount = int.MaxValue);
|
||||||
|
|||||||
@@ -306,10 +306,6 @@ public static class KernelExports
|
|||||||
{
|
{
|
||||||
var threadId = ctx[CpuRegister.Rdi];
|
var threadId = ctx[CpuRegister.Rdi];
|
||||||
var returnValueAddress = ctx[CpuRegister.Rsi];
|
var returnValueAddress = ctx[CpuRegister.Rsi];
|
||||||
if (returnValueAddress != 0 && !ctx.TryWriteUInt64(returnValueAddress, 0))
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ShouldTracePthread())
|
if (ShouldTracePthread())
|
||||||
{
|
{
|
||||||
@@ -317,6 +313,30 @@ public static class KernelExports
|
|||||||
$"[LOADER][TRACE] pthread_join: thread=0x{threadId:X16} retval_out=0x{returnValueAddress:X16}");
|
$"[LOADER][TRACE] pthread_join: thread=0x{threadId:X16} retval_out=0x{returnValueAddress:X16}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var returnValue = 0UL;
|
||||||
|
if (GuestThreadExecution.Scheduler is { } scheduler &&
|
||||||
|
!scheduler.TryJoinThread(ctx, threadId, out returnValue, out var error))
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[LOADER][ERROR] pthread_join: thread=0x{threadId:X16}: {error}");
|
||||||
|
var result = string.Equals(
|
||||||
|
error,
|
||||||
|
"thread cannot join itself",
|
||||||
|
StringComparison.Ordinal)
|
||||||
|
? OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT
|
||||||
|
: OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||||
|
ctx[CpuRegister.Rax] = unchecked((ulong)(int)result);
|
||||||
|
return (int)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnValueAddress != 0 &&
|
||||||
|
!ctx.TryWriteUInt64(returnValueAddress, returnValue))
|
||||||
|
{
|
||||||
|
ctx[CpuRegister.Rax] =
|
||||||
|
unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
ctx[CpuRegister.Rax] = 0;
|
ctx[CpuRegister.Rax] = 0;
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,10 +99,12 @@ public static class KernelMemoryCompatExports
|
|||||||
private static readonly object _tlsGate = new();
|
private static readonly object _tlsGate = new();
|
||||||
private static readonly object _ioTraceGate = new();
|
private static readonly object _ioTraceGate = new();
|
||||||
private static readonly object _statCacheGate = new();
|
private static readonly object _statCacheGate = new();
|
||||||
|
private static readonly object _guestMountGate = new();
|
||||||
private static readonly Dictionary<ulong, DirectAllocation> _directAllocations = new();
|
private static readonly Dictionary<ulong, DirectAllocation> _directAllocations = new();
|
||||||
private static readonly Dictionary<ulong, LibcHeapAllocation> _libcAllocations = new();
|
private static readonly Dictionary<ulong, LibcHeapAllocation> _libcAllocations = new();
|
||||||
private static readonly Dictionary<ulong, MappedRegion> _mappedRegions = new();
|
private static readonly Dictionary<ulong, MappedRegion> _mappedRegions = new();
|
||||||
private static readonly Dictionary<ulong, ulong> _tlsModuleBlocks = new();
|
private static readonly Dictionary<ulong, ulong> _tlsModuleBlocks = new();
|
||||||
|
private static readonly Dictionary<string, string> _guestMounts = new(StringComparer.OrdinalIgnoreCase);
|
||||||
private static readonly HashSet<string> _tracedStatResults = new(StringComparer.Ordinal);
|
private static readonly HashSet<string> _tracedStatResults = new(StringComparer.Ordinal);
|
||||||
private static readonly HashSet<string> _negativeStatCache = new(StringComparer.OrdinalIgnoreCase);
|
private static readonly HashSet<string> _negativeStatCache = new(StringComparer.OrdinalIgnoreCase);
|
||||||
private static long _nextFileDescriptor = 2;
|
private static long _nextFileDescriptor = 2;
|
||||||
@@ -153,6 +155,32 @@ public static class KernelMemoryCompatExports
|
|||||||
private readonly record struct MappedRegion(ulong Address, ulong Length, int Protection, bool IsFlexible, bool IsDirect, ulong DirectStart);
|
private readonly record struct MappedRegion(ulong Address, ulong Length, int Protection, bool IsFlexible, bool IsDirect, ulong DirectStart);
|
||||||
private readonly record struct BatchMapEntry(ulong Start, ulong Offset, ulong Length, byte Protection, byte Type, int Operation);
|
private readonly record struct BatchMapEntry(ulong Start, ulong Offset, ulong Length, byte Protection, byte Type, int Operation);
|
||||||
|
|
||||||
|
public static void RegisterGuestPathMount(string guestMountPoint, string hostRoot)
|
||||||
|
{
|
||||||
|
ArgumentException.ThrowIfNullOrWhiteSpace(guestMountPoint);
|
||||||
|
ArgumentException.ThrowIfNullOrWhiteSpace(hostRoot);
|
||||||
|
|
||||||
|
var normalizedMountPoint = NormalizeGuestStatCachePath(guestMountPoint);
|
||||||
|
if (normalizedMountPoint is null || normalizedMountPoint == "/")
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Guest mount point must name a directory.", nameof(guestMountPoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedHostRoot = Path.GetFullPath(hostRoot);
|
||||||
|
Directory.CreateDirectory(normalizedHostRoot);
|
||||||
|
lock (_guestMountGate)
|
||||||
|
{
|
||||||
|
_guestMounts[normalizedMountPoint] = normalizedHostRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_statCacheGate)
|
||||||
|
{
|
||||||
|
_negativeStatCache.RemoveWhere(path =>
|
||||||
|
string.Equals(path, normalizedMountPoint, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
path.StartsWith(normalizedMountPoint + "/", StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal static bool TryAllocateHleData(
|
internal static bool TryAllocateHleData(
|
||||||
CpuContext ctx,
|
CpuContext ctx,
|
||||||
ulong length,
|
ulong length,
|
||||||
@@ -4033,6 +4061,11 @@ public static class KernelMemoryCompatExports
|
|||||||
return guestPath;
|
return guestPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TryResolveRegisteredGuestMount(guestPath, out var mountedPath))
|
||||||
|
{
|
||||||
|
return mountedPath;
|
||||||
|
}
|
||||||
|
|
||||||
if (guestPath.StartsWith("/devlog/app/", StringComparison.OrdinalIgnoreCase))
|
if (guestPath.StartsWith("/devlog/app/", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var relative = NormalizeMountRelativePath(guestPath["/devlog/app/".Length..]);
|
var relative = NormalizeMountRelativePath(guestPath["/devlog/app/".Length..]);
|
||||||
@@ -4131,6 +4164,51 @@ public static class KernelMemoryCompatExports
|
|||||||
return guestPath;
|
return guestPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryResolveRegisteredGuestMount(string guestPath, out string hostPath)
|
||||||
|
{
|
||||||
|
hostPath = string.Empty;
|
||||||
|
var normalizedGuestPath = NormalizeGuestStatCachePath(guestPath);
|
||||||
|
if (normalizedGuestPath is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string? matchedMountPoint = null;
|
||||||
|
string? matchedHostRoot = null;
|
||||||
|
lock (_guestMountGate)
|
||||||
|
{
|
||||||
|
foreach (var (mountPoint, hostRoot) in _guestMounts)
|
||||||
|
{
|
||||||
|
if ((string.Equals(normalizedGuestPath, mountPoint, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
normalizedGuestPath.StartsWith(mountPoint + "/", StringComparison.OrdinalIgnoreCase)) &&
|
||||||
|
(matchedMountPoint is null || mountPoint.Length > matchedMountPoint.Length))
|
||||||
|
{
|
||||||
|
matchedMountPoint = mountPoint;
|
||||||
|
matchedHostRoot = hostRoot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchedMountPoint is null || matchedHostRoot is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var relativePath = normalizedGuestPath[matchedMountPoint.Length..].TrimStart('/');
|
||||||
|
var candidate = Path.GetFullPath(Path.Combine(
|
||||||
|
matchedHostRoot,
|
||||||
|
NormalizeMountRelativePath(relativePath)));
|
||||||
|
var rootWithSeparator = Path.TrimEndingDirectorySeparator(matchedHostRoot) + Path.DirectorySeparatorChar;
|
||||||
|
if (!string.Equals(candidate, matchedHostRoot, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!candidate.StartsWith(rootWithSeparator, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hostPath = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static string? ResolveApp0Root()
|
private static string? ResolveApp0Root()
|
||||||
{
|
{
|
||||||
var cached = Volatile.Read(ref _cachedApp0Root);
|
var cached = Volatile.Read(ref _cachedApp0Root);
|
||||||
|
|||||||
Reference in New Issue
Block a user