macOS下如何通过C/Swift编程获取file命令输出的架构信息?
在macOS通过C/Swift编程获取Mach-O文件架构信息
完全可以不用调用file命令,直接通过macOS提供的系统库解析Mach-O文件结构来获取架构信息,以下是具体实现:
C语言实现
利用macOS自带的<mach-o/fat.h>和<mach-o/loader.h>头文件,直接解析通用二进制(Fat Binary)和单一架构的Mach-O文件:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <mach-o/fat.h> #include <mach-o/loader.h> #include <sys/mman.h> #include <sys/stat.h> // 映射架构名称 const char* get_arch_name(cpu_type_t cputype, cpu_subtype_t cpusubtype) { if (cputype == CPU_TYPE_X86_64) { return "x86_64"; } else if (cputype == CPU_TYPE_ARM64) { if (cpusubtype == CPU_SUBTYPE_ARM64E) { return "arm64e"; } return "arm64"; } return "unknown"; } // 获取Mach-O文件类型描述 const char* get_file_type(uint32_t filetype) { switch (filetype) { case MH_EXECUTE: return "executable"; case MH_BUNDLE: return "bundle"; case MH_DYLIB: return "dylib"; default: return "unknown"; } } void print_macho_archs(const char* path) { int fd = open(path, O_RDONLY); if (fd == -1) { perror("open failed"); return; } struct stat stat_buf; if (fstat(fd, &stat_buf) == -1) { perror("fstat failed"); close(fd); return; } void* file_data = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (file_data == MAP_FAILED) { perror("mmap failed"); close(fd); return; } uint32_t magic = *(uint32_t*)file_data; if (magic == FAT_MAGIC || magic == FAT_CIGAM) { // 处理通用二进制 struct fat_header* fat_hdr = (struct fat_header*)file_data; uint32_t nfat_arch = OSSwapBigToHostInt32(fat_hdr->nfat_arch); struct fat_arch* fat_archs = (struct fat_arch*)((char*)file_data + sizeof(struct fat_header)); printf("%s: Mach-O universal binary with %u architectures:\n", path, nfat_arch); for (uint32_t i = 0; i < nfat_arch; i++) { cpu_type_t cputype = OSSwapBigToHostInt32(fat_archs[i].cputype); cpu_subtype_t cpusubtype = OSSwapBigToHostInt32(fat_archs[i].cpusubtype); cpusubtype &= CPU_SUBTYPE_MASK; const char* arch_name = get_arch_name(cputype, cpusubtype); // 读取该架构的Mach-O头 struct mach_header_64* mach_hdr = (struct mach_header_64*)((char*)file_data + OSSwapBigToHostInt32(fat_archs[i].offset)); const char* file_type = get_file_type(mach_hdr->filetype); printf(" [%s]: Mach-O 64-bit %s %s\n", arch_name, file_type, arch_name); } } else if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) { // 处理单一64位架构 struct mach_header_64* mach_hdr = (struct mach_header_64*)file_data; bool is_big_endian = (magic == MH_CIGAM_64); cpu_type_t cputype = is_big_endian ? OSSwapBigToHostInt32(mach_hdr->cputype) : mach_hdr->cputype; cpu_subtype_t cpusubtype = is_big_endian ? OSSwapBigToHostInt32(mach_hdr->cpusubtype) : mach_hdr->cpusubtype; cpusubtype &= CPU_SUBTYPE_MASK; const char* arch_name = get_arch_name(cputype, cpusubtype); const char* file_type = get_file_type(is_big_endian ? OSSwapBigToHostInt32(mach_hdr->filetype) : mach_hdr->filetype); printf("%s: Mach-O 64-bit %s %s\n", path, file_type, arch_name); } else { printf("%s: Not a valid Mach-O file\n", path); } munmap(file_data, stat_buf.st_size); close(fd); } int main(int argc, char* argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <mach-o-file-path>\n", argv[0]); return 1; } print_macho_archs(argv[1]); return 0; }
编译运行:
gcc -o macho_arch macho_arch.c ./macho_arch /Library/Audio/Plug-Ins/HAL/ACE.driver/Contents/MacOS/ACE
Swift语言实现
通过Foundation框架读取文件,结合Mach-O结构解析,实现逻辑与C版本一致:
import Foundation // 映射架构名称 func archName(from cputype: UInt32, cpusubtype: UInt32) -> String { let cpuType = CPUType(rawValue: cputype) let cpuSubtype = CPUSubtype(rawValue: cpusubtype & CPU_SUBTYPE_MASK) switch (cpuType, cpuSubtype) { case (.x86_64, _): return "x86_64" case (.arm64, .arm64e): return "arm64e" case (.arm64, _): return "arm64" default: return "unknown" } } // 获取Mach-O文件类型描述 func fileType(from filetype: UInt32) -> String { switch filetype { case MH_EXECUTE: return "executable" case MH_BUNDLE: return "bundle" case MH_DYLIB: return "dylib" default: return "unknown" } } func parseMachOArchs(at path: String) throws { let url = URL(fileURLWithPath: path) let fileHandle = try FileHandle(forReadingFrom: url) defer { fileHandle.closeFile() } let data = fileHandle.readDataToEndOfFile() guard data.count >= MemoryLayout<UInt32>.size else { print("\(path): Invalid file") return } let magic = data.withUnsafeBytes { $0.load(as: UInt32.self) } switch magic { case FAT_MAGIC, FAT_CIGAM: // 处理通用二进制 let fatHeader = data.withUnsafeBytes { $0.load(as: fat_header.self) } let nfatArch = OSSwapBigToHostInt32(fatHeader.nfat_arch) print("\(path): Mach-O universal binary with \(nfatArch) architectures:") var offset = MemoryLayout<fat_header>.size for _ in 0..<nfatArch { guard offset + MemoryLayout<fat_arch>.size <= data.count else { break } let fatArch = data.subdata(in: offset..<offset+MemoryLayout<fat_arch>.size) .withUnsafeBytes { $0.load(as: fat_arch.self) } let cputype = OSSwapBigToHostInt32(fatArch.cputype) let cpusubtype = OSSwapBigToHostInt32(fatArch.cpusubtype) let arch = archName(from: cputype, cpusubtype: cpusubtype) // 读取该架构的Mach-O头 let machOffset = Int(OSSwapBigToHostInt32(fatArch.offset)) guard machOffset + MemoryLayout<mach_header_64>.size <= data.count else { print(" [\(arch)]: Invalid Mach-O slice") offset += MemoryLayout<fat_arch>.size continue } let machHeader = data.subdata(in: machOffset..<machOffset+MemoryLayout<mach_header_64>.size) .withUnsafeBytes { $0.load(as: mach_header_64.self) } let type = fileType(from: machHeader.filetype) print(" [\(arch)]: Mach-O 64-bit \(type) \(arch)") offset += MemoryLayout<fat_arch>.size } case MH_MAGIC_64, MH_CIGAM_64: // 处理单一64位架构 let isBigEndian = magic == MH_CIGAM_64 let machHeader = data.withUnsafeBytes { $0.load(as: mach_header_64.self) } let cputype = isBigEndian ? OSSwapBigToHostInt32(machHeader.cputype) : machHeader.cputype let cpusubtype = isBigEndian ? OSSwapBigToHostInt32(machHeader.cpusubtype) : machHeader.cpusubtype let arch = archName(from: cputype, cpusubtype: cpusubtype) let type = fileType(from: isBigEndian ? OSSwapBigToHostInt32(machHeader.filetype) : machHeader.filetype) print("\(path): Mach-O 64-bit \(type) \(arch)") default: print("\(path): Not a valid Mach-O file") } } // 示例调用 if CommandLine.arguments.count != 2 { print("Usage: \(CommandLine.arguments[0]) <mach-o-file-path>") exit(1) } do { try parseMachOArchs(at: CommandLine.arguments[1]) } catch { print("Error: \(error.localizedDescription)") }
编译运行:
swiftc -o MachOArch MachOArch.swift ./MachOArch /usr/bin/plutil
以上代码直接解析Mach-O文件的底层结构,和file命令的实现逻辑一致,无需调用外部命令,效率更高且更可控。
内容的提问来源于stack exchange,提问作者Rick
相关产品推荐
相关产品推荐

