// Copyright (C) 2026 SharpEmu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later using SharpEmu.HLE; using System; using System.Collections.Concurrent; using System.Reflection; namespace SharpEmu.Libs.Kernel; internal static class KernelVirtualRangeAllocator { private static readonly ConcurrentDictionary _accessors = new(); public static bool TryReserve( CpuContext ctx, ulong desiredAddress, ulong length, bool executable, ulong alignment, bool allowSearch, bool allowAllocateAtAlternative, string traceName, out ulong mappedAddress) { mappedAddress = 0; if (length == 0) { return false; } try { if (!TryResolveAccessor(ctx.Memory, out var target, out var accessor)) { Console.Error.WriteLine($"[LOADER][TRACE] {traceName}: AllocateAt missing on {ctx.Memory.GetType().FullName}"); return false; } if (allowSearch && accessor.AllocateAtOrAbove is not null) { var searchArgs = new object[] { desiredAddress, length, executable, alignment, 0UL }; var searchResult = accessor.AllocateAtOrAbove.Invoke(target, searchArgs); if (searchResult is bool trueValue && trueValue && searchArgs[4] is ulong searchedAddress && searchedAddress != 0) { mappedAddress = searchedAddress; return true; } } if (accessor.AllocateAt is null) { Console.Error.WriteLine($"[LOADER][TRACE] {traceName}: AllocateAt missing on {target.GetType().FullName}"); return false; } var invokeArgs = accessor.AllocateAtHasAllowAlternativeArg ? new object[] { desiredAddress, length, executable, allowAllocateAtAlternative } : new object[] { desiredAddress, length, executable }; var result = accessor.AllocateAt.Invoke(target, invokeArgs); if (result is not ulong allocated || allocated == 0) { var resultType = result?.GetType().FullName ?? "null"; Console.Error.WriteLine($"[LOADER][TRACE] {traceName}: AllocateAt returned {resultType} value={result ?? "null"}"); return false; } mappedAddress = allocated; return true; } catch { Console.Error.WriteLine($"[LOADER][TRACE] {traceName}: AllocateAt invocation threw"); return false; } } private static bool TryResolveAccessor(object rootMemory, out object target, out Accessor accessor) { target = rootMemory; accessor = default; for (var depth = 0; depth < 4; depth++) { accessor = _accessors.GetOrAdd(target.GetType(), DiscoverAccessor); if (accessor.AllocateAt is not null || accessor.AllocateAtOrAbove is not null) { return true; } if (accessor.InnerProperty is null) { break; } var innerValue = accessor.InnerProperty.GetValue(target); if (innerValue is null || ReferenceEquals(innerValue, target)) { break; } target = innerValue; } return false; } private static Accessor DiscoverAccessor(Type type) { MethodInfo? allocateAt = null; MethodInfo? allocateAtOrAbove = null; var allocateAtHasAllowAlternativeArg = false; foreach (var candidate in type.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { var parameters = candidate.GetParameters(); if (string.Equals(candidate.Name, "TryAllocateAtOrAbove", StringComparison.Ordinal) && parameters.Length == 5 && parameters[0].ParameterType == typeof(ulong) && parameters[1].ParameterType == typeof(ulong) && parameters[2].ParameterType == typeof(bool) && parameters[3].ParameterType == typeof(ulong) && parameters[4].ParameterType == typeof(ulong).MakeByRefType()) { allocateAtOrAbove = candidate; } else if (string.Equals(candidate.Name, "AllocateAt", StringComparison.Ordinal)) { if (parameters.Length == 3 && parameters[0].ParameterType == typeof(ulong) && parameters[1].ParameterType == typeof(ulong) && parameters[2].ParameterType == typeof(bool)) { allocateAt = candidate; allocateAtHasAllowAlternativeArg = false; } else if (parameters.Length == 4 && parameters[0].ParameterType == typeof(ulong) && parameters[1].ParameterType == typeof(ulong) && parameters[2].ParameterType == typeof(bool) && parameters[3].ParameterType == typeof(bool)) { allocateAt = candidate; allocateAtHasAllowAlternativeArg = true; } } } var innerProperty = type.GetProperty("Inner", BindingFlags.Public | BindingFlags.Instance); return new Accessor(allocateAt, allocateAtOrAbove, allocateAtHasAllowAlternativeArg, innerProperty); } private readonly record struct Accessor( MethodInfo? AllocateAt, MethodInfo? AllocateAtOrAbove, bool AllocateAtHasAllowAlternativeArg, PropertyInfo? InnerProperty); }