Cocoa in the Shell

moatool : Mach-O Archive Tool

After my post on the Universal Binaries, I present you moatool, a tiny tool to manipulate Mach-O archives.

What can it do ?

  1. Print the fat_section of a Mach-O file if it’s an archive.
  2. Split the archive into as much binaries as there are architectures.
  3. Reduce the size of the binary by removing the useless architectures, in the end it’s a simple binary.

moatool is written in C, it contains 2 files, moatool.h and moatool.c which can be compiled to obtain a static or dynamic library.

There is no GUI, if you want one you will have to do it by yourself because I’m not going to make one.

moatool was tested on Mac OS 10.5 (Leopard) and 10.6 (Snow Leopard) to remove useless 32-bit code on a machine and vice-versa.
I only give the source code, no compiled binary, if you want to build it, you can use GCC, or create a project in an IDE, as you wish.

Here is how to build it :

Static library (.a)
gcc -c moatool.c -o moatool.o
ar rcs libmoatool.a moatool.o
Dynamic library (.dylib)
gcc moatool.c -dynamiclib -Wall -o libmoatool.dylib
And a simple example
#include "moatool.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum e_mode {LIST, SPLIT, REDUCE} mode;

moa_boolean_t parse_args(int, char**);
void usage(const char*);

int main(int argc, char** argv)
{
    if (!parse_args(argc, argv))
    {
        usage(argv[0]);
        return EXIT_FAILURE;
    }

    struct s_mach_file* mach_file = NULL;
    moa_error_t ret;

    if ((ret = moa_alloc(&mach_file, argv[2])) != MOA_SUCCESS)
    {
        moa_error(ret);
        exit(EXIT_FAILURE);
    }

    if ((ret = moa_read_fat_section(mach_file)) != MOA_SUCCESS)
    {
        moa_error(ret);
        moa_dealloc(&mach_file);
        exit(EXIT_FAILURE);
    }

    switch (mode)
    {
        case LIST:
            printf("[+] %s fat description (%llu bytes)\n", argv[2], mach_file->file_stats.st_size);
            moa_print_fat_section(mach_file);
            break;
        case SPLIT:
            printf("[+] Splitting %s (%llu bytes)\n", argv[2], mach_file->file_stats.st_size);
            if ((ret = moa_split(mach_file)) != MOA_SUCCESS)
            {
                printf("[+] Couldn't split binary.\n");
                moa_error(ret);
            }
            break;
        case REDUCE:
            printf("[+] Reducing %s (%llu bytes)\n", argv[2], mach_file->file_stats.st_size);
            if ((ret = moa_reduce(mach_file))  != MOA_SUCCESS)
            {
                printf("[+] Couldn't reduce binary.\n");
                moa_error(ret);
            }
            break;
        default:
            printf("[+] Invalid option : %s\n", argv[1]);
            usage(argv[0]);
    }

    moa_dealloc(&mach_file);

    return EXIT_SUCCESS;
}

void usage(const char* ptr_prog_name)
{
    printf("\n%s usage :\n\n", ptr_prog_name);
    printf("\t%s -[lrs] file\n", ptr_prog_name);
    printf("\t-l : Print the content of universal binaries headers.\n");
    printf("\t-r : Remove the useless architecture code from the binary.\n");
    printf("\t-s : Split the universal binary archive in 2 independant binaries.\n");
}

moa_boolean_t parse_args(int argc, char** argv)
{
    if (argc != 3)
        return FALSE;

    if (0 == strncmp(argv[1], "-l", strlen(argv[1])))
        mode = LIST;
    else if (0 == strncmp(argv[1], "-s", strlen(argv[1])))
        mode = SPLIT;
    else if (0 == strncmp(argv[1], "-r", strlen(argv[1])))
        mode = REDUCE;
    else
        return FALSE;

    return TRUE;
}

When you build, don’t forget to specify the path to the lib if necessary

gcc main.c -Wall -o moatool -L"/PATH/lib" -lmoatool

You can get moatool here.

Tags: ,