ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Madeira 兼容层:在 iOS 上运行 x86-64 Windows 程序实战

Madeira 兼容层:在 iOS 上运行 x86-64 Windows 程序实战 1. 从“Madeira”这个名字说起它到底想解决什么问题第一次看到“Madeira”这个项目名很多人会以为是某个葡萄酒产区或者旅游地。但把热搜词摊开来看——Wine、FEX-Emu、DXMT、iOS、x86-64——方向就很清楚了这是一个围绕在非 x86 平台上运行 x86-64 Windows 程序的兼容层项目而且重点落在 iOS 设备上。Madeira 这个名字本身是葡萄牙的一座岛也是马德拉酒的产地用它来命名一个“把 Windows 生态搬到移动端”的项目多少带点“跨海搬运”的意味。我接触这类兼容层项目有些年头了从早期在 Linux 上折腾 Wine到后来看 Box64、FEX-Emu 这些动态二进制翻译器怎么把 x86-64 指令翻译到 ARM64再到 DXMT 这种把 Direct3D 调用转成 Metal 的方案整个链路其实是一条非常清晰的“翻译流水线”。Madeira 要做的就是把这条流水线打包成一个能在 iOS 上跑起来的东西。它解决的核心问题很具体iOS 设备是 ARM64 架构系统封闭App Store 不允许你随便跑一个 Windows 可执行文件。但总有人想在 iPad 上跑一些只有 Windows 版的老软件、老游戏或者某些行业工具。Madeira 的思路就是——不越狱、不装双系统用一层兼容层把 PE 文件加载起来把 x86-64 指令翻译成 ARM64把 Windows API 映射到 POSIX把 Direct3D 转成 Metal最后在 iOS 的沙盒里跑起来。适合看这篇的人有三类一是想在 iOS 上跑 Windows 程序但不想折腾越狱的普通用户二是对 Wine、FEX-Emu、DXMT 这套技术栈好奇、想自己搭一套试试的开发者三是做跨平台兼容层、模拟器相关工作的同行想看看别人是怎么把这么多组件缝在一起的。下面我会把 Madeira 涉及的核心技术点、实操路径、踩坑经验一条条拆开讲。2. 核心组件拆解Wine、FEX-Emu、DXMT 各自扮演什么角色2.1 WineWindows API 的“翻译字典”Wine 是整个链条里最上层的东西。它的工作不是模拟 Windows而是把 Windows 程序调用的 API 翻译成宿主系统能听懂的调用。比如一个 Windows 程序调用CreateWindowExWine 会把它翻译成 iOS 上对应的视图创建逻辑程序调用ReadFileWine 会转成 POSIX 的read。这里有个常见误解很多人以为 Wine 是模拟器。不是。Wine 是“兼容层”它不模拟 CPU 指令只翻译 API。所以 Wine 本身跑不了 x86 代码——它需要底下有个东西把 x86-64 指令翻译成 ARM64。这就是 FEX-Emu 的位置。Wine 在 iOS 上跑最大的障碍是它依赖大量 Linux 系统调用和动态链接库。iOS 的沙盒限制了很多东西比如fork、exec、mmap的可执行权限。Madeira 要做的第一件事就是给 Wine 打补丁把那些 iOS 不允许的调用替换成可行的方案。热搜里出现的“wine 乱码”“wine 栏是乱码”大概率就是字体和 locale 没配好——Wine 默认用系统字体iOS 上没有 Windows 那套字体中文就变成方块了。2.2 FEX-Emux86-64 到 ARM64 的“同声传译”FEX-Emu 是一个用户态的 x86-64 模拟器专门为 ARM64 宿主设计。它的工作方式是把 x86-64 的机器码块翻译成 ARM64 的机器码块然后缓存起来重复使用。和 QEMU 那种全系统模拟不同FEX-Emu 是用户态的只翻译应用程序本身不模拟整个操作系统。为什么选 FEX-Emu 而不是 Box64Box64 更轻量但 FEX-Emu 对 x86-64 指令集的覆盖更完整尤其是 SSE、AVX 这些 SIMD 指令。很多 Windows 程序编译时默认开了 SSE2Box64 处理某些指令会掉性能FEX-Emu 在这块优化得更狠。另外 FEX-Emu 有个“TSO 模式”能模拟 x86 的强内存序这对多线程程序很重要——ARM 是弱内存序不处理的话多线程程序会出各种诡异 bug。在 iOS 上跑 FEX-Emu难点在于 JIT即时编译。iOS 默认不允许 App 动态生成可执行代码除非你有com.apple.security.cs.allow-jit权限。Madeira 要么走企业签名拿到这个权限要么用解释模式——但解释模式性能会掉一个数量级。这也是为什么这类项目在 iOS 上一直很难普及。2.3 DXMTDirect3D 到 Metal 的“转接头”DXMT 是“DirectX Metal Translation”的缩写作用是把 Windows 程序发出的 Direct3D 调用翻译成苹果的 Metal API。Windows 游戏和图形程序大量用 D3D11、D3D12而 iOS 只认 Metal。没有 DXMTWine 跑起来的程序只能软件渲染帧率惨不忍睹。DXMT 的工作分两层上层把 D3D 的接口调用转成 Metal 的接口调用下层把 HLSL 着色器编译成 Metal Shading Language。着色器编译是性能瓶颈因为 HLSL 和 MSL 的语义有差异有些特性比如几何着色器Metal 支持得不好需要绕路实现。热搜里“DXMT”和“FEX-Emu”经常一起出现因为它们俩是搭档FEX-Emu 负责 CPU 指令翻译DXMT 负责 GPU 指令翻译。两个都跑通了一个 Windows 游戏才能在 iOS 上真正“动起来”。2.4 组件协作关系一览组件职责输入输出iOS 上的主要障碍WineWindows API 翻译PE 可执行文件、Win32 调用POSIX 调用、Unix 信号沙盒限制、字体缺失、注册表FEX-Emux86-64 指令翻译x86-64 机器码ARM64 机器码JIT 权限、内存序、SIMDDXMTDirect3D 到 MetalD3D11/D3D12 调用、HLSLMetal 调用、MSL着色器编译、特性差异Madeira整合与调度上述三者可运行的 iOS App签名、权限、性能调优这张表是我自己整理的理解框架实际项目中各组件边界会有交叉比如 Wine 的图形驱动层可能直接和 DXMT 交互FEX-Emu 也可能需要 Wine 提供的内存管理配合。3. 在 iOS 上跑通 Madeira 的实操路径3.1 环境准备你需要什么先说清楚Madeira 不是一个 App Store 上能下载的成品。它是一个需要自己编译、自己签名、自己部署的项目。你需要一台 macOS 电脑编译和签名必须用 Xcode一台 iOS 设备建议 A12 以上芯片内存 4GB 起步Xcode 最新稳定版以及对应的 iOS SDK一个 Apple 开发者账号免费账号也能签名但有效期只有 7 天足够的耐心——这套东西的编译链很长中间任何一环出错都会卡住热搜里“xcode从证书配置到上架全流程”“免费证书ios”“xcode打包ios突然很慢如何解决”这些词说明很多人卡在签名和打包环节。Madeira 这种项目通常不会上架 App Store而是用侧载方式安装所以你需要熟悉ios-deploy、AltStore或者 Xcode 直接部署的流程。3.2 编译 Wine 的 iOS 移植版Wine 官方不支持 iOS你需要用 Madeira 项目提供的补丁集。大致步骤# 克隆 Wine 源码和 Madeira 补丁 git clone https://gitlab.winehq.org/wine/wine.git cd wine git checkout wine-9.0 # 具体版本看 Madeira 文档 # 应用 Madeira 的 iOS 补丁 patch -p1 /path/to/madeira/wine-ios.patch # 配置编译目标为 arm64-apple-ios ./configure --hostarm64-apple-ios \ --with-wine-tools/path/to/wine-tools \ --disable-tests \ --without-x \ --without-alsa \ --without-pulse make -j$(sysctl -n hw.ncpu)这里有几个关键点。--without-x是必须的iOS 没有 X11--without-alsa和--without-pulse也是必须的iOS 的音频走 CoreAudioWine 的音频驱动要换成 Madeira 提供的 CoreAudio 后端。--with-wine-tools指向一个在 macOS 上编译的 Wine 工具集因为交叉编译时有些工具需要在宿主上运行。编译过程中最常见的错误是头文件找不到。iOS SDK 的路径和 macOS 不一样你需要设置SDKROOT和CFLAGSexport SDKROOT$(xcrun --sdk iphoneos --show-sdk-path) export CFLAGS-isysroot $SDKROOT -miphoneos-version-min14.0 -arch arm64 export LDFLAGS-isysroot $SDKROOT -miphoneos-version-min14.0 -arch arm64-miphoneos-version-min14.0是最低部署版本设太低有些 API 用不了设太高老设备跑不了。我一般设 14.0覆盖大部分还在用的设备。3.3 编译 FEX-Emu 并处理 JIT 权限FEX-Emu 的编译相对独立git clone https://github.com/FEX-Emu/FEX.git cd FEX mkdir build cd build cmake .. -DCMAKE_TOOLCHAIN_FILE/path/to/ios.toolchain.cmake \ -DCMAKE_BUILD_TYPERelease \ -DENABLE_JITON \ -DCMAKE_INSTALL_PREFIX/path/to/install make -j$(sysctl -n hw.ncpu)ENABLE_JITON是性能关键。但如前所述iOS 上 JIT 需要特殊权限。如果你用免费开发者账号签名JIT 权限拿不到只能跑解释模式。解释模式下 FEX-Emu 会把每条 x86 指令逐条翻译执行性能大概是 JIT 的十分之一。跑个记事本还行跑游戏基本没戏。拿到 JIT 权限的合法途径是加入 Apple Developer Program付费账号然后在 entitlements 文件里声明keycom.apple.security.cs.allow-jit/key true/ keycom.apple.security.cs.allow-unsigned-executable-memory/key true/即便如此iOS 的 JIT 也有额外限制生成的代码页必须用MAP_JIT标志映射而且要在pthread_jit_write_protect_np的保护下写入。FEX-Emu 的 iOS 移植版需要专门处理这些。3.4 集成 DXMT 并配置图形后端DXMT 的编译依赖 Metal 框架必须在 macOS 上做git clone https://github.com/3Shain/dxmt.git cd dxmt meson setup build --cross-file /path/to/ios-cross.txt ninja -C build编译出来的dxmt.dll和d3d11.dll、d3d12.dll要放到 Wine 的drive_c/windows/system32目录下。Wine 加载 D3D 程序时会优先找这些 DLL找到就用 DXMT 翻译找不到就回退到 Wine 自带的软件渲染。配置图形后端时Wine 的注册表要改# 在 Wine 的注册表里设置 DXMT 为默认 D3D 实现 wine reg add HKEY_CURRENT_USER\Software\Wine\Direct3D /v renderer /t REG_SZ /d dxmt /f这一步不做的话Wine 可能还是走 wined3dWine 自带的 D3D 实现性能差很多。DXMT 直接对接 Metal少了中间层。3.5 打包成 iOS App 并侧载所有组件编译完后你需要一个“壳”App 来加载它们。Madeira 项目通常提供一个 Xcode 工程模板里面包含一个 iOS App 目标负责创建沙盒环境Wine 的libwine.a静态库FEX-Emu 的libfex.aDXMT 的 Metal 着色器库一个启动器界面让用户选择要运行的 exe 文件打包命令xcodebuild -project Madeira.xcodeproj \ -scheme Madeira \ -configuration Release \ -sdk iphoneos \ -archivePath build/Madeira.xcarchive \ archive然后导出 ipa用ios-deploy或 AltStore 安装到设备。免费账号签名的 App 7 天后过期需要重新签名付费账号可以签一年。热搜里“ios开发者模式”“ios 26.3.1怎么开发者模式”说明很多人卡在开发者模式开启上。iOS 16 以后侧载 App 需要在“设置-隐私与安全性-开发者模式”里手动开启而且设备要重启。这个步骤不做App 装上了也打不开。4. 实操中绕不开的坑与排查手册4.1 Wine 中文乱码字体和 locale 双管齐下“wine 乱码”“wine 栏是乱码”是最高频的问题。原因有两个一是 Wine 找不到中文字体二是 locale 没设对。解决字体问题最直接的办法是把中文字体复制到 Wine 的字体目录# 把 iOS 系统字体或自己准备的中文字体复制到 Wine 字体目录 cp /System/Library/Fonts/PingFang.ttc \ ~/Library/Application\ Support/Madeira/drive_c/windows/Fonts/然后在 Wine 注册表里把默认字体替换掉wine reg add HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes \ /v MS Shell Dlg /t REG_SZ /d PingFang SC /f wine reg add HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes \ /v MS Shell Dlg 2 /t REG_SZ /d PingFang SC /flocale 问题用LANG环境变量解决export LANGzh_CN.UTF-8 export LC_ALLzh_CN.UTF-8但 iOS 上不一定有zh_CN.UTF-8的 locale 数据你可能需要把 glibc 的 locale 文件打包进 App。Madeira 项目一般会自带一份精简的 locale 数据。4.2 FEX-Emu 崩溃内存序和 SIMD 是重灾区FEX-Emu 跑某些程序时崩溃日志里常见SIGSEGV或SIGILL。排查思路先看是不是 TSO 模式没开。多线程程序在 ARM 的弱内存序下会读到脏数据开 TSO 能解决大部分诡异崩溃。FEX-Emu 的配置项TSOEnabled要设为1。再看是不是 AVX 指令。FEX-Emu 对 AVX 的支持是分阶段的有些 AVX2 指令翻译不完整。可以试试用FEX_AVX0禁用 AVX让程序回退到 SSE。最后看 JIT 缓存。FEX-Emu 的 JIT 缓存如果损坏会导致随机崩溃。删掉缓存目录重新跑rm -rf ~/Library/Caches/Madeira/FEXCache4.3 DXMT 黑屏或花屏着色器编译失败DXMT 跑游戏时黑屏大概率是着色器编译失败。Metal 的着色器编译器比 D3D 的严格有些 HLSL 写法在 MSL 里不合法。排查方法开 DXMT 的调试日志看哪个着色器编译报错export DXMT_LOG_LEVELdebug export DXMT_LOG_FILE/tmp/dxmt.log如果是特定游戏的问题可以试试禁用某些 D3D 特性export DXMT_FEATURE_LEVEL11_0 # 强制用 D3D11 feature level 11_0 export DXMT_DISABLE_TESSELLATION1 # 禁用曲面细分花屏通常是纹理格式不匹配。Metal 对某些压缩纹理格式如 BC6H支持不好DXMT 需要做格式转换。如果转换失败就会花屏。这种情况只能等 DXMT 更新或者换一个纹理格式兼容的游戏。4.4 常见问题速查表现象可能原因排查步骤解决方案Wine 中文显示方块字体缺失或 locale 错误检查drive_c/windows/Fonts目录复制中文字体设LANGzh_CN.UTF-8FEX-Emu 随机崩溃TSO 未开或 JIT 缓存损坏看日志是否有 SIGSEGV开 TSO清 JIT 缓存DXMT 黑屏着色器编译失败开 debug 日志降 feature level禁用高级特性App 装完打不开开发者模式未开设置里看开发者模式开启开发者模式并重启签名 7 天过期免费账号限制看 App 是否闪退重新签名或换付费账号性能极低JIT 未启用看 FEX 日志是否走解释器拿 JIT 权限开ENABLE_JIT音频爆音CoreAudio 后端配置错误检查采样率设置设WINE_AUDIO_DRIVERcoreaudio采样率 48000这张表是我自己踩坑后整理的实际排查时按“先看日志再查配置最后换组件”的顺序来能省很多时间。4.5 几个容易被忽略的细节第一iOS 的沙盒路径和 Linux 完全不同。Wine 默认把C:盘映射到~/.wine/drive_c但 iOS 上~是 App 的沙盒目录路径里有随机字符串。Madeira 需要把 Wine 的路径解析逻辑改掉否则程序找不到文件。第二iOS 的内存限制很严。一个 App 最多用设备内存的一半左右iPad Pro 16GB 内存也只能用 8GB 左右。Wine FEX-Emu DXMT 三层加起来基础内存占用就 1GB 起步。跑大游戏时很容易被系统杀掉。解决办法是开内存压缩或者限制 FEX-Emu 的 JIT 缓存大小。第三iOS 的后台策略很激进。App 切到后台几秒钟就可能被挂起Wine 里的程序会直接卡死。Madeira 需要申请后台任务权限或者用音频后台模式保持活跃。但音频后台模式会被 App Store 审核拒绝侧载则无所谓。5. 性能调优与进阶玩法5.1 让 FEX-Emu 跑得更快FEX-Emu 的性能调优有几个方向开大 JIT 缓存默认缓存可能只有几十 MB跑大程序时频繁重新翻译。把JITCacheSize设到 256MB 或更大能显著减少翻译开销。用多线程翻译FEX-Emu 支持多线程 JITNumJITThreads设为 CPU 核心数的一半左右。设太多会抢主线程资源设太少翻译跟不上。关掉不必要的指令集模拟如果程序不用 AVX就FEX_AVX0不用 x87就FEX_X870。每关一个翻译器就少一层判断。实测下来一个简单的 Windows 记事本程序JIT 模式下启动时间 2 秒左右解释模式下要 20 秒以上。差距就是这么明显。5.2 DXMT 的 Metal 着色器缓存DXMT 每次启动都要重新编译着色器很浪费时间。Metal 支持着色器缓存可以把编译结果存到磁盘export DXMT_SHADER_CACHE1 export DXMT_SHADER_CACHE_PATH~/Library/Caches/Madeira/DXMTShaders第一次跑游戏时编译慢之后就从缓存加载启动快很多。但缓存文件可能很大一个 3A 游戏的着色器缓存能到几百 MBiOS 设备存储紧张的话要定期清理。5.3 用 MetalFX 做超分辨率MetalFX 是苹果的超分辨率技术类似 DLSS。DXMT 可以把游戏的渲染分辨率降低然后用 MetalFX 放大到屏幕分辨率帧率能提升不少。配置export DXMT_METALFX1 export DXMT_METALFX_SCALE0.7 # 渲染分辨率是屏幕的 70%0.7 是个比较平衡的值再低画面就糊了。这个功能对 GPU 瓶颈的游戏效果明显对 CPU 瓶颈的游戏没用——因为瓶颈在 FEX-Emu 的指令翻译上。5.4 外接键鼠和手柄iOS 支持蓝牙键鼠和手柄但 Wine 默认的输入处理不一定认。Madeira 需要把 iOS 的 GameController 框架事件转成 Windows 的输入消息。手柄相对简单键鼠麻烦一些——iOS 的鼠标事件和 Windows 的鼠标事件语义不同右键、滚轮、侧键都要单独映射。我试过用 Xbox 手柄跑一个 Windows 老游戏按键映射对了之后体验还行但震动反馈没有——Wine 的震动 API 在 iOS 上没有对应实现。这个只能等 Madeira 后续版本补。6. 这套东西的边界在哪里Madeira 能跑的东西有明确边界。简单的 Win32 程序、老游戏、行业工具跑起来问题不大。但以下几类基本没戏依赖内核态驱动的程序反作弊系统、虚拟光驱、某些加密狗驱动这些需要内核权限Wine 在用户态根本模拟不了。依赖 .NET 大型框架的程序Wine 的 Mono 实现覆盖不了 .NET 的全部 APIWPF、WCF 这些跑不起来。依赖最新 DirectX 特性的游戏D3D12 Ultimate 的特性如光线追踪DXMT 支持有限跑起来要么崩溃要么效果缺失。32 位程序FEX-Emu 主要处理 x86-6432 位 x86 的支持不完整。虽然 Wine 有 WoW64 方案但在 iOS 上这套还没跑通。热搜里“银行模拟器ios”“ios游戏”这些词说明有人想跑金融类或游戏类程序。金融类程序通常有反调试和驱动级保护Madeira 跑不了游戏类要看具体引擎Unity 和 Unreal 的老版本有成功案例新版本够呛。7. 我个人在实际操作中的体会折腾 Madeira 这套东西最大的感受是“每一层都在和平台限制搏斗”。Wine 在和 iOS 的沙盒搏斗FEX-Emu 在和 JIT 权限搏斗DXMT 在和 Metal 的特性差异搏斗。你能跑通一个程序不是因为某一层特别强而是因为所有层的短板刚好没撞上。如果让我给想入坑的人一个建议先从最简单的 Win32 程序开始比如记事本、计算器把整条链路跑通再逐步换更复杂的程序。不要一上来就怼 3A 游戏那样你会在无数个崩溃日志里迷失方向。每跑通一个程序就记录下它的配置和遇到的坑慢慢积累自己的“兼容性数据库”。另外这类项目的更新频率很高今天能跑的明天可能就崩了。建议锁定一个稳定版本不要盲目追新。我自己的做法是每个组件都留一个“已知可用”的版本快照出问题了就回滚比从头排查快得多。最后分享一个小技巧Wine 的WINEDEBUG环境变量能输出大量调试信息但全开的话日志会大到没法看。我一般用WINEDEBUGloaddll,module只看模块加载排查 DLL 缺失问题用WINEDEBUGseh看异常排查崩溃问题。按需开别全开。
返回列表