真机 ARM64 架构转模拟器 ARM64 架构


本文字数:2051字
预计阅读时间:15分钟
![]()
01
需要转换架构的原因
老版 Mac 使用 Intel 芯片,是x86_64架构,相应地在老版 Mac 上运行的模拟器使用的也就是 x86_64架构。
由于模拟器的 x86_64 架构与真机的 arm64、armv7 等架构不冲突,业界为了方便库文件管理,通常会将模拟器架构与真机架构通过 lipo 命令合并为一个 fat 文件。
对于 Intel 芯片的 Mac 这样处理就很高效合理的,但是 Apple 推出了 M 系列芯片的新版 Mac,这就导致模拟器也变成了 arm64 架构,同时 lipo 命令合并二进制文件时不允许出现同名的两个不同架构。也就是只要还采用通用二进制文件(fat 文件)的库管理方式,就无法同时使用真机 arm64 架构和模拟器 arm64 架构。
对于上述的问题, Apple 提供了两个解决方案:
1、XCFramework。可以根据编译环境指定使用的库文件,不用合成就避免了架构重名的问题。但是项目使用的第三方库很多,其他部门的库内又链接了其他的第三方库,全部更换不现实。(我们项目目前只有一个部门支持了XCFramework);
2、Rosetta Simulator。在 Xcode -》Product -》Destination 选项中,可以选择显示 Rosetta 模拟器,这样模拟器运行时使用的依旧是 x86_64 架构,同时搭配 Excluded Architectures = arm64 ,可以解决大部分项目模拟器运行的问题:
但是,visionOS Simulator 被 Apple 远程禁止了 Rosetta 选项!(早期的 Xcode15 beta 版还能显示 Rosetta 的 visionOS 模拟器,更新了一次就不见了。解包下载下来的 visionOS_1_beta_7_Simulator_Runtime,在 Contents/Resources/profile.plist 文件中可以看到支持的架构是包含x86_64的,应该是在Xcode层面禁止了Rosetta选项的出现)。如果我们想要提前验证项目在visionOS上跑起来的效果的话,就需要研究能否将链接库中的真机arm64架构提供给模拟器使用。
02
真机架构与模拟器架构的区别
为了弄清楚真机架构与模拟器架构的区别,我们需要将库文件解剖并对比二进制文件中的差异。
得益于外网的这篇博文arm64-to-sim,我们可以看到博主利用llvm对同一代码分别编译了真机与模拟器架构版本,并直接对比出了差异:
# in the FirebaseAnalytics.xcframework directory
$ otool -fahl ios-arm64_i386_x86_64-simulator/FirebaseAnalytics.framework/FirebaseAnalytics | grep -E 'cmd |\.o' > simulator_cmds
$ otool -fahl ios-arm64_armv7/FirebaseAnalytics.framework/FirebaseAnalytics | grep -E 'cmd |\.o' > native_cmds
$ diff -u native_cmds simulator_cmds
-ios-arm64_armv7/FirebaseAnalytics.framework/FirebaseAnalytics(FirebaseAnalytics_vers.o):
+ios-arm64_i386_x86_64-simulator/FirebaseAnalytics.framework/FirebaseAnalytics(FirebaseAnalytics_vers.o):
cmd LC_SEGMENT_64
- cmd LC_VERSION_MIN_IPHONEOS
+ cmd LC_BUILD_VERSION
cmd LC_SYMTAB
(...) 从上面的结果可以看到,区别只有一处,真机架构使用的loadcommand是LC_VERSION_MIN_IPHONEOS,而模拟器是LC_BUILD_VERSION。我们使用otool命令:
otool -lV xxx.o 可以查看二进制文件中load command的内容,对比结果如下图:
03
如何修改
知道了区别在于load command,自然是要将LC_VERSION_MIN_IPHONEOS改为LC_BUILD_VERSION。但是Mach-O是一种很紧凑的文件格式,Mach-O中的三个区域Header & Load Commands & Data,其中Header记录了平台、文件类型、指令数、指令总大小等信息,Load Commands紧跟Header,其中部分Load Command包含了Data中数据段的起始位置和数据大小:

