Proper support for digging through the Mach-O load-command table

Uses kernel_read() to carefully iterate through all the Mach-O load
commands and logs their properties to the dmesg buffer. Partial support is
also present for VM setup, but it's lacking a necessary symbol export on
PowerPC (arch_pick_mmap_layout). The process currently receives a SIGKILL
after reading through all the load commands; pending completion of the
memory-mapping and setup code.

Signed-off-by: Kyle Moffett <[EMAIL PROTECTED]>
---
fs/mach-o/binfmt.c   |  258 +++++++++++++++++++++++----------
fs/mach-o/cpus.h     |   49 ++++---
fs/mach-o/files.h    |   67 +++++----
fs/mach-o/loadcmds.h | 389 ++++++++++++++++++++++++++++++++++++++++++ ++++++++
4 files changed, 640 insertions(+), 123 deletions(-)

diff --git a/fs/mach-o/binfmt.c b/fs/mach-o/binfmt.c
index b5ea936..ea2f46e 100644
--- a/fs/mach-o/binfmt.c
+++ b/fs/mach-o/binfmt.c
@@ -14,44 +14,13 @@
 #include <linux/binfmts.h>
 #include <linux/fs.h>
 #include <linux/err.h>
+#include <linux/personality.h>
 
 #include "debug.h"
 #include "cpus.h"
 #include "headers.h"
 #include "files.h"
-
-#if 0
-#include <linux/kernel.h>
-#include <linux/stat.h>
-#include <linux/time.h>
-#include <linux/mm.h>
-#include <linux/mman.h>
-#include <linux/a.out.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/string.h>
-#include <linux/file.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/shm.h>
-#include <linux/personality.h>
-#include <linux/elfcore.h>
-#include <linux/init.h>
-#include <linux/highuid.h>
-#include <linux/smp.h>
-#include <linux/smp_lock.h>
-#include <linux/compiler.h>
-#include <linux/highmem.h>
-#include <linux/pagemap.h>
-#include <linux/security.h>
-#include <linux/syscalls.h>
-#include <linux/random.h>
-
-#include <asm/uaccess.h>
-#include <asm/param.h>
-#include <asm/page.h>
-#endif
+#include "loadcmds.h"
 
 MODULE_LICENSE("GPL");
 
@@ -187,21 +156,25 @@ static int load_macho_binary(struct linux_binprm *bprm, 
struct pt_regs *regs)
                        struct macho_mach32_header mach32;
                        struct macho_mach64_header mach64;
                } header;
-#if 0
-               struct macho_loadcmd loadcmd;
-#endif
+               union macho_loadcmd loadcmd;
        } *data = NULL;
-       unsigned long offset;
+       unsigned long hdr_cmd_count, hdr_cmd_size;
+       unsigned long cur_offset, loadcmd_size, i;
+       unsigned long arch_offset;
        long retval;
-       int err;
+       int err = -ENOEXEC;
+
+       /* Make sure the file has appropriate ops */
+       if (!bprm->file->f_op || !bprm->file->f_op->mmap)
+               goto out;
 
        /*
         * Read the Mach-O file headers to find the offset of our
         * architecture-specific portion
         */
-       offset = get_arch_offset(bprm);
-       if (IS_ERR_VALUE(offset)) {
-               err = (long)offset;
+       arch_offset = get_arch_offset(bprm);
+       if (IS_ERR_VALUE(arch_offset)) {
+               err = (long)arch_offset;
                goto out;
        }
 
@@ -213,14 +186,14 @@ static int load_macho_binary(struct linux_binprm *bprm, 
struct pt_regs *regs)
        }
 
        /* Read the header data and check the magic */
-       retval = kernel_read(bprm->file, offset,
+       retval = kernel_read(bprm->file, arch_offset,
                        (void *)&data->header, sizeof(data->header));
        if (retval != sizeof(data->header)) {
                /*
                 * If we didn't see an arch table then it must not really be
                 * a Mach-O file so just return as not executable.
                 */
-               if (!offset)
+               if (!arch_offset)
                        err = -ENOEXEC;
 
                /* If kernel_read() returned an error, handle it */
@@ -251,7 +224,7 @@ static int load_macho_binary(struct linux_binprm *bprm, 
struct pt_regs *regs)
         */
        if (data->header.magic != MACHO_MACH32_MAGIC &&
                        data->header.magic != MACHO_MACH64_MAGIC) {
-               if (offset)
+               if (arch_offset)
                        macho_dbg("Corrupt embedded Mach-O object!\n");
                err = -ENOEXEC;
                goto out;
@@ -260,13 +233,12 @@ static int load_macho_binary(struct linux_binprm *bprm, 
struct pt_regs *regs)
        /* CPU type (lines up between 32-bit and 64-bit Mach-O files) */
        if (!macho_check_cpu(data->header.mach32.cpu_type,
                        data->header.mach32.cpu_subtype)) {
-
                /*
                 * The CPU didn't match, so either this is Mach-O file is for
-                * a different platform (offset == 0) or the FAT Mach-O has
-                * been corrupted.
+                * a different platform (arch_offset == 0) or the FAT Mach-O
+                * has been corrupted.
                 */
-               if (offset)
+               if (arch_offset)
                        macho_dbg("FAT Mach-O wrapper has mismatched CPU"
                                        " types:  Mach-O file corrupt?\n");
                else
@@ -283,39 +255,175 @@ static int load_macho_binary(struct linux_binprm *bprm, 
struct pt_regs *regs)
                goto out;
        }
 
+       /*
+        * Get some load-command info out of the header (NOTE: Lines up
+        * between 32-bit and 64-bit Mach-O files
+        */
+       hdr_cmd_count = data->header.mach32.cmd_count;
+       hdr_cmd_size  = data->header.mach32.cmd_size;
+
+       /* 
+        * FIXME:  Limits the total space for load commands to 1MB, but this
+        * should be made configurable somehow for embedded systems with
+        * minimal available resources.  Is there an rlimit or some page
+        * accounting we can hook into?  I don't think this is that critical
+        * of an issue since for the most part we only store one load-command
+        * in kernel memory at a time, however some things like unixthread
+        * and thread load-commands need to be kept around until after we've
+        * loaded the dynamic linker.
+        */
+       if (hdr_cmd_size > (1 << 20)) {
+               macho_dbg("Mach-O file has more than 1MB worth of "
+                               "load-commands (%lu bytes)\n", hdr_cmd_size);
+               err = -ENOMEM;
+               goto out;
+       }
+
        /* Move past the Mach-O header to find the first load_command */
-       if (data->header.magic == MACHO_MACH32_MAGIC)
-               offset += sizeof(struct macho_mach32_header);
-       else
-               offset += sizeof(struct macho_mach64_header);
+       cur_offset = arch_offset +
+               ((data->header.magic == MACHO_MACH32_MAGIC)
+                       ? sizeof(struct macho_mach32_header)
+                       : sizeof(struct macho_mach64_header));
+
+#if 0
+       /*
+        * Just to avoid allocating gobs of kernel memory while we parse the
+        * load-command table, we're going to go past the point of no return
+        * nice and early, so we can just start mapping things into memory
+        */
+       err = flush_old_exec(bprm);
+       if (err)
+               goto out;
+
+       /* FIXME: This needs to actually switch to the darwin personality */
+       set_personality(PER_LINUX);
+       
+       /* FIXME: Set up ugly stack/brk/etc for testing, need to fix later */
+       current->mm->start_data = 0;
+       current->mm->end_data   = 0;
+       current->mm->end_code   = 0;
+       current->mm->mmap       = NULL;
+       current->mm->def_flags  = 0;
+
+       /* Make sure to clear the fork-but-not-exec bit on this process */
+       current->flags &= ~PF_FORKNOEXEC;
+
+       /*
+        * FIXME: Figure out how to do MMAP, may need to be changed to be
+        * darwinish
+        */
+       current->mm->mmap_base = TASK_UNMAPPED_BASE;
+       current->mm->get_unmapped_area = arch_get_unmapped_area;
+       current->mm->unmap_area = arch_unmap_area;
+       current->mm->free_area_cache = current->mm->mmap_base;
+       current->mm->cached_hole_size = 0;
+#endif
+
+       /* Iterate over reading the load commands */
+       loadcmd_size = 0;
+       for (i = 0; i < hdr_cmd_count; i++) {
+               unsigned long size;
+
+               /* Check that we have room for another load command */
+               if (loadcmd_size + sizeof(data->loadcmd.hdr) > hdr_cmd_size) {
+                       macho_dbg("Mach-O header doesn't allocate enough "
+                                       "space for load-commands %lu-%lu!\n",
+                                       i, hdr_cmd_count-1);
+                       err = -ENOEXEC;
+                       goto out_noreturn;
+               }
+
+               /* First read the itty-bitty command num and size fields */
+               retval = kernel_read(bprm->file, cur_offset,
+                               (void *)&data->loadcmd.hdr,
+                               sizeof(data->loadcmd.hdr));
+               if (retval != sizeof(data->loadcmd.hdr)) {
+                       /* If kernel_read() returned an error, handle it */
+                       if (retval < 0) {
+                               macho_dbg("Error while reading Mach-O load"
+                                       " command %lu: %li\n", i, retval);
+                               err = retval;
+                       } else {
+                               macho_dbg("Truncated Mach-O load command "
+                                       "%lu: (got %lib, wanted %lub)\n", i,
+                                       retval, sizeof(data->loadcmd.hdr));
+                               err = -ENOEXEC;
+                       }
+                       goto out_noreturn;
+               }
+
+               /* Check/display the load command number */
+               macho_check_loadcmd(data->loadcmd.hdr.cmd);
+               size = data->loadcmd.hdr.size;
+
+               /* 
+                * Ensure the load-command size is a multiple of 4 bytes and
+                * large enough to contain the load-command header.
+                */
+               if (size & 0x3 || size < sizeof(data->loadcmd.hdr)) {
+                       macho_dbg("Invalid size for load command %lu: %lu\n",
+                               i, (unsigned long)data->loadcmd.hdr.size);
+                       err = -ENOEXEC;
+                       goto out_noreturn;
+               }
+
+               /* Check that we have room for another load command */
+               if (loadcmd_size + size > hdr_cmd_size) {
+                       macho_dbg("Mach-O header doesn't allocate enough "
+                                       "space for load-commands %lu-%lu!\n",
+                                       i, hdr_cmd_count-1);
+                       err = -ENOEXEC;
+                       goto out_noreturn;
+               }
+
+               /* Check that it isn't too big */
+               if (size > sizeof(data->loadcmd)) {
+                       macho_dbg("Load command %lu is too big (%lu > %lu): "
+                               "skipped!\n", i, size, sizeof(data->loadcmd));
+                       loadcmd_size += size;
+                       cur_offset   += size;
+                       continue;
+               }
+
+               /* Read in the rest of the load-command and move past it */
+               retval = kernel_read(bprm->file, cur_offset,
+                               (void *)&data->loadcmd, size);
+               if (retval != size) {
+                       /* If kernel_read() returned an error, handle it */
+                       if (retval < 0) {
+                               macho_dbg("Error while reading Mach-O load"
+                                       " command %lu: %li\n", i, retval);
+                               err = retval;
+                       } else {
+                               macho_dbg("Truncated Mach-O load command "
+                                       "%lu: (got %lib, wanted %lub)\n", i,
+                                       retval, size);
+                               err = -ENOEXEC;
+                       }
+                       goto out_noreturn;
+               }
+               loadcmd_size += size;
+               cur_offset   += size;
+
+               switch (data->loadcmd.hdr.cmd) {
+                       case MACHO_LOADCMD_NUM_SEGMENT64:
+                       case MACHO_LOADCMD_NUM_SEGMENT32:
+                       case MACHO_LOADCMD_NUM_THREAD_NOSTACK:
+                       case MACHO_LOADCMD_NUM_THREAD:
+                       case MACHO_LOADCMD_NUM_REF_DYLD:
+                               macho_dbg("    Load command completed!\n");
+                               break;
+               }
+               /* XXX FIXME XXX: Process the load-command */
+       }
 
        err = -EINVAL;
-       goto out;
+       goto out_noreturn;
 
+out_noreturn:
+       send_sig(SIGKILL, current, 0);
 out:
        kfree(data);
        return err;
 }
 
-
-
-#if 0
-       char buf[BINPRM_BUF_SIZE];
-       struct page *page[MAX_ARG_PAGES];
-       struct mm_struct *mm;
-       unsigned long p; /* current top of mem */
-       int sh_bang;
-       struct file * file;
-       int e_uid, e_gid;
-       kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
-       void *security;
-       int argc, envc;
-       char * filename;        /* Name of binary as seen by procps */
-       char * interp;          /* Name of the binary really executed. Most
-                                  of the time same as filename, but could be
-                                  different for binfmt_{misc,script} */
-       unsigned interp_flags;
-       unsigned interp_data;
-       unsigned long loader, exec;
-#endif
-
diff --git a/fs/mach-o/cpus.h b/fs/mach-o/cpus.h
index a1f2276..00fa3c0 100644
--- a/fs/mach-o/cpus.h
+++ b/fs/mach-o/cpus.h
@@ -12,20 +12,27 @@ typedef __u32 __bitwise macho_cpu_type_t;
 #define _ENTRY(type, num) \
        MACHO_CPU_TYPE_##type = ((__force macho_cpu_type_t)num)
 enum {
-       _ENTRY(VAX,     0x00000001),
-       _ENTRY(M68K,    0x00000006),
-       _ENTRY(I386,    0x00000007),
-       _ENTRY(MIPS,    0x00000008),
-       _ENTRY(M98K,    0x0000000a),
-       _ENTRY(PARISC,  0x0000000b),
-       _ENTRY(ARM,     0x0000000c),
-       _ENTRY(M88K,    0x0000000d),
-       _ENTRY(SPARC,   0x0000000e),
-       _ENTRY(I860,    0x0000000f),
-       _ENTRY(ALPHA,   0x00000010),
-       _ENTRY(PPC32,   0x00000012),
-       _ENTRY(PPC64,   0x01000012),
-       _ENTRY(ANY,     0xffffffff),
+       /* Historical architecture codes */
+       _ENTRY(ANY,     0xffffffff),    /* CPU_TYPE_ANY         */
+       _ENTRY(VAX,     0x00000001),    /* CPU_TYPE_VAX         */
+       _ENTRY(M68K,    0x00000006),    /* CPU_TYPE_MC680x0     */
+       _ENTRY(MIPS,    0x00000008),    /* CPU_TYPE_MIPS        */
+       _ENTRY(M98K,    0x0000000a),    /* CPU_TYPE_M98000      */
+       _ENTRY(PARISC,  0x0000000b),    /* CPU_TYPE_HPPA        */
+       _ENTRY(ARM,     0x0000000c),    /* CPU_TYPE_ARM         */
+       _ENTRY(M88K,    0x0000000d),    /* CPU_TYPE_M88000      */
+       _ENTRY(SPARC,   0x0000000e),    /* CPU_TYPE_SPARC       */
+       _ENTRY(I860,    0x0000000f),    /* CPU_TYPE_I860        */
+       _ENTRY(ALPHA,   0x00000010),    /* CPU_TYPE_ALPHA       */
+
+       /*
+        * Modern supported architectures. Note that a "CPU_TYPE_ABI64" flag
+        * is OR-ed into the architecture code for a 64-bit architecture
+        */
+       _ENTRY(I386,    0x00000007),    /* CPU_TYPE_X86 / CPU_TYPE_I386 */
+       _ENTRY(X86_64,  0x01000007),    /* CPU_TYPE_X86_64              */
+       _ENTRY(PPC32,   0x00000012),    /* CPU_TYPE_POWERPC             */
+       _ENTRY(PPC64,   0x01000012),    /* CPU_TYPE_POWERPC64           */
 };
 #undef _ENTRY
 
@@ -84,6 +91,9 @@ enum {
        _ENTRY(I386_XEON,               0x0000000c),
        _ENTRY(I386_XEON_MP,            0x0000001c),
 
+       /* AMD 64-bit subtypes */
+       _ENTRY(X86_64_ALL,              0x00000003),
+
        /* MIPS subtypes */
        _ENTRY(MIPS_ALL,                0x00000000),
        _ENTRY(MIPS_R2300,              0x00000001),
@@ -131,7 +141,7 @@ enum {
 
        /* Motorola/Freescale/IBM PowerPC64 subtypes */
        _ENTRY(PPC64_ALL,               0x00000000),
-       /*_ENTRY(PPC64_970,             0x00000001),*/
+       _ENTRY(PPC64_970,               0x00000064),
 };
 #undef _ENTRY
 
@@ -198,6 +208,10 @@ static const struct macho_cpu_subentry 
macho_cpu_i386_subtypes[] = {
        _ENTRY(I386,    XEON_MP,        "Intel Xeon SMP",                0),
        { .name = NULL },
 };
+static const struct macho_cpu_subentry macho_cpu_x86_64_subtypes[] = {
+       _ENTRY(X86_64,  ALL,            "all 64-bit AMD",                0),
+       { .name = NULL },
+};
 static const struct macho_cpu_subentry macho_cpu_mips_subtypes[] = {
        _ENTRY(MIPS,    ALL,            "all MIPS",                      0),
        _ENTRY(MIPS,    R2300,          "MIPS r2300",                    0),
@@ -252,7 +266,7 @@ static const struct macho_cpu_subentry 
macho_cpu_ppc32_subtypes[] = {
 };
 static const struct macho_cpu_subentry macho_cpu_ppc64_subtypes[] = {
        _ENTRY(PPC64,   ALL,            "all PowerPC64",                14),
-       /*_ENTRY(PPC64, 970,            "PowerPC 970",                   1),*/
+       _ENTRY(PPC64,   970,            "PowerPC 970",                  15),
        { .name = NULL },
 };
 #undef _ENTRY
@@ -273,7 +287,8 @@ static const struct macho_cpu_entry macho_cpu_types[] = {
        }
        _ENTRY(PPC32,   macho_cpu_ppc32_subtypes,       "PowerPC"       ),
        _ENTRY(PPC64,   macho_cpu_ppc64_subtypes,       "PowerPC64"     ),
-       _ENTRY(I386,    macho_cpu_i386_subtypes,        "Intel x86"     ),
+       _ENTRY(I386,    macho_cpu_i386_subtypes,        "Intel/AMD x86" ),
+       _ENTRY(X86_64,  macho_cpu_x86_64_subtypes,      "AMD 64-bit x86"),
        _ENTRY(VAX,     macho_cpu_vax_subtypes,         "VAX"           ),
        _ENTRY(M68K,    macho_cpu_m68k_subtypes,        "Motorola 68xxx"),
        _ENTRY(MIPS,    macho_cpu_mips_subtypes,        "MIPS"          ),
diff --git a/fs/mach-o/files.h b/fs/mach-o/files.h
index 17253d9..cdbd028 100644
--- a/fs/mach-o/files.h
+++ b/fs/mach-o/files.h
@@ -8,20 +8,21 @@
  * Mach-O file types, borrowed from Darwin
  */
 typedef __u32 __bitwise macho_file_type_t;
+enum {
 # define _ENTRY(type, num) \
        MACHO_FILE_TYPE_##type = ((__force macho_file_type_t)num)
-enum {
-       _ENTRY(OBJECT,          1),
-       _ENTRY(EXECUTE,         2),
-       _ENTRY(FVMLIB,          3),
-       _ENTRY(CORE,            4),
-       _ENTRY(PRELOAD,         5),
-       _ENTRY(DYLIB,           6),
-       _ENTRY(DYLINKER,        7),
-       _ENTRY(BUNDLE,          8),
-       _ENTRY(DYLIB_STUB,      9),
-};
+       _ENTRY(OBJECT,          0x01), /* MH_OBJECT     */
+       _ENTRY(EXECUTE,         0x02), /* MH_EXECUTE    */
+       _ENTRY(FVMLIB,          0x03), /* MH_FVMLIB     */
+       _ENTRY(CORE,            0x04), /* MH_CORE       */
+       _ENTRY(PRELOAD,         0x05), /* MH_PRELOAD    */
+       _ENTRY(DYLIB,           0x06), /* MH_DYLIB      */
+       _ENTRY(DYLD,            0x07), /* MH_DYLINKER   */
+       _ENTRY(BUNDLE,          0x08), /* MH_BUNDLE     */
+       _ENTRY(DYLIB_STUB,      0x09), /* MH_DYLIB_STUB */
+       _ENTRY(DEBUG_SYM,       0x0a), /* MY_DSYM       */
 #undef _ENTRY
+};
 
 /*
  * A file type mapping table with human-readable strings
@@ -44,9 +45,10 @@ static const struct macho_file_type_entry macho_file_types[] 
= {
        _ENTRY(CORE,            "coredump",                             0),
        _ENTRY(PRELOAD,         "preloaded executable",                 1),
        _ENTRY(DYLIB,           "dynamically linked shared library",    0),
-       _ENTRY(DYLINKER,        "dynamic link editor",                  0),
+       _ENTRY(DYLD,            "dynamic link editor",                  0),
        _ENTRY(BUNDLE,          "dynamically linked module",            0),
        _ENTRY(DYLIB_STUB,      "shared library stub for static link",  0),
+       _ENTRY(DEBUG_SYM,       "debug symbol file",                    0),
        { .name = NULL, }
 #undef _ENTRY
 };
@@ -55,24 +57,26 @@ static const struct macho_file_type_entry 
macho_file_types[] = {
  * Mach-O file flags, borrowed from Darwin
  */
 enum macho_file_flag {
-       MACHO_FILE_FLAG_NO_UNDEF        = 0,
-       MACHO_FILE_FLAG_INCR_LINK       = 1,
-       MACHO_FILE_FLAG_DYN_LINK        = 2,
-       MACHO_FILE_FLAG_BIND_AT_LOAD    = 3,
-       MACHO_FILE_FLAG_PREBOUND        = 4,
-       MACHO_FILE_FLAG_SPLIT_SEGS      = 5,
-       MACHO_FILE_FLAG_LAZY_INIT       = 6,
-       MACHO_FILE_FLAG_TWO_LEVEL       = 7,
-       MACHO_FILE_FLAG_FORCE_FLAT      = 8,
-       MACHO_FILE_FLAG_NO_MULT_DEFS    = 9,
-       MACHO_FILE_FLAG_NO_FIX_PREBIND  = 10,
-       MACHO_FILE_FLAG_PREBINDABLE     = 11,
-       MACHO_FILE_FLAG_ALL_MODS_BOUND  = 12,
-       MACHO_FILE_FLAG_SUBSECT_VIA_SYM = 13,
-       MACHO_FILE_FLAG_CANONICAL       = 14,
-       MACHO_FILE_FLAG_WEAK_DEFINES    = 15,
-       MACHO_FILE_FLAG_BINDS_TO_WEAK   = 16,
-       MACHO_FILE_FLAG_EXECSTACK       = 17,
+#define _ENTRY(type, num) MACHO_FILE_FLAG_##type = num
+       _ENTRY(NO_UNDEF,         0), /* MH_NOUNDEFS                     */
+       _ENTRY(INCR_LINK,        1), /* MH_INCRLINK                     */
+       _ENTRY(DYN_LINK,         2), /* MH_DYLDLINK                     */
+       _ENTRY(BIND_AT_LOAD,     3), /* MH_BINDATLOAD                   */
+       _ENTRY(PREBOUND,         4), /* MH_PREBOUND                     */
+       _ENTRY(SPLIT_SEGS,       5), /* MH_SPLIT_SEGS                   */
+       _ENTRY(LAZY_INIT,        6), /* MH_LAZY_INIT                    */
+       _ENTRY(TWO_LEVEL,        7), /* MH_TWOLEVEL                     */
+       _ENTRY(FORCE_FLAT,       8), /* MH_FORCE_FLAT                   */
+       _ENTRY(NO_MULT_DEFS,     9), /* MH_NOMULTIDEFS                  */
+       _ENTRY(NO_FIX_PREBIND,  10), /* MH_NOFIXPREBINDING              */
+       _ENTRY(PREBINDABLE,     11), /* MH_PREBINDABLE                  */
+       _ENTRY(ALL_MODS_BOUND,  12), /* MH_ALLMODSBOUND                 */
+       _ENTRY(SUBSECT_VIA_SYM, 13), /* MH_SUBSECTIONS_VIA_SYMBOLS      */
+       _ENTRY(CANONICAL,       14), /* MH_CANONICAL                    */
+       _ENTRY(WEAK_DEFINES,    15), /* MH_WEAK_DEFINES                 */
+       _ENTRY(BINDS_TO_WEAK,   16), /* MH_BINDS_TO_WEAK                */
+       _ENTRY(EXECSTACK,       17), /* MH_ALLOW_STACK_EXECUTION        */
+#undef _ENTRY
 };
 
 /*
@@ -98,7 +102,8 @@ static const char *macho_file_flags[] = {
        _ENTRY( WEAK_DEFINES,   "exports weak symbols"                  ),
        _ENTRY( BINDS_TO_WEAK,  "imports weak symbols"                  ),
        _ENTRY( EXECSTACK,      "requires executable stack"             ),
-       NULL
+       NULL,
+#undef _ENTRY
 };
 
 static int macho_check_file(macho_file_type_t type, __u32 flags)
diff --git a/fs/mach-o/loadcmds.h b/fs/mach-o/loadcmds.h
new file mode 100644
index 0000000..0ee35c3
--- /dev/null
+++ b/fs/mach-o/loadcmds.h
@@ -0,0 +1,389 @@
+#ifndef  _MACHO_LOADCMDS_H
+# define _MACHO_LOADCMDS_H 1
+
+# include <linux/types.h>
+
+/*
+ * Mach-O load-command types, borrowed from Darwin
+ */
+typedef __u32 __bitwise macho_loadcmd_num_t;
+enum {
+# define _ENTRY(type, num) \
+       MACHO_LOADCMD_NUM_##type = ((__force macho_loadcmd_num_t)num)
+       _ENTRY(SEGMENT32,       0x01), /* LC_SEGMENT            */
+       _ENTRY(SYMTAB_STAB,     0x02), /* LC_SYMTAB             */
+       _ENTRY(SYMTAB_GDB,      0x03), /* LC_SYMSEG             */
+       _ENTRY(THREAD_NOSTACK,  0x04), /* LC_THREAD             */
+       _ENTRY(THREAD,          0x05), /* LC_UNIXTHREAD         */
+       _ENTRY(REF_FVMLIB,      0x06), /* LC_LOADFVMLIB         */
+       _ENTRY(ID_FVMLIB,       0x07), /* LC_IDFVMLIB           */
+       _ENTRY(ID,              0x08), /* LC_IDENT              */
+       _ENTRY(FVMFILE,         0x09), /* LC_FVMFILE            */
+       _ENTRY(PREPAGE,         0x0a), /* LC_PREPAGE            */
+       _ENTRY(SYMTAB_DYLD,     0x0b), /* LC_DYSYMTAB           */
+       _ENTRY(REF_DYLIB,       0x0c), /* LC_LOAD_DYLIB         */
+       _ENTRY(ID_DYLIB,        0x0d), /* LC_ID_DYLIB           */
+       _ENTRY(REF_DYLD,        0x0e), /* LC_LOAD_DYLINKER      */
+       _ENTRY(ID_DYLD,         0x0f), /* LC_ID_DYLINKER        */
+       _ENTRY(PREBINDING,      0x10), /* LC_PREBOUND_DYLIB     */
+       _ENTRY(INITCODE32,      0x11), /* LC_ROUTINES           */
+       _ENTRY(SUB_FRAMEWORK,   0x12), /* LC_SUB_FRAMEWORK      */
+       _ENTRY(SUB_UMBRELLA,    0x13), /* LC_SUB_UMBRELLA       */
+       _ENTRY(SUB_CLIENT,      0x14), /* LC_SUB_CLIENT         */
+       _ENTRY(SUB_LIBRARY,     0x15), /* LC_SUB_LIBRARY        */
+       _ENTRY(HINTS_2LEVEL,    0x16), /* LC_TWOLEVEL_HINTS     */
+       _ENTRY(PREBIND_CKSUM,   0x17), /* LC_PREBIND_CKSUM      */
+       _ENTRY(WEAKREF_DYLIB,   0x18), /* LC_LOAD_DYLIB_WEAK    */
+       _ENTRY(SEGMENT64,       0x19), /* LC_SEGMENT_64         */
+       _ENTRY(INITCODE64,      0x1a), /* LC_ROUTINES_64        */
+       _ENTRY(UUID,            0x1b), /* LC_UUID               */
+# undef _ENTRY
+};
+
+/* Mach-O load command header */
+struct macho_loadcmd_hdr {
+       __u32 cmd;
+       __u32 size;
+} __attribute__((__packed__));
+
+/* Variable-length string in a Mach-O load command */
+struct macho_loadcmd_varstr {
+       __u32 off;
+} __attribute__((__packed__));
+
+/* Generic large load command */
+struct macho_loadcmd_unknown {
+       struct macho_loadcmd_hdr hdr;
+       __u8 data[2048 - sizeof(struct macho_loadcmd_hdr)];
+} __attribute__((__packed__));
+
+/* Mach-O memory-mapped file segment (32-bit) */
+struct macho_loadcmd_segment32 {
+       struct macho_loadcmd_hdr hdr;
+       char name[16];
+       __u32 vm_addr;
+       __u32 vm_size;
+       __u32 file_off;
+       __u32 file_size;
+       __u32 prot_max;
+       __u32 prot_init;
+       __u32 sect_count;
+       __u32 flags;
+} __attribute__((__packed__));
+
+/* Mach-O symbol table (stab) */
+struct macho_loadcmd_symtab_stab {
+       struct macho_loadcmd_hdr hdr;
+       __u32 sym_off;
+       __u32 sym_count;
+       __u32 str_off;
+       __u32 str_size;
+} __attribute__((__packed__));
+
+/* Mach-O symbol table (gdb, obsolete) */
+struct macho_loadcmd_symtab_gdb {
+       struct macho_loadcmd_hdr hdr;
+       __u32 symtab_off;
+       __u32 symtab_size;
+} __attribute__((__packed__));
+
+/* Mach-O new thread (with or without stack) */
+# define MACHO_LOADCMD_THREAD_MAX_PPC 144
+# define MACHO_LOADCMD_THREAD_MAX_X86 144
+struct macho_loadcmd_thread {
+       struct macho_loadcmd_hdr hdr;
+       __u32 threaddata[];
+} __attribute__((__packed__));
+
+/* Mach-O reference to or identity of a fixed-VM shared lib */
+struct macho_loadcmd_fvmlib {
+       struct macho_loadcmd_hdr hdr;
+       struct macho_loadcmd_varstr path;
+       __u32 vers;
+       __u32 addr;
+       __u8 data[];
+} __attribute__((__packed__));
+
+/* Mach-O identity of an object (obsolete) */
+struct macho_loadcmd_id {
+       struct macho_loadcmd_hdr hdr;
+       __u8 iddata[];
+} __attribute__((__packed__));
+
+/* Mach-O reference to a fixed VM file */
+struct macho_loadcmd_fvmfile {
+       struct macho_loadcmd_hdr hdr;
+       struct macho_loadcmd_varstr path;
+       __u32 addr;
+       __u8 data[];
+} __attribute__((__packed__));
+
+/* Mach-O unknown (LC_PREPAGE) */
+struct macho_loadcmd_prepage {
+       struct macho_loadcmd_hdr hdr;
+       __u8 prepagedata[];
+} __attribute__((__packed__));
+
+/* Mach-O symbol table (dyld) */
+struct macho_loadcmd_symtab_dyld {
+       struct macho_loadcmd_hdr hdr;
+       __u32 sym_local_off;
+       __u32 sym_local_num;
+       __u32 sym_extern_off;
+       __u32 sym_extern_num;
+       __u32 sym_undef_off;
+       __u32 sym_undef_num;
+       __u32 toc_off;
+       __u32 toc_num;
+       __u32 modtab_off;
+       __u32 modtab_num;
+       __u32 extref_off;
+       __u32 extref_num;
+       __u32 indirect_off;
+       __u32 indirect_num;
+       __u32 extrel_off;
+       __u32 extrel_num;
+       __u32 localrel_off;
+       __u32 localrel_num;
+} __attribute__((__packed__));
+
+/* Mach-O reference to or identity of a dynamic shared lib */
+struct macho_loadcmd_dylib {
+       struct macho_loadcmd_hdr hdr;
+       struct macho_loadcmd_varstr path;
+       __u32 timestamp;
+       __u32 vers_current;
+       __u32 vers_compat;
+       __u8 data[];
+} __attribute__((__packed__));
+
+/* Mach-O reference to or identity of a dynamic linker */
+struct macho_loadcmd_dyld {
+       struct macho_loadcmd_hdr hdr;
+       struct macho_loadcmd_varstr path;
+       __u8 data[];
+} __attribute__((__packed__));
+
+/* Mach-O prebinding against a dynamic shared lib */
+struct macho_loadcmd_prebinding {
+       struct macho_loadcmd_hdr hdr;
+       struct macho_loadcmd_varstr path;
+       __u32 modvec_num;
+       __u32 modvec_off;
+       __u8 data[];
+} __attribute__((__packed__));
+
+/* Mach-O shared library init routine (32-bit) */
+struct macho_loadcmd_initcode32 {
+       struct macho_loadcmd_hdr hdr;
+       __u32 init_address;
+       __u32 init_module;
+       __u32 reserved[6];
+} __attribute__((__packed__));
+
+/* Mach-O sub-framework file */
+struct macho_loadcmd_sub_framework {
+       struct macho_loadcmd_hdr hdr;
+       struct macho_loadcmd_varstr parent;
+       __u8 data[];
+} __attribute__((__packed__));
+
+/* Mach-O sub-umbrella-framework file */
+struct macho_loadcmd_sub_umbrella {
+       struct macho_loadcmd_hdr hdr;
+       struct macho_loadcmd_varstr parent;
+       __u8 data[];
+} __attribute__((__packed__));
+
+/* Mach-O sub-framework client entry */
+struct macho_loadcmd_sub_client {
+       struct macho_loadcmd_hdr hdr;
+       struct macho_loadcmd_varstr client;
+       __u8 data[];
+} __attribute__((__packed__));
+
+/* Mach-O sub-framework library */
+struct macho_loadcmd_sub_library {
+       struct macho_loadcmd_hdr hdr;
+       struct macho_loadcmd_varstr parent;
+       __u8 data[];
+} __attribute__((__packed__));
+
+/* Mach-O two-level namespace hinting table */
+struct macho_loadcmd_hints_2level {
+       struct macho_loadcmd_hdr hdr;
+       __u32 hinttable_offset;
+       __u32 hinttable_count;
+} __attribute__((__packed__));
+
+/* Mach-O prebinding checksum */
+struct macho_loadcmd_prebind_cksum {
+       struct macho_loadcmd_hdr hdr;
+       __u8 data[];
+} __attribute__((__packed__));
+
+/* Mach-O memory-mapped file segment (64-bit) */
+struct macho_loadcmd_segment64 {
+       struct macho_loadcmd_hdr hdr;
+       char name[16];
+       __u64 vm_addr;
+       __u64 vm_size;
+       __u32 file_off;
+       __u32 file_size;
+       __u32 prot_max;
+       __u32 prot_init;
+       __u32 sect_count;
+       __u32 flags;
+} __attribute__((__packed__));
+
+/* Mach-O shared library init routine (64-bit) */
+struct macho_loadcmd_initcode64 {
+       struct macho_loadcmd_hdr hdr;
+       __u64 init_address;
+       __u64 init_module;
+       __u64 reserved[6];
+} __attribute__((__packed__));
+
+/* Mach-O linked object UUID (LC_UUID) */
+struct macho_loadcmd_uuid {
+       struct macho_loadcmd_hdr hdr;
+       __u8 uuid[16];
+} __attribute__((__packed__));
+
+/* A generic Mach-O load command */
+union macho_loadcmd {
+       struct macho_loadcmd_hdr                hdr;
+       struct macho_loadcmd_unknown            unknown;
+       struct macho_loadcmd_segment32          segment32;
+       struct macho_loadcmd_symtab_stab        symtab_stab;
+       struct macho_loadcmd_symtab_gdb         symtab_gdb;
+       struct macho_loadcmd_thread             thread;
+       struct macho_loadcmd_fvmlib             fvmlib;
+       struct macho_loadcmd_id                 id;
+       struct macho_loadcmd_fvmfile            fvmfile;
+       struct macho_loadcmd_prepage            prepage;
+       struct macho_loadcmd_symtab_dyld        symtab_dyld;
+       struct macho_loadcmd_dylib              dylib;
+       struct macho_loadcmd_dyld               dyld;
+       struct macho_loadcmd_prebinding         prebinding;
+       struct macho_loadcmd_initcode32         initcode32;
+       struct macho_loadcmd_sub_framework      sub_framework;
+       struct macho_loadcmd_sub_umbrella       sub_umbrella;
+       struct macho_loadcmd_sub_client         sub_client;
+       struct macho_loadcmd_sub_library        sub_library;
+       struct macho_loadcmd_hints_2level       hints_2level;
+       struct macho_loadcmd_prebind_cksum      prebind_cksum;
+       struct macho_loadcmd_segment64          segment64;
+       struct macho_loadcmd_initcode64         initcode64;
+       struct macho_loadcmd_uuid               uuid;
+} __attribute__((__packed__));
+
+/* Inernal function to check and return a load-command string */
+static inline const __u8 *macho_loadcmd_varstr_get__(
+               const struct macho_loadcmd_hdr *hdr,
+               const struct macho_loadcmd_varstr *varstr,
+               const __u8 *loadcmd_data)
+{
+       unsigned long data_offset = ((const void *)loadcmd_data) -
+                       ((const void *)hdr);
+       unsigned long str_offset, i;
+
+       /* Make sure the varstr points into the data area */
+       if (varstr->off < data_offset)
+               return NULL;
+       str_offset = varstr->off - data_offset;
+
+       /*
+        * Iterate over all the characters in the variable-length string to
+        * ensure that there is a trailing 0 before the end of the data area.
+        */
+       for (i = str_offset; i < hdr->size; i++)
+               if (!loadcmd_data[i])
+                       break;
+
+       /* If we quit before we hit end-of-data then it's a valid string */
+       if (i < hdr->size)
+               return &loadcmd_data[str_offset];
+
+       /* Sadly no such luck, it's a bogus Mach-O file */
+       return NULL;
+}
+
+/*
+ * Wrapper around the macho_loadcmd_varstr_get__ function above to make the
+ * calling-convention much easier.
+ *
+ * Example usage:
+ *   macho_loadcmd_dyld *dyld = (...);
+ *   const char *dyld_path = macho_loadcmd_varstr_get(dyld, path);
+ *   if (!dyld_path)
+ *           return -ENOEXEC;
+ */
+#define macho_loadcmd_varstr_get(LOADCMD, VARSTR) \
+       (macho_loadcmd_varstr_get__(&((LOADCMD)->hdr), \
+                       &((LOADCMD)->VARSTR), (LOADCMD)->data))
+
+/*
+ * A Mach-O load-command mapping table with human-readable strings
+ */
+struct macho_loadcmd_entry {
+       const char *name;
+       macho_loadcmd_num_t cmd;
+       int pass;
+};
+
+static const struct macho_loadcmd_entry macho_loadcmds[] = {
+#define _ENTRY(TYPE, NAME) {                   \
+               .name = NAME,                           \
+               .cmd = MACHO_LOADCMD_NUM_ ## TYPE,      \
+       }
+       _ENTRY(SEGMENT32,       "memory-mapped file segment (32-bit)"),
+       _ENTRY(SYMTAB_STAB,     "symbol table (stab)"),
+       _ENTRY(SYMTAB_GDB,      "symbol table (gdb, obsolete)"),
+       _ENTRY(THREAD_NOSTACK,  "new thread (without stack)"),
+       _ENTRY(THREAD,          "new thread (with stack)"),
+       _ENTRY(REF_FVMLIB,      "reference to a fixed-VM shared lib"),
+       _ENTRY(ID_FVMLIB,       "identity of a fixed-VM shared lib"),
+       _ENTRY(ID,              "identity of an object (obsolete)"),
+       _ENTRY(FVMFILE,         "reference to a fixed VM file"),
+       _ENTRY(PREPAGE,         "unknown (LC_PREPAGE)"),
+       _ENTRY(SYMTAB_DYLD,     "symbol table (dyld)"),
+       _ENTRY(REF_DYLIB,       "reference to a dynamic shared lib"),
+       _ENTRY(ID_DYLIB,        "identity of a dynamic shared lib"),
+       _ENTRY(REF_DYLD,        "reference to a dynamic linker"),
+       _ENTRY(ID_DYLD,         "identity of a dynamic linker"),
+       _ENTRY(PREBINDING,      "prebinding against a dynamic shared lib"),
+       _ENTRY(INITCODE32,      "shared library init routine (32-bit)"),
+       _ENTRY(SUB_FRAMEWORK,   "sub-framework file"),
+       _ENTRY(SUB_UMBRELLA,    "sub-umbrella-framework file"),
+       _ENTRY(SUB_CLIENT,      "sub-framework client entry"),
+       _ENTRY(SUB_LIBRARY,     "sub-framework library"),
+       _ENTRY(HINTS_2LEVEL,    "two-level namespace hinting table"),
+       _ENTRY(PREBIND_CKSUM,   "prebinding checksum"),
+       _ENTRY(WEAKREF_DYLIB,   "weak reference to a dynamic shared lib"),
+       _ENTRY(SEGMENT64,       "memory-mapped file segment (64-bit)"),
+       _ENTRY(INITCODE64,      "shared library init routine (64-bit)"),
+       _ENTRY(UUID,            "linked object UUID"),
+       { .name = NULL, }
+#undef _ENTRY
+};
+
+static void macho_check_loadcmd(macho_loadcmd_num_t cmd)
+{
+       unsigned long i;
+
+       /* Iterate over the list of file types */
+       for (i = 0; macho_loadcmds[i].name; i++)
+               if (macho_loadcmds[i].cmd == cmd)
+                       break;
+
+       /* If we didn't find the load command type then ignore it */
+       if (!macho_loadcmds[i].name) {
+               macho_dbg("  Mach-O load command: unknown (%u)\n", cmd);
+               return;
+       }
+
+       macho_dbg("  Mach-O load command: %s\n", macho_loadcmds[i].name);
+}
+
+#endif /* not _MACHO_LOADCMDS_H */

Reply via email to