
1. 从Madeira这个名字说起一个跨平台兼容层的野心第一次看到Madeira这个项目名我脑子里蹦出来的不是葡萄牙那座产葡萄酒的岛屿而是它背后那串关键词——FEX-Emu、Wine、DXMT、iOS、x86-64。这几个词凑在一起稍微有点系统层经验的人都能嗅到味道这是一个想在非x86架构上跑x86-64 Windows程序的兼容方案而且目标平台很可能涉及移动端。先把这几个关键词的关系理清楚不然后面全是雾水。FEX-Emu是一个用户态的x86-64指令翻译层它做的事情是把x86-64的机器指令在运行时翻译成ARM64指令。注意它和QEMU那种全系统模拟不是一回事——FEX-Emu工作在用户态直接复用宿主机的内核所以性能损耗比全系统模拟小得多。它最早是为ARM Linux设备设计的后来被广泛用于在ARM平台上跑x86的Linux程序。Wine大家相对熟悉它是一个Windows API翻译层把Windows的系统调用翻译成POSIX调用。Wine本身不做CPU指令翻译它假设你的CPU能直接跑x86代码。所以在ARM设备上单独跑Wine是跑不起来的必须有一个指令翻译层垫在下面。DXMT是这两年比较新的一个项目它是Direct3D到Metal的翻译层。以前在macOS上跑Windows游戏主流方案是DXVKD3D转Vulkan再套一层MoltenVKVulkan转Metal链路长、损耗大。DXMT直接把D3D翻译成Metal少了一层效率上有优势。iOS和x86-64放在一起指向就很明确了在ARM64的iOS设备上通过FEX-Emu翻译x86-64指令再通过Wine提供Windows API再通过DXMT把图形调用落到Metal上最终让Windows程序在iOS上跑起来。这个技术栈的每一层都不是新东西但把它们串起来、并且针对iOS这个封闭平台做适配就是Madeira这个项目真正有意思的地方。iOS的限制比macOS和Linux都多得多不能随便fork进程、JIT权限受限、内存管理严格、后台执行几乎不可能。这些限制会直接决定整个方案的架构设计。我下面会按照为什么要这么设计→每一层怎么工作→实际会遇到什么问题→怎么排查的顺序把这个技术栈拆开讲。如果你只是想快速了解能不能在iPhone上跑某个Windows程序可以直接跳到第4节看实测结论如果你想自己动手搭一套那第2、3节的原理和配置细节值得细读。2. 四层技术栈的协作逻辑谁在什么时候接管控制权2.1 从一条x86指令到屏幕像素的完整链路要理解这个方案最好的方式是跟踪一条指令的完整生命周期。假设你在iOS上运行一个Windows下的老游戏它执行了一条mov eax, [ebx0x10]这样的x86-64指令最终这个操作的结果要变成屏幕上的一个像素。第一步FEX-Emu接管。程序加载时FEX-Emu会把x86-64的代码段映射到内存里但不是直接执行而是先做一次翻译缓存。它把x86-64指令块翻译成ARM64指令块缓存起来。第一次执行某段代码时会有翻译开销之后命中缓存就直接跑ARM64代码。这就是为什么FEX-Emu的首次启动总是慢但跑起来之后性能还能接受。第二步Wine接管系统调用。当程序调用CreateFileW、RegOpenKeyEx这类Windows API时Wine的DLL比如kernel32.dll、user32.dll会拦截这些调用转换成对应的POSIX操作。在iOS上这些POSIX操作最终落到沙盒内的文件系统上。第三步图形调用分流。如果程序用的是Direct3D 9/10/11DXMT会把这些D3D调用翻译成Metal API调用。Metal是苹果的原生图形APIiOS上只有这一条路可走OpenGL ES虽然还在但已经废弃。DXMT需要处理着色器编译、资源绑定、状态管理等一大堆细节。第四步Metal渲染到屏幕。这一步是苹果自己的东西不用我们操心但要注意iOS的CAMetalLayer和显示刷新率的配合帧率不稳往往出在这一层。整个链路里FEX-Emu和Wine是串行关系FEX-Emu负责CPU指令翻译Wine负责API翻译两者互不干扰。而DXMT是Wine的一个组件它作为Wine的图形驱动存在Wine把D3D调用转给它它再转给Metal。2.2 为什么不用QEMU全系统模拟很多人第一反应是既然要跑x86程序为什么不直接用QEMU做全系统模拟答案很简单——性能。QEMU的全系统模拟需要模拟整个CPU、内存控制器、外设每一条指令都要经过动态二进制翻译TCG开销极大。在ARM设备上跑x86 WindowsQEMU方案的性能通常是原生性能的5%到15%跑个记事本都卡。FEX-Emu的用户态翻译只翻译用户程序的指令系统调用直接走宿主机内核省掉了大量模拟开销。实测在ARM64设备上FEX-Emu跑x86-64 Linux程序的性能能达到原生的40%到70%具体取决于程序的计算密集程度。对于Windows程序再叠加Wine的API翻译开销整体性能大概在20%到50%之间。这个性能差距决定了QEMU方案只能用来能跑就行的场景FEX-Emu方案才有可能用得下去。Madeira选择FEX-Emu路线本质上是在可用性和兼容性之间做了取舍。2.3 iOS平台带来的额外约束在Linux或macOS上搭这套栈最大的坑是配置和依赖。但在iOS上坑变成了系统根本不让你这么干。JIT权限问题。FEX-Emu的翻译缓存需要可执行内存也就是需要JIT即时编译权限。iOS默认不允许普通App申请可执行内存除非你有特殊的 entitlement比如通过开发者模式或者企业签名。这是整个方案在iOS上最大的门槛。没有JITFEX-Emu只能做解释执行性能会掉到原来的十分之一。进程模型限制。iOS不允许fork只允许创建线程。Wine的某些实现依赖fork来创建进程这在iOS上需要改成线程模型或者用posix_spawn的替代方案。Madeira在这方面做了不少适配工作。内存限制。iOS对每个App的内存使用有硬性上限老设备可能只有1GB到2GB。Wine加上FEX-Emu的翻译缓存再加上Windows程序本身的内存占用很容易触顶。实际使用中需要控制翻译缓存的大小必要时牺牲性能换内存。后台执行。iOS App切到后台后很快会被挂起Windows程序如果依赖后台线程做事情切出去再切回来可能状态就乱了。这个目前没有完美的解决方案只能尽量避免在后台做重活。理解了这些约束再看Madeira的架构设计就能明白它为什么在某些地方做了看起来别扭的选择——那些选择都是被iOS的限制逼出来的。3. 动手搭建从环境准备到跑通第一个程序3.1 环境准备中最容易忽略的三个细节假设你已经有了一个可以侧载App的iOS设备具体怎么侧载不展开这是另一个话题接下来要准备开发环境。这里有几个细节文档里通常不会强调但实际会卡住你。第一个细节Xcode版本和iOS版本的匹配。FEX-Emu和Wine的iOS移植版对Xcode版本有要求通常需要较新的Xcode才能编译通过。但iOS设备本身的版本又不能太新因为太新的iOS会收紧JIT相关的限制。实测下来iOS 15到iOS 17之间的版本相对好搞iOS 18之后限制更严。如果你手头的设备是iOS 26.x对应热搜里提到的ios 26.3.1怎么开发者模式那基本只能走开发者模式自签名路线而且JIT能不能开还得看具体实现。第二个细节依赖库的架构。FEX-Emu、Wine、DXMT这三个项目都需要编译成ARM64的iOS版本。但Wine内部有些组件默认会编译成x86-64因为它假设自己在x86上跑需要显式指定交叉编译目标。DXMT依赖Metal框架只能在macOS上编译不能在Linux上交叉编译。所以整个构建流程必须在macOS上进行Linux只能做部分组件的预编译。第三个细节签名和entitlement。要让App获得JIT权限需要在签名时加上com.apple.security.cs.allow-jit这个entitlement。但普通的免费开发者证书不支持这个entitlement需要付费开发者账号或者企业证书。这是很多人卡住的地方——代码都编译好了装到设备上一跑就崩就是因为JIT权限没给。提示如果你只是想验证方案可行性可以先用macOS版本跑通整个链路macOS对JIT限制宽松得多确认WineDXMT能正常工作后再迁移到iOS。这样能省掉大量在iOS上调试JIT的时间。3.2 FEX-Emu的配置翻译缓存怎么调FEX-Emu的配置主要通过环境变量控制核心的几个参数如下# 翻译缓存大小默认是128MBiOS上建议调小到64MB export FEX_TCACHE_SIZE67108864 # 是否启用多线程翻译iOS上建议开启能明显提升首次加载速度 export FEX_MULTIBLOCK1 # 翻译精度0是快速模式1是精确模式 # 快速模式下某些浮点运算结果会有偏差但性能更好 export FEX_ACCURATE0 # 日志级别调试时设为2正常使用设为0 export FEX_LOG_LEVEL0这几个参数里FEX_TCACHE_SIZE最需要根据设备调整。翻译缓存越大能缓存的翻译块越多重复执行的代码命中率越高。但iOS内存紧张缓存太大会导致OOM。我的经验是iPhone 12及以后的设备可以给到96MB更老的设备给48MB到64MB。FEX_MULTIBLOCK这个参数值得单独说。FEX-Emu默认是单线程翻译也就是一个线程负责翻译其他线程等着。开启多线程翻译后多个线程可以并行翻译不同的代码块首次加载速度能提升30%到50%。但多线程翻译对内存带宽要求更高在低端设备上可能反而变慢。建议在A14及以后的芯片上开启更老的芯片保持关闭。3.3 Wine的配置Windows版本和DLL覆盖Wine的配置核心是winecfg但在iOS上没有图形界面只能通过注册表文件和环境变量配置。关键配置项# 设置Windows版本建议设为win10 export WINEPREFIX/path/to/prefix # 在prefix的system.reg里设置 # [Software\\Microsoft\\Windows NT\\CurrentVersion] # CurrentVersion10.0 # CurrentBuild19041 # DLL覆盖某些程序需要特定的DLL实现 export WINEDLLOVERRIDESmscoree,mshtmlWINEDLLOVERRIDES这个环境变量很关键。mscoree是.NET运行时mshtml是IE的HTML渲染引擎这两个在Wine里实现不完整很多程序加载它们会卡住或者崩溃。把它们禁用掉等号后面为空表示禁用能让不少程序正常启动。另外Wine在iOS上的文件系统映射需要特别注意。iOS的沙盒机制导致Wine的C:\盘实际映射到App沙盒内的某个目录程序如果硬编码了路径比如C:\Program Files\...需要确保这个路径在沙盒内存在。3.4 DXMT的配置Metal设备选择和着色器缓存DXMT的配置相对简单主要是选择Metal设备和配置着色器缓存# 选择Metal设备iOS上通常只有一个 export DXMT_METAL_DEVICE0 # 着色器缓存目录放在沙盒的可写目录下 export DXMT_SHADER_CACHE/path/to/cache # 是否启用异步着色器编译 export DXMT_ASYNC_COMPILE1DXMT_ASYNC_COMPILE这个选项在iOS上特别有用。Metal的着色器编译是同步的如果游戏在运行时动态编译大量着色器会导致明显的卡顿。开启异步编译后着色器编译在后台线程进行主线程继续渲染卡顿会明显减少。但异步编译有个副作用某些着色器在编译完成前对应的绘制调用会被跳过可能导致画面短暂缺失。这个取舍需要根据具体游戏来定。着色器缓存目录要放在沙盒的可写路径下而且要注意缓存大小。iOS对App的磁盘占用也有限制缓存太大可能触发系统清理。建议定期清理旧的缓存文件。4. 实测中的意外情况那些文档不会告诉你的坑4.1 中文乱码从字体到编码的完整排查热搜里出现了wine 乱码和wine 栏是乱码这几乎是所有Wine用户的必经之路。乱码的根因通常有三个层次需要逐层排查。第一层字体缺失。Wine默认不带中文字体Windows程序调用CreateFont创建中文字体时Wine找不到对应字体就会用默认字体渲染结果就是方块或者乱码。解决方案是把中文字体比如思源黑体、文泉驿复制到Wine的字体目录下通常是drive_c/windows/Fonts/。第二层编码不匹配。Windows程序内部可能用GBK编码存储字符串而Wine默认用UTF-8。当程序把GBK字符串传给Wine的API时Wine按UTF-8解析就会乱码。这个问题需要在Wine的locale设置里指定正确的编码export LANGzh_CN.GBK export LC_ALLzh_CN.GBK但iOS的locale支持有限可能需要在Wine内部做编码转换。Madeira在这方面做了一些适配具体实现需要看它的源码。第三层渲染后端问题。如果字体和编码都没问题但界面上的文字还是乱码那可能是DXMT的字体渲染有问题。Metal的字体渲染和Direct3D的字体渲染在抗锯齿、字距调整上有差异某些程序的自绘字体比如用D3D直接画字的可能显示异常。这个只能等DXMT完善或者换用GDI渲染路径。排查顺序建议是先确认字体文件存在再确认locale设置正确最后才怀疑渲染后端。大部分乱码问题在前两步就能解决。4.2 性能断崖什么时候会突然变卡实测下来这套方案在iOS上的性能表现不是线性的有几个明显的断崖点。断崖一翻译缓存溢出。当程序执行的代码量超过翻译缓存大小时FEX-Emu需要淘汰旧的翻译块重新翻译新代码。这个淘汰和重翻译的过程会导致明显的卡顿。表现是程序运行一段时间后突然变卡过一会儿又恢复。解决方案是增大翻译缓存或者限制程序的代码量比如不要同时开太多功能。断崖二着色器编译。前面提到过Metal的着色器编译是同步的。当游戏进入新场景、加载新特效时会触发大量着色器编译导致帧率骤降。这个断崖在DXMT的异步编译开启后会缓解但不能完全消除。实测某些游戏在进入新场景时会有1到2秒的卡顿之后恢复正常。断崖三内存压力。iOS的内存管理很激进当系统内存紧张时会向App发送内存警告App需要释放内存。如果Wine或FEX-Emu没有及时释放缓存系统可能直接杀掉App。表现是程序运行一段时间后突然闪退没有崩溃日志。解决方案是配置Wine和FEX-Emu的内存上限让它们在收到内存警告时主动释放缓存。4.3 兼容性矩阵哪些程序能跑哪些跑不了根据实测和社区反馈这套方案对不同类型的Windows程序兼容性差异很大。下面是一个粗略的兼容性矩阵程序类型兼容性主要问题简单Win32程序记事本、计算器高基本能跑偶有字体问题老游戏D3D92010年前中高大部分能跑性能尚可较新游戏D3D112015年后中部分能跑性能吃紧着色器编译卡顿.NET程序低mscoree实现不完整多数跑不起来依赖IE的程序低mshtml实现不完整需要内核驱动的程序极低iOS不允许加载内核模块反作弊保护的游戏极低反作弊会检测到Wine环境这个矩阵的核心逻辑是越接近纯Win32 API D3D的程序兼容性越好越依赖Windows特有机制.NET、IE、内核驱动、反作弊的程序兼容性越差。4.4 调试技巧怎么定位崩溃点在iOS上调试这套栈最大的困难是日志获取。iOS的日志系统不像Linux那么方便需要一些技巧。技巧一用os_log输出日志。Wine和FEX-Emu的日志可以通过os_log输出到系统日志然后用Console.app或者idevicesyslog查看。需要在代码里把printf替换成os_log或者用重定向的方式把stdout/stderr接到os_log。技巧二崩溃日志符号化。iOS的崩溃日志默认是地址需要符号化才能看懂。把编译时生成的dSYM文件保留好用atos或者Xcode的symbolicatecrash工具符号化崩溃日志。FEX-Emu的翻译代码在崩溃日志里可能显示为未知地址需要结合FEX-Emu自己的日志一起分析。技巧三分阶段验证。不要一上来就跑复杂的Windows程序先用最简单的程序比如一个只调用MessageBox的exe验证整条链路。如果MessageBox能弹出来说明FEX-Emu和Wine的基本功能正常如果弹不出来问题在更底层。然后再逐步增加复杂度跑带图形界面的程序跑带D3D的程序这样能快速定位问题出在哪一层。5. 这套方案的价值边界与后续演进方向5.1 它适合谁不适合谁把话说直白一点Madeira这套方案目前适合的是技术爱好者和研究者不适合普通用户日常使用。适合的场景你想在iOS设备上跑某个特定的老Windows程序这个程序不依赖.NET、不依赖IE、不需要内核驱动、对性能要求不高。你愿意花时间配置环境、调试问题、接受偶尔的崩溃和卡顿。不适合的场景你想用iOS设备替代Windows电脑玩游戏或者办公。性能、兼容性、稳定性都还达不到日常使用的标准。特别是反作弊保护的游戏基本没戏。从技术演进的角度看这套方案的价值不在于现在能跑什么而在于它验证了在iOS上跑x86 Windows程序的技术可行性。随着FEX-Emu的翻译效率提升、Wine的API覆盖度增加、DXMT的Metal适配完善以及iOS对JIT限制的可能松动这个不确定未来这套方案的可用性会逐步提升。5.2 和替代方案的对比在iOS上跑Windows程序除了Madeira这套方案还有几个替代路线路线一远程桌面。在Windows电脑上跑程序iOS设备通过远程桌面协议连接。这个方案性能最好因为计算在Windows端但依赖网络且不能离线使用。路线二云游戏/云电脑。类似远程桌面但服务在云端。性能取决于网络质量且通常需要付费。路线三UTM等全系统模拟。在iOS上跑QEMU全系统模拟然后装Windows。兼容性最好因为是完整的Windows但性能最差且同样受JIT限制。路线四等待苹果开放。苹果在macOS上已经通过Rosetta 2实现了x86到ARM的翻译但iOS上没有类似机制。如果未来苹果在iOS上开放类似的翻译层那Madeira这类方案就没必要存在了。但这个可能性目前看不大。对比下来Madeira的定位是在iOS本地跑x86 Windows程序这个细分场景它的优势是本地执行、不依赖网络劣势是性能损耗和兼容性限制。选择哪条路线取决于你的具体需求。5.3 我在实际配置中总结的几条经验最后分享几条踩坑之后总结的经验都是文档里不会写的。经验一先跑通再优化。不要一上来就追求性能先用默认配置跑通一个最简单的程序确认整条链路没问题然后再逐步调优。我见过太多人卡在配置阶段就是因为想一步到位。经验二日志是你的朋友。FEX-Emu、Wine、DXMT都有日志输出遇到问题先看日志。大部分崩溃和异常在日志里都有线索比盲目猜测高效得多。经验三版本匹配很重要。FEX-Emu、Wine、DXMT三个项目的版本要匹配不要混用不同时期的版本。社区里通常有推荐的版本组合跟着用就行。经验四接受不完美。这套方案目前的状态就是能跑但不完美。会有乱码、会有卡顿、会有崩溃。如果你的需求是稳定可靠那这套方案还不适合你。但如果你的需求是探索技术边界那它很有意思。经验五关注社区动态。FEX-Emu、Wine、DXMT都是活跃的开源项目更新频繁。今天跑不起来的程序可能下个版本就能跑了。定期关注项目的release和issue能省掉很多自己摸索的时间。这套技术栈的每一层都在快速演进现在遇到的问题可能几个月后就不是问题了。保持耐心持续跟进是这个领域最重要的心态。