diff options
Diffstat (limited to 'qemu/roms/openhackware/src/libexec/macho.c')
-rw-r--r-- | qemu/roms/openhackware/src/libexec/macho.c | 517 |
1 files changed, 517 insertions, 0 deletions
diff --git a/qemu/roms/openhackware/src/libexec/macho.c b/qemu/roms/openhackware/src/libexec/macho.c new file mode 100644 index 000000000..f07c92144 --- /dev/null +++ b/qemu/roms/openhackware/src/libexec/macho.c @@ -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, §ion, sizeof(mach_section_t)) < 0) { + ERROR("Cannot load MACH-O section\n"); + return -1; + } + pos += sizeof(mach_section_t); + section.vmaddr = get_be32(§ion.vmaddr); + section.size = get_be32(§ion.size); + section.offset = get_be32(§ion.offset); + section.align = get_be32(§ion.align); + section.reloc_offset = get_be32(§ion.reloc_offset); + section.nreloc = get_be32(§ion.nreloc); + section.flags = get_be32(§ion.flags); + section.res1 = get_be32(§ion.res1); + section.res2 = get_be32(§ion.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; +} |