1919#if defined(_M_ARM_64) && defined(__APPLE__)
2020#include < pthread.h>
2121#endif
22+ #if defined(__APPLE__) && defined(__aarch64__)
23+ #include < mach/mach.h>
24+ #endif
2225#if defined __APPLE__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
2326#include < sys/sysctl.h>
2427#elif defined __HAIKU__
3033
3134#ifdef IPHONEOS
3235#include " Common/JITMemoryTracker.h"
36+ #ifdef __LIBRETRO__
37+ #include < algorithm>
38+ #include < mutex>
39+ #include < vector>
40+ #include " DolphinLibretro/Common/Globals.h"
41+ static void * s_last_exec_rw = nullptr ;
42+ static unsigned s_last_exec_mode = 0 ;
43+ // Regions the frontend allocated for us. Whether they are dual-mapped says
44+ // nothing about who owns them, so ownership is recorded rather than inferred.
45+ static std::mutex s_frontend_regions_mutex;
46+ static std::vector<void *> s_frontend_regions;
47+ #endif
48+ #endif
49+
50+ #ifdef IPHONEOS
51+ namespace Common
52+ {
53+ static JitType g_jit_type = JitType::Legacy;
54+
55+ void SetJitType (JitType type)
56+ {
57+ g_jit_type = type;
58+ }
59+ } // namespace Common
3360#endif
3461
3562namespace Common
@@ -43,19 +70,43 @@ static JITMemoryTracker g_jit_memory_tracker;
4370
4471void * AllocateExecutableMemory (size_t size)
4572{
73+ #if defined(IPHONEOS) && defined(__LIBRETRO__)
74+ {
75+ struct retro_exec_mem_alloc alloc = {};
76+ alloc.version = 1 ;
77+ alloc.size = size;
78+ if (Libretro::environ_cb &&
79+ Libretro::environ_cb (RETRO_ENVIRONMENT_EXEC_MEM_ALLOC , &alloc) &&
80+ alloc.mode != RETRO_EXEC_MEM_MODE_UNAVAILABLE &&
81+ alloc.rx != nullptr )
82+ {
83+ s_last_exec_rw = alloc.rw ;
84+ s_last_exec_mode = alloc.mode ;
85+ SetJitType (alloc.mode == RETRO_EXEC_MEM_MODE_DUAL_MAP ? JitType::LuckTXM :
86+ JitType::Legacy);
87+ {
88+ std::scoped_lock lk (s_frontend_regions_mutex);
89+ s_frontend_regions.push_back (alloc.rx );
90+ }
91+ g_jit_memory_tracker.RegisterJITRegion (alloc.rx , size);
92+ return alloc.rx ;
93+ }
94+ }
95+ #endif
4696#if defined(_WIN32)
4797 void * ptr = VirtualAlloc (nullptr , size, MEM_COMMIT , PAGE_EXECUTE_READWRITE );
98+ #elif defined(__APPLE__) && defined(__aarch64__) && !defined(IPHONEOS)
99+ // macOS ARM: mmap R-X for dual mapping (no MAP_JIT, which prevents vm_remap)
100+ void * ptr = mmap (nullptr , size, PROT_READ | PROT_EXEC , MAP_ANON | MAP_PRIVATE , -1 , 0 );
101+ if (ptr == MAP_FAILED )
102+ ptr = nullptr ;
48103#else
49104 int map_flags = MAP_ANON | MAP_PRIVATE ;
50105#if defined(__APPLE__) && !defined(IPHONEOS)
51106 map_flags |= MAP_JIT ;
52107#endif
53108
54- int map_prot = PROT_READ | PROT_EXEC ;
55- #ifndef IPHONEOS
56- // The default protection is r-x on non-iOS platforms.
57- map_prot |= PROT_WRITE ;
58- #endif
109+ int map_prot = PROT_READ | PROT_WRITE | PROT_EXEC ;
59110
60111 void * ptr = mmap (nullptr , size, map_prot, map_flags, -1 , 0 );
61112 if (ptr == MAP_FAILED )
@@ -142,12 +193,14 @@ void JITPageWriteDisableExecuteEnable()
142193#else
143194void JITPageWriteEnableExecuteDisable (void * ptr)
144195{
145- g_jit_memory_tracker.JITRegionWriteEnableExecuteDisable (ptr);
196+ if (g_jit_type == JitType::Legacy)
197+ g_jit_memory_tracker.JITRegionWriteEnableExecuteDisable (ptr);
146198}
147199
148200void JITPageWriteDisableExecuteEnable (void * ptr)
149201{
150- g_jit_memory_tracker.JITRegionWriteDisableExecuteEnable (ptr);
202+ if (g_jit_type == JitType::Legacy)
203+ g_jit_memory_tracker.JITRegionWriteDisableExecuteEnable (ptr);
151204}
152205#endif
153206
@@ -317,4 +370,81 @@ size_t MemPhysical()
317370#endif
318371}
319372
373+ #if defined(__APPLE__) && defined(__aarch64__)
374+ ptrdiff_t AllocateWritableRegionAndGetDiff (void * rx_ptr, size_t size)
375+ {
376+ #if defined(IPHONEOS) && defined(__LIBRETRO__)
377+ if (s_last_exec_rw && s_last_exec_mode == RETRO_EXEC_MEM_MODE_DUAL_MAP )
378+ {
379+ ptrdiff_t diff = static_cast <u8 *>(s_last_exec_rw) - static_cast <u8 *>(rx_ptr);
380+ s_last_exec_rw = nullptr ;
381+ return diff;
382+ }
383+ return 0 ;
384+ #endif
385+ vm_address_t rw_region = 0 ;
386+ vm_prot_t cur_protection = 0 ;
387+ vm_prot_t max_protection = 0 ;
388+
389+ kern_return_t retval =
390+ vm_remap (mach_task_self (), &rw_region, size, 0 , true , mach_task_self (),
391+ (vm_address_t )rx_ptr, false , &cur_protection, &max_protection,
392+ VM_INHERIT_DEFAULT );
393+ if (retval != KERN_SUCCESS )
394+ {
395+ PanicAlertFmt (" AllocateWritableRegionAndGetDiff failed! vm_remap returned {0:#x}" , retval);
396+ return 0 ;
397+ }
398+
399+ u8 * rw_ptr = reinterpret_cast <u8 *>(rw_region);
400+ if (mprotect (rw_ptr, size, PROT_READ | PROT_WRITE ) != 0 )
401+ {
402+ PanicAlertFmt (" AllocateWritableRegionAndGetDiff failed! mprotect returned {}" ,
403+ LastStrerrorString ());
404+ return 0 ;
405+ }
406+
407+ return rw_ptr - static_cast <u8 *>(rx_ptr);
408+ }
409+
410+ void FreeWritableRegion (void * rx_ptr, size_t size, ptrdiff_t diff)
411+ {
412+ #if defined(IPHONEOS) && defined(__LIBRETRO__)
413+ // Frontend owns the writable region; nothing to do here.
414+ // FreeExecutableMemory will tell the frontend to free everything.
415+ #else
416+ if (diff == 0 )
417+ return ;
418+ u8 * rw_ptr = static_cast <u8 *>(rx_ptr) + diff;
419+ vm_deallocate (mach_task_self (), reinterpret_cast <vm_address_t >(rw_ptr), size);
420+ #endif
421+ }
422+
423+ void FreeExecutableMemory (void * ptr, size_t size)
424+ {
425+ if (!ptr)
426+ return ;
427+ #ifdef IPHONEOS
428+ g_jit_memory_tracker.UnregisterJITRegion (ptr);
429+ #endif
430+ #if defined(IPHONEOS) && defined(__LIBRETRO__)
431+ {
432+ std::scoped_lock lk (s_frontend_regions_mutex);
433+ auto it = std::find (s_frontend_regions.begin (), s_frontend_regions.end (), ptr);
434+ if (it != s_frontend_regions.end ())
435+ {
436+ s_frontend_regions.erase (it);
437+ struct retro_exec_mem_free f = {};
438+ f.rx = ptr;
439+ if (Libretro::environ_cb)
440+ Libretro::environ_cb (RETRO_ENVIRONMENT_EXEC_MEM_FREE , &f);
441+ return ;
442+ }
443+ }
444+ #endif
445+ munmap (ptr, size);
446+ }
447+ #endif
448+
449+
320450} // namespace Common
0 commit comments