qemu: new JIT workaround for iOS 26

We map the JIT region originally as RX and also mirror map
it as RX. Then we use attached debugger to flag the mirror
mapping as debugger owned. Finally, we change the original
map to RW which should not invalidate code-signing as the
mirror is debugger owned.

Thanks to @JJTech0130 for this workaround.
This commit is contained in:
osy
2025-09-14 11:58:38 -07:00
parent 5423e6d849
commit 678534e6a5
+112
View File
@@ -427,3 +427,115 @@ index 6c91e2d292..86978f4671 100644
--
2.41.0
From 4904c333425bbc882ebd975a100b7495c1c37514 Mon Sep 17 00:00:00 2001
From: osy <[email protected]>
Date: Sun, 14 Sep 2025 00:55:51 -0700
Subject: [PATCH] tcg: new JIT workaround for iOS 26
We map the JIT region originally as RX and also mirror map it
as RX. Then we use attached debugger to flag the mirror
mapping as debugger owned. Finally, we change the original map
to RW which should not invalidate code-signing as the mirror
is debugger owned.
Thanks to @JJTech0130 for this workaround.
---
tcg/region.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 67 insertions(+), 2 deletions(-)
diff --git a/tcg/region.c b/tcg/region.c
index 70996b5ab1..031688b85c 100644
--- a/tcg/region.c
+++ b/tcg/region.c
@@ -623,14 +623,57 @@ extern kern_return_t mach_vm_remap(vm_map_t target_task,
vm_prot_t *max_protection,
vm_inherit_t inheritance);
+#include <TargetConditionals.h>
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+#include <sys/types.h>
+#include <sys/sysctl.h>
+static int is_debugger_attached(void)
+{
+ int mib[4];
+ struct kinfo_proc info;
+ size_t size;
+
+ info.kp_proc.p_flag = 0;
+
+ /* Initialize MIB for sysctl call */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+
+ size = sizeof(info);
+
+ if (sysctl(mib, 4, &info, &size, NULL, 0) == -1) {
+ return 0; // sysctl failed, be conservative
+ }
+
+ /* P_TRACED means the process is being debugged */
+ return (info.kp_proc.p_flag & P_TRACED) != 0;
+}
+
+static void break_prepare_jit_region(mach_vm_address_t addr, size_t len)
+{
+ asm ("mov x0, %0\n"
+ "mov x1, %1\n"
+ "brk #0x69" :: "r" (addr), "r" (len) : "x0", "x1");
+}
+#endif
+
static int alloc_code_gen_buffer_splitwx_vmremap(size_t size, Error **errp)
{
kern_return_t ret;
mach_vm_address_t buf_rw, buf_rx;
vm_prot_t cur_prot, max_prot;
+ int orig_prot = PROT_READ | PROT_WRITE;
- /* Map the read-write portion via normal anon memory. */
- if (!alloc_code_gen_buffer_anon(size, PROT_READ | PROT_WRITE,
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+ /* iOS 26 with TXM requires new workaround*/
+ if (__builtin_available(iOS 26, visionOS 26, watchOS 26, tvOS 26, *)) {
+ orig_prot = PROT_READ | PROT_EXEC;
+ }
+#endif
+
+ if (!alloc_code_gen_buffer_anon(size, orig_prot,
MAP_PRIVATE | MAP_ANONYMOUS, errp)) {
return -1;
}
@@ -662,6 +705,28 @@ static int alloc_code_gen_buffer_splitwx_vmremap(size_t size, Error **errp)
return -1;
}
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+ if (__builtin_available(iOS 26, visionOS 26, watchOS 26, tvOS 26, *)) {
+ if (!is_debugger_attached()) {
+ error_setg(errp, "debugger must be attached for jit workaround");
+ munmap((void *)buf_rx, size);
+ munmap((void *)buf_rw, size);
+ return -1;
+ }
+
+ /* give let debugger modify the page permission */
+ break_prepare_jit_region(buf_rx, size);
+
+ /* finally mark the read-write portion as RW */
+ if (mprotect((void *)buf_rw, size, PROT_READ | PROT_WRITE) != 0) {
+ error_setg_errno(errp, errno, "mprotect for jit splitwx (rw)");
+ munmap((void *)buf_rx, size);
+ munmap((void *)buf_rw, size);
+ return -1;
+ }
+ }
+#endif
+
tcg_splitwx_diff = buf_rx - buf_rw;
return PROT_READ | PROT_WRITE;
}
--
2.41.0