本文字数:2051字
预计阅读时间:15分钟
01
需要转换架构的原因
但是,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
真机架构与模拟器架构的区别
# 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
(...)
otool -lV xxx.o
可以查看二进制文件中load command的内容,对比结果如下图:
03
如何修改
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)
}
}
}
}
var header: mach_header_64 = headerData.asStruct()
header.sizeofcmds = UInt32(editedCommandsData.count)
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
}
}
try shellOut(to: "ar", arguments: ["cr", "lib.arm64", "*.o"])
04
踩坑&进阶
Load command 11
cmd LC_BUILD_VERSION
cmdsize 32
platform IOSSIMULATOR
minos 15.5
sdk 15.5
ntools 1
tool LD
version 764.0
Load command 34
cmd LC_BUILD_VERSION
cmdsize 24
platform IOSSIMULATOR
minos 13.0
sdk 13.0
ntools 0
struct build_tool_version {
uint32_t tool;
uint32_t version;
};
struct build_version_command {
uint32_t cmd; // LC_BUILD_VERSION
uint32_t cmdsize; // sizeof(build_version_command) + (ntools * sizeof(build_tool_version)
uint32_t platform; // MachoPlatform
uint32_t minos; // X.Y.Z is encoded in nibbles xxxx.yy.zz
uint32_t sdk; // X.Y.Z is encoded in nibbles xxxx.yy.zz
uint32_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对应的就是架构信息:
-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
结尾
参考文档:
1、https://bogo.wtf/arm64-to-sim.html
2、https://github.com/luosheng/arm64-to-sim