Skip to content

Macho

Description

Parsed Mach-O file. Holds the raw bytes plus decoded indices. All three MachoOpen* constructors leave the parser as the sole owner of data – see the OpenFromMemory / OpenFromMemoryCopy docs below for the L / R semantics (mirrors VecInsertL / VecInsertR).

Fields

Name Description
data Raw Mach-O bytes as a Buf (owned). Carries its own length and allocator – read via BufLength / BufData / BufAllocator.
cputype Mach-O cputype value (e.g. 0x01000007 = x86_64, 0x0100000C = arm64).
filetype MachoType value.
uuid 16-byte UUID from LC_UUID if present.
has_uuid True iff LC_UUID was found.
segments All LC_SEGMENT_64 entries.
sections Flat list of all sections across segments.
symbols Entries from LC_SYMTAB; may be empty if stripped.

Usage example (Cross-references)

Usage examples (Cross-references)
        Str            module_path;
        u64            slide;
        Macho          main;
        bool           main_open;
        Macho          dsym;
        Macho          main;
        bool           main_open;
        Macho          dsym;
        bool           dsym_open;
        DwarfFunctions fns;
        MachoSections sections;
        MachoSymbols  symbols;
    } Macho;
    
    ///
    /// TAGS: Parser, MachO, Memory, Ownership
    ///
    bool MachoOpenFromMemory(Macho *out, Buf *in);
    
    ///
    /// TAGS: Parser, MachO, Deinit, Lifecycle
    ///
    void MachoDeinit(Macho *self);
    
    ///
    /// TAGS: Parser, MachO, Symbol, Resolve
    ///
    const MachoSymbol *MachoResolveAddress(const Macho *self, u64 vaddr);
    
    #endif // MISRA_PARSERS_MACHO_H
    
    typedef struct MachoContext {
        Macho *out;
        u32    ncmds;
        u32    sizeofcmds;
    
    static bool decode_header(MachoContext *ctx) {
        Macho *m = ctx->out;
        if (BufLength(&m->data) < MH_HEADER_64_SIZE) {
            LOG_ERROR("MachO: file too small for header");
    // MemSets the caller's view to zero. Anything that fails past the
    // snapshot cleans up via MachoDeinit -- the buffer never leaks.
    bool MachoOpenFromMemory(Macho *out, Buf *in) {
        if (!out || !in || !BufData(in) || !BufAllocator(in)) {
            LOG_FATAL("MachoOpenFromMemory: NULL argument (contract violation)");
    
    // R-value form: allocate Buf, copy, hand `&copy` to the L-form.
    bool macho_open_from_memory_copy(Macho *out, const u8 *data, size data_size, Allocator *alloc) {
        if (!out || !data || !alloc) {
            LOG_FATAL("MachoOpenFromMemoryCopy: NULL argument (contract violation)");
    }
    
    bool macho_open(Macho *out, Zstr path, Allocator *alloc) {
        if (!out || !path || !alloc) {
            LOG_FATAL("MachoOpen: NULL argument (contract violation)");
    }
    
    void MachoDeinit(Macho *self) {
        if (!self)
            return;
    }
    
    const MachoSection *macho_find_section(const Macho *self, Zstr segment, Zstr section) {
        if (!self || !segment || !section)
            return NULL;
    // same section (or the section end). N_STAB entries are skipped:
    // stab iff any of the high three bits of n_type is set.
    const MachoSymbol *MachoResolveAddress(const Macho *self, u64 vaddr) {
        if (!self || VecLen(&self->symbols) == 0)
            return NULL;
    // Open helper. Returns true if opened; *out filled. Caller must
    // MachoDeinit on success.
    static bool open_blob(Macho *out, const u8 *bytes, u32 len, DefaultAllocator *alloc) {
        return MachoOpenFromMemoryCopy(out, bytes, len, ALLOCATOR_OF(alloc));
    }
        build_macho_blob();
    
        Macho m;
        bool  ok = MachoOpenFromMemoryCopy(&m, blob, sizeof(blob), base);
        if (!ok) {
        build_macho_blob();
    
        Macho m;
        if (!MachoOpenFromMemoryCopy(&m, blob, sizeof(blob), base)) {
            DefaultAllocatorDeinit(&alloc);
        wr_u32(&fat[0], 0xCAFEBABEu);
    
        Macho m;
        bool  ok = !MachoOpenFromMemoryCopy(&m, fat, sizeof(fat), base);
        build_macho_blob();
    
        Macho m;
        if (!MachoOpenFromMemoryCopy(&m, blob, sizeof(blob), base)) {
            DefaultAllocatorDeinit(&alloc);
    static bool macho_rejects(const u8 *bytes, u64 len) {
        DefaultAllocator alloc = DefaultAllocatorInit();
        Macho            m;
        bool             opened = MachoOpenFromMemoryCopy(&m, bytes, len, ALLOCATOR_OF(&alloc));
        if (opened)
        build_two_symbol_blob();
    
        Macho m;
        if (!MachoOpenFromMemoryCopy(&m, two_blob, sizeof(two_blob), base)) {
            DefaultAllocatorDeinit(&alloc);
        Allocator       *base  = ALLOCATOR_OF(&alloc);
    
        Macho m;
        if (!MachoOpen(&m, path, base)) {
            DefaultAllocatorDeinit(&alloc);
        Allocator       *base  = ALLOCATOR_OF(&alloc);
    
        Macho m;
        if (!MachoOpen(&m, path, base)) {
            DefaultAllocatorDeinit(&alloc);
        MemCopy(&b[cfg.stroff + 1], nm, sizeof(nm));
    
        Macho m;
        bool  ok = open_blob(&m, b, BUF, &alloc);
        ok       = ok && VecLen(&m.symbols) == 1;
        build_scaffold(b, &cfg);
    
        Macho m;
        bool  ok = open_blob(&m, b, BUF, &alloc);
        ok       = ok && m.has_uuid && MemCompare(m.uuid, kUuid, 16) == 0;
        MemCopy(&b[stroff + 7], "beta", 5);  // includes NUL at stroff+11
    
        Macho m;
        bool  ok = open_blob(&m, b, BUF, &alloc);
        ok       = ok && VecLen(&m.symbols) == 2;
        MemCopy(&b[cfg.stroff + 1], "z", 2);
    
        Macho m;
        bool  ok = open_blob(&m, b, BUF, &alloc);
        ok       = ok && VecLen(&m.symbols) == 1 && VecPtrAt(&m.symbols, 0)->section_index == 5;
        MemCopy(&b[stroff + 1], "fit", 4);
    
        Macho m;
        bool  ok = open_blob(&m, b, BUF, &alloc); // real: accepts
        ok       = ok && VecLen(&m.symbols) == 1 && ZstrCompare(VecPtrAt(&m.symbols, 0)->name, "fit") == 0;
        MemCopy(&b[stroff + 1], "edge", 5);
    
        Macho m;
        bool  ok = open_blob(&m, b, BUF, &alloc);
        ok       = ok && VecLen(&m.symbols) == 1 && ZstrCompare(VecPtrAt(&m.symbols, 0)->name, "edge") == 0;
        MemCopy(&b[stroff + 1], "sym", 4);
    
        Macho m;
        bool  ok = open_blob(&m, b, BUF, &alloc); // real accepts
        ok       = ok && VecLen(&m.symbols) == NSY;
        MemCopy(&b[stroff + 1], "ovr", 4);
    
        Macho m;
        bool  opened = open_blob(&m, b, BUF, &alloc);
        if (opened)
        build_scaffold(b, &cfg); // symoff + 100*16 = 0x100 + 0x640 >> 320 -> overrun
    
        Macho m;
        bool  opened = open_blob(&m, b, BUF, &alloc);
        if (opened)
        b[stroff] = 0x41;                 // ensure byte0 non-zero too
    
        Macho m;
        bool  ok = open_blob(&m, b, BUF, &alloc);
        ok       = ok && VecLen(&m.symbols) == 0; // real: symbol skipped
        b[stroff + strsize] = 0x00;       // the off-by-one byte is NUL
    
        Macho m;
        bool  ok = open_blob(&m, b, BUF, &alloc);
        ok       = ok && VecLen(&m.symbols) == 0; // real: skipped (no NUL in range)
        b[stroff + n_strx + 2] = 0x00; // forward NUL terminator
    
        Macho m;
        bool  ok = open_blob(&m, b, BUF, &alloc);
        ok       = ok && VecLen(&m.symbols) == 1; // real: forward scan finds NUL
        build_scaffold(b, &cfg);
    
        Macho m;
        bool  ok = open_blob(&m, b, BUF, &alloc);
        if (!ok) {
        MemCopy(&uc[8], kUuid, 16);
    
        Macho m;
        bool  ok = open_blob(&m, b, BUF, &alloc);
        ok       = ok && VecLen(&m.sections) == 2;
        put_header(buf, 0, 0); // ncmds = 0, sizeofcmds = 0
    
        Macho m;
        bool  ok = MachoOpenFromMemoryCopy(&m, buf, sizeof(buf), base);
        ok       = ok && VecLen(&m.segments) == 0 && VecLen(&m.sections) == 0;
        wr_u32(&seg[68], 0);        // flags
    
        Macho m;
        bool  ok = MachoOpenFromMemoryCopy(&m, buf, sizeof(buf), base);
        ok       = ok && VecLen(&m.segments) == 1 && VecLen(&m.sections) == 0;
        wr_u32(&seg[64], 0); // nsects == 0
    
        Macho m;
        bool  ok = MachoOpenFromMemoryCopy(&m, buf, sizeof(buf), base);
        ok       = ok && VecLen(&m.segments) == 1;
        u64 len = build_seg_with_sections(buf, 2);
    
        Macho m;
        bool  ok = MachoOpenFromMemoryCopy(&m, buf, len, base);
        ok       = ok && VecLen(&m.segments) == 1 && VecLen(&m.sections) == 2;
        wr_u64(&sec[40], 0x100);          // size
    
        Macho m;
        bool  ok = MachoOpenFromMemoryCopy(&m, buf, sizeof(buf), base);
        ok       = ok && VecLen(&m.sections) == 1;
        wr_u32(&seg[64], 0); // nsects == 0
    
        Macho m;
        bool  ok = MachoOpenFromMemoryCopy(&m, buf, sizeof(buf), base);
        ok       = ok && VecLen(&m.segments) == 1;
        wr_u32(&lc[4], 8);     // cmdsize == 8 (minimum); remaining == 8 here
    
        Macho m;
        bool  ok = MachoOpenFromMemoryCopy(&m, buf, sizeof(buf), base);
        ok       = ok && VecLen(&m.segments) == 0 && VecLen(&m.sections) == 0;
        u64 len       = build_symbol_blob(values, 2);
    
        Macho m;
        if (!MachoOpenFromMemoryCopy(&m, g_symblob, len, base)) {
            DefaultAllocatorDeinit(&alloc);
        g_symblob[nlist_off + 1 * NLIST64_SIZE + 5] = 2; // second -> n_sect 2
    
        Macho m;
        if (!MachoOpenFromMemoryCopy(&m, g_symblob, len, base)) {
            DefaultAllocatorDeinit(&alloc);
        bool ok    = (wrote == (i64)sizeof(buf));
    
        Macho m;
        bool  opened = MachoOpen(&m, &path, base);
        ok           = ok && opened && VecLen(&m.segments) == 1;
        bool ok    = (wrote == (i64)sizeof(garbage));
    
        Macho m;
        bool  opened = MachoOpen(&m, &path, base);
        if (opened)
        wr_u32(&lc[4], 0x1000); // cmdsize far exceeds remaining -> reject
    
        Macho m;
        bool  opened = MachoOpenFromMemoryCopy(&m, buf, sizeof(buf), base);
        if (opened)
        MemCopy(&uc[8], kUuid, 16);
    
        Macho m;
        bool  ok = MachoOpenFromMemoryCopy(&m, b, BUF, base);
        // Canonical-true check: real code stores exactly `true` (== 1); the
Last updated on