@@ -427,3 +427,115 @@ index 6c91e2d292..86978f4671 100644
427427- -
4284282.41.0
429429
430+ From 9a800289e762e543d23d73846668221bc9c59f65 Mon Sep 17 00:00:00 2001
431+ 432+ Date: Sun, 14 Sep 2025 00:55:51 -0700
433+ Subject: [PATCH] tcg: new JIT workaround for iOS 26
434+
435+ We map the JIT region originally as RX and also mirror map it
436+ as RX. Then we use attached debugger to flag the mirror
437+ mapping as debugger owned. Finally, we change the original map
438+ to RW which should not invalidate code-signing as the mirror
439+ is debugger owned.
440+
441+ Thanks to @JJTech0130 for this workaround.
442+ ---
443+ tcg/region.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++--
444+ 1 file changed, 60 insertions(+), 2 deletions(-)
445+
446+ diff --git a/tcg/region.c b/tcg/region.c
447+ index 70996b5ab1..231438c58e 100644
448+ --- a/tcg/region.c
449+ +++ b/tcg/region.c
450+ @@ -610,6 +610,8 @@ static int alloc_code_gen_buffer_splitwx_memfd(size_t size, Error **errp)
451+
452+ #ifdef CONFIG_DARWIN
453+ #include <mach/mach.h>
454+ + #include <sys/types.h>
455+ + #include <sys/sysctl.h>
456+
457+ extern kern_return_t mach_vm_remap(vm_map_t target_task,
458+ mach_vm_address_t *target_address,
459+ @@ -623,14 +625,50 @@ extern kern_return_t mach_vm_remap(vm_map_t target_task,
460+ vm_prot_t *max_protection,
461+ vm_inherit_t inheritance);
462+
463+ + static int is_debugger_attached(void)
464+ + {
465+ + int mib[4];
466+ + struct kinfo_proc info;
467+ + size_t size;
468+ +
469+ + info.kp_proc.p_flag = 0;
470+ +
471+ + /* Initialize MIB for sysctl call */
472+ + mib[0] = CTL_KERN;
473+ + mib[1] = KERN_PROC;
474+ + mib[2] = KERN_PROC_PID;
475+ + mib[3] = getpid();
476+ +
477+ + size = sizeof(info);
478+ +
479+ + if (sysctl(mib, 4, &info, &size, NULL, 0) == -1) {
480+ + return 0; // sysctl failed, be conservative
481+ + }
482+ +
483+ + /* P_TRACED means the process is being debugged */
484+ + return (info.kp_proc.p_flag & P_TRACED) != 0;
485+ + }
486+ +
487+ + static void break_prepare_jit_region(mach_vm_address_t addr, size_t len)
488+ + {
489+ + asm ("mov x0, %0\n"
490+ + "mov x1, %1\n"
491+ + "brk #0x69" :: "r" (addr), "r" (len) : "x0", "x1");
492+ + }
493+ +
494+ static int alloc_code_gen_buffer_splitwx_vmremap(size_t size, Error **errp)
495+ {
496+ kern_return_t ret;
497+ mach_vm_address_t buf_rw, buf_rx;
498+ vm_prot_t cur_prot, max_prot;
499+ + int orig_prot = PROT_READ | PROT_WRITE;
500+ +
501+ + /* iOS 26 with TXM requires new workaround*/
502+ + if (__builtin_available(iOS 26, visionOS 26, *)) {
503+ + orig_prot = PROT_READ | PROT_EXEC;
504+ + }
505+
506+ - /* Map the read-write portion via normal anon memory. */
507+ - if (!alloc_code_gen_buffer_anon(size, PROT_READ | PROT_WRITE,
508+ + if (!alloc_code_gen_buffer_anon(size, orig_prot,
509+ MAP_PRIVATE | MAP_ANONYMOUS, errp)) {
510+ return -1;
511+ }
512+ @@ -662,6 +700,26 @@ static int alloc_code_gen_buffer_splitwx_vmremap(size_t size, Error **errp)
513+ return -1;
514+ }
515+
516+ + if (__builtin_available(iOS 26, visionOS 26, *)) {
517+ + if (!is_debugger_attached()) {
518+ + error_setg(errp, "debugger must be attached for jit workaround");
519+ + munmap((void *)buf_rx, size);
520+ + munmap((void *)buf_rw, size);
521+ + return -1;
522+ + }
523+ +
524+ + /* give let debugger modify the page permission */
525+ + break_prepare_jit_region(buf_rx, size);
526+ +
527+ + /* finally mark the read-write portion as RW */
528+ + if (mprotect((void *)buf_rw, size, PROT_READ | PROT_WRITE) != 0) {
529+ + error_setg_errno(errp, errno, "mprotect for jit splitwx (rw)");
530+ + munmap((void *)buf_rx, size);
531+ + munmap((void *)buf_rw, size);
532+ + return -1;
533+ + }
534+ + }
535+ +
536+ tcg_splitwx_diff = buf_rx - buf_rw;
537+ return PROT_READ | PROT_WRITE;
538+ }
539+ - -
540+ 2.41.0
541+
0 commit comments