You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apple Silicon平台mmap可执行页触发EXC_BAD_ACCESS(code=2)的解决方法

解决Apple Silicon上JIT内存执行EXC_BAD_ACCESS的问题

你的代码存在两个核心问题:指令写入错误和JIT内存权限/签名流程不规范,以下是具体解决步骤:

1. 修正代码中的指令写入错误

你当前的代码错误地将instr变量的地址写入JIT内存,而非预期的add x0, x0, x0指令:

memcpy(instr, &instr, 4); // 错误:写入instr的地址,不是目标指令的机器码

应改为:

memcpy(instr, &instr1, 4); // 正确:写入add指令的机器码

同时注意:你之前把instr指向mmap区域的+4084位置,这超出了分配的1024字节内存范围(合法范围是[instr, instr+1023]),直接使用mmap返回的起始地址即可。

2. 规范JIT内存的权限切换流程

Apple Silicon要求JIT内存遵循**W^X(写或执行二选一)**原则,正确流程如下:

  • 分配JIT内存时先申请读写权限
  • 关闭JIT写保护以写入指令
  • 写入完成后重新开启写保护,再将内存权限修改为只读+执行

修正后的内存操作代码:

// 分配JIT内存,初始权限为RW
instr = (uint8_t*)mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON|MAP_JIT, 0, 0);
if(instr == MAP_FAILED){
    perror("mmap");
    exit(-1);
}

// 关闭JIT写保护,允许写入指令
pthread_jit_write_protect_np(0);
memcpy(instr, &instr1, 4);
memcpy(instr+4, &instr2, 4);
// 开启JIT写保护,切换为执行状态
pthread_jit_write_protect_np(1);
// 修改内存权限为RX(强化W^X原则)
mprotect(instr, 1024, PROT_READ|PROT_EXEC);

3. 添加JIT权限签名

Apple Silicon上的程序要使用JIT,必须具备com.apple.security.cs.allow-jit权限,操作步骤如下:

  1. 创建entitlements.plist文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
</dict>
</plist>
  1. 用codesign重新签名可执行文件:
codesign -s - --entitlements entitlements.plist --force your_executable_name

如果是Xcode项目,可直接在「Signing & Capabilities」中添加「Hardened Runtime」,并勾选「Allow JIT」选项。

修正后的完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <pthread.h>
#include <stdint.h>

int main() {
 
    uint8_t *instr;
    uint32_t instr1 = 0x8b000000; // add x0, x0, x0
    uint32_t instr2 = 0xd65f03c0; // ret
 
    if(pthread_jit_write_protect_supported_np() == 1)
        printf("jit write supported\n");
    else
        printf("jit write not supported\n");

    // 分配JIT内存,初始权限为RW
    instr = (uint8_t*)mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON|MAP_JIT, 0, 0);
    if(instr == MAP_FAILED){
        perror("mmap");
        exit(-1);
    }

    printf("instr addr : %lx\n", (uintptr_t)instr);

    // 关闭写保护,写入指令
    pthread_jit_write_protect_np(0);
    memcpy(instr, &instr1, 4);
    memcpy(instr+4, &instr2, 4);
    // 开启写保护,切换为执行状态
    pthread_jit_write_protect_np(1);
    // 修改内存权限为RX
    mprotect(instr, 1024, PROT_READ|PROT_EXEC);

    printf("instr1 is %x\n", *(uint32_t *)instr);
    printf("instr2 is %x\n", *(uint32_t *)(instr+4));

    asm volatile(
    "eor x0, x0, x0\n"
    "eor x1, x1, x1\n"
    "eor x2, x2, x2\n"
    "eor x3, x3, x3\n"
    );

    asm volatile(
    "ldr x1, %[ptr]\n"
    "br x1\n"
    ::[ptr]"m"(instr)
    );

    // 释放内存(可选)
    munmap(instr, 1024);
    return 0;
}

编译时需链接pthread库:

clang -o jit_test jit_test.c -lpthread

签名后运行即可正常执行JIT代码。

内容的提问来源于stack exchange,提问作者purple_potato

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 15:44:55