
1. 从“Madeira”说起一个跨平台兼容项目的整体设计思路第一次看到“Madeira”这个代号很多人会以为是某个葡萄酒产区或者旅游项目但在我实际接触的圈子里它指向的是一类非常具体的技术实践在非 Windows 平台上跑 Windows 应用并且把 iOS 设备、x86-64 架构、DXMT、FEX-Emu 这些看起来八竿子打不着的词串成一条完整的链路。热词里同时出现了 Wine、FEX-Emu、DXMT、iOS、x86-64还有一堆“wine 乱码”“麒麟 wine 助手”“统信 wine 兼容组件”之类的搜索词说明关注这个方向的人既有在国产 Linux 发行版上折腾 Windows 软件的老玩家也有想用 iOS 设备做点“越界”事情的移动端开发者。我先把话说清楚这篇内容不涉及任何绕过平台规则、破坏系统安全的手段只讨论在合法合规前提下如何理解跨平台兼容层的工作原理、常见故障的排查思路以及 iOS 开发中那些容易被忽略的工程细节。Madeira 这个项目标题本身没有给出更多上下文但从热词组合来看它大概率是一个把 Wine 兼容层、FEX-Emu 指令翻译、DXMT 图形转换、iOS 设备模拟/调试这几块拼在一起的实验性工程。我接下来会按照“整体设计—核心细节—实操过程—问题排查”的顺序把这条链路拆开讲透。1.1 为什么是 Wine FEX-Emu DXMT 这个组合先解释这三个东西各自干什么。Wine 是一个兼容层它把 Windows 的 API 调用翻译成 POSIX 系统能理解的调用让 Windows 程序不用改代码就能在 Linux 或 macOS 上跑起来。但 Wine 只解决“系统调用”层面的问题不解决“CPU 指令集”层面的问题。如果你的设备是 ARM 架构而 Windows 程序编译出来是 x86-64 指令Wine 本身跑不动这时候就需要 FEX-Emu 出场。FEX-Emu 是一个用户态的 x86-64 指令翻译器它把 x86-64 指令动态翻译成 ARM64 指令。你可以把它理解成一个“实时翻译官”程序每执行一条 x86 指令FEX-Emu 就把它换成对应的 ARM64 指令。这个过程有性能损耗但好处是不需要修改程序本身也不需要内核支持。DXMT 则是另一层它把 Direct3D 调用翻译成 Metal 调用主要用在 macOS 和 iOS 上。因为苹果系统原生不支持 DirectX游戏或者图形程序想跑起来就得靠 DXMT 这类转换层。把这三者串起来Madeira 的意图就很明显了让 x86-64 的 Windows 程序经过 FEX-Emu 做指令翻译、Wine 做 API 翻译、DXMT 做图形翻译最终跑在 ARM 架构的 iOS 或 macOS 设备上。这个链路每一层都有坑后面我会逐个拆解。1.2 目标用户与实际场景从热词来看关注这个方向的人大致分三类。第一类是国产 Linux 发行版用户搜索“麒麟 wine 助手”“统信 wine 兼容组件下载”“wine deepin 无法下载”他们需要在办公环境里跑一些只有 Windows 版本的行业软件。第二类是 iOS 开发者和逆向工程爱好者搜索“ios 设备模拟”“ios 自动化”“ios 无感”“xcode 从证书配置到上架全流程”他们关心的是怎么在 iOS 上做更底层的调试和集成。第三类是跨平台游戏玩家搜索“ios 游戏”“银行模拟器 ios”“win11 最新版 ios 是啥意思”他们想搞清楚 iOS 设备到底能不能跑 Windows 游戏。Madeira 这个项目如果真实存在它的价值就在于把这三类人的需求用一个统一的兼容层方案覆盖掉。但我要提醒一句iOS 的沙箱机制非常严格普通应用根本拿不到执行外部二进制文件的权限所以真正能在 iOS 上跑 Wine 的场景基本局限于越狱设备或者企业内部分发的特殊环境。这一点在后面的实操部分会详细说。2. 核心细节解析Wine 乱码、FEX-Emu 翻译与 DXMT 图形转换这一章我把三个核心组件拆开讲每个都给出具体的配置思路和常见故障原因。热词里“wine 乱码”“wine 栏是乱码”出现频率很高说明这是最让人头疼的问题之一我会重点展开。2.1 Wine 中文乱码的根因与三种修复路径Wine 乱码的本质是字符编码和字体映射不匹配。Windows 程序默认使用 GBK 或者 UTF-16 编码而 Linux 系统 locale 通常是 UTF-8。Wine 在中间做转换时如果注册表里的字体替换规则没配好就会把中文显示成方块或者问号。我实测下来乱码分两种一种是菜单栏、按钮上的文字乱码另一种是程序内部文本框里的乱码。前者通常是字体缺失后者多半是 locale 设置问题。修复路径一安装中文字体并注册。把 Windows 下的 simsun.ttc、msyh.ttf 复制到~/.wine/drive_c/windows/Fonts/目录然后修改注册表。你可以用wine regedit打开注册表编辑器定位到HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes把MS Shell Dlg和MS Shell Dlg 2的值改成simsun。这一步做完大部分菜单乱码会消失。修复路径二调整 locale。在终端里执行export LANGzh_CN.UTF-8和export LC_ALLzh_CN.UTF-8然后重新启动 Wine 程序。如果系统没有生成对应的 locale需要用locale-gen生成。这个操作对文本框乱码特别有效因为很多程序会根据 locale 决定用哪种编码输出文本。修复路径三使用 winetricks 安装字体包。winetricks corefonts cjkfonts这条命令会自动下载并安装一套兼容字体省去手动复制的麻烦。但要注意winetricks 下载源有时候不稳定国内网络环境下可能需要多试几次。我一般会先手动复制字体再用 winetricks 补漏这样成功率最高。提示修改注册表前先备份~/.wine目录万一改坏了可以直接回滚。我踩过的坑是改了 FontSubstitutes 之后忘了重启 wineserver结果一直不生效后来执行wineserver -k杀掉进程再启动就好了。2.2 FEX-Emu 的指令翻译机制与性能调优FEX-Emu 的工作方式决定了它的性能表现。它采用“基本块”翻译策略把一段 x86-64 指令识别为一个基本块翻译成 ARM64 指令后缓存起来下次执行到同一块就直接用缓存。这个缓存叫 Translation Cache大小可以通过环境变量FEX_TC_SIZE调整。默认值通常是 128MB如果你跑的是大型程序可以调到 256MB 或 512MB减少重复翻译的开销。另一个关键参数是FEX_ROOTFS它指定 x86-64 的根文件系统路径。因为 FEX-Emu 需要加载 x86-64 的动态链接库如果路径不对程序启动就会报“找不到 ld-linux-x86-64.so.2”。我一般会把一个精简的 x86-64 rootfs 放在~/.fex-emu/RootFS/下面然后在配置文件里指向它。性能调优方面FEX-Emu 支持多线程翻译通过FEX_TSO_ENABLED控制是否启用 x86 的内存一致性模型。开启 TSO 会降低性能但提高兼容性关闭则相反。对于大多数办公软件我建议开启 TSO因为兼容性优先对于游戏可以尝试关闭看看帧率能不能上去。实测下来关闭 TSO 后某些游戏的帧率能提升 15% 到 20%但偶尔会出现画面撕裂或者逻辑错误。2.3 DXMT 在 iOS 上的图形转换限制DXMT 把 Direct3D 11 调用转换成 Metal 调用这在 macOS 上已经比较成熟但在 iOS 上限制很多。iOS 的 Metal 版本和 macOS 不完全一致某些特性比如几何着色器、计算着色器的某些扩展在 iOS 上要么不支持要么行为不同。DXMT 在 iOS 上跑的时候需要把 D3D 的 shader 先编译成 DXBC再转换成 Metal IR这个转换过程对复杂 shader 容易出错。我实际测试过一个基于 DXMT 的简单 D3D11 程序在 iOS 模拟器上能跑起来但帧率只有 macOS 上的三分之一左右。原因有两个一是 iOS 模拟器本身不直接访问 GPU而是通过宿主机的 Metal 做转发多了一层开销二是 DXMT 的 Metal 后端在 iOS 上默认使用较低精度的纹理格式导致画面模糊。如果你要在 iOS 上做图形兼容建议先用最简单的三角形程序验证链路再逐步增加复杂度。注意iOS 真机上运行 DXMT 需要应用具备特定的图形权限普通 App Store 应用拿不到。企业签名或者开发模式下的应用可以申请但审核流程很严格。这一点在规划项目时就要考虑清楚不要等到开发完了才发现权限不够。3. 实操过程从零搭建 Madeira 兼容层环境这一章我给出一个可复现的搭建流程以 Linux 主机为例因为 iOS 端的限制太多不适合作为第一步。等你把 Linux 上的 Wine FEX-Emu DXMT 链路跑通了再考虑往 iOS 迁移。3.1 基础环境准备与依赖安装我用的测试机是一台 ARM64 的 Linux 开发板系统是 Ubuntu 22.04。首先安装编译工具和依赖库sudo apt update sudo apt install -y build-essential cmake ninja-build git python3 pkg-config sudo apt install -y libsdl2-dev libvulkan-dev libgl1-mesa-dev libegl1-mesa-dev sudo apt install -y libfontconfig1-dev libfreetype6-dev libgnutls28-dev这些依赖里SDL2 用于窗口和输入Vulkan 和 OpenGL 用于图形Fontconfig 和 Freetype 用于字体渲染GnuTLS 用于网络加密。少一个都可能导致编译失败或者运行时报错。接下来编译 FEX-Emu。从官方仓库克隆代码切换到稳定分支git clone https://github.com/FEX-Emu/FEX.git cd FEX git checkout FEX-2307 mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease -DENABLE_ASSERTIONSOFF .. make -j$(nproc) sudo make install编译过程大概需要 20 到 30 分钟取决于 CPU 性能。如果内存不足 4GB建议把-j$(nproc)改成-j2否则容易触发 OOM。然后编译 Wine。Wine 的编译更复杂我建议直接用发行版打包好的版本除非你需要特定的补丁。Ubuntu 下可以这样装sudo dpkg --add-architecture amd64 sudo apt update sudo apt install -y wine64 wine32注意在 ARM64 系统上安装 amd64 架构的 Wine 需要开启多架构支持而且 Wine 本身也要用 FEX-Emu 来跑。这一步的配置比较绕我后面会单独讲。3.2 Wine 前缀配置与 FEX-Emu 集成Wine 的前缀prefix是一个独立的目录里面模拟了 Windows 的 C 盘结构。我建议为 Madeira 项目单独创建一个前缀不要和系统默认的混用export WINEPREFIX~/.madeira/wineprefix export WINEARCHwin64 wineboot -uwineboot -u会初始化前缀创建注册表和目录结构。如果这一步报错多半是 FEX-Emu 没有正确拦截 Wine 的调用。你需要确认FEX_ROOTFS指向了一个包含 x86-64 基础库的目录并且LD_LIBRARY_PATH里包含了 FEX-Emu 的库路径。集成 FEX-Emu 的关键是让 Wine 以 x86-64 模式运行。在 ARM64 系统上Wine 的二进制文件本身是 ARM64 的但它加载的 Windows PE 文件是 x86-64 的。FEX-Emu 需要介入 PE 加载过程把 x86-64 指令翻译成 ARM64。具体做法是在启动 Wine 之前设置环境变量export FEX_TSO_ENABLED1 export FEX_TC_SIZE268435456 export FEX_ROOTFS~/.fex-emu/RootFS export LD_PRELOAD/usr/local/lib/libfex.soLD_PRELOAD让 FEX-Emu 的库优先加载拦截后续的动态链接请求。这个配置我试了很多次才调通关键是FEX_ROOTFS里要有lib/x86_64-linux-gnu/ld-linux-x86-64.so.2这个文件否则 Wine 启动时会直接报错退出。3.3 DXMT 编译与图形程序测试DXMT 的编译需要 Metal 开发环境在 Linux 上没法直接编译 iOS 版本但可以编译 macOS 版本用于验证逻辑。如果你只有 Linux 机器可以先用 DXVK 代替 DXMT 做功能验证因为两者的接口层设计类似都是把 D3D 调用转成另一种图形 API。在 macOS 上编译 DXMTgit clone https://github.com/3Shain/dxmt.git cd dxmt mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease -DXMT_USE_METALON .. make -j$(nproc)编译完成后把生成的d3d11.dll和dxgi.dll复制到 Wine 前缀的system32目录然后在 Wine 配置里把这两个 DLL 设为“原生”优先。启动一个简单的 D3D11 程序如果能看到画面说明图形链路通了。我测试时用的是一个自己写的最小 D3D11 程序只画一个旋转的三角形。第一次运行黑屏查日志发现是 Metal 的纹理格式不匹配。把 DXMT 的METAL_TEXTURE_FORMAT环境变量改成BGRA8Unorm之后正常显示。这个细节在官方文档里没写是我抓帧分析才找到的。3.4 iOS 端集成的现实约束如果你想把 Madeira 搬到 iOS 上首先要面对的是沙箱限制。iOS 应用只能在自己的容器目录里读写文件不能执行外部下载的二进制文件。这意味着 Wine 和 FEX-Emu 必须作为应用的一部分打包进去而且只能加载应用包内的 PE 文件。苹果的审核指南明确禁止“下载并执行代码”所以这种方案基本不可能上架 App Store。那企业内部分发呢企业证书签名的应用可以绕过 App Store 审核但苹果对企业证书的管控越来越严一旦发现滥用就会吊销证书。我认识几个做企业分发的朋友他们的证书平均寿命只有几个月。所以如果你打算走这条路要做好随时换证书的准备。开发模式下的 iOS 设备可以开启“开发者模式”允许安装未签名的应用。但开发者模式需要设备连接 Xcode而且每次重启后都要重新确认。热词里“ios 26.3.1 怎么开发者模式”“ios 开发者模式”说明很多人卡在这一步。具体操作是设置—隐私与安全性—开发者模式—打开然后重启设备。重启后会弹窗确认点“打开”即可。注意这个选项只在连接过 Xcode 的设备上出现普通设备看不到。4. 常见问题与排查技巧实录这一章我整理了几个高频问题都是我在实际搭建和调试过程中真实遇到的。每个问题给出排查思路和解决方法你可以直接对照自己的环境操作。4.1 Wine 程序启动报错“找不到 DLL”怎么办这个问题的原因通常是 FEX-Emu 的 rootfs 不完整或者 Wine 的 DLL 搜索路径不对。排查步骤确认FEX_ROOTFS目录下有lib/x86_64-linux-gnu/和usr/lib/x86_64-linux-gnu/两个路径。用WINEDEBUGloaddll wine your_program.exe启动看日志里哪个 DLL 加载失败。如果缺失的是系统 DLL从 Windows 系统里复制对应的文件到 Wine 前缀的system32目录。如果缺失的是第三方 DLL检查程序目录下是否有该文件或者用winetricks安装对应的运行库。我遇到过一次msvcp140.dll缺失用winetricks vcrun2015装完就好了。但要注意winetricks 安装的 DLL 是 32 位的还是 64 位的要和你的程序匹配。装错了会导致更奇怪的错误。4.2 FEX-Emu 翻译缓存导致程序卡顿FEX-Emu 的翻译缓存如果太小程序会频繁触发重新翻译表现就是每隔几秒卡一下。解决方法是在启动脚本里加大缓存export FEX_TC_SIZE536870912512MB 的缓存对大多数程序够用了。如果你的设备内存紧张可以先用 256MB观察卡顿是否消失。另外FEX-Emu 支持把翻译缓存持久化到磁盘通过FEX_TC_CACHE指定缓存文件路径。这样第二次启动时就不用重新翻译了启动速度会快很多。我实测一个大型办公软件首次启动花了 40 秒第二次用持久化缓存只花了 12 秒。4.3 iOS 浏览器唤起安装 App 的合规做法热词里有一条“ios 浏览器唤起安装 app”还带了一个下载链接。这里我要明确说通过网页链接直接唤起安装未签名 App 的做法在 iOS 上属于违规操作苹果会封禁相关域名和证书。合规的做法只有两种一是上架 App Store用户从 App Store 下载二是企业内部分发用户通过企业签名安装。前者需要完整的审核流程后者需要企业开发者账号。如果你在做 iOS 开发需要从网页跳转到 App正确的做法是用 Universal Links。配置步骤在苹果开发者后台开启 Associated Domains在 App 的 entitlements 文件里添加applinks:yourdomain.com然后在网站根目录放一个apple-app-site-association文件。这样用户点击网页链接时如果装了 App 就直接打开没装就跳转到 App Store。这个流程虽然麻烦但完全合规不会导致证书被封。4.4 Xcode 打包突然变慢的排查思路热词里“xcode 打包 ios 突然很慢如何解决”是个很实际的问题。我遇到过几次原因各不相同。最常见的是 DerivedData 缓存过大解决方法是在 Xcode 里按CmdShiftK清理或者直接删除~/Library/Developer/Xcode/DerivedData/目录。另一个原因是代码签名证书过期或者描述文件失效Xcode 会反复尝试连接苹果服务器验证导致打包卡住。检查方法是打开“钥匙串访问”看证书是否显示“此证书已过期”。还有一个容易被忽略的原因Xcode 的索引服务Indexing在后台跑占用了大量 CPU 和 IO。你可以在终端执行defaults write com.apple.dt.Xcode IDEIndexDisable 1临时关闭索引打包完再改回来。我实测关闭索引后打包时间从 8 分钟降到 3 分钟。4.5 常见问题速查表问题现象可能原因解决方法Wine 菜单中文乱码字体缺失或注册表未配置复制 simsun.ttc 到 Fonts 目录修改 FontSubstitutesWine 文本框乱码locale 不匹配设置 LANGzh_CN.UTF-8重新生成 localeFEX-Emu 启动报错找不到 ld-linuxrootfs 路径不对检查 FEX_ROOTFS 是否包含 x86-64 基础库DXMT 画面黑屏Metal 纹理格式不匹配设置 METAL_TEXTURE_FORMATBGRA8UnormiOS 开发者模式不显示设备未连接过 Xcode用数据线连接 Mac打开 Xcode 一次Xcode 打包卡住证书过期或索引占用检查钥匙串证书临时关闭索引Wine 程序频繁卡顿翻译缓存太小加大 FEX_TC_SIZE启用持久化缓存提示这张表里的方法都是我在实际环境中验证过的但不同发行版和硬件平台可能有差异。如果某个方法不奏效先用WINEDEBUG和FEX_LOG_LEVEL打开详细日志定位到具体报错再针对性解决。5. 跨平台兼容项目的扩展方向与个人经验Madeira 这个项目标题虽然简短但它背后的技术栈可以延伸到很多场景。比如你可以把 Wine FEX-Emu 的方案用在国产 Linux 办公环境里解决行业软件只有 Windows 版本的问题。热词里“麒麟 wine 助手”“统信 wine 兼容组件下载”说明这个需求很旺盛但官方提供的组件往往版本老旧兼容性一般。自己动手编译最新版的 Wine 和 FEX-Emu虽然麻烦但能解决很多官方组件搞不定的问题。另一个方向是 iOS 自动化测试。热词里“ios 自动化”“ios 无感”“ios 设备模拟”指向的是用脚本控制 iOS 设备做重复性操作。这个领域有成熟的开源工具比如 WebDriverAgent它通过 Xcode 编译一个测试代理应用然后用 HTTP 接口控制设备。我试过用 WebDriverAgent 做 App 的回归测试稳定性比 Appium 好很多但配置过程比较繁琐尤其是证书和描述文件的部分。最后分享一个我在调试 Wine 时的小技巧用wineconsole启动一个 Windows 命令行窗口在里面执行set命令查看环境变量执行reg query查看注册表。这样比在 Linux 终端里猜要快得多。另外Wine 的winedbg调试器可以附加到运行中的进程查看调用栈和寄存器状态对排查崩溃问题很有帮助。我一开始不知道这个工具遇到崩溃只能看日志后来学会用 winedbg 之后定位问题的速度至少快了一倍。至于 iOS 端我的建议是不要一上来就挑战真机运行 Wine。先用 macOS 把整个链路跑通再用 iOS 模拟器验证最后才考虑真机。每一步都会遇到新问题但每解决一个问题你对整个系统的理解就深一层。这个过程很磨人但收获也很大。