mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-14 12:56:14 +08:00
[core] Update native execution and kernel exports, phtread improvement (#13)
This commit is contained in:
@@ -723,6 +723,7 @@ public sealed partial class DirectExecutionBackend
|
||||
"Vo5V8KAwCmk" or // sceSystemServiceHideSplashScreen
|
||||
"TywrFKCoLGY" or // sceSaveDataInitialize3
|
||||
"dyIhnXq-0SM" or // sceSaveDataDirNameSearch
|
||||
"ZP4e7rlzOUk" or // sceSaveDataMount3
|
||||
"ERKzksauAJA" or // sceSaveDataDialogGetStatus
|
||||
"KK3Bdg1RWK0" or // sceSaveDataDialogUpdateStatus
|
||||
"en7gNVnh878" or // sceSaveDataDialogIsReadyToDisplay
|
||||
|
||||
@@ -355,6 +355,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
|
||||
public GuestThreadRunState State { get; set; }
|
||||
|
||||
public ulong ExitValue { get; set; }
|
||||
|
||||
public string? BlockReason { 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;
|
||||
}
|
||||
|
||||
private static bool PreferLleForLibcExport(string exportName)
|
||||
private bool PreferLleForLibcExport(string exportName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(exportName))
|
||||
{
|
||||
@@ -1287,6 +1289,10 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (IsLibcAllocatorExport(exportName))
|
||||
{
|
||||
return CanUseLleLibcAllocatorFamily();
|
||||
}
|
||||
if (string.Equals(value, "0", StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
@@ -1298,6 +1304,51 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
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)
|
||||
{
|
||||
return exportName switch
|
||||
@@ -2406,6 +2457,69 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
|
||||
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)
|
||||
{
|
||||
_ = callerContext;
|
||||
@@ -3254,6 +3368,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
switch (exitReason)
|
||||
{
|
||||
case GuestNativeCallExitReason.Returned:
|
||||
thread.ExitValue = thread.Context[CpuRegister.Rax];
|
||||
thread.State = GuestThreadRunState.Exited;
|
||||
break;
|
||||
case GuestNativeCallExitReason.Blocked:
|
||||
|
||||
@@ -29,6 +29,12 @@ public interface IGuestThreadScheduler
|
||||
|
||||
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);
|
||||
|
||||
int WakeBlockedThreads(string wakeKey, int maxCount = int.MaxValue);
|
||||
|
||||
@@ -306,10 +306,6 @@ public static class KernelExports
|
||||
{
|
||||
var threadId = ctx[CpuRegister.Rdi];
|
||||
var returnValueAddress = ctx[CpuRegister.Rsi];
|
||||
if (returnValueAddress != 0 && !ctx.TryWriteUInt64(returnValueAddress, 0))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
if (ShouldTracePthread())
|
||||
{
|
||||
@@ -317,6 +313,30 @@ public static class KernelExports
|
||||
$"[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;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
@@ -99,10 +99,12 @@ public static class KernelMemoryCompatExports
|
||||
private static readonly object _tlsGate = new();
|
||||
private static readonly object _ioTraceGate = 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, LibcHeapAllocation> _libcAllocations = new();
|
||||
private static readonly Dictionary<ulong, MappedRegion> _mappedRegions = 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> _negativeStatCache = new(StringComparer.OrdinalIgnoreCase);
|
||||
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 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(
|
||||
CpuContext ctx,
|
||||
ulong length,
|
||||
@@ -4033,6 +4061,11 @@ public static class KernelMemoryCompatExports
|
||||
return guestPath;
|
||||
}
|
||||
|
||||
if (TryResolveRegisteredGuestMount(guestPath, out var mountedPath))
|
||||
{
|
||||
return mountedPath;
|
||||
}
|
||||
|
||||
if (guestPath.StartsWith("/devlog/app/", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var relative = NormalizeMountRelativePath(guestPath["/devlog/app/".Length..]);
|
||||
@@ -4131,6 +4164,51 @@ public static class KernelMemoryCompatExports
|
||||
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()
|
||||
{
|
||||
var cached = Volatile.Read(ref _cachedApp0Root);
|
||||
|
||||
Reference in New Issue
Block a user