如何逐步构建 Madeira 的 Wine ARM64EC PE 模块:build/wine-pe 构建链完全指南
【免费下载链接】MadeiraRun x86-64 Windows PC games on jailed iOS via FEX-Emu + Wine + DXMT项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira
Madeira 是一个在无越狱 iOS 设备上运行 x86-64 Windows PC 游戏的开源项目,它把 Wine(ARM64EC 版本)、FEX-Emu 和 DXMT 组合成单一 Mach 进程。本文带你逐步走通build/wine-pe构建链:如何编译出 ntdll.dll 等 Wine ARM64EC PE 模块,并完成 strip、填充(pad)后拷贝进 iOS 应用目录。全文面向新手,无需提前熟悉 Wine 内部结构。
🎯 先搞懂:Wine PE 模块在 Madeira 里扮演什么角色
Madeira 的 Wine 部分被拆成了两个"半边":
| 半边 | 产物 | 运行位置 |
|---|---|---|
| unix 侧 | libntdll_unix.a、libwineserver.a、libwin32u_unix.a | iOS 原生代码,直接链接进 App |
| PE 侧 | ntdll.dll、kernel32.dll等 ARM64EC Windows 动态库 | 以文件形式打包进 App,由加载器映射 |
PE 侧正是build/wine-pe构建链的产物。它被放进 app/Madeira/arm64ec-windows/ 目录,游戏运行时由 iOS 侧加载器按 PE 文件格式映射。理解这一点很重要:ntdll.dll 是整个 x86-64 翻译栈的"地基"——Windows API 的入口、异常分发、进程/线程语义都靠它,所以构建脚本对它的处理比其它模块更严格。
💡 为什么要 ARM64EC?ARM64EC 让 Windows 代码以 x64 的位宽模型运行在 ARM64 硬件上,配合 FEX-Emu 的 x86-64→ARM64 翻译,游戏代码无需 32 位兼容层,性能损失最小。
✅ 构建前置条件:工具链与子模块一次备齐
在动手之前,确认三样输入都就位(详见 docs/BUILDING.md 的输入清单):
- llvm-mingw 工具链:放在
toolchains/llvm-mingw-20260421-ucrt-macos-universal/,约 122 MB,仓库中不含此工具链,需要从 mstorsjo/llvm-mingw 的 20260421 版本 tarball 解压获取。构建脚本会自动把它的bin目录加进 PATH。 - wine 子模块:位于
wine/目录,必须指向分支madeira-lgpl的 fork(上游 Wine 无法直接构建本项目)。注意FEX、wine、research/dxmt三个子模块都指向包含 iOS 移植工作的 fork。 - 递归克隆仓库:
git clone --recurse-submodules https://gitcode.com/GitHub_Trending/mad/Madeira⚠️ 提醒:Microsoft Visual C++ 运行时 DLL 不在本仓库分发范围内,需要按 tools/fetch-vcruntime.md 自行准备。
📌 第一步:configure wine/build-arm64ec
核心脚本只有一个:build/wine-pe/build-ntdll.sh。首次运行wine/build-arm64ec/config.status不存在时,脚本会执行一次性配置:
mkdir -p wine/build-arm64ec && cd wine/build-arm64ec ../configure --enable-archs=arm64ec --without-x --disable-tests三个选项的含义很直白:
--enable-archs=arm64ec:只构建 ARM64EC 架构,跳过其它目标,大幅缩短编译时间;--without-x:iOS 上不需要 X11;--disable-tests:跳过 Wine 自带测试。
这一步耗时较长(Wine 全量配置),但只需做一次——后续构建其它 PE 模块都复用同一棵构建树。
🔨 第二步:编译 ntdll 模块
配置完成后,脚本执行 build/wine-pe/build-ntdll.sh 第 16 行:
make -C dlls/ntdll产物落在wine/build-arm64ec/dlls/ntdll/arm64ec-windows/ntdll.dll。这一步是标准的 Wine 构建目标编译,使用工具链中的arm64ec-w64-mingw32-*交叉编译工具。
✂️ 第三步:strip 与"SizeOfImage + 0x50000"填充
这是整个构建链里最有技术含量的一步。ntdll 是特例:它不像其它 DLL 那样原样拷贝,而是要经过 strip + 精确填充,原因是iOS 侧加载器按整份文件映像来映射这个文件,而映射路径依赖文件尾部预留的"松弛空间"。
脚本做了两件事(见 build-ntdll.sh 第 18-51 行):
1. strip:用arm64ec-w64-mingw32-strip去掉调试段和符号,文件会明显变小。
2. 填充到 SizeOfImage + 0x50000:脚本内嵌的 Python 片段从 PE 头解析出OptionalHeader.SizeOfImage,然后把文件补足到SizeOfImage + 0x50000字节。
这里藏着一个真实的踩坑故事(代码注释中的 "ml1004" 修订):
- 旧逻辑先断言
文件大小 == SizeOfImage再追加 0x50000 字节。但 strip 会删除段落,导致剥离后的文件比 SizeOfImage更小(实测 0x120000 vs 0x140000),断言每一次都失败——这个脚本从未完整跑通过,早期都是手工补齐的; - 修订后改为"算出目标大小,填到目标为止",结果正好复现已知良好的手工尺寸:
0x140000 + 0x50000 = 0x190000(即 1,638,400 字节); - 同时加了防呆:若剥离后文件已大于目标,脚本直接报错退出,拒绝继续填充。
🧠 新手理解要点:PE 文件里记录了自己的虚拟内存映像大小(SizeOfImage)。iOS 加载器把磁盘文件直接映射进内存,所以磁盘文件必须"足够大";多余的 0x50000 就是这条映射路径需要的余量。
📦 第四步:拷贝进 App 目录
填充成功后,脚本将成品移动到 app/Madeira/arm64ec-windows/ntdll.dll,并打印最终大小供核对。这个目录就是游戏运行时所有 ARM64EC 模块的家。
🔁 构建其它 PE 模块:一条 make 命令搞定
ntdll 之后的其它 Windows 系统库(如kernel32、user32等)不需要 strip/pad,流程简化为两步(见 build-ntdll.sh 头部注释):
# 1. 在已配置的构建树中构建目标模块 make -C wine/build-arm64ec/dlls/<模块名> # 2. 把产物拷贝进 App cp wine/build-arm64ec/dlls/<模块名>/arm64ec-windows/<模块名>.dll \ app/Madeira/arm64ec-windows/注意cp这一步没有strip 和 pad——它们只服务于 ntdll 的特殊映射需求。
🗺️ wine-pe 在整个构建链中的位置
build/wine-pe不是孤立运行的。按 docs/BUILDING.md 记录的标准顺序,完整链条是:
| 顺序 | 构建链 | 产物 |
|---|---|---|
| 1 | build/gnutls-ios | GMP / Nettle / GnuTLS 静态库 |
| 2 | build/fex-ios+build/fex-arm64ec | FEX 静态库、xtajit64.dll |
| 3a | build/ntdll-unix、build/wineserver、build/win32u-unix | unix 侧三个静态库 |
| 3b | build/wine-pe(本文主角) | ntdll.dll等 ARM64EC PE 模块 |
| 4 | build/dxmt-ios+ dxmt PE 构建 | D3D11→Metal 运行时 |
| 5 | build/madeira-d3d12 | 原生 D3D12 运行时 |
| 6 | xcodebuild编译 App | 最终 IPA |
wine 的两"半边"(3a 与 3b)互不依赖,可以并行做;但 App 链接(第 6 步)需要全部产物就位。
❓ 常见问题与验证状态
Q:脚本运行失败,报配置文件缺失怎么办?先检查toolchains/llvm-mingw-20260421-ucrt-macos-universal/bin是否存在,以及wine/子模块是否处于madeira-lgpl分支——这两项是脚本唯一的硬性外部依赖。
Q:如何验证产物是否正确?运行末尾会打印类似ntdll.dll: stripped N + pad M = 1638400 bytes的汇总。核对 app/Madeira/arm64ec-windows/ntdll.dll 文件大小是否为 0x190000(1,638,400 字节)是最直观的验收标准。
Q:从零 clone 一定能一次构建成功吗?坦诚说:不能。docs/BUILDING.md 如实记录了各步骤的验证状态——strip/pad 步骤已验证通过,但首次 configure 在干净机器上仍标记为 UNVERIFIED,LGPL 重链接义务的端到端干净构建也尚未完成。这份文档本身就是可复现性记录的"整改清单",照着它执行并补上缺失输入是最稳妥的路径。
📚 延伸阅读
- docs/BUILDING.md:完整构建链的权威记录,含每项输入的获取方式与 SHA-256 校验值
- ARCHITECTURE_ANALYSIS.md:Wine + FEX + DXMT 三层架构的深度解析
- app/Madeira/:最终打包目录,
arm64ec-windows/子目录是所有 PE 模块的落脚点 - docs/LICENSING.md:LGPL 重链接义务与构建记录的关系
掌握build/wine-pe构建链后,你已经打通了 Madeira 中 Windows 兼容层最核心的一块拼图:configure 一次、make 按需、strip+pad 只留给 ntdll。下一步建议按构建链顺序推进 DXMT 与 App 编译,最终在 iPhone 上亲眼看到 Windows 游戏跑起来。🚀
【免费下载链接】MadeiraRun x86-64 Windows PC games on jailed iOS via FEX-Emu + Wine + DXMT项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考