using System; using System.Runtime.InteropServices; using System.Runtime.Versioning; namespace Ryujinx.Memory { [SupportedOSPlatform("ios")] static unsafe partial class MachJitWorkaround { [LibraryImport("libc")] public static partial int mach_task_self(); [LibraryImport("libc")] public static partial int mach_make_memory_entry_64(IntPtr target_task, IntPtr* size, IntPtr offset, int permission, IntPtr* object_handle, IntPtr parent_entry); [LibraryImport("libc")] public static partial int mach_memory_entry_ownership(IntPtr mem_entry, IntPtr owner, int ledger_tag, int ledger_flags); [LibraryImport("libc")] public static partial int vm_map(IntPtr target_task, IntPtr* address, IntPtr size, IntPtr mask, int flags, IntPtr obj, IntPtr offset, int copy, int cur_protection, int max_protection, int inheritance); [LibraryImport("libc")] public static partial int vm_allocate(IntPtr target_task, IntPtr* address, IntPtr size, int flags); [LibraryImport("libc")] public static partial int vm_deallocate(IntPtr target_task, IntPtr address, IntPtr size); [LibraryImport("libc")] public static partial int vm_remap(IntPtr target_task, IntPtr* target_address, IntPtr size, IntPtr mask, int flags, IntPtr src_task, IntPtr src_address, int copy, int* cur_protection, int* max_protection, int inheritance); private static class Flags { public const int MAP_MEM_LEDGER_TAGGED = 0x002000; public const int MAP_MEM_NAMED_CREATE = 0x020000; public const int VM_PROT_READ = 0x01; public const int VM_PROT_WRITE = 0x02; public const int VM_PROT_EXECUTE = 0x04; public const int VM_LEDGER_TAG_DEFAULT = 0x00000001; public const int VM_LEDGER_FLAG_NO_FOOTPRINT = 0x00000001; public const int VM_INHERIT_COPY = 1; public const int VM_INHERIT_DEFAULT = VM_INHERIT_COPY; public const int VM_FLAGS_FIXED = 0x0000; public const int VM_FLAGS_ANYWHERE = 0x0001; public const int VM_FLAGS_OVERWRITE = 0x4000; } private const IntPtr TASK_NULL = 0; private static readonly IntPtr _selfTask; private static readonly int DEFAULT_CHUNK_SIZE = 1024 * 1024; static MachJitWorkaround() { _selfTask = mach_task_self(); } private static int CalculateOptimalChunkSize(int totalSize) { // Dynamically calculate chunk size based on total allocation size // For smaller allocations, use smaller chunks to avoid waste if (totalSize <= DEFAULT_CHUNK_SIZE) { return totalSize; } int chunkCount = Math.Max(4, totalSize / DEFAULT_CHUNK_SIZE); return (totalSize + chunkCount - 1) / chunkCount; } private static void HandleMachError(int error, string operation) { if (error != 0) { throw new InvalidOperationException($"Mach operation '{operation}' failed with error: {error}"); } } private static IntPtr ReallocateBlock(IntPtr address, int size) { IntPtr memorySize = (IntPtr)size; IntPtr memoryObjectPort = IntPtr.Zero; try { // Create memory entry HandleMachError( mach_make_memory_entry_64( _selfTask, &memorySize, IntPtr.Zero, Flags.MAP_MEM_NAMED_CREATE | Flags.MAP_MEM_LEDGER_TAGGED | Flags.VM_PROT_READ | Flags.VM_PROT_WRITE | Flags.VM_PROT_EXECUTE, &memoryObjectPort, IntPtr.Zero), "make_memory_entry_64"); if (memorySize != (IntPtr)size) { throw new InvalidOperationException($"Memory allocation size mismatch. Requested: {size}, Allocated: {(long)memorySize}"); } // Set ownership HandleMachError( mach_memory_entry_ownership( memoryObjectPort, TASK_NULL, Flags.VM_LEDGER_TAG_DEFAULT, Flags.VM_LEDGER_FLAG_NO_FOOTPRINT), "memory_entry_ownership"); IntPtr mapAddress = address; // Map memory HandleMachError( vm_map( _selfTask, &mapAddress, memorySize, IntPtr.Zero, Flags.VM_FLAGS_OVERWRITE, memoryObjectPort, IntPtr.Zero, 0, Flags.VM_PROT_READ | Flags.VM_PROT_WRITE, Flags.VM_PROT_READ | Flags.VM_PROT_WRITE | Flags.VM_PROT_EXECUTE, Flags.VM_INHERIT_COPY), "vm_map"); if (address != mapAddress) { throw new InvalidOperationException("Memory mapping address mismatch"); } return mapAddress; } finally { // Proper cleanup of memory object port if (memoryObjectPort != IntPtr.Zero) { // mach_port_deallocate(_selfTask, memoryObjectPort); } } } public static void ReallocateAreaWithOwnership(IntPtr address, int size) { int chunkSize = CalculateOptimalChunkSize(size); IntPtr currentAddress = address; IntPtr endAddress = address + size; while (currentAddress < endAddress) { int blockSize = Math.Min(chunkSize, (int)(endAddress - currentAddress)); ReallocateBlock(currentAddress, blockSize); currentAddress += blockSize; } } public static IntPtr AllocateSharedMemory(ulong size, bool reserve) { IntPtr address = IntPtr.Zero; HandleMachError( vm_allocate(_selfTask, &address, (IntPtr)size, Flags.VM_FLAGS_ANYWHERE), "vm_allocate"); return address; } public static void DestroySharedMemory(IntPtr handle, ulong size) { if (handle != IntPtr.Zero && size > 0) { vm_deallocate(_selfTask, handle, (IntPtr)size); } } public static IntPtr MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, ulong size) { if (size == 0 || sharedMemory == IntPtr.Zero) { throw new ArgumentException("Invalid mapping parameters"); } IntPtr srcAddress = (IntPtr)((ulong)sharedMemory + srcOffset); IntPtr dstAddress = location; int curProtection = 0; int maxProtection = 0; HandleMachError( vm_remap( _selfTask, &dstAddress, (IntPtr)size, IntPtr.Zero, Flags.VM_FLAGS_OVERWRITE, _selfTask, srcAddress, 0, &curProtection, &maxProtection, Flags.VM_INHERIT_DEFAULT), "vm_remap"); return dstAddress; } public static void UnmapView(IntPtr location, ulong size) { if (location != IntPtr.Zero && size > 0) { vm_deallocate(_selfTask, location, (IntPtr)size); } } } }