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

1fe29b3b6a6acc8ce985075bb1d1244e.jpeg

0a572c9592763eecb9ecc0c3348ec2e6.gif

本文字数:2051

预计阅读时间:15分钟

4a6a53646cc377e5e3b78b8d89da1461.png

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的内容,对比结果如下图:

1189538595f439c4c9c9bd71c16e3137.png

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中数据段的起始位置和数据大小:

2283880617b714dd93a458eedabc5b07.png

修改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对应的就是架构信息:

0af03a955810876670ab703731ab53e2.png

参照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

d472d44fee03f30a6461e5f72da8a172.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/577959.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#手麻系统源码,医院手术麻醉信息系统源码,前端框架:Vue,Ant-Design,后端框架:百小僧开源框架

手术麻醉管理系统覆盖了从患者入院&#xff0c;经过术前、术中、术后&#xff0c;直至出院的全过程。医院手术麻醉系统能够规范麻醉科和手术室的工作流程、实现麻醉手术过程中的信息数字化和网络化、自动生成麻醉手术中的各种医疗文书、完整共享HIS、LIS和PACS等手术患者信息&a…

使用hexo框架快速在github上搭建静态博客

今天来说一下使用hexo框架搭建静态博客&#xff0c;玩玩还不错。 我的操作系统 文章目录 一、部署到本地二、新建博客三、更换主题四、部署到github五、其他 一、部署到本地 首先下载好nodejs和git工具&#xff0c;建议直接去清华镜像源下载 node.js git 这中间环境变量的配置…

Word、Excel、PPT文件转PDF文件(C#)

一、添加依赖 为wpf项目引用Microsoft.Office.Interop.Excel、Microsoft.Office.Interop.PowerPoint、Microsoft.Office.Interop.Word、Office&#xff0c;依赖文件已经打到源代码包里了。 二、先定义一些命名空间 using Word Microsoft.Office.Interop.Word;using Excel M…

首个基于SSM-Transformer混合架构,开源商业大模型Jamba

3月29日&#xff0c;知名AI研究实验室AI21在官网开源了&#xff0c;首个基于SSM-Transformer混合架构的商业大模型——Jamba。 目前&#xff0c;ChatGPT、Stable Difusion 、Lyria等产品使用的皆是Transformer架构&#xff0c;虽然在捕捉序列内长距离依赖关系、泛化能力、特征…

A fatal error occurred: MD5 of file does not match data in flash!问题解决

采用的芯片是ESP32-S3-WROOM&#xff0c;16MB FLASH 开发环境是Arduino&#xff0c;烧录到100%后直接报错。 以为是Arduino的问题&#xff0c;用esp-idf开发的程序&#xff0c; 烧录的过程中&#xff0c;也是直接报错如下&#xff1a; esptool.py v4.7.0 Serial port /dev/…

6.2物联网RK3399项目开发实录-驱动开发之GPIO使用(wulianjishu666)

物联网嵌入式开发源码例程&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1B3oqq5QBhN-VmTFt9CI-7A?pwd2ihg ******************************************************************************************* GPIO 使用 简介 GPIO, 全称 General-Purpose Input/…

环境温度对测量平板有什么影响

环境温度可以对测量平板有影响。温度变化可以导致平板的尺寸发生变化。根据热膨胀原理&#xff0c;当环境温度升高时&#xff0c;平板的尺寸会扩大&#xff1b;当环境温度降低时&#xff0c;平板的尺寸会缩小。这种尺寸变化可能会导致测量结果的误差。因此&#xff0c;在测量平…

OSPF之单区域配置

文章目录 单区域配置项目背景项目分析拓扑图配置思路基础配置命令查看路由器接口IP地址信息OSPF配置 测试PC1与PC2互通查看OSPF邻居表修改OSPF路由器的router-id完美的OSPF配置命令写法常用查询命令 单区域配置 项目背景 企业内部存在多个部门&#xff0c;分别属于不同的网段…

关于Ansible的模块②

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 接《关于Ansible的模块 ①-CSDN博客》&#xff0c;继续学习和梳理Ansible的常用文件类模块 1. copy模块 从当前机器上复制文件到…

婚恋交友APP小程序H5源码交付-支持二开!实名制交友,可服务器审核,亦可后台自己审核!同城交友,多人语音!

一、需求分析 在征婚交友网站开发初期&#xff0c;需求分析是至关重要的环节。这需要深入了解目标用户的需求和期望&#xff0c;包括他们的年龄、职业、兴趣爱好、交友条件等方面。通过收集和分析这些信息&#xff0c;开发团队可以明确网站的目标用户&#xff0c;并为他们提供…

考研数学|高效刷透汤家凤《1800》经验分享

当然不需要换老师&#xff0c;如果你在基础阶段连汤老师的课都听不进去&#xff0c;那么换其他老师的话&#xff0c;很大可能也是白搭。 如果你现在对于1800还是一筹莫展的话&#xff0c;那么很明显&#xff0c;这反映出前期基础不扎实&#xff0c;没有真正理解和掌握这部分内…

STM32 软件I2C方式读取AS5600磁编码器获取角度例程

STM32 软件I2C方式读取AS5600磁编码器获取角度例程 &#x1f516;本例程使用正点原子例程作为工程模板创建。 &#x1f4d8; 硬件电路部分 &#x1f33f;原理图部分&#xff1a; &#x1f33f;PCB布线和电路 &#x1f4d9;驱动代码部分 int main(void) {u16 i 0;u16 ra…