ARCH(3)

πŸ“¦ version 1010.6, in nixpkgs unstable

πŸ’… ARCH(3) β€” the one where we learn everyone's architecture πŸ’…

okay bestie, so this is ARCH(3), the Mach-O architecture-info page, and it's lowkey a whole vibe. it's all about the NXArchInfo world β€” asking the system what CPU types and subtypes exist, finding the right slice in a fat binary, all that. no cap, you're gonna want this one in your back pocket. 🫢


✨ NAME ✨

NXGetAllArchInfos, NXGetLocalArchInfo, NXGetArchInfoFromName, NXGetArchInfoFromCpuType, NXFreeArchInfo, NXFindBestFatArch, NXCombineCpuSubtypes β€” get architecture information

like, all of these are the little friend group that tells you everything about architectures. ✨ literally the whole squad is here:

  • NXGetAllArchInfos β€” the "gimme the entire roster" one
  • NXGetLocalArchInfo β€” the "what am I running on tho" one
  • NXGetArchInfoFromName β€” the "look it up by name" one
  • NXGetArchInfoFromCpuType β€” the "look it up by cpu type + subtype" one
  • NXFreeArchInfo β€” the cleanup crew, the one who takes out the trash πŸ—‘οΈ
  • NXFindBestFatArch β€” the matchmaker for fat binaries (32-bit flavoured)
  • NXCombineCpuSubtypes β€” the "can we merge these two subtypes or nah" one

and don't forget the synopsis below is packing NXFindBestFatArch_64 too β€” it's in the header, she's just not in the NAME list. iykyk. πŸ’…


🌈 SYNOPSIS 🌈

first thing's first, you gotta invite the header to the party:

#include <mach-o/arch.h>

and here's every single signature, exactly as they live in the wild β€” no touch-ups, promise πŸŽ€:

extern const NXArchInfo *
NXGetAllArchInfos(void);

extern const NXArchInfo *
NXGetLocalArchInfo(void);

extern const NXArchInfo *
NXGetArchInfoFromName(const char *name);

extern const NXArchInfo *
NXGetArchInfoFromCpuType(cpu_type_t cputype, cpu_subtype_t cpusubtype);

extern void
NXFreeArchInfo(NXArchInfo *x);

extern struct fat_arch *
NXFindBestFatArch(cpu_type_t cputype, cpu_subtype_t cpusubtype,
    struct fat_arch *fat_archs, uint32_t nfat_archs);

extern struct fat_arch_64 *
NXFindBestFatArch_64(cpu_type_t cputype, cpu_subtype_t cpusubtype,
    struct fat_arch_64 *fat_archs64, uint32_t nfat_archs);

extern cpu_subtype_t
NXCombineCpuSubtypes(cpu_type_t cputype, cpu_subtype_t cpusubtype1,
    cpu_subtype_t cpusubtype2);
🌸 note bestie: five of these hand you back a const NXArchInfo *, NXFreeArchInfo returns void (she's just here to free x, no receipts), and the two fat-arch finders return pointers into your own struct fat_arch / struct fat_arch_64 arrays. keep the exact types in mind β€” cpu_type_t, cpu_subtype_t, uint32_t, struct fat_arch, struct fat_arch_64. no guessing, no vibes-based typing. πŸ’€

okay that's the header and the cast of characters β€” the juicy DESCRIPTION and OPTIONS bits are coming up next, so stay tuned bestie, we're not done yet πŸ’–

πŸ’… DESCRIPTION

okay so bestie, listen β€” these functions are literally made for programs that have to deal with universal files, or programs that can target multiple architectures at once. You know how a program will usually take a command-line argument that starts with -arch name, where that thing specifies an architecture? Yeah. That. These functions and data structures are here to help you process those architecture flags and then go deal with the contents of a universal file. 🫢

The structure NXArchInfo is defined in ⟨mach-o/arch.h⟩:

typedef struct {
        const char *name;
        cpu_type_t cputype;
        cpu_subtype_t cpusubtype;
        enum NXByteOrder byteorder;
        const char *description;
} NXArchInfo;

It's the whole package, honestly β€” it holds the name of the architecture, the corresponding CPU type and CPU subtype, plus the architecture's byte order and a lil brief description string. Iconic behaviour. ✨


🌈 the currently known architectures (yes, all of them, bestie)

The currently known architectures are:

  • x86_64 β€” CPU type CPU_TYPE_X86_64, CPU subtype CPU_SUBTYPE_X86_64_ALL β€” Intel x86-64
  • i386 β€” CPU type CPU_TYPE_I386, CPU subtype CPU_SUBTYPE_I386_ALL β€” Intel 80x86
  • arm β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_ALL β€” ARM
  • arm64 β€” CPU type CPU_TYPE_ARM64, CPU subtype CPU_SUBTYPE_ARM64_ALL β€” ARM64
  • arm64e β€” CPU type CPU_TYPE_ARM64, CPU subtype CPU_SUBTYPE_ARM64E β€” ARM64E
  • arm64_32 β€” CPU type CPU_TYPE_ARM64_32, CPU subtype CPU_SUBTYPE_ARM64_32_V8 β€” ARM64_32
  • ppc β€” CPU type CPU_TYPE_POWERPC, CPU subtype CPU_SUBTYPE_POWERPC_ALL β€” PowerPC
  • ppc64 β€” CPU type CPU_TYPE_POWERPC64, CPU subtype CPU_SUBTYPE_POWERPC64_ALL β€” PowerPC 64-bit
  • m68k β€” CPU type CPU_TYPE_MC680x0, CPU subtype CPU_SUBTYPE_MC680x0_ALL β€” Motorola 68K
  • hppa β€” CPU type CPU_TYPE_HPPA, CPU subtype CPU_SUBTYPE_HPPA_ALL β€” HP-PA
  • i860 β€” CPU type CPU_TYPE_I860, CPU subtype CPU_SUBTYPE_I860_ALL β€” Intel 860
  • m88k β€” CPU type CPU_TYPE_MC88000, CPU subtype CPU_SUBTYPE_MC88000_ALL β€” Motorola 88K
  • sparc β€” CPU type CPU_TYPE_SPARC, CPU subtype CPU_SUBTYPE_SPARC_ALL β€” SPARC
  • i486 β€” CPU type CPU_TYPE_I386, CPU subtype CPU_SUBTYPE_486 β€” Intel 486
  • i486SX β€” CPU type CPU_TYPE_I386, CPU subtype CPU_SUBTYPE_486SX β€” Intel 486SX
  • pentium β€” CPU type CPU_TYPE_I386, CPU subtype CPU_SUBTYPE_PENT β€” Intel Pentium
  • i586 β€” CPU type CPU_TYPE_I386, CPU subtype CPU_SUBTYPE_586 β€” Intel 586
  • pentpro β€” CPU type CPU_TYPE_I386, CPU subtype CPU_SUBTYPE_PENTPRO β€” Intel Pentium Pro
  • i686 β€” CPU type CPU_TYPE_I386, CPU subtype CPU_SUBTYPE_PENTPRO β€” Intel Pentium Pro
  • pentIIm3 β€” CPU type CPU_TYPE_I386, CPU subtype CPU_SUBTYPE_PENTII_M3 β€” Intel Pentium II Model 3
  • pentIIm5 β€” CPU type CPU_TYPE_I386, CPU subtype CPU_SUBTYPE_PENTII_M5 β€” Intel Pentium II Model 5
  • pentium4 β€” CPU type CPU_TYPE_I386, CPU subtype CPU_SUBTYPE_PENTIUM_4 β€” Intel Pentium 4
  • armv4t β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_V4T β€” arm v4t
  • armv5 β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_V5TEJ β€” arm v5
  • xscale β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_XSCALE β€” arm xscale
  • armv6 β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_V6 β€” arm v6
  • armv6m β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_V6M β€” arm v6m
  • armv7 β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_V7 β€” arm v7
  • armv7f β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_V7F β€” arm v7f
  • armv7s β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_V7S β€” arm v7s
  • armv7k β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_V7K β€” arm v7k
  • armv7m β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_V7M β€” arm v7m
  • armv7em β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_V7EM β€” arm v7em
  • armv8 β€” CPU type CPU_TYPE_ARM, CPU subtype CPU_SUBTYPE_ARM_V8 β€” arm v8
  • arm64 β€” CPU type CPU_TYPE_ARM64, CPU subtype CPU_SUBTYPE_ARM64_V8 β€” arm64 v8
  • ppc601 β€” CPU type CPU_TYPE_POWERPC, CPU subtype CPU_SUBTYPE_POWERPC_601 β€” PowerPC 601
  • ppc603 β€” CPU type CPU_TYPE_POWERPC, CPU subtype CPU_SUBTYPE_POWERPC_603 β€” PowerPC 603
  • ppc604 β€” CPU type CPU_TYPE_POWERPC, CPU subtype CPU_SUBTYPE_POWERPC_604 β€” PowerPC 604
  • ppc604e β€” CPU type CPU_TYPE_POWERPC, CPU subtype CPU_SUBTYPE_POWERPC_604e β€” PowerPC 604e
  • ppc750 β€” CPU type CPU_TYPE_POWERPC, CPU subtype CPU_SUBTYPE_POWERPC_750 β€” PowerPC 750
  • ppc7400 β€” CPU type CPU_TYPE_POWERPC, CPU subtype CPU_SUBTYPE_POWERPC_7400 β€” PowerPC 7400
  • ppc7450 β€” CPU type CPU_TYPE_POWERPC, CPU subtype CPU_SUBTYPE_POWERPC_7450 β€” PowerPC 7450
  • ppc970 β€” CPU type CPU_TYPE_POWERPC, CPU subtype CPU_SUBTYPE_POWERPC_970 β€” PowerPC 970
  • m68030 β€” CPU type CPU_TYPE_MC680x0, CPU subtype CPU_SUBTYPE_MC68030_ONLY β€” Motorola 68030
  • m68040 β€” CPU type CPU_TYPE_MC680x0, CPU subtype CPU_SUBTYPE_MC68040 β€” Motorola 68040
  • hppa7100LC β€” CPU type CPU_TYPE_HPPA, CPU subtype CPU_SUBTYPE_HPPA_7100LC β€” HP-PA 7100LC

The first set of entries is what you use for the architecture family, mkay? The second set of entries is for when you want one specific architecture β€” that's the case when more than one specific architecture is supported inside a family of architectures. πŸ¦‹

πŸŽ€ the functions, one by one

NXGetAllArchInfos() returns a pointer to an array of all known NXArchInfo structures. The last NXArchInfo is marked by a NULL name β€” that's how you know you've hit the end, bestie, don't keep walking. πŸ’€

NXGetLocalArchInfo() returns the NXArchInfo for the local host, or NULL if none is known.

NXGetArchInfoFromName() and NXGetArchInfoFromCpuType() return the NXArchInfo from the architecture's name, or from the CPU type / CPU subtype combination. A CPU subtype of CPU_SUBTYPE_MULTIPLE can be used to request the most general NXArchInfo known for the given CPU type. NULL is returned if no matching NXArchInfo can be found. no match, no drama, just NULL. πŸ™ˆ

NXFreeArchInfo() gets passed a pointer to an NXArchInfo returned by the above interfaces, and if that thing is not in the array returned in NXGetAllArchInfos(), it will free(3) the description field and then the NXArchInfo pointer. clean up after yourself, it's giving main character energy. ✨


😭 the whole malloc situation (read this one, fr fr)

The above interfaces that return pointers to NXArchInfo structs in normal cases return a pointer from the array returned in NXGetAllArchInfos(). But in some cases, when the cputype is CPU_TYPE_I386 or CPU_TYPE_POWERPC, it will return a malloc(3)'ed NXArchInfo struct which contains a string in the description field that is also a malloc(3)'ed pointer. To allow programs not to leak memory, they can call NXFreeArchInfo() on pointers returned from the above interfaces. Since this is a new API on older systems, you can use the code below. Going forward, the above interfaces will only return pointers from the array returned in NXGetAllArchInfos().

static void NXFreeArchInfo(
const NXArchInfo *x)
{
        const NXArchInfo *p;

        p = NXGetAllArchInfos();
        while(p->name != NULL){
                if(x == p)
                        return;
                p++;
        }
        free((char *)x->description);
        free((NXArchInfo *)x);
}
lowkey important: use that snippet if you're on an older system, because leaking memory is not the vibe. πŸ’…

NXFindBestFatArch() gets passed a CPU type and CPU subtype plus a set of fat_arch structs. It selects the best one that matches (if any) and returns a pointer to that fat_arch struct (or NULL). The fat_arch structs must be in the host byte order, and correct such that fat_archs really points to enough memory for nfat_archs structs. Heads up though: it's possible that this routine could fail if new CPU types or CPU subtypes are added and an old version of this routine is used. But β€” if there's an exact match between the CPU type and CPU subtype and one of the fat_arch structs, this routine will always succeed. always. 🫢

NXFindBestFatArch_64() is the same as NXFindBestFatArch() but returns and takes fat_arch_64 structs. same energy, wider ints. πŸ¦‹

NXCombineCpuSubtypes() returns the resulting CPU subtype when combining two different CPU subtypes for the specified CPU type. If the two CPU subtypes can't be combined (the specific subtypes are mutually exclusive), -1 is returned, indicating it's an error to combine them. This can also fail and return -1 if new CPU types or CPU subtypes are added and an old version of this routine is used. But if the CPU subtypes are the same, they can always be combined and this routine will return the CPU subtype passed in. them matching? slay. πŸ’–


🌸 SEE ALSO


that's the whole page, bestie β€” go forth and process those architecture flags πŸ’œ

✧ο½₯゚ ARCH(3), from the June 8, 2016 era, part of the macOS/BSD man page family ✧ο½₯゚ πŸŽ€

πŸ“¦ where this page comes from