Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openhackware / src / libexec / macho.c
diff --git a/qemu/roms/openhackware/src/libexec/macho.c b/qemu/roms/openhackware/src/libexec/macho.c
new file mode 100644 (file)
index 0000000..f07c921
--- /dev/null
@@ -0,0 +1,517 @@
+/*
+ * <macho.c>
+ *
+ * Open Hack'Ware BIOS MACH-O executable file loader
+ * 
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ * 
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License V2
+ *   as published by the Free Software Foundation
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "bios.h"
+#include "exec.h"
+
+/* MACH-O executable loader */
+/* FAT definitions */
+/* CPU type definitions */
+typedef enum cpu_type_t {
+    CPU_TYPE_ANY     = -1,
+    CPU_TYPE_VAX     = 1,
+    CPU_TYPE_MC680x0 = 6,
+    CPU_TYPE_I386    = 7,
+    CPU_TYPE_MIPS    = 8,
+    CPU_TYPE_MC98000 = 10,
+    CPU_TYPE_HPPA    = 11,
+    CPU_TYPE_ARM     = 12,
+    CPU_TYPE_MC88000 = 13,
+    CPU_TYPE_SPARC   = 14,
+    CPU_TYPE_I860    = 15,
+    CPU_TYPE_ALPHA   = 16,
+    CPU_TYPE_POWERPC = 18,
+} cpu_type_t;
+
+/* Any CPU */
+typedef enum cpu_subtype_any_t {
+    CPU_SUBTYPE_MULTIPLE      = -1,
+    CPU_SUBTYPE_LITTLE_ENDIAN = 0,
+    CPU_SUBTYPE_BIG_ENDIAN    = 1,
+} cpu_subtype_any_t;
+
+/* PowerPC */
+typedef enum cpu_subtype_ppc_t {
+    CPU_SUBTYPE_PPC_ALL   = 0,
+    CPU_SUBTYPE_PPC_601   = 1,
+    CPU_SUBTYPE_PPC_602   = 2,
+    CPU_SUBTYPE_PPC_603   = 3,
+    CPU_SUBTYPE_PPC_603e  = 4,
+    CPU_SUBTYPE_PPC_603ev = 5,
+    CPU_SUBTYPE_PPC_604   = 6,
+    CPU_SUBTYPE_PPC_604e  = 7,
+    CPU_SUBTYPE_PPC_620   = 8,
+    CPU_SUBTYPE_PPC_750   = 9,
+    CPU_SUBTYPE_PPC_7400  = 10,
+    CPU_SUBTYPE_PPC_7450  = 11,
+} cpu_subtype_ppc_t;
+
+/* Fat header definition */
+#define FAT_MAGIC 0xCAFEBABE
+
+typedef struct fat_head_t {
+    uint32_t magic;
+    uint32_t nfat_arch;
+} fat_head_t;
+
+typedef struct fat_arch_t {
+    cpu_type_t    cpu_type;
+    cpu_subtype_ppc_t cpu_subtype;
+    uint32_t offset;
+    uint32_t size;
+    uint32_t align;
+} fat_arch_t;
+
+/* Mach-O binary definitions */
+#define MACH_O_MAGIC 0xFEEDFACE
+
+typedef enum filetype_t {
+    MH_OBJECT     = 0x1,
+    MH_EXECUTE    = 0x2,
+    MH_FVMLIB     = 0x3,
+    MH_CORE       = 0x4,
+    MH_PRELOAD    = 0x5,
+    MH_DYLIB      = 0x6,
+    MH_DYLINKER   = 0x7,
+    MH_BUNDLE     = 0x8,
+} filetype_t;
+
+enum {
+    MH_NOUNDEFS   = 0x01,
+    MH_INCRLINK   = 0x02,
+    MH_DYLDLINK   = 0x04,
+    MH_BINDATLOAD = 0x08,
+    MH_PREBOUND   = 0x10,
+};
+
+typedef struct mach_head_t {
+    uint32_t magic;
+    cpu_type_t cpu_type;
+    cpu_subtype_ppc_t subtype;
+    filetype_t file_type;
+    uint32_t nb_cmds;
+    uint32_t cmds_size;
+    uint32_t flags;
+} mach_head_t;
+
+typedef enum load_cmd_t {
+    LC_SEGMENT        = 0x01,
+    LC_SYMTAB         = 0x02,
+    LC_SYMSEG         = 0x03,
+    LC_THREAD         = 0x04,
+    LC_UNIXTHREAD     = 0x05,
+    LC_LOADFVMLIB     = 0x06,
+    LC_IDFVMLIB       = 0x07,
+    LC_IDENT          = 0x08,
+    LC_FVMFILE        = 0x09,
+    LC_PREPAGE        = 0x0A,
+    LC_DYSYMTAB       = 0x0B,
+    LC_LOAD_DYLIB     = 0x0C,
+    LC_ID_DYLIB       = 0x0D,
+    LC_LOAD_DYLINKER  = 0x0E,
+    LC_ID_DYLINKER    = 0x0F,
+    LC_PREBOUND_DYLIB = 0x10,
+    LC_0x17           = 0x17,
+} load_cmd_t;
+
+typedef struct mach_load_cmd_t {
+    load_cmd_t cmd;
+    uint32_t cmd_size;
+} mach_load_cmd_t;
+
+typedef struct mach_string_t {
+    uint32_t offset;
+} mach_string_t;
+
+enum {
+    SG_HIGHVM  = 0x1,
+    SG_FVMLIB  = 0x2,
+    SG_NORELOC = 0x4,
+};
+
+typedef struct mach_segment_t {
+    unsigned char segname[16];
+    uint32_t vmaddr;
+    uint32_t vmsize;
+    uint32_t file_offset;
+    uint32_t file_size;
+    uint32_t max_prot;
+    uint32_t init_prot;
+    uint32_t nsects;
+    uint32_t flags;
+} mach_segment_t;
+
+enum {
+    SECTION_TYPE               = 0xFF,
+    S_REGULAR                  = 0x0,
+    S_ZEROFILL                 = 0x1,
+    S_CSTRING_LITERALS         = 0x2,
+    S_4BYTE_LITERALS           = 0x3,
+    S_8BYTE_LITERALS           = 0x4,
+    S_LITERAL_POINTERS         = 0x5,
+    S_NON_LAZY_SYMBOL_POINTERS = 0x6,
+    S_LAZY_SYMBOL_POINTERS     = 0x7,
+    S_SYMBOL_STUBS             = 0x8,
+    S_MOD_INIT_FUNC_POINTERS   = 0x9,
+};
+
+enum {
+    S_ATTR_PURE_INSTRUCTIONS   = 0x80000000,
+    S_ATTR_SOME_INSTRUCTIONS   = 0x00000400,
+    S_ATTR_EXT_RELOC           = 0x00000200,
+    S_ATTR_LOC_RELOC           = 0x00000100,
+};
+
+typedef struct mach_section_t {
+    unsigned char sectname[16];
+    unsigned char segname[16];
+    uint32_t vmaddr;
+    uint32_t size;
+    uint32_t offset;
+    uint32_t align;
+    uint32_t reloc_offset;
+    uint32_t nreloc;
+    uint32_t flags;
+    uint32_t res1;
+    uint32_t res2;
+} mach_section_t;
+
+typedef struct mach_symtab_t {
+    uint32_t offset;
+    uint32_t nsyms;
+    uint32_t str_offset;
+    uint32_t str_size;
+} mach_symtab_t;
+
+typedef struct mach_symseg_t {
+    uint32_t offset;
+    uint32_t size;
+} mach_symseg_t;
+
+typedef struct mach_unixth_t {
+    uint32_t flavor;
+    uint32_t count;
+    /* This is supposed to be a stack.
+     * Let's assume it's less than 1kB (arbitrary !)
+     */
+    uint32_t data[256];
+} mach_unixth_t;
+
+typedef struct mach_fvmlib_t {
+    uint32_t str_offset;
+    uint32_t minor_version;
+    uint32_t header_addr;
+} mach_fvmlib_t;
+
+typedef struct mach_fvmfile_t {
+    uint32_t str_offset;
+    uint32_t vmaddr;
+} mach_fvmfile_t;
+
+typedef struct mach_dysymtab_t {
+    uint32_t ilocal_syms;
+    uint32_t nlocal_syms;
+    uint32_t iext_syms;
+    uint32_t next_syms;
+    uint32_t iundef_syms;
+    uint32_t nundef_syms;
+    uint32_t toc_offset;
+    uint32_t ntoc;
+    uint32_t modtab_offset;
+    uint32_t nmodtab;
+    uint32_t extsym_offset;
+    uint32_t nextsym;
+    uint32_t indirect_offset;
+    uint32_t nindirect;
+    uint32_t ext_reloc_offset;
+    uint32_t next_reloc;
+    uint32_t local_reloc_offset;
+    uint32_t nlocal_reloc;
+} mach_dysymtab_t;
+
+typedef struct mach_dylib_t {
+    uint32_t str_offset;
+    uint32_t timestamp;
+    uint32_t cur_version;
+    uint32_t compat_version;
+} mach_dylib_t;
+
+typedef struct mach_prebound_t {
+    uint32_t str_offset;
+    uint32_t nb_modules;
+    unsigned char linked_modules[256];
+} mach_prebound_t;
+
+int exec_load_macho (inode_t *file, void **dest, void **entry, void **end,
+                     uint32_t loffset)
+{
+    mach_head_t mhdr;
+    mach_load_cmd_t lcmd;
+    fat_head_t fhdr;
+    fat_arch_t fahdr;
+    void *address, *first, *last;
+    uint32_t k, j, best, offset;
+    int entry_set;
+
+    /* Probe FAT */
+    file_seek(file, loffset);
+    if (fs_read(file, &fhdr, sizeof(fat_head_t)) < 0) {
+        ERROR("Cannot load fat header...\n");
+        return -1;
+    }
+    fhdr.magic = get_be32(&fhdr.magic);
+    if (fhdr.magic != FAT_MAGIC)
+        goto macho_probe;
+    fhdr.nfat_arch = get_be32(&fhdr.nfat_arch);
+    DPRINTF("FAT file: %d archs\n", fhdr.nfat_arch);
+    /* Find the best architecture */
+    best = -1;
+    offset = 0;
+    for (k = 0; k < fhdr.nfat_arch; k++) {
+        if (fs_read(file, &fahdr, sizeof(fat_arch_t)) < 0) {
+            ERROR("Cannot load fat arch header\n");
+            return -1;
+        }
+        fahdr.cpu_type = get_be32(&fahdr.cpu_type);
+        if (fahdr.cpu_type != CPU_TYPE_POWERPC)
+            continue;
+        fahdr.cpu_subtype = get_be32(&fahdr.cpu_subtype);
+        fahdr.offset = get_be32(&fahdr.offset);
+        fahdr.size = get_be32(&fahdr.size);
+        fahdr.align = get_be32(&fahdr.align);
+        switch (fahdr.cpu_subtype) {
+        case CPU_SUBTYPE_PPC_750:
+            best = k;
+            offset = fahdr.offset;
+            goto fat_cpu_ok;
+        case CPU_SUBTYPE_PPC_ALL:
+            if (best == (uint32_t)-1) {
+                offset = fahdr.offset;
+                best = k;
+            }
+            break;
+        case CPU_SUBTYPE_PPC_603:
+        case CPU_SUBTYPE_PPC_603e:
+        case CPU_SUBTYPE_PPC_603ev:
+        case CPU_SUBTYPE_PPC_604:
+        case CPU_SUBTYPE_PPC_604e:
+            best = k;
+            offset = fahdr.offset;
+            break;
+        default:
+            break;
+        }
+    }
+    if (best == (uint32_t)-1) {
+        ERROR("No matching PPC FAT arch\n");
+        return -1;
+    }
+    DPRINTF("Use FAT arch %d at %08x %08x\n", best, offset, loffset);
+ fat_cpu_ok:
+    loffset += offset;
+
+    /* Probe macho */
+ macho_probe:
+    file_seek(file, loffset);
+    if (fs_read(file, &mhdr, sizeof(mach_head_t)) < 0) {
+        ERROR("Cannot load MACH-O header...\n");
+        return -1;
+    }
+    mhdr.magic = get_be32(&mhdr.magic);
+    if (mhdr.magic != MACH_O_MAGIC) {
+        ERROR("Not a MACH-O file\n");
+        return -2;
+    }
+    mhdr.cpu_type = get_be32(&mhdr.cpu_type);
+    mhdr.subtype = get_be32(&mhdr.subtype);
+    mhdr.file_type = get_be32(&mhdr.file_type);
+    mhdr.nb_cmds = get_be32(&mhdr.nb_cmds);
+    mhdr.cmds_size = get_be32(&mhdr.cmds_size);
+    mhdr.flags = get_be32(&mhdr.flags);
+    DPRINTF("MACHO-O file cpu %d %d file type %08x %d cmds size %08x flags "
+            "%08x\n", mhdr.cpu_type, mhdr.subtype, mhdr.file_type,
+            mhdr.nb_cmds, mhdr.cmds_size, mhdr.flags);
+    offset = sizeof(mach_head_t);
+    first = (void *)-1;
+    last = NULL;
+    entry_set = 0;
+    for (k = 0; k < mhdr.nb_cmds; k++) {
+        file_seek(file, loffset + offset);
+        if (fs_read(file, &lcmd, sizeof(mach_load_cmd_t)) < 0) {
+            ERROR("Unable to load MACH-O cmd %d\n", k);
+            return -1;
+        }
+        lcmd.cmd = get_be32(&lcmd.cmd);
+        lcmd.cmd_size = get_be32(&lcmd.cmd_size);
+        DPRINTF("Cmd %d : %08x size %08x (%08x %08x)\n", k, lcmd.cmd,
+                lcmd.cmd_size, offset, offset + loffset);
+        switch (lcmd.cmd) {
+        case LC_SEGMENT:
+            /* To be loaded for execution */
+            {
+                mach_segment_t segment;
+                mach_section_t section;
+                uint32_t pos;
+                
+                pos = offset + sizeof(mach_load_cmd_t);
+                if (fs_read(file, &segment, sizeof(mach_segment_t)) < 0) {
+                    ERROR("Cannot load MACH-O segment\n");
+                    return -1;
+                }
+                pos += sizeof(mach_segment_t);
+                segment.vmaddr = get_be32(&segment.vmaddr);
+                segment.vmsize = get_be32(&segment.vmsize);
+                segment.file_offset = get_be32(&segment.file_offset);
+                segment.file_size = get_be32(&segment.file_size);
+                segment.max_prot = get_be32(&segment.max_prot);
+                segment.init_prot = get_be32(&segment.init_prot);
+                segment.nsects = get_be32(&segment.nsects);
+                segment.flags = get_be32(&segment.flags);
+                DPRINTF("MACH-O segment addr %08x size %08x off %08x fsize "
+                        "%08x ns %d fl %08x\n", segment.vmaddr, segment.vmsize,
+                        segment.file_offset, segment.file_size,
+                        segment.nsects, segment.flags);
+                for (j = 0; j < segment.nsects; j++) {
+                    file_seek(file, loffset + pos);
+                    if (fs_read(file, &section, sizeof(mach_section_t)) < 0) {
+                        ERROR("Cannot load MACH-O section\n");
+                        return -1;
+                    }
+                    pos += sizeof(mach_section_t);
+                    section.vmaddr = get_be32(&section.vmaddr);
+                    section.size = get_be32(&section.size);
+                    section.offset = get_be32(&section.offset);
+                    section.align = get_be32(&section.align);
+                    section.reloc_offset = get_be32(&section.reloc_offset);
+                    section.nreloc = get_be32(&section.nreloc);
+                    section.flags = get_be32(&section.flags);
+                    section.res1 = get_be32(&section.res1);
+                    section.res2 = get_be32(&section.res2);
+                    DPRINTF("MACH-O section vmaddr %08x size %08x off %08x "
+                            "flags %08x\n", section.vmaddr, section.size,
+                            section.offset, section.flags);
+                    switch (section.flags & SECTION_TYPE) {
+                    case S_REGULAR:
+                    case S_CSTRING_LITERALS:
+                    case S_4BYTE_LITERALS:
+                    case S_8BYTE_LITERALS:
+                    case S_LITERAL_POINTERS:
+                    case S_NON_LAZY_SYMBOL_POINTERS:
+                    case S_LAZY_SYMBOL_POINTERS:
+                    case S_SYMBOL_STUBS:
+                    case S_MOD_INIT_FUNC_POINTERS:
+                        DPRINTF("Load section of type %d from %08x to %08x"
+                                " %08x\n", section.flags, section.offset,
+                                section.vmaddr, section.size);
+                        file_seek(file, section.offset + loffset);
+                        address = (void *)section.vmaddr;
+                        if (address < first && address != NULL)
+                            first = address;
+                        if (address + section.size > last)
+                            last = address + section.size;
+                        if (fs_read(file, address, section.size) < 0) {
+                            ERROR("Cannot load MACH-O section %d %d...\n",
+                                  k, j);
+                            return -1;
+                        }
+                        break;
+                    case S_ZEROFILL:
+                        DPRINTF("Fill zero section to %08x %08x\n",
+                                section.vmaddr, section.size);
+                        address = (void *)section.vmaddr;
+                        if (address < first && address != NULL)
+                            first = address;
+                        if (address + section.size > last)
+                            last = address + section.size;
+                        memset(address, 0, section.size);
+                        break;
+                    default:
+                        ERROR("Unknown MACH-O section type: %d\n",
+                              section.flags);
+                        return -1;
+                    }
+                }
+            }
+            break;
+        case LC_SYMTAB:
+            /* Don't care */
+            break;
+        case LC_SYMSEG:
+            /* Don't care */
+            break;
+        case LC_UNIXTHREAD:
+            /* To be loaded for execution */
+            {
+                mach_unixth_t unixth;
+
+                if (fs_read(file, &unixth, sizeof(mach_unixth_t)) < 0) {
+                    ERROR("Cannot load MACH-O UNIX thread\n");
+                    return -1;
+                }
+                DPRINTF("Set entry point to %08x\n", unixth.data[0]);
+                *entry = (void *)unixth.data[0];
+                entry_set = 1;
+            }
+            break;
+        case LC_THREAD:
+            break;
+        case LC_LOADFVMLIB:
+            break;
+        case LC_IDFVMLIB:
+            break;
+        case LC_IDENT:
+            break;
+        case LC_FVMFILE:
+            break;
+        case LC_PREPAGE:
+            printf("Prepage command\n");
+            break;
+        case LC_DYSYMTAB:
+            break;
+        case LC_LOAD_DYLIB:
+            break;
+        case LC_ID_DYLIB:
+            break;
+        case LC_LOAD_DYLINKER:
+            /* To be loaded for execution */
+            break;
+        case LC_ID_DYLINKER:
+            break;
+        case LC_PREBOUND_DYLIB:
+            break;
+        case LC_0x17:
+            /* ? */
+            break;
+        default:
+            printf("unknown MACH-O command (%d %d)\n", k, lcmd.cmd);
+            return -1;
+        }
+        offset += lcmd.cmd_size;
+    }
+    *dest = first;
+    *end = last;
+    //    if (entry_set == 0)
+        *entry = *dest;
+
+    return 0;
+}