You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ARM64 Linux下JIT编译指令缓存问题及技术问询

解答:ARM64 Linux下JIT代码追加执行的缓存一致性问题

核心问题1:ARM64 Linux下真的无法刷新指令缓存吗?

不是。mprotect调用的flush_cache_range确实在ARM64下对「已执行页内追加代码」的场景无效,但ARM64 Linux提供了可靠的指令缓存刷新方式:

  • GCC内置函数__builtin___clear_cache(void *start, void *end):该函数专为JIT场景设计,会自动处理ARM64架构下的数据缓存与指令缓存的同步,包括执行必要的缓存维护操作(如数据缓存清理、指令缓存失效)和同步屏障,确保修改后的代码能被CPU正确读取。
  • 注意:不要手动执行特权级缓存指令(如ic ivau),用户态无法直接执行这类指令,会触发异常。

核心问题2:是否有可靠方法确定可安全写入指令的地址偏移以避免缓存问题?

结论:依赖地址偏移完全不可靠,正确做法是显式刷新缓存

关于预取器的疑问:

  • CPU预取缓存行的数量没有统一上限:不同ARM64 CPU(如树莓派5搭载的Cortex-A76)的预取器行为是硬件相关的,会根据代码执行模式、分支预测结果动态调整预取范围,无法通过固定偏移规避。
  • 预取器不会严格在页边界停止:部分ARM64 CPU的预取器会跨页预取指令,你测试中相邻页不崩溃只是巧合,不能作为可靠方案。

修正后的JIT代码追加执行流程

将原流程调整为:

  1. 使用mmap分配内存页(建议初始设置PROT_READ | PROT_WRITE权限)
  2. memcpy复制代码到该页
  3. 调用__builtin___clear_cache(code_start, code_end)刷新缓存(end参数为代码末地址+1,因函数采用排他范围)
  4. mprotect设置为PROT_READ | PROT_EXEC
  5. 执行代码
  6. mprotect重新设置为PROT_READ | PROT_WRITE
  7. memcpy追加新代码到该页
  8. 调用__builtin___clear_cache(new_code_start, new_code_end)刷新新写入区域的缓存
  9. mprotect再次设置为PROT_READ | PROT_EXEC
  10. 执行新复制的指令

额外注意事项

  • 若系统允许,可在mmap时直接设置PROT_READ | PROT_WRITE | PROT_EXEC权限(需确保系统未限制可执行匿名内存,可通过调整/proc/sys/vm/mmap_min_addr或使用MAP_ANONYMOUS | MAP_PRIVATE标志),减少频繁切换权限的开销,仅需在每次写入后调用缓存刷新函数即可。

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

火山引擎 最新活动