修改LC_VERSION_MIN_IPHONEOS为LC_BUILD_VERSION会导致Mach-O整体大小发生变化(如图所示,cmdsize不同),Header中的大小信息需要同步修改,由于Data处于Load Commands位置后面,Data所在位置也就发生了偏移,部分load command所指向的位置也需要进行对应的偏移。
体现到代码上,参照arm64-to-sim开源命令行工具。
1、提取静态库中的arm64架构:
private static func extract(inputFileAtUrl url: URL, withArch arch: String, toURL: URL) throws {try shellOut(to: "lipo", arguments: ["-thin",arch,url.path,"-output","lib.\(arch)"], at: toURL.path)
} 2、将提取出的文件切为最小组成成分,并依次处理:
try shellOut(to: "ar", arguments: ["x", extractionUrl.appendingPathComponent("lib.arm64").path])
if let emulator = FileManager.default.enumerator(atPath: extractionUrl.path) {for file in emulator {if let fileString = file as? String {if fileString.hasSuffix(".o") {Transmogrifier.processBinary(atPath: extractionUrl.appendingPathComponent(fileString).path, minos: minos, sdk: sdk)}}}
} 3、更新Header中数据大小:
var header: mach_header_64 = headerData.asStruct()
header.sizeofcmds = UInt32(editedCommandsData.count) 4、替换LC_VERSION_MIN_IPHONEOS,根据cmdsize变化更新其他load command:
static func updatePreiOS12ObjectFile(lc: Data, minos: UInt32, sdk: UInt32) -> Data {let offset = UInt32(abs(MemoryLayout<build_version_command>.stride - MemoryLayout<version_min_command>.stride))let cmd = Int32(bitPattern: lc.loadCommand)switch cmd {case LC_SEGMENT_64:return updateSegment64(lc, offset)case LC_VERSION_MIN_IPHONEOS:return createVersionMin(lc, offset, minos: minos, sdk: sdk)case LC_DATA_IN_CODE, LC_LINKER_OPTIMIZATION_HINT:return updateDataInCode(lc, offset)case LC_SYMTAB:return updateSymTab(lc, offset)case LC_BUILD_VERSION:return updateVersionMin(lc, offset, minos: minos, sdk: sdk)default:return lc}
} 5、将处理后的切片合并组装:
try shellOut(to: "ar", arguments: ["cr", "lib.arm64", "*.o"]) 04
踩坑&进阶
arm64-to-sim在计算LC_BUILD_VERSION大小时没有考虑build_tool_version:
Load command 11cmd LC_BUILD_VERSIONcmdsize 32platform IOSSIMULATORminos 15.5sdk 15.5ntools 1tool LDversion 764.0 Load command 34cmd LC_BUILD_VERSIONcmdsize 24platform IOSSIMULATORminos 13.0sdk 13.0ntools 0 参照MachOView源码:
struct build_tool_version {uint32_t tool;uint32_t version;
};struct build_version_command {uint32_t cmd; // LC_BUILD_VERSIONuint32_t cmdsize; // sizeof(build_version_command) + (ntools * sizeof(build_tool_version)uint32_t platform; // MachoPlatformuint32_t minos; // X.Y.Z is encoded in nibbles xxxx.yy.zzuint32_t sdk; // X.Y.Z is encoded in nibbles xxxx.yy.zzuint32_t ntools; // number build_tool_version entries
}; build_version_command的cmdsize 需要加上build_tool_version 的大小,不然会读取失败。
有些库中包含bitcode文件,bitcode文件与Mach-O文件结构并不相同,上述的处理过程并不能完成架构转换。
llvm提供了llvm-dis、llvm-as工具,llvm-dis可以将bitcode文件转换为文本格式的 .ll文件,llvm-as工具可以将 .ll文件转换为bitcode文件。
打开一个转换好的 .ll 文件,我们可以看到target triple对应的就是架构信息:

参照Xcode Build Log:
-Xlinker -reproducible -target arm64-apple-ios9.0-simulator -isysroot 此处target triple改为arm64-apple-ios8.0.0-simulator即可。
由于bitcode是llvm编译过程的中间产物,不同版本的llvm所生成的bitcode格式并不兼容,需要采用和Xcodecommand line tool所使用的llvm相近的版本。
05
结尾
如果你顺利地完成了以上所有步骤,将项目中的链接库全部转换完成,那么你就可以直接在arm64架构的模拟器上运行你们的项目了。
参考文档:
1、https://bogo.wtf/arm64-to-sim.html
2、https://github.com/luosheng/arm64-to-sim

相关文章:
真机 ARM64 架构转模拟器 ARM64 架构
本文字数:2051字 预计阅读时间:15分钟 01 需要转换架构的原因 老版 Mac 使用 Intel 芯片,是x86_64架构,相应地在老版 Mac 上运行的模拟器使用的也就是 x86_64架构。 由于模拟器的 x86_64 架构与真机的 arm64、armv7 等架构不冲突&…...
敏捷教练CSM认证考了有没有用,谁说了算?
敏捷教练CSM证书是近年来备受关注的一项证书,它被认为可以提升敏捷开发团队的管理能力和项目执行效率。然而,对于这个证书的价值和含金量,人们的观点却不尽相同。那么,CSM证书到底有没有用,谁来说了算呢? 首…...
Docker-Container
Docker ①什么是容器②为什么需要容器③容器的生命周期容器 OOM容器异常退出容器暂停 ④容器命令清单总览docker createdocker rundocker psdocker logsdocker attachdocker execdocker startdocker stopdocker restartdocker killdocker topdocker statsdocker container insp…...
下载安装anaconda和pytorch的详细方法,以及遇到的问题和解决办法
下载安装Anaconda 首先需要下载Anaconda,可以到官网Anaconda官网或者这里提供一个镜像网站去下载anaconda镜像网站 安装步骤可参考该文章:Anaconda安装步骤,本篇不再赘述 注意环境变量的配置,安装好Anaconda之后一定要在环境变量…...
2020年天津市二级分类土地利用数据(矢量)
天津市,位于华北平原海河五大支流汇流处,东临渤海,北依燕山。地势以平原和洼地为主,北部有低山丘陵,海拔由北向南逐渐下降,地貌总轮廓为西北高而东南低。天津有山地、丘陵和平原三种地形,平原约…...
设计模式——结构型——外观模式Facade
处理器类 public class Cpu {public void start() {System.out.println("处理器启动了...");} } 内存类 public class Memory {public void start() {System.out.println("内存启动了...");} } 硬盘类 public class Disk {public void start() {Syste…...
OpenGL的MVP矩阵理解
OpenGL的MVP矩阵理解 右手坐标系 右手坐标系与左手坐标系都是三维笛卡尔坐标系,他们唯一的不同在于z轴的方向,如下图,左边是左手坐标系,右边是右手坐标系 OpenGL中一般用的是右手坐标系 1.模型坐标系(Local Space&…...
前端超分辨率技术应用:图像质量提升与场景实践探索-设计篇
超分辨率! 引言 在数字化时代,图像质量对于用户体验的重要性不言而喻。随着显示技术的飞速发展,尤其是移动终端视网膜屏幕的广泛应用,用户对高分辨率、高质量图像的需求日益增长。然而,受限于网络流量、存储空间和图像…...
C++11入门手册第一节,学完直接上手Qt(共两节)
入门 hello.cpp #include <iostream>int main() { std::cout << "Hello Quick Reference\n"<<endl; return 0;} 编译运行 $ g hello.cpp -o hello$ ./helloHello Quick Reference 变量 int number 5; // 整数float f 0.95; //…...
Docker部署MinIO对象存储服务
1. 拉取MinIO镜像 # 下载镜像 docker pull minio/minio#查看镜像 docker images2. 创建目录 # 文件存储目录 mkdir -p /opt/minio/data# 配置文件 mkdir -p /opt/minio/config# 日志文件 mkdir -p /opt/minio/logs3. 创建Minio容器并运行 docker run \ -p 9000:9000 \ -p 90…...
基于Echarts的超市销售可视化分析系统(数据+程序+论文)
本论文旨在研究Python技术和ECharts可视化技术在超市销售数据分析系统中的应用。本系统通过对超市销售数据进行分析和可视化展示,帮助决策层更好地了解销售情况和趋势,进而做出更有针对性的决策。本系统主要包括数据处理、数据可视化和系统测试三个模块。…...
使用ai智能写作场景之gpt整理资料,如何ai智能写作整理资料
Ai智能写作助手:Ai智能整理资料小助手 Ai智能整理资料小助手可试用3天! 通俗的解释一下怎么用ChatGPT来进行资料整理: 搜寻并获取指定数量的特定领域文章: 想像你在和我说话一样,告诉我你想要多少篇关于某个话题的文…...
C/C++ 内存管理
1、C/C内存分布 首先我们来了解在一个程序中,代码主要存储在哪些地方; 1.栈:又叫堆栈,其中一般存储非静态局部变量、函数参数、返回值等,栈的增长是向下的。 2.内存映射段:是高效的 I/O 映射方式࿰…...
android pdf框架-10,相册浏览
MupdfViewer 这是最后apk,源码在前面的文章已经贴过了本站下载地址,只是不是最新的.可能不少是旧的内容. subsampling-scale-image-view这是一个大图片的分块加载的实现.比较不错的.滑动方面我觉得使用flinger的效果比它要流畅,惯性要好. 也有人把这个作成pdf渲染器.但翻页就…...
基于SSM的高校普法系统(有报告)。Javaee项目。ssm项目。
演示视频: 基于SSM的高校普法系统(有报告)。Javaee项目。ssm项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构,通过Spring Spri…...
数据结构刷题篇 之 【力扣二叉树基础OJ】详细讲解(含每道题链接及递归图解)
有没有一起拼用银行卡的,取钱的时候我用,存钱的时候你用 1、相同的树 难度等级:⭐ 直达链接:相同的树 2、单值二叉树 难度等级:⭐ 直达链接:单值二叉树 3、对称二叉树 难度等级:⭐⭐ 直达…...
Jackson 2.x 系列【6】注解大全篇二
有道无术,术尚可求,有术无道,止于术。 本系列Jackson 版本 2.17.0 源码地址:https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 注解大全2.11 JsonValue2.12 JsonKey2.13 JsonAnySetter2.14 JsonAnyGetter2.15 …...
在低成本loT mcu上实现深度神经网络端到端自动部署-深度神经网络、物联网、边缘计算、DNN加速——文末完整资料
目录 前言 DNN 量化神经网络 并行超低功耗计算范式 面向内存的部署 结果 原文与源码下载链接 REFERENCES 前言 在物联网极端边缘的终端节点上部署深度神经网络( Deep Neural Networks,DNNs )是支持普适深度学习增强应用的关键手段。基于低成本MCU的终端节点…...
【linux】基础IO |文件操作符
需要掌握:操作文件,本质:进程操作文件。进程和文件的关系 向文件中写入,本质上向硬件中写入->用户没有权利直接写入->操作系统是硬件的管理者,我们可以通过操作系统往硬件写入->操作系统必须提供系统调用&…...
探索 2024 年 Web 开发最佳前端框架
前端框架通过简化和结构化的网站开发过程改变了 Web 开发人员设计和实现用户界面的方法。随着 Web 应用程序变得越来越复杂,交互和动画功能越来越多,这是开发前端框架的初衷之一。 在网络的早期,网页相当简单。它们主要以静态 HTML 为特色&a…...
【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
