网站开发专业考啥证书,保定网站制作哪家好建设,三只松鼠的软文范例,安平做网站的公司本文字数#xff1a;2051字 预计阅读时间#xff1a;15分钟 01 需要转换架构的原因 老版 Mac 使用 Intel 芯片#xff0c;是x86_64架构#xff0c;相应地在老版 Mac 上运行的模拟器使用的也就是 x86_64架构。 由于模拟器的 x86_64 架构与真机的 arm64、armv7 等架构不冲突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(MemoryLayoutbuild_version_command.stride - MemoryLayoutversion_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