macOS Mach-O multiarch ("fat") binary: C++11/STL parsing library

This is a simple container format that encapsulates multiple Mach-O files, each generally for a different architecture. XNU can execute these files just like single-arch Mach-Os and will pick the appropriate entry.

KS implementation details

License: CC0-1.0

This page hosts a formal specification of macOS Mach-O multiarch ("fat") binary using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for C++11/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.

For C++, the easiest way is to clone the runtime library sources and build them along with your project.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (std::istream). One can open local file for that, or use existing std::string or char* buffer.
    #include <fstream>
    
    std::ifstream is("path/to/local/file.bin", std::ifstream::binary);
    
    #include <sstream>
    
    std::istringstream is(str);
    
    #include <sstream>
    
    const char buf[] = { ... };
    std::string str(buf, sizeof buf);
    std::istringstream is(str);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    mach_o_fat_t data(&ks);
    

After that, one can get various attributes from the structure by invoking getter methods like:

data.magic() // => get magic

C++11/STL source code to parse macOS Mach-O multiarch ("fat") binary

mach_o_fat.h

#pragma once

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <memory>
#include "mach_o.h"
#include <vector>

#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
class mach_o_t;

/**
 * This is a simple container format that encapsulates multiple Mach-O files,
 * each generally for a different architecture. XNU can execute these files just
 * like single-arch Mach-Os and will pick the appropriate entry.
 * \sa https://opensource.apple.com/source/xnu/xnu-7195.121.3/EXTERNAL_HEADERS/mach-o/fat.h.auto.html Source
 */

class mach_o_fat_t : public kaitai::kstruct {

public:
    class fat_arch_t;

    mach_o_fat_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mach_o_fat_t* p__root = nullptr);

private:
    void _read();
    void _clean_up();

public:
    ~mach_o_fat_t();

    class fat_arch_t : public kaitai::kstruct {

    public:

        fat_arch_t(kaitai::kstream* p__io, mach_o_fat_t* p__parent = nullptr, mach_o_fat_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~fat_arch_t();

    private:
        bool f_object;
        std::unique_ptr<mach_o_t> m_object;

    public:
        mach_o_t* object();

    private:
        mach_o_t::cpu_type_t m_cpu_type;
        uint32_t m_cpu_subtype;
        uint32_t m_ofs_object;
        uint32_t m_len_object;
        uint32_t m_align;
        mach_o_fat_t* m__root;
        mach_o_fat_t* m__parent;
        std::string m__raw_object;
        std::unique_ptr<kaitai::kstream> m__io__raw_object;

    public:
        mach_o_t::cpu_type_t cpu_type() const { return m_cpu_type; }
        uint32_t cpu_subtype() const { return m_cpu_subtype; }
        uint32_t ofs_object() const { return m_ofs_object; }
        uint32_t len_object() const { return m_len_object; }
        uint32_t align() const { return m_align; }
        mach_o_fat_t* _root() const { return m__root; }
        mach_o_fat_t* _parent() const { return m__parent; }
        std::string _raw_object() const { return m__raw_object; }
        kaitai::kstream* _io__raw_object() const { return m__io__raw_object.get(); }
    };

private:
    std::string m_magic;
    uint32_t m_num_fat_arch;
    std::unique_ptr<std::vector<std::unique_ptr<fat_arch_t>>> m_fat_archs;
    mach_o_fat_t* m__root;
    kaitai::kstruct* m__parent;

public:
    std::string magic() const { return m_magic; }
    uint32_t num_fat_arch() const { return m_num_fat_arch; }
    std::vector<std::unique_ptr<fat_arch_t>>* fat_archs() const { return m_fat_archs.get(); }
    mach_o_fat_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

mach_o_fat.cpp

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "mach_o_fat.h"
#include "kaitai/exceptions.h"

mach_o_fat_t::mach_o_fat_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mach_o_fat_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_fat_archs = nullptr;
    _read();
}

void mach_o_fat_t::_read() {
    m_magic = m__io->read_bytes(4);
    if (!(magic() == std::string("\xCA\xFE\xBA\xBE", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\xCA\xFE\xBA\xBE", 4), magic(), _io(), std::string("/seq/0"));
    }
    m_num_fat_arch = m__io->read_u4be();
    m_fat_archs = std::unique_ptr<std::vector<std::unique_ptr<fat_arch_t>>>(new std::vector<std::unique_ptr<fat_arch_t>>());
    const int l_fat_archs = num_fat_arch();
    for (int i = 0; i < l_fat_archs; i++) {
        m_fat_archs->push_back(std::move(std::unique_ptr<fat_arch_t>(new fat_arch_t(m__io, this, m__root))));
    }
}

mach_o_fat_t::~mach_o_fat_t() {
    _clean_up();
}

void mach_o_fat_t::_clean_up() {
}

mach_o_fat_t::fat_arch_t::fat_arch_t(kaitai::kstream* p__io, mach_o_fat_t* p__parent, mach_o_fat_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_object = nullptr;
    m__io__raw_object = nullptr;
    f_object = false;
    _read();
}

void mach_o_fat_t::fat_arch_t::_read() {
    m_cpu_type = static_cast<mach_o_t::cpu_type_t>(m__io->read_u4be());
    m_cpu_subtype = m__io->read_u4be();
    m_ofs_object = m__io->read_u4be();
    m_len_object = m__io->read_u4be();
    m_align = m__io->read_u4be();
}

mach_o_fat_t::fat_arch_t::~fat_arch_t() {
    _clean_up();
}

void mach_o_fat_t::fat_arch_t::_clean_up() {
    if (f_object) {
    }
}

mach_o_t* mach_o_fat_t::fat_arch_t::object() {
    if (f_object)
        return m_object.get();
    std::streampos _pos = m__io->pos();
    m__io->seek(ofs_object());
    m__raw_object = m__io->read_bytes(len_object());
    m__io__raw_object = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_object));
    m_object = std::unique_ptr<mach_o_t>(new mach_o_t(m__io__raw_object.get()));
    m__io->seek(_pos);
    f_object = true;
    return m_object.get();
}