Re: [RFC][PATCH 2/2] Darwinux: Proper support for digging through the Mach-O load-command table

From: Kyle Moffett
Date: Fri Mar 30 2007 - 14:09:01 EST


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 <mrmacman_g4@xxxxxxx>
---
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 */