Kakehashi:在 Linux ARM 上运行 macOS 二进制文件的实验性用户态翻译层
用户态 macOS ARM64 → Linux aarch64 翻译层(CLI 优先,无 JIT)。
在 Linux aarch64 上加载 Darwin Mach-O,映射一个独立的 libSystem,翻译 BSD 系统调用,并运行真实的客户程序(clang 探测、7-Zip 7zz、curl、线程)。
| 实时执行 | Linux aarch64(裸机、虚拟机、Colima/Docker) |
| 静态加载/检查 | 任意主机(包括 macOS) |
| 设计参考 | docs/ |
可运行内容
已在 Docker/Colima 和 UTM(Linux aarch64)上验证。安装一次即可:
cargo install kakehashi # 或从代码检出安装: cargo install --path crates/kh-cli --force kh bottle ensure kh install 7zip # Darwin 7zz → 客户机 /usr/local/bin/7zz kh install curl # Darwin curl → 客户机 /usr/local/bin/curl
相对路径 -o / 归档路径相对于 kh 进程的主机当前工作目录解析(自行创建父目录,或依赖 O_CREAT 的自动创建目录功能)。通过 bottle,/Volumes/linux/… 桥接到主机根目录(/ → 主机 /)。
7-Zip (7zz)
# 版本/帮助 kh run 7zz -- kh run 7zz -- --help # 创建归档(相对于当前工作目录) kh run 7zz -- a demo.7z README.md kh run 7zz -- t demo.7z kh run 7zz -- l demo.7z kh run 7zz -- x -o./out demo.7z # 多线程压缩(正确性验证) kh run 7zz -- a -t7z -m0=lzma2 -mx=5 -mmt=4 mt.7z README.md kh run 7zz -- t mt.7z # 预期:Everything is Ok,退出码 0
Docker 辅助脚本(产物位于主机 .tmp/kh-out/ 下):
./scripts/docker-7zz.sh --help ./scripts/docker-7zz.sh a /Volumes/linux/out/demo.7z /Volumes/linux/src/README.md ls -lh .tmp/kh-out/demo.7z KAKEHASHI_HYPERCALL=1 ./scripts/docker-7zz.sh a -t7z -m0=lzma2 -mx=5 -mmt=4 \ /Volumes/linux/out/mt.7z /Volumes/linux/src/README.md ./scripts/docker-7zz.sh t /Volumes/linux/out/mt.7z
curl
# Banner (G1) kh run curl -- --version # HTTP GET → 文件 (G3 / G5)。如果 -o 指定的父目录不存在,会自动创建。 kh run curl -- -sS -o .tmp/kh-out/body http://example.com/ # 预期:退出码 0,约 559 字节,HTML 包含 "Example Domain" wc -c .tmp/kh-out/body head -c 80 .tmp/kh-out/body; echo # HTTP 输出到标准输出 kh run curl -- -sS http://example.com/ | head -c 80; echo # HTTPS GET (G4) — OpenSSL + 瓶装 CA(来自主机或 curl.se 下载) kh run curl -- -sS -o .tmp/kh-out/https-body https://example.com/ wc -c .tmp/kh-out/https-body # 负面测试:错误/自签名证书必须失败(返回码 ≠ 0) kh run curl -- -sS -o /dev/null https://self-signed.badssl.com/; echo exit:$?
Docker 辅助工具:
./scripts/docker-curl.sh --version ./scripts/docker-curl.sh -sS -o /Volumes/linux/out/body http://example.com/ ./scripts/docker-curl.sh -sS -o /Volumes/linux/out/https-body https://example.com/ ls -lh .tmp/kh-out/body .tmp/kh-out/https-body # 首次追踪探针日志 → .tmp/kh-curl-probe/ ./scripts/docker-curl-probe.sh --version # 选项矩阵(大型层级)→ .tmp/kh-curl-options/ ./scripts/docker-curl-options.sh tier1 ./scripts/docker-curl-options.sh tier9-10 ./scripts/docker-curl-options.sh all # tier1..10
多次运行中无害的噪音:
kh: open fail ENOENT(openat) path=/etc/ssl/openssl.cnf— OpenSSL 可选配置;通过种子 CA 包,HTTP/HTTPS 仍可正常工作。WARN … skip dylib … Security/CoreFoundation— 瓶装中不含 Apple 框架;软桩覆盖了加载路径。unresolved strong symbol; bound to named missing trampoline— 正常路径中未命中的符号。
详情和关卡:docs/curl.md。
其他通过项
| 测试面 | 备注 |
|---|---|
| Clang / 固定探针 | tests/clang-probe/、tests/fixtures/ |
多线程 7zz -mmt=4 |
Docker + UTM |
瓶装 + 独立 libSystem |
kh bottle ensure 嵌入 dylib |
| 单元测试 + clippy | cargo test / clippy 工作区(不含 kh-libsystem) |
尚未作为产品声明
完整的 curl 功能集(POST 请求体、代理、端到端 HTTP/3、所有协议),
真正的 Apple Security.framework,git / CLT,GUI,代码签名。下一个产品切片:
通过 kh install xcode-tools 安装 git — 详见 docs/git.md。
Crates
| Crate | 角色 |
|---|---|
kakehashi |
二进制文件 kh(安装此包) |
kh-loader |
Mach-O 解析、映射、执行 |
kh-runtime |
内存、陷阱、BSD 系统调用、bottle;内嵌独立的 libSystem.B.dylib |
kh-libsystem |
该 dylib 的源码(仅限 aarch64-apple-darwin;不是 Linux 宿主 crate) |
客户 dylib 位于 crates/kh-runtime/resources/libSystem.B.dylib,通过 include_bytes! 编译进运行时。发布 kh-runtime 时会附带该 dylib;最终用户无需单独下载。
系统要求
- Rust 1.88+
- 运行
kh run/kh trace需要 Linux aarch64 - 页面大小:4 KiB(容器)和 16 KiB(Asahi 级别)
- 可选:
kh install 7zip/kh install curl需要curl/wget+tar
安装
cargo install kakehashi # 或从检出目录安装:cargo install --path crates/kh-cli kh bottle ensure kh install 7zip kh install curl
Bottle 目录结构
默认根目录:~/.local/share/kakehashi/bottle/(可通过 KAKEHASHI_DATA_DIR / KAKEHASHI_ROOT 覆盖)。
| 宿主 | 客户 |
|---|---|
…/bottle/ |
/ |
…/usr/local/bin/7zz |
/usr/local/bin/7zz |
…/usr/local/bin/curl |
/usr/local/bin/curl |
…/usr/lib/libSystem.B.dylib |
/usr/lib/libSystem.B.dylib |
…/private/etc/ssl/cert.pem |
/etc/ssl/cert.pem(主机 CA 或下载的 Mozilla 证书) |
…/Volumes/linux/… |
/Volumes/linux/… → 主机文件系统 |
性能(实话实说)
Kakehashi 在 CPU 上原生运行客户代码。性能开销来自系统调用边界(TLS 切换、备用栈、NEON 保存/恢复、Rust 调度),具体取决于客户程序的调用频率——它不是指令模拟器。
实测差距
在 Ubuntu aarch64 裸机(UTM)上,多文件 7zz 压缩(-t7z -m0=lzma2 -mx=5 -mmt=4,约 8k 文件 / 约 240 MiB 目录树):
原生 Linux 7zz |
Darwin 7zz 在 kh 下 |
比例 | |
|---|---|---|---|
| 实际时间 | 约 22.5 秒 | 约 118 秒 | 约 ×5.2 |
在压缩密集、文件数少的场景下,差距通常小得多(约 ×1.1–1.2)。多文件场景下的大差距主要来自路径遍历 + 每次系统调用的边界开销,而非“LZMA 算法错误”。
Hypercall 默认对所有客户线程开启。仅在调试时设置 KAKEHASHI_HYPERCALL=0 关闭(残留的 svc→brk / SIGTRAP)。
为什么 ×5 在 CI 中仍有价值
CI 的产品目标不是“和原生 macOS 一样快”,而是在廉价的 Linux aarch64 运行器上运行 Darwin CLI/工具,从而替代稀缺且昂贵的 macOS 资源。
GitHub Actions 托管运行器(私有仓库超额费率,美元/分钟;参见 Actions 运行器定价):
| 运行器 | 每分钟费率 |
|---|---|
| Linux 2 核 arm64 | $0.005 |
| Linux 2 核 x64 | $0.006 |
| macOS 3–4 核(M1/Intel) | $0.062 |
| macOS 更大规格(如 12 核 / M2 Pro) | $0.077–$0.102 |
macOS 的标准费率大约是 Linux arm64 每分钟计费的 ×10–×12 倍(不计实际耗时差异)。即使某个任务在 Linux arm64 上用 kh 运行比在 macOS runner 上慢 5 倍,计费成本仍可能更低,因为 macOS 每分钟的价格贵了一个数量级(举例:5 × $0.005 ≈ $0.025 对比 1 × $0.062)。公共仓库的免费分钟数和自托管 Linux 会进一步放大这一优势;macOS 托管容量也往往排队更久,并且在 GitLab SaaS 上受 Premium/Ultimate 或 beta 权限限制。
macOS runner 仍然胜出的场景: GUI、代码签名/公证、Xcode UI 测试,或任何无法在独立 libSystem 下作为纯 CLI Darwin 二进制运行的工作负载。
关注功能门,而非跑分: CI 正确性由 cargo test / 冒烟测试 / 7zz -mmt=4 验证,而不是“匹配原生时钟时间”。性能工作记录在 docs/roadmap.md 中。
快速开始(Apple Silicon 上的 Docker / Colima)
docker build -t kakehashi:dev -f Dockerfile.dev . docker run --rm -v "$PWD":/src -w /src kakehashi:dev \ cargo test --workspace --exclude kh-libsystem # 完整冒烟测试(构建 + clippy + 测试 + 微运行) ./scripts/docker-smoke.sh
构建
cargo build -p kakehashi --release cargo test --workspace --exclude kh-libsystem cargo clippy --workspace --exclude kh-libsystem --all-targets -- -D warnings # 维护者:在独立 ABI 变更后刷新嵌入内容 cargo build -p kh-libsystem --release --target aarch64-apple-darwin ./scripts/stage-libsystem.sh # → crates/kh-runtime/resources/libSystem.B.dylib
libSystem 发现顺序:--libsystem → KAKEHASHI_LIBSYSTEM → kh 旁边的路径 → crate resources/ → kh-runtime 中的嵌入字节。
测试映射
| 目标 | 命令 | 产物 |
|---|---|---|
| 单元测试 | cargo test --workspace --exclude kh-libsystem |
终端输出 |
| Docker 冒烟测试 | ./scripts/docker-smoke.sh |
以 smoke ok 结束 |
| 测试夹具 | kh run --expect-code … tests/fixtures/… |
参见 tests/fixtures/README.md |
| Clang 探针 | kh run --root tests/fixtures/bottle tests/clang-probe/puts_hello |
标准输出 hello |
真实 Darwin 7zz |
./scripts/docker-7zz.sh … |
宿主机 .tmp/kh-out/ |
真实 Darwin curl |
./scripts/docker-curl.sh … |
宿主机 .tmp/kh-out/;探针 → .tmp/kh-curl-probe/ |
| 公平 CPU 基准测试 | ./scripts/bench-fair-local.sh |
宿主机 .tmp/kh-bench-fair/ |
.tmp/、.kh/ 和 target/ 已被 gitignore 忽略。
客户机路径 ↔ 宿主机路径(Docker 辅助工具)
Bottle 将 Linux 文件系统桥接为 /Volumes/linux/…:
| 客户机路径 | 宿主机 |
|---|---|
/Volumes/linux/src/README.md |
<repo>/README.md |
/Volumes/linux/out/demo.7z |
<repo>/.tmp/kh-out/demo.7z(持久化;docker-7zz.sh 的默认路径) |
/Volumes/linux/tmp/… |
容器 /tmp/… — 执行 docker run --rm 后**消失** |
脚本
| 脚本 | 用途 |
|---|---|
scripts/stage-libsystem.sh |
构建产物 → crates/kh-runtime/resources/ |
scripts/install-linux.sh |
本地构建 + 安装 kh + bottle ensure |
scripts/docker-smoke.sh |
在 Dockerfile 镜像内运行冒烟测试套件 |
scripts/docker-7zz.sh |
在 kh 下运行 Darwin 7zz(输出 → .tmp/kh-out) |
scripts/docker-curl.sh |
在 kh 下运行 Darwin curl(结构与 docker-7zz 相同) |
scripts/docker-curl-probe.sh |
KH_CURL_PROBE=1 包装脚本(日志 → .tmp/kh-curl-probe) |
scripts/docker-curl-options.sh |
分层 curl 参数冒烟测试(tier1…tier10、tier9-10、all → .tmp/kh-curl-options) |
scripts/docker-git.sh |
在 kh 下运行 CLT 中的 Apple git(swscan + .kh/data 缓存) |
scripts/bench-fair-local.sh |
原生 vs kh 压缩对比(产物 → .tmp/kh-bench-fair) |
许可证
Apache License 2.0。详见 LICENSE.txt 和 NOTICE。
本项目并非衍生自 Darling。请勿引入专有的 Apple SDK 或二进制 blob。