You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 17:05:21