首页 / 资讯中心 / 文章详情

16 KiB 页面与 TPIDR_EL0:Husk 如何让 Android 原生库在 iPhone 上直接执行

16 KiB 页面与 TPIDR_EL0:Husk 如何让 Android 原生库在 iPhone 上直接执行 ★ FEATURED ARTICLE
【免费下载链接】HuskRun Android on your iPhone. Natively or Emulated.项目地址https://gitcode.com/gh_mirrors/husk2/Husk点击查看免费下载想让 Android 应用和游戏直接在 iPhone 上运行开源项目Husk做到了。它包含两种运行方式其中Translation Layer翻译层不启动 Android 系统而是让 APK 自带的 arm64 原生库.so文件直接在 iPhone 的 CPU 上执行——不经过任何模拟。但这背后有两个容易被忽视的硬骨头iOS 的内存页大小是 Android 的 4 倍以及 Android 代码依赖的一个线程寄存器TPIDR_EL0。本文将带你拆解 Husk 是如何逐一解决它们的。在 Husk 的 Library 里翻译层模式可以让《GTA: San Andreas》这类经典安卓游戏在 iPhone 上直接跑起来翻译层不启动 Android 的运行方式Husk 的两种模式见 README.mdTranslation Layer翻译层游戏自带的 64 位 Android 代码直接在 iPhone 处理器上运行Husk 在周围扮演Android——C 库、Java 调用、OpenGL ES通过 ANGLE、Vulkan通过 MoltenVK、声音、触摸与手柄。不启动 Android游戏几秒内就能打开代码也从未被模拟。Emulation模拟完整 Android 系统在 QEMU 内启动用于翻译层尚不能运行的应用速度明显更慢。翻译层的架构规划记录在 docs/04-translation-layer.md。它的核心是一个自研加载器负责把 APK 里的 arm64.so库搬进 iPhone 上只有调试器才能授予的可执行内存并在搬运过程中完成下面要讲的那些改造。16 KiB 页面iOS 与 Android 的页大小之争 问题代码和数据挤在同一页里iOS 的内存页是16 KiB16384 字节见 husk-tl-elf.c 中的TL_PAGE 16384u而 Android 的原生库传统上按4 KiB页对齐构建。页大了 4 倍意味着一个按 4 KiB 对齐的库可能把可执行代码和可写数据塞进了同一个 16 KiB 页。而 iOS 严格执行 W^X 策略——同一页不能既可执行又可写这类库无法原样映射。解法加载前先做一份 16 KiB 页规划Husk 的 ELF 解析器逐字节、小端读取库文件iOS SDK 没有elf.h一切按 ELF 规范手工解析核心是页规划函数 tl_page_plan把每个可加载段PT_LOAD投到 16 KiB 页网格上为每页标记需要的权限只读、可执行、可写PT_GNU_RELRO覆盖的可写字节不计入可写标记——它们只在重定位阶段被写入而写入是通过加载器自己的可写别名完成的输出一个紧凑的页布局摘要形如R1 X4 R1 W21 页只读、4 页代码……冲突页数会被单独统计。页类型定义在 husk-tl-internal.h 的注释里讲得很直白一个页可能装着多个段的一部分——在 4 KiB 对齐下一个 16 KiB 页经常如此。扫描器加载之前就知道命运husk-tl-scan.c 会对 APK 里每个 arm64 库生成一份 JSON 报告字段包括pages、execPages、conflictPages、layout以及指令级统计。判定逻辑一目了然lib_status判定条件blocked解析失败或存在加载器不支持的重定位类型work有 16 KiB 页权限冲突、直接系统调用、文本重定位、或自行写入线程寄存器ok其余情况可直接映射值得留意的是自 2025 年 11 月起Google Play 要求面向 Android 15 的新应用和更新必须携带 16 KiB 兼容的原生代码。也就是说从 Play 上下载的现代 APK 大多天然通过这道检查而老应用若冲突报告显示为需要处理对策是对这几页做写模拟详见 docs/04-translation-layer.md 的 16 KiB pages 一节。TPIDR_EL0Android 代码藏在线程寄存器里的秘密 栈保护 cookie 住在哪里TPIDR_EL0是 AArch64 架构的线程指针寄存器——指向当前线程的家。Android 的 clang 默认开启栈保护stack protector每个受保护函数的入口和出口都会生成这样一条读取测试套件在 fixture 上确认过mrs x8, tpidr_el0 ldr x9, [x8, #0x28] ; 读取栈保护 cookie问题在于DarwinmacOS/iOS把自己的线程指针放在了只读寄存器TPIDRRO_EL0里TPIDR_EL0可能根本没人用。所以关键问题是Android 代码读TPIDR_EL0时这个寄存器能不能安全地由 Husk 自己控制先在真机上做实验文档中明确区分已测量/已处理/仍开放。Husk 没有拍脑袋而是写了设备检查程序 husk-tl-probe.c 在真机上验证tpidr检查标题 Thread register (TPIDR_EL0)依次测量启动时TPIDR_EL0的初始值是否为零、是否被系统占用在4 个线程上各写入不同值往返150 轮检查值在数百次上下文切换后是否存活值能否穿越信号处理器信号打断时寄存器值是否还在。结论是 Android 代码的这条读取要么原样可用要么由加载器统一改写——下一节讲改写方案。加载时补丁把寄存器读变成内存读如果真机检查判定TPIDR_EL0不可用Husk 的加载器husk-tl-ld.c 的补丁策略说明会做两件事造一个假线程块分配一块页在偏移0x28处写入一个固定的栈保护 cookieensure_tcb。所有线程共享同一个 cookie 是无害的——cookie 只需要在同一个函数的入口和出口保持一致而它确实一致逐条重写指令把每条mrs Xt, tpidr_el0改写成adrp Xt, 假线程块。这样原来的ldr x9, [x8, #0x28]无需任何改动就从读寄存器再取 cookie变成了读一块普通内存取到的正是那个固定 cookie。补丁统计会写进加载日志例如patch: rewritten N mrs Xt, tpidr_el0 - adrp stack guardhusk-tl-load.c。同时加载器对同一份可写视图还会重写指向可写页的adrp代码通过 PC 相对寻址找全局变量而可执行视图不可写、把svc #0改写为对 Linux 系统调用桩的跳转——这些都是直接执行背后的常规动作。一个容易翻车的地方是不少库会把常量表直接放在.text段里OpenSSL 的 SHA 常量表就是典型这些数据词长得像指令。补丁前会先利用 unwind 表.eh_frame_hdr划出真实函数的范围范围之外的词一律按数据处理、绝不动手避免把哈希常量重写成分支指令。从 APK 到运行加载器要做的事 把上面拼起来一个库从 APK 到执行的路径是扫描tl_elf_analyze解析 ELF输出页规划、指令统计、重定位统计含 Android 特有的 APS2 打包重定位和 RELR 解码器见 husk-tl-elf.c规划16 KiB 页布局确定每个库需要多少页、哪些页可执行/可写、是否有冲突页映射库被搬进 JIT 内存——这块可执行内存必须由调试器授予且 双映射的 RW/RX 地址都必须 16 KiB 页对齐补丁tpidr_el0读取、指向可写页的adrp、svc、以及 Apple 保留的 x18 寄存器用 pthread 特定存储模拟出一个虚拟 x18槽位逐一改写执行重定位完成后JNI_OnLoad之类的入口直接运行在 iPhone 的 CPU 上。页面规划的正确性由测试守护tests/translation-layer/run.sh 用人工推演的布局测页规划器用 clang/lld 真实构建的 arm64 库测扫描器并与llvm-readelf、llvm-objdump的统计交叉核对两个解析器在 ASan/UBSan 下跑过 30 万轮模糊测试没有崩溃。动手体验添加应用并读懂兼容性报告翻译层入口在Settings › Experimental › Android Translation Layer界面代码TranslationLayer.swift。把 APK 添加进去后每个应用会得到一份运行它需要什么的报告页面冲突、svc系统调用、tpidrReads/tpidrWrites等字段都会在报告里出现——这正是本文讲的两个机制在用户侧的投影。设备检查tpidr、x18、JIT 内存布局的结果也会在同一界面展示。需要完整构建源码的话git clone https://gitcode.com/gh_mirrors/husk2/Husk cd Husk ./scripts/ci_build.sh构建需要 Mac Xcode外加meson、ninja、pkg-config、python3、xcodegen和带aarch64-apple-ios目标的 Rust 工具链。首次构建约两小时。关键源码导读模块路径ELF 解析与 16 KiB 页规划tl_page_planhusk-tl-elf.c页类型定义与报告结构husk-tl-internal.h逐库扫描与判定ok / work / blockedhusk-tl-scan.c真机探测tpidr、x18、JIT 内存husk-tl-probe.c加载器与tpidr_el0补丁日志husk-tl-load.c假线程块、指令重写tpidr / adrp / svc / x18husk-tl-ld.c翻译层设计文档docs/04-translation-layer.md整体架构模拟模式对比docs/00-architecture.md页面规划与扫描器测试tests/translation-layer/run.shMinecraft Dungeons 这类重 3D 游戏同样走这套原生路径加载器的每一项改造都只发生在启动瞬间写在最后让 Android 库在 iPhone 上直接执行听起来只是一句口号落到工程上是用 16 KiB 页规划解决页大小差异用真机探测加指令重写驯服TPIDR_EL0再配合 JIT 可执行内存与 bionic 模拟层才凑齐代码从不被模拟这句话的全部前提。Husk 的文档把每项工作都标注了测量状态这种先验证再宣称的工程习惯是读这个项目源码时最值得借鉴的地方。赞分享【免费下载链接】HuskRun Android on your iPhone. Natively or Emulated.项目地址https://gitcode.com/gh_mirrors/husk2/Husk点击查看免费下载相关推荐bbport 原生直跑实验让 Bloodborne 的 PS4 原始 x86-64 代码在 Linux 上直接执行bbport 原生直跑实验让 Bloodborne 的 PS4 原始 x86 64 代码在 Linux 上直接执行 本文基于 bbport 项目的开发日志 d如何在嵌入式系统中部署Avahi轻量级配置与优化实践如何在嵌入式系统中部署Avahi轻量级配置与优化实践 Avahi是一款开源的零配置网络实现工具专为在局域网中自动发现和配置网络服务而设计。对于资源受限的嵌入如何用 zx 直接执行远程 URL 上的脚本如何用 zx 直接执行远程 URL 上的脚本 当你有一份 zx 脚本托管在远程服务器上比如放在代码仓库的 raw 地址里又不想先 curl / wget开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站