int main(int argc, char * argv[]) { KXKextManagerError err; int fd; const char * output_name = NULL; uint32_t i, zero = 0, num_files = 0; uint32_t filenum; uint32_t strx, strtabsize, strtabpad; struct symbol * import_symbols; struct symbol * export_symbols; uint32_t num_import_syms, num_export_syms, num_removed_syms; uint32_t import_idx, export_idx; const NXArchInfo * host_arch; const NXArchInfo * target_arch; boolean_t require_imports = true; boolean_t diff = false; struct { struct mach_header hdr; struct symtab_command symcmd; } load_cmds; struct file { vm_offset_t mapped; vm_size_t mapped_size; uint32_t nsyms; boolean_t import; const char * path; }; struct file files[64]; host_arch = NXGetLocalArchInfo(); target_arch = host_arch; for( i = 1; i < argc; i += 2) { boolean_t import; if (!strcmp("-sect", argv[i])) { require_imports = false; i--; continue; } if (!strcmp("-diff", argv[i])) { require_imports = false; diff = true; i--; continue; } if (i == (argc - 1)) { fprintf(stderr, "bad arguments: %s\n", argv[i]); exit(1); } if (!strcmp("-arch", argv[i])) { target_arch = NXGetArchInfoFromName(argv[i + 1]); if (!target_arch) { fprintf(stderr, "unknown architecture name: %s\n", argv[i+1]); exit(1); } continue; } if (!strcmp("-output", argv[i])) { output_name = argv[i+1]; continue; } if (!strcmp("-import", argv[i])) import = true; else if (!strcmp("-export", argv[i])) import = false; else { fprintf(stderr, "unknown option: %s\n", argv[i]); exit(1); } err = readFile(argv[i+1], &files[num_files].mapped, &files[num_files].mapped_size); if (kKXKextManagerErrorNone != err) exit(1); if (files[num_files].mapped && files[num_files].mapped_size) { files[num_files].import = import; files[num_files].path = argv[i+1]; num_files++; } } if (!output_name) { fprintf(stderr, "no output file\n"); exit(1); } num_import_syms = 0; num_export_syms = 0; for (filenum = 0; filenum < num_files; filenum++) { files[filenum].nsyms = count_symbols((char *) files[filenum].mapped); if (files[filenum].import) num_import_syms += files[filenum].nsyms; else num_export_syms += files[filenum].nsyms; } if (!num_export_syms) { fprintf(stderr, "no export names\n"); exit(1); } import_symbols = calloc(num_import_syms, sizeof(struct symbol)); export_symbols = calloc(num_export_syms, sizeof(struct symbol)); strtabsize = 4; import_idx = 0; export_idx = 0; for (filenum = 0; filenum < num_files; filenum++) { if (files[filenum].import) { store_symbols((char *) files[filenum].mapped, import_symbols, import_idx, num_import_syms); import_idx += files[filenum].nsyms; } else { strtabsize += store_symbols((char *) files[filenum].mapped, export_symbols, export_idx, num_export_syms); export_idx += files[filenum].nsyms; } if (!files[filenum].nsyms) { fprintf(stderr, "warning: file %s contains no names\n", files[filenum].path); } } qsort(import_symbols, num_import_syms, sizeof(struct symbol), &qsort_cmp); qsort(export_symbols, num_export_syms, sizeof(struct symbol), &qsort_cmp); num_removed_syms = 0; if (num_import_syms) { for (export_idx = 0; export_idx < num_export_syms; export_idx++) { boolean_t found = true; if (!bsearch(export_symbols[export_idx].name, import_symbols, num_import_syms, sizeof(struct symbol), &bsearch_cmp)) { if (require_imports) fprintf(stderr, "exported name not in import list: %s\n", export_symbols[export_idx].name); found = false; } if (export_symbols[export_idx].indirect) { if (!bsearch(export_symbols[export_idx].indirect, import_symbols, num_import_syms, sizeof(struct symbol), &bsearch_cmp)) { if (require_imports) fprintf(stderr, "exported name not in import list: %s\n", export_symbols[export_idx].indirect); found = false; } } if (found && !diff) continue; if (!found && diff) continue; num_removed_syms++; strtabsize -= (export_symbols[export_idx].name_len + export_symbols[export_idx].indirect_len); export_symbols[export_idx].name = 0; } } if (require_imports && num_removed_syms) { err = kKXKextManagerErrorUnspecified; goto finish; } fd = open(output_name, O_WRONLY|O_CREAT|O_TRUNC, 0755); if (-1 == fd) { perror("couldn't write output"); err = kKXKextManagerErrorFileAccess; goto finish; } strtabpad = (strtabsize + 3) & ~3; load_cmds.hdr.magic = MH_MAGIC; load_cmds.hdr.cputype = target_arch->cputype; load_cmds.hdr.cpusubtype = target_arch->cpusubtype; load_cmds.hdr.filetype = MH_OBJECT; load_cmds.hdr.ncmds = 1; load_cmds.hdr.sizeofcmds = sizeof(load_cmds.symcmd); load_cmds.hdr.flags = MH_INCRLINK; load_cmds.symcmd.cmd = LC_SYMTAB; load_cmds.symcmd.cmdsize = sizeof(load_cmds.symcmd); load_cmds.symcmd.symoff = sizeof(load_cmds); load_cmds.symcmd.nsyms = (num_export_syms - num_removed_syms); load_cmds.symcmd.stroff = (num_export_syms - num_removed_syms) * sizeof(struct nlist) + load_cmds.symcmd.symoff; load_cmds.symcmd.strsize = strtabpad; if (target_arch->byteorder != host_arch->byteorder) { swap_mach_header(&load_cmds.hdr, target_arch->byteorder); swap_symtab_command(&load_cmds.symcmd, target_arch->byteorder); } err = writeFile(fd, &load_cmds, sizeof(load_cmds)); if (kKXKextManagerErrorNone != err) goto finish; strx = 4; for (export_idx = 0; export_idx < num_export_syms; export_idx++) { struct nlist nl; if (!export_symbols[export_idx].name) continue; nl.n_sect = 0; nl.n_desc = 0; nl.n_un.n_strx = strx; strx += export_symbols[export_idx].name_len; if (export_symbols[export_idx].indirect) { nl.n_type = N_INDR | N_EXT; nl.n_value = strx; strx += export_symbols[export_idx].indirect_len; } else { nl.n_type = N_UNDF | N_EXT; nl.n_value = 0; } if (target_arch->byteorder != host_arch->byteorder) swap_nlist(&nl, 1, target_arch->byteorder); err = writeFile(fd, &nl, sizeof(nl)); if (kKXKextManagerErrorNone != err) goto finish; } strx = sizeof(uint32_t); err = writeFile(fd, &zero, strx); if (kKXKextManagerErrorNone != err) goto finish; for (export_idx = 0; export_idx < num_export_syms; export_idx++) { if (!export_symbols[export_idx].name) continue; err = writeFile(fd, export_symbols[export_idx].name, export_symbols[export_idx].name_len + export_symbols[export_idx].indirect_len); if (kKXKextManagerErrorNone != err) goto finish; } err = writeFile(fd, &zero, strtabpad - strtabsize); if (kKXKextManagerErrorNone != err) goto finish; close(fd); finish: if (kKXKextManagerErrorNone != err) { if (output_name) unlink(output_name); exit(1); } else exit(0); return(0); }
enum bool swap_object_headers( void *mach_header, struct load_command *load_commands) { unsigned long i; uint32_t magic, ncmds, sizeofcmds, cmd_multiple; cpu_type_t cputype; cpu_subtype_t cpusubtype; struct mach_header *mh; struct mach_header_64 *mh64; enum byte_sex target_byte_sex; struct load_command *lc, l; struct segment_command *sg; struct segment_command_64 *sg64; struct section *s; struct section_64 *s64; struct symtab_command *st; struct dysymtab_command *dyst; struct symseg_command *ss; struct fvmlib_command *fl; struct thread_command *ut; struct ident_command *id; struct dylib_command *dl; struct sub_framework_command *sub; struct sub_umbrella_command *usub; struct sub_library_command *lsub; struct sub_client_command *csub; struct prebound_dylib_command *pbdylib; struct dylinker_command *dyld; struct routines_command *rc; struct routines_command_64 *rc64; struct twolevel_hints_command *hints; struct prebind_cksum_command *cs; struct uuid_command *uuid; unsigned long flavor, count, nflavor; char *p, *state; magic = *((uint32_t *)mach_header); if(magic == MH_MAGIC){ mh = (struct mach_header *)mach_header; ncmds = mh->ncmds; sizeofcmds = mh->sizeofcmds; cputype = mh->cputype; cpusubtype = mh->cpusubtype; cmd_multiple = 4; mh64 = NULL; } else{ mh64 = (struct mach_header_64 *)mach_header; ncmds = mh64->ncmds; sizeofcmds = mh64->sizeofcmds; cputype = mh64->cputype; cpusubtype = mh64->cpusubtype; cmd_multiple = 8; mh = NULL; } /* * Make a pass through the load commands checking them to the level * that they can be parsed and then swapped. */ for(i = 0, lc = load_commands; i < ncmds; i++){ l = *lc; /* check load command size for a correct multiple size */ if(lc->cmdsize % cmd_multiple != 0){ error("in swap_object_headers(): malformed load command %lu " "(cmdsize not a multiple of %u)", i, cmd_multiple); return(FALSE); } /* check that load command does not extends past end of commands */ if((char *)lc + lc->cmdsize > (char *)load_commands + sizeofcmds){ error("in swap_object_headers(): truncated or malformed load " "command %lu (extends past the end of the all load " "commands)", i); return(FALSE); } /* check that the load command size is not zero */ if(lc->cmdsize == 0){ error("in swap_object_headers(): malformed load command %lu " "(cmdsize is zero)", i); return(FALSE); } switch(lc->cmd){ case LC_SEGMENT: sg = (struct segment_command *)lc; if(sg->cmdsize != sizeof(struct segment_command) + sg->nsects * sizeof(struct section)){ error("in swap_object_headers(): malformed load command " "(inconsistent cmdsize in LC_SEGMENT command %lu for " "the number of sections)", i); return(FALSE); } break; case LC_SEGMENT_64: sg64 = (struct segment_command_64 *)lc; if(sg64->cmdsize != sizeof(struct segment_command_64) + sg64->nsects * sizeof(struct section_64)){ error("in swap_object_headers(): malformed load command " "(inconsistent cmdsize in LC_SEGMENT_64 command %lu " "for the number of sections)", i); return(FALSE); } break; case LC_SYMTAB: st = (struct symtab_command *)lc; if(st->cmdsize != sizeof(struct symtab_command)){ error("in swap_object_headers(): malformed load commands " "(LC_SYMTAB command %lu has incorrect cmdsize", i); return(FALSE); } break; case LC_DYSYMTAB: dyst = (struct dysymtab_command *)lc; if(dyst->cmdsize != sizeof(struct dysymtab_command)){ error("in swap_object_headers(): malformed load commands " "(LC_DYSYMTAB command %lu has incorrect cmdsize", i); return(FALSE); } break; case LC_SYMSEG: ss = (struct symseg_command *)lc; if(ss->cmdsize != sizeof(struct symseg_command)){ error("in swap_object_headers(): malformed load command " "(LC_SYMSEG command %lu has incorrect cmdsize", i); return(FALSE); } break; case LC_IDFVMLIB: case LC_LOADFVMLIB: fl = (struct fvmlib_command *)lc; if(fl->cmdsize < sizeof(struct fvmlib_command)){ error("in swap_object_headers(): malformed load commands " "(%s command %lu has too small cmdsize field)", fl->cmd == LC_IDFVMLIB ? "LC_IDFVMLIB" : "LC_LOADFVMLIB", i); return(FALSE); } if(fl->fvmlib.name.offset >= fl->cmdsize){ error("in swap_object_headers(): truncated or malformed " "load commands (name.offset field of %s command %lu " "extends past the end of all load commands)", fl->cmd == LC_IDFVMLIB ? "LC_IDFVMLIB" : "LC_LOADFVMLIB", i); return(FALSE); } break; case LC_ID_DYLIB: case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: dl = (struct dylib_command *)lc; if(dl->cmdsize < sizeof(struct dylib_command)){ error("in swap_object_headers(): malformed load commands " "(%s command %lu has too small cmdsize field)", dl->cmd == LC_ID_DYLIB ? "LC_ID_DYLIB" : (dl->cmd == LC_LOAD_DYLIB ? "LC_LOAD_DYLIB" : "LC_LOAD_WEAK_DYLIB"), i); return(FALSE); } if(dl->dylib.name.offset >= dl->cmdsize){ error("in swap_object_headers(): truncated or malformed " "load commands (name.offset field of %s command %lu " "extends past the end of all load commands)", dl->cmd == LC_ID_DYLIB ? "LC_ID_DYLIB" : (dl->cmd == LC_LOAD_DYLIB ? "LC_LOAD_DYLIB" : "LC_LOAD_WEAK_DYLIB"), i); return(FALSE); } break; case LC_SUB_FRAMEWORK: sub = (struct sub_framework_command *)lc; if(sub->cmdsize < sizeof(struct sub_framework_command)){ error("in swap_object_headers(): malformed load commands " "(LC_SUB_FRAMEWORK command %lu has too small cmdsize " "field)", i); return(FALSE); } if(sub->umbrella.offset >= sub->cmdsize){ error("in swap_object_headers(): truncated or malformed " "load commands (umbrella.offset field of " "LC_SUB_FRAMEWORK command %lu extends past the end " "of all load commands)", i); return(FALSE); } break; case LC_SUB_UMBRELLA: usub = (struct sub_umbrella_command *)lc; if(usub->cmdsize < sizeof(struct sub_umbrella_command)){ error("in swap_object_headers(): malformed load commands " "(LC_SUB_UMBRELLA command %lu has too small cmdsize " "field)", i); return(FALSE); } if(usub->sub_umbrella.offset >= usub->cmdsize){ error("in swap_object_headers(): truncated or malformed " "load commands (sub_umbrella.offset field of " "LC_SUB_UMBRELLA command %lu extends past the end " "of all load commands)", i); return(FALSE); } break; case LC_SUB_LIBRARY: lsub = (struct sub_library_command *)lc; if(lsub->cmdsize < sizeof(struct sub_library_command)){ error("in swap_object_headers(): malformed load commands " "(LC_SUB_LIBRARY command %lu has too small cmdsize " "field)", i); return(FALSE); } if(lsub->sub_library.offset >= lsub->cmdsize){ error("in swap_object_headers(): truncated or malformed " "load commands (sub_library.offset field of " "LC_SUB_LIBRARY command %lu extends past the end " "of all load commands)", i); return(FALSE); } break; case LC_SUB_CLIENT: csub = (struct sub_client_command *)lc; if(csub->cmdsize < sizeof(struct sub_client_command)){ error("in swap_object_headers(): malformed load commands " "(LC_SUB_CLIENT command %lu has too small cmdsize " "field)", i); return(FALSE); } if(csub->client.offset >= csub->cmdsize){ error("in swap_object_headers(): truncated or malformed " "load commands (client.offset field of " "LC_SUB_CLIENT command %lu extends past the end " "of all load commands)", i); return(FALSE); } break; case LC_PREBOUND_DYLIB: pbdylib = (struct prebound_dylib_command *)lc; if(pbdylib->cmdsize < sizeof(struct prebound_dylib_command)){ error("in swap_object_headers(): malformed load commands " "(LC_PREBOUND_DYLIB command %lu has too small " "cmdsize field)", i); return(FALSE); } if(pbdylib->name.offset >= pbdylib->cmdsize){ error("in swap_object_headers(): truncated or malformed " "load commands (name.offset field of " "LC_PREBOUND_DYLIB command %lu extends past the end " "of all load commands)", i); return(FALSE); } if(pbdylib->linked_modules.offset >= pbdylib->cmdsize){ error("in swap_object_headers(): truncated or malformed " "load commands (linked_modules.offset field of " "LC_PREBOUND_DYLIB command %lu extends past the end " "of all load commands)", i); return(FALSE); } break; case LC_ID_DYLINKER: case LC_LOAD_DYLINKER: dyld = (struct dylinker_command *)lc; if(dyld->cmdsize < sizeof(struct dylinker_command)){ error("in swap_object_headers(): malformed load commands " "(%s command %lu has too small cmdsize field)", dyld->cmd == LC_ID_DYLINKER ? "LC_ID_DYLINKER" : "LC_LOAD_DYLINKER", i); return(FALSE); } if(dyld->name.offset >= dyld->cmdsize){ error("in swap_object_headers(): truncated or malformed " "load commands (name.offset field of %s command %lu " "extends past the end of all load commands)", dyld->cmd == LC_ID_DYLINKER ? "LC_ID_DYLINKER" : "LC_LOAD_DYLINKER", i); return(FALSE); } break; case LC_UNIXTHREAD: case LC_THREAD: ut = (struct thread_command *)lc; state = (char *)ut + sizeof(struct thread_command); if(cputype == CPU_TYPE_MC680x0){ struct m68k_thread_state_regs *cpu; struct m68k_thread_state_68882 *fpu; struct m68k_thread_state_user_reg *user_reg; nflavor = 0; p = (char *)ut + ut->cmdsize; while(state < p){ flavor = *((unsigned long *)state); state += sizeof(unsigned long); count = *((unsigned long *)state); state += sizeof(unsigned long); switch(flavor){ case M68K_THREAD_STATE_REGS: if(count != M68K_THREAD_STATE_REGS_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not M68K_THREAD_STATE_REGS_COUNT for " "flavor number %lu which is a M68K_THREAD_" "STATE_REGS flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } cpu = (struct m68k_thread_state_regs *)state; state += sizeof(struct m68k_thread_state_regs); break; case M68K_THREAD_STATE_68882: if(count != M68K_THREAD_STATE_68882_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not M68K_THREAD_STATE_68882_COUNT for " "flavor number %lu which is a M68K_THREAD_" "STATE_68882 flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } fpu = (struct m68k_thread_state_68882 *)state; state += sizeof(struct m68k_thread_state_68882); break; case M68K_THREAD_STATE_USER_REG: if(count != M68K_THREAD_STATE_USER_REG_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not M68K_THREAD_STATE_USER_REG_COUNT for " "flavor number %lu which is a M68K_THREAD_" "STATE_USER_REG flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } user_reg = (struct m68k_thread_state_user_reg *)state; state += sizeof(struct m68k_thread_state_user_reg); break; default: error("in swap_object_headers(): malformed " "load commands (unknown " "flavor %lu for flavor number %lu in %s command" " %lu can't byte swap it)", flavor, nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } nflavor++; } break; } if(cputype == CPU_TYPE_POWERPC || cputype == CPU_TYPE_VEO || cputype == CPU_TYPE_POWERPC64){ ppc_thread_state_t *cpu; ppc_float_state_t *fpu; ppc_exception_state_t *except; ppc_thread_state64_t *cpu64; nflavor = 0; p = (char *)ut + ut->cmdsize; while(state < p){ flavor = *((unsigned long *)state); state += sizeof(unsigned long); count = *((unsigned long *)state); state += sizeof(unsigned long); switch(flavor){ case PPC_THREAD_STATE: if(count != PPC_THREAD_STATE_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not PPC_THREAD_STATE_COUNT for " "flavor number %lu which is a PPC_THREAD_" "STATE flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } cpu = (ppc_thread_state_t *)state; state += sizeof(ppc_thread_state_t); break; case PPC_FLOAT_STATE: if(count != PPC_FLOAT_STATE_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not PPC_FLOAT_STATE_COUNT for " "flavor number %lu which is a PPC_FLOAT_" "STATE flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } fpu = (ppc_float_state_t *)state; state += sizeof(ppc_float_state_t); break; case PPC_EXCEPTION_STATE: if(count != PPC_EXCEPTION_STATE_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not PPC_EXCEPTION_STATE_COUNT for " "flavor number %lu which is a PPC_EXCEPT" "ION_STATE flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } except = (ppc_exception_state_t *)state; state += sizeof(ppc_exception_state_t); break; case PPC_THREAD_STATE64: if(count != PPC_THREAD_STATE64_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not PPC_THREAD_STATE64_COUNT for " "flavor number %lu which is a PPC_THREAD_" "STATE64 flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } cpu64 = (ppc_thread_state64_t *)state; state += sizeof(ppc_thread_state64_t); break; default: error("in swap_object_headers(): malformed " "load commands (unknown " "flavor %lu for flavor number %lu in %s command" " %lu can't byte swap it)", flavor, nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } nflavor++; } break; } if(cputype == CPU_TYPE_MC88000){ m88k_thread_state_grf_t *cpu; m88k_thread_state_xrf_t *fpu; m88k_thread_state_user_t *user; m88110_thread_state_impl_t *spu; nflavor = 0; p = (char *)ut + ut->cmdsize; while(state < p){ flavor = *((unsigned long *)state); state += sizeof(unsigned long); count = *((unsigned long *)state); state += sizeof(unsigned long); switch(flavor){ case M88K_THREAD_STATE_GRF: if(count != M88K_THREAD_STATE_GRF_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not M88K_THREAD_STATE_GRF_COUNT for " "flavor number %lu which is a M88K_THREAD_" "STATE_GRF flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } cpu = (m88k_thread_state_grf_t *)state; state += sizeof(m88k_thread_state_grf_t); break; case M88K_THREAD_STATE_XRF: if(count != M88K_THREAD_STATE_XRF_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not M88K_THREAD_STATE_XRF_COUNT for " "flavor number %lu which is a M88K_THREAD_" "STATE_XRF flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } fpu = (m88k_thread_state_xrf_t *)state; state += sizeof(m88k_thread_state_xrf_t); break; case M88K_THREAD_STATE_USER: if(count != M88K_THREAD_STATE_USER_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not M88K_THREAD_STATE_USER_COUNT for " "flavor number %lu which is a M88K_THREAD_" "STATE_USER flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } user = (m88k_thread_state_user_t *)state; state += sizeof(m88k_thread_state_user_t); break; case M88110_THREAD_STATE_IMPL: if(count != M88110_THREAD_STATE_IMPL_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not M88110_THREAD_STATE_IMPL_COUNT for " "flavor number %lu which is a M88110_THREAD" "_STATE_IMPL flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } spu = (m88110_thread_state_impl_t *)state; state += sizeof(m88110_thread_state_impl_t); break; default: error("in swap_object_headers(): malformed " "load commands (unknown " "flavor %lu for flavor number %lu in %s command" " %lu can't byte swap it)", flavor, nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } nflavor++; } break; } if(cputype == CPU_TYPE_I860){ struct i860_thread_state_regs *cpu; nflavor = 0; p = (char *)ut + ut->cmdsize; while(state < p){ flavor = *((unsigned long *)state); state += sizeof(unsigned long); count = *((unsigned long *)state); state += sizeof(unsigned long); switch(flavor){ case I860_THREAD_STATE_REGS: if(count != I860_THREAD_STATE_REGS_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not I860_THREAD_STATE_REGS_COUNT for " "flavor number %lu which is a I860_THREAD_" "STATE_REGS flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } cpu = (struct i860_thread_state_regs *)state; state += sizeof(struct i860_thread_state_regs); break; default: error("in swap_object_headers(): malformed " "load commands (unknown " "flavor %lu for flavor number %lu in %s command" " %lu can't byte swap it)", flavor, nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } nflavor++; } break; } if(cputype == CPU_TYPE_I386 #ifdef x86_THREAD_STATE64 || cputype == CPU_TYPE_X86_64 #endif /* x86_THREAD_STATE64 */ ){ i386_thread_state_t *cpu; #ifdef x86_THREAD_STATE64 x86_thread_state64_t *cpu64; #endif /* x86_THREAD_STATE64 */ /* current i386 thread states */ #if i386_THREAD_STATE == 1 struct i386_float_state *fpu; i386_exception_state_t *exc; #endif /* i386_THREAD_STATE == 1 */ /* i386 thread states on older releases */ #if i386_THREAD_STATE == -1 i386_thread_fpstate_t *fpu; i386_thread_exceptstate_t *exc; i386_thread_cthreadstate_t *user; #endif /* i386_THREAD_STATE == -1 */ nflavor = 0; p = (char *)ut + ut->cmdsize; while(state < p){ flavor = *((unsigned long *)state); state += sizeof(unsigned long); count = *((unsigned long *)state); state += sizeof(unsigned long); switch(flavor){ case i386_THREAD_STATE: /* current i386 thread states */ #if i386_THREAD_STATE == 1 case -1: #endif /* i386_THREAD_STATE == 1 */ /* i386 thread states on older releases */ #if i386_THREAD_STATE == -1 case 1: #endif /* i386_THREAD_STATE == -1 */ if(count != i386_THREAD_STATE_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not i386_THREAD_STATE_COUNT for flavor " "number %lu which is a i386_THREAD_STATE " "flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } cpu = (i386_thread_state_t *)state; state += sizeof(i386_thread_state_t); break; /* current i386 thread states */ #if i386_THREAD_STATE == 1 case i386_FLOAT_STATE: if(count != i386_FLOAT_STATE_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not i386_FLOAT_STATE_COUNT for flavor " "number %lu which is a i386_FLOAT_STATE " "flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } fpu = (struct i386_float_state *)state; state += sizeof(struct i386_float_state); break; case i386_EXCEPTION_STATE: if(count != I386_EXCEPTION_STATE_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not I386_EXCEPTION_STATE_COUNT for " "flavor number %lu which is a i386_" "EXCEPTION_STATE flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } exc = (i386_exception_state_t *)state; state += sizeof(i386_exception_state_t); break; #endif /* i386_THREAD_STATE == 1 */ /* i386 thread states on older releases */ #if i386_THREAD_STATE == -1 case i386_THREAD_FPSTATE: if(count != i386_THREAD_FPSTATE_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not i386_THREAD_FPSTATE_COUNT for flavor " "number %lu which is a i386_THREAD_FPSTATE " "flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } fpu = (i386_thread_fpstate_t *)state; state += sizeof(i386_thread_fpstate_t); break; case i386_THREAD_EXCEPTSTATE: if(count != i386_THREAD_EXCEPTSTATE_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not i386_THREAD_EXCEPTSTATE_COUNT for " "flavor number %lu which is a i386_THREAD_" "EXCEPTSTATE flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } exc = (i386_thread_exceptstate_t *)state; state += sizeof(i386_thread_fpstate_t); break; case i386_THREAD_CTHREADSTATE: if(count != i386_THREAD_CTHREADSTATE_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not i386_THREAD_CTHREADSTATE_COUNT for " "flavor number %lu which is a i386_THREAD_" "CTHREADSTATE flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } user = (i386_thread_cthreadstate_t *)state; state += sizeof(i386_thread_fpstate_t); break; #endif /* i386_THREAD_STATE == -1 */ #ifdef x86_THREAD_STATE64 case x86_THREAD_STATE64: if(count != x86_THREAD_STATE64_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not x86_THREAD_STATE64_COUNT for " "flavor number %lu which is an x86_THREAD_" "STATE64 flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } cpu64 = (x86_thread_state64_t *)state; state += sizeof(x86_thread_state64_t); break; #endif /* x86_THREAD_STATE64 */ default: error("in swap_object_headers(): malformed " "load commands (unknown " "flavor %lu for flavor number %lu in %s command" " %lu can't byte swap it)", flavor, nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } nflavor++; } break; } if(cputype == CPU_TYPE_HPPA){ struct hp_pa_integer_thread_state *cpu; struct hp_pa_frame_thread_state *frame; struct hp_pa_fp_thread_state *fpu; nflavor = 0; p = (char *)ut + ut->cmdsize; while(state < p){ flavor = *((unsigned long *)state); state += sizeof(unsigned long); count = *((unsigned long *)state); state += sizeof(unsigned long); switch(flavor){ case HPPA_INTEGER_THREAD_STATE: if(count != HPPA_INTEGER_THREAD_STATE_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not HPPA_INTEGER_THREAD_STATE_COUNT for " "flavor number %lu which is a HPPA_INTEGER" "_THREAD_STATE flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } cpu = (struct hp_pa_integer_thread_state *)state; state += sizeof(struct hp_pa_integer_thread_state); break; case HPPA_FRAME_THREAD_STATE: if(count != HPPA_FRAME_THREAD_STATE_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not HPPA_FRAME_THREAD_STATE_COUNT for " "flavor number %lu which is a HPPA_FRAME" "_THREAD_STATE flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } frame = (struct hp_pa_frame_thread_state *)state; state += sizeof(struct hp_pa_frame_thread_state); break; case HPPA_FP_THREAD_STATE: if(count != HPPA_FP_THREAD_STATE_COUNT){ error("in swap_object_headers(): malformed " "load commands (count " "not HPPA_FP_THREAD_STATE_COUNT for " "flavor number %lu which is a HPPA_FP" "_THREAD_STATE flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } fpu = (struct hp_pa_fp_thread_state *)state; state += sizeof(struct hp_pa_fp_thread_state); break; default: error("in swap_object_headers(): malformed " "load commands (unknown " "flavor %lu for flavor number %lu in %s command" " %lu can't byte swap it)", flavor, nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } nflavor++; } break; } if(cputype == CPU_TYPE_SPARC) { struct sparc_thread_state_regs *cpu; struct sparc_thread_state_fpu *fpu; nflavor = 0; p = (char *)ut + ut->cmdsize; while (state < p) { flavor = *((unsigned long *) state); state += sizeof(unsigned long); count = *((unsigned int *) state); state += sizeof(unsigned long); switch (flavor) { case SPARC_THREAD_STATE_REGS: if (count != SPARC_THREAD_STATE_REGS_COUNT) { error("in swap_object_headers(): malformed " "load commands (count " "not SPARC_THREAD_STATE_REGS_COUNT for " "flavor number %lu which is a SPARC_THREAD_" "STATE_REGS flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } cpu = (struct sparc_thread_state_regs *) state; state += sizeof(struct sparc_thread_state_regs); break; case SPARC_THREAD_STATE_FPU: if (count != SPARC_THREAD_STATE_FPU_COUNT) { error("in swap_object_headers(): malformed " "load commands (count " "not SPARC_THREAD_STATE_FPU_COUNT for " "flavor number %lu which is a SPARC_THREAD_" "STATE_FPU flavor in %s command %lu)", nflavor, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); } fpu = (struct sparc_thread_state_fpu *) state; state += sizeof(struct sparc_thread_state_fpu); break; } } break; } if (cputype == CPU_TYPE_ARM) { nflavor = 0; p = (char *)ut + ut->cmdsize; while (state < p) { state += 8 + sizeof(arm_thread_state_t); nflavor++; } break; } error("in swap_object_headers(): malformed load commands " "(unknown cputype (%d) and cpusubtype (%d) of object and " "can't byte swap %s command %lu)", cputype, cpusubtype, ut->cmd == LC_UNIXTHREAD ? "LC_UNIXTHREAD" : "LC_THREAD", i); return(FALSE); case LC_IDENT: id = (struct ident_command *)lc; if((char *)id + id->cmdsize > (char *)load_commands + sizeofcmds){ error("in swap_object_headers(): truncated or malformed " "load commands (cmdsize field of LC_IDENT command %lu " "extends past the end of the load commands)", i); return(FALSE); } break; case LC_ROUTINES: rc = (struct routines_command *)lc; if(rc->cmdsize != sizeof(struct routines_command)){ error("in swap_object_headers(): malformed load commands (" "LC_ROUTINES command %lu has incorrect cmdsize", i); return(FALSE); } break; case LC_ROUTINES_64: rc64 = (struct routines_command_64 *)lc; if(rc64->cmdsize != sizeof(struct routines_command_64)){ error("in swap_object_headers(): malformed load commands (" "LC_ROUTINES_64 command %lu has incorrect cmdsize", i); return(FALSE); } break; case LC_TWOLEVEL_HINTS: hints = (struct twolevel_hints_command *)lc; if(hints->cmdsize != sizeof(struct twolevel_hints_command)){ error("in swap_object_headers(): malformed load commands " "(LC_TWOLEVEL_HINTS command %lu has incorrect " "cmdsize", i); return(FALSE); } break; case LC_PREBIND_CKSUM: cs = (struct prebind_cksum_command *)lc; if(cs->cmdsize != sizeof(struct prebind_cksum_command)){ error("in swap_object_headers(): malformed load commands " "(LC_PREBIND_CKSUM command %lu has incorrect cmdsize", i); return(FALSE); } break; case LC_UUID: uuid = (struct uuid_command *)lc; if(uuid->cmdsize != sizeof(struct uuid_command)){ error("in swap_object_headers(): malformed load commands " "(LC_UUID command %lu has incorrect cmdsize", i); return(FALSE); } break; default: error("in swap_object_headers(): malformed load commands " "(unknown load command %lu)", i); return(FALSE); } lc = (struct load_command *)((char *)lc + l.cmdsize); /* check that next load command does not extends past the end */ if((char *)lc > (char *)load_commands + sizeofcmds){ error("in swap_object_headers(): truncated or malformed load " "commands (load command %lu extends past the end of all " "load commands)", i + 1); return(FALSE); } } /* check for an inconsistent size of the load commands */ if((char *)load_commands + sizeofcmds != (char *)lc){ error("in swap_object_headers(): malformed load commands " "(inconsistent sizeofcmds field in mach header)"); return(FALSE); } /* * Now knowing the load commands can be parsed swap them. */ target_byte_sex = get_host_byte_sex() == BIG_ENDIAN_BYTE_SEX ? LITTLE_ENDIAN_BYTE_SEX : BIG_ENDIAN_BYTE_SEX; for(i = 0, lc = load_commands; i < ncmds; i++){ l = *lc; switch(lc->cmd){ case LC_SEGMENT: sg = (struct segment_command *)lc; s = (struct section *) ((char *)sg + sizeof(struct segment_command)); swap_section(s, sg->nsects, target_byte_sex); swap_segment_command(sg, target_byte_sex); break; case LC_SEGMENT_64: sg64 = (struct segment_command_64 *)lc; s64 = (struct section_64 *) ((char *)sg64 + sizeof(struct segment_command_64)); swap_section_64(s64, sg64->nsects, target_byte_sex); swap_segment_command_64(sg64, target_byte_sex); break; case LC_SYMTAB: st = (struct symtab_command *)lc; swap_symtab_command(st, target_byte_sex); break; case LC_DYSYMTAB: dyst = (struct dysymtab_command *)lc; swap_dysymtab_command(dyst, target_byte_sex); break; case LC_SYMSEG: ss = (struct symseg_command *)lc; swap_symseg_command(ss, target_byte_sex); break; case LC_IDFVMLIB: case LC_LOADFVMLIB: fl = (struct fvmlib_command *)lc; swap_fvmlib_command(fl, target_byte_sex); break; case LC_ID_DYLIB: case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: dl = (struct dylib_command *)lc; swap_dylib_command(dl, target_byte_sex); break; case LC_SUB_FRAMEWORK: sub = (struct sub_framework_command *)lc; swap_sub_framework_command(sub, target_byte_sex); break; case LC_SUB_UMBRELLA: usub = (struct sub_umbrella_command *)lc; swap_sub_umbrella_command(usub, target_byte_sex); break; case LC_SUB_LIBRARY: lsub = (struct sub_library_command *)lc; swap_sub_library_command(lsub, target_byte_sex); break; case LC_SUB_CLIENT: csub = (struct sub_client_command *)lc; swap_sub_client_command(csub, target_byte_sex); break; case LC_PREBOUND_DYLIB: pbdylib = (struct prebound_dylib_command *)lc; swap_prebound_dylib_command(pbdylib, target_byte_sex); break; case LC_ID_DYLINKER: case LC_LOAD_DYLINKER: dyld = (struct dylinker_command *)lc; swap_dylinker_command(dyld, target_byte_sex); break; case LC_UNIXTHREAD: case LC_THREAD: ut = (struct thread_command *)lc; state = (char *)ut + sizeof(struct thread_command); p = (char *)ut + ut->cmdsize; swap_thread_command(ut, target_byte_sex); if(cputype == CPU_TYPE_MC680x0){ struct m68k_thread_state_regs *cpu; struct m68k_thread_state_68882 *fpu; struct m68k_thread_state_user_reg *user_reg; while(state < p){ flavor = *((unsigned long *)state); *((unsigned long *)state) = SWAP_LONG(flavor); state += sizeof(unsigned long); count = *((unsigned long *)state); *((unsigned long *)state) = SWAP_LONG(count); state += sizeof(unsigned long); switch(flavor){ case M68K_THREAD_STATE_REGS: cpu = (struct m68k_thread_state_regs *)state; swap_m68k_thread_state_regs(cpu, target_byte_sex); state += sizeof(struct m68k_thread_state_regs); break; case M68K_THREAD_STATE_68882: fpu = (struct m68k_thread_state_68882 *)state; swap_m68k_thread_state_68882(fpu, target_byte_sex); state += sizeof(struct m68k_thread_state_68882); break; case M68K_THREAD_STATE_USER_REG: user_reg = (struct m68k_thread_state_user_reg *)state; swap_m68k_thread_state_user_reg(user_reg, target_byte_sex); state += sizeof(struct m68k_thread_state_user_reg); break; } } break; } if(cputype == CPU_TYPE_POWERPC || cputype == CPU_TYPE_VEO || cputype == CPU_TYPE_POWERPC64){ ppc_thread_state_t *cpu; ppc_thread_state64_t *cpu64; ppc_float_state_t *fpu; ppc_exception_state_t *except; while(state < p){ flavor = *((unsigned long *)state); *((unsigned long *)state) = SWAP_LONG(flavor); state += sizeof(unsigned long); count = *((unsigned long *)state); *((unsigned long *)state) = SWAP_LONG(count); state += sizeof(unsigned long); switch(flavor){ case PPC_THREAD_STATE: cpu = (ppc_thread_state_t *)state; swap_ppc_thread_state_t(cpu, target_byte_sex); state += sizeof(ppc_thread_state_t); break; case PPC_THREAD_STATE64: cpu64 = (ppc_thread_state64_t *)state; swap_ppc_thread_state64_t(cpu64, target_byte_sex); state += sizeof(ppc_thread_state64_t); break; case PPC_FLOAT_STATE: fpu = (ppc_float_state_t *)state; swap_ppc_float_state_t(fpu, target_byte_sex); state += sizeof(ppc_float_state_t); case PPC_EXCEPTION_STATE: except = (ppc_exception_state_t *)state; swap_ppc_exception_state_t(except, target_byte_sex); state += sizeof(ppc_exception_state_t); break; } } break; } if(cputype == CPU_TYPE_MC88000){ m88k_thread_state_grf_t *cpu; m88k_thread_state_xrf_t *fpu; m88k_thread_state_user_t *user; m88110_thread_state_impl_t *spu; while(state < p){ flavor = *((unsigned long *)state); *((unsigned long *)state) = SWAP_LONG(flavor); state += sizeof(unsigned long); count = *((unsigned long *)state); *((unsigned long *)state) = SWAP_LONG(count); state += sizeof(unsigned long); switch(flavor){ case M88K_THREAD_STATE_GRF: cpu = (m88k_thread_state_grf_t *)state; swap_m88k_thread_state_grf_t(cpu, target_byte_sex); state += sizeof(m88k_thread_state_grf_t); break; case M88K_THREAD_STATE_XRF: fpu = (m88k_thread_state_xrf_t *)state; swap_m88k_thread_state_xrf_t(fpu, target_byte_sex); state += sizeof(m88k_thread_state_xrf_t); break; case M88K_THREAD_STATE_USER: user = (m88k_thread_state_user_t *)state; swap_m88k_thread_state_user_t(user, target_byte_sex); state += sizeof(m88k_thread_state_user_t); break; case M88110_THREAD_STATE_IMPL: spu = (m88110_thread_state_impl_t *)state; swap_m88110_thread_state_impl_t(spu, target_byte_sex); state += sizeof(m88110_thread_state_impl_t); break; } } break; } if(cputype == CPU_TYPE_I860){ struct i860_thread_state_regs *cpu; while(state < p){ flavor = *((unsigned long *)state); *((unsigned long *)state) = SWAP_LONG(flavor); state += sizeof(unsigned long); count = *((unsigned long *)state); *((unsigned long *)state) = SWAP_LONG(count); state += sizeof(unsigned long); switch(flavor){ case I860_THREAD_STATE_REGS: cpu = (struct i860_thread_state_regs *)state; swap_i860_thread_state_regs(cpu, target_byte_sex); state += sizeof(struct i860_thread_state_regs); break; } } break; } if(cputype == CPU_TYPE_I386 #ifdef x86_THREAD_STATE64 || cputype == CPU_TYPE_X86_64 #endif /* x86_THREAD_STATE64 */ ){ i386_thread_state_t *cpu; #ifdef x86_THREAD_STATE64 x86_thread_state64_t *cpu64; #endif /* x86_THREAD_STATE64 */ /* current i386 thread states */ #if i386_THREAD_STATE == 1 struct i386_float_state *fpu; i386_exception_state_t *exc; #endif /* i386_THREAD_STATE == 1 */ /* i386 thread states on older releases */ #if i386_THREAD_STATE == -1 i386_thread_fpstate_t *fpu; i386_thread_exceptstate_t *exc; i386_thread_cthreadstate_t *user; #endif /* i386_THREAD_STATE == -1 */ while(state < p){ flavor = *((unsigned long *)state); *((unsigned long *)state) = SWAP_LONG(flavor); state += sizeof(unsigned long); count = *((unsigned long *)state); *((unsigned long *)state) = SWAP_LONG(count); state += sizeof(unsigned long); switch(flavor){ case i386_THREAD_STATE: /* current i386 thread states */ #if i386_THREAD_STATE == 1 case -1: #endif /* i386_THREAD_STATE == 1 */ /* i386 thread states on older releases */ #if i386_THREAD_STATE == -1 case 1: #endif /* i386_THREAD_STATE == -1 */ cpu = (i386_thread_state_t *)state; swap_i386_thread_state(cpu, target_byte_sex); state += sizeof(i386_thread_state_t); break; /* current i386 thread states */ #if i386_THREAD_STATE == 1 case i386_FLOAT_STATE: fpu = (struct i386_float_state *)state; swap_i386_float_state(fpu, target_byte_sex); state += sizeof(struct i386_float_state); break; case i386_EXCEPTION_STATE: exc = (i386_exception_state_t *)state; swap_i386_exception_state(exc, target_byte_sex); state += sizeof(i386_exception_state_t); break; #endif /* i386_THREAD_STATE == 1 */ /* i386 thread states on older releases */ #if i386_THREAD_STATE == -1 case i386_THREAD_FPSTATE: fpu = (i386_thread_fpstate_t *)state; swap_i386_thread_fpstate(fpu, target_byte_sex); state += sizeof(i386_thread_fpstate_t); break; case i386_THREAD_EXCEPTSTATE: exc = (i386_thread_exceptstate_t *)state; swap_i386_thread_exceptstate(exc, target_byte_sex); state += sizeof(i386_thread_exceptstate_t); break; case i386_THREAD_CTHREADSTATE: user = (i386_thread_cthreadstate_t *)state; swap_i386_thread_cthreadstate(user,target_byte_sex); state += sizeof(i386_thread_cthreadstate_t); break; #endif /* i386_THREAD_STATE == -1 */ #ifdef x86_THREAD_STATE64 case x86_THREAD_STATE64: cpu64 = (x86_thread_state64_t *)state; swap_x86_thread_state64(cpu64, target_byte_sex); state += sizeof(x86_thread_state64_t); break; #endif /* x86_THREAD_STATE64 */ } } break; } if(cputype == CPU_TYPE_HPPA){ struct hp_pa_integer_thread_state *cpu; struct hp_pa_frame_thread_state *frame; struct hp_pa_fp_thread_state *fpu; while(state < p){ flavor = *((unsigned long *)state); *((unsigned long *)state) = SWAP_LONG(flavor); state += sizeof(unsigned long); count = *((unsigned long *)state); *((unsigned long *)state) = SWAP_LONG(count); state += sizeof(unsigned long); switch(flavor){ case HPPA_INTEGER_THREAD_STATE: cpu = (struct hp_pa_integer_thread_state *)state; swap_hppa_integer_thread_state(cpu, target_byte_sex); state += sizeof(struct hp_pa_integer_thread_state); break; case HPPA_FRAME_THREAD_STATE: frame = (struct hp_pa_frame_thread_state *)state; swap_hppa_frame_thread_state(frame, target_byte_sex); state += sizeof(struct hp_pa_frame_thread_state); break; case HPPA_FP_THREAD_STATE: fpu = (struct hp_pa_fp_thread_state *)state; swap_hppa_fp_thread_state(fpu, target_byte_sex); state += sizeof(struct hp_pa_fp_thread_state); break; } } break; } if(cputype == CPU_TYPE_SPARC) { struct sparc_thread_state_regs *cpu; struct sparc_thread_state_fpu *fpu; while (state < p) { flavor = *((unsigned long *) state); *((unsigned long *) state) = SWAP_LONG(flavor); state += sizeof(unsigned long); count = *((unsigned int *) state); *((unsigned int *) state) = SWAP_LONG(count); state += sizeof(unsigned long); switch (flavor) { case SPARC_THREAD_STATE_REGS: cpu = (struct sparc_thread_state_regs *) state; swap_sparc_thread_state_regs(cpu, target_byte_sex); state += sizeof(struct sparc_thread_state_regs); break; case SPARC_THREAD_STATE_FPU: fpu = (struct sparc_thread_state_fpu *) state; swap_sparc_thread_state_fpu(fpu, target_byte_sex); state += sizeof(struct sparc_thread_state_fpu); break; } } break; } if (cputype == CPU_TYPE_ARM) { arm_thread_state_t *thread_state; while (state < p) { u_int32_t n; n = *((u_int32_t *)state); *((u_int32_t *)state) = SWAP_LONG(n); state += 4; n = *((u_int32_t *)state); *((u_int32_t *)state) = SWAP_LONG(n); state += 4; thread_state = (arm_thread_state_t *)state; swap_arm_thread_state(thread_state, target_byte_sex); state += sizeof(arm_thread_state_t); } } break; case LC_IDENT: id = (struct ident_command *)lc; swap_ident_command(id, target_byte_sex); break; case LC_ROUTINES: rc = (struct routines_command *)lc; swap_routines_command(rc, target_byte_sex); break; case LC_ROUTINES_64: rc64 = (struct routines_command_64 *)lc; swap_routines_command_64(rc64, target_byte_sex); break; case LC_TWOLEVEL_HINTS: hints = (struct twolevel_hints_command *)lc; swap_twolevel_hints_command(hints, target_byte_sex); break; case LC_PREBIND_CKSUM: cs = (struct prebind_cksum_command *)lc; swap_prebind_cksum_command(cs, target_byte_sex); break; case LC_UUID: uuid = (struct uuid_command *)lc; swap_uuid_command(uuid, target_byte_sex); break; } lc = (struct load_command *)((char *)lc + l.cmdsize); } if(mh != NULL) swap_mach_header(mh, target_byte_sex); else swap_mach_header_64(mh64, target_byte_sex); return(TRUE); }
/* * write_object() writes a Mach-O object file from the built up data structures. */ void write_object( char *out_file_name) { /* The structures for Mach-O relocatables */ mach_header_t header; segment_command_t reloc_segment; struct symtab_command symbol_table; struct dysymtab_command dynamic_symbol_table; uint32_t section_type; uint32_t *indirect_symbols; isymbolS *isymbolP; uint32_t i, j, nsects, nsyms, strsize, nindirectsyms; /* locals to fill in section struct fields */ uint32_t offset, zero; /* The GAS data structures */ struct frchain *frchainP, *p; struct symbol *symbolP; struct frag *fragP; struct fix *fixP; uint32_t output_size; char *output_addr; kern_return_t r; enum byte_sex host_byte_sex; uint32_t reloff, nrelocs; int32_t count; char *fill_literal; int32_t fill_size; int32_t num_bytes; char *symbol_name; int fd; uint32_t local; struct stat stat_buf; #ifdef I860 I860_tweeks(); #endif i = 0; /* to shut up a compiler "may be used uninitialized" warning */ /* * The first group of things to do is to set all the fields in the * header structures which includes offsets and determining the final * sizes of things. */ /* * Fill in the addr and size fields of each section structure and count * the number of sections. */ nsects = 0; for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){ frchainP->frch_section.addr = frchainP->frch_root->fr_address; frchainP->frch_section.size = frchainP->frch_last->fr_address - frchainP->frch_root->fr_address; nsects++; } /* * Setup the indirect symbol tables by looking up or creating symbol * from the indirect symbol names and recording the symbol pointers. */ nindirectsyms = layout_indirect_symbols(); /* * Setup the symbol table to include only those symbols that will be in * the object file, assign the string table offsets into the symbols * and size the string table. */ nsyms = 0; strsize = 0; layout_symbols((int32_t *)&nsyms, (int32_t *)&strsize); /* fill in the Mach-O header */ header.magic = MH_MAGIC_VALUE; header.cputype = md_cputype; if(archflag_cpusubtype != -1) header.cpusubtype = archflag_cpusubtype; else header.cpusubtype = md_cpusubtype; header.filetype = MH_OBJECT; header.ncmds = 0; header.sizeofcmds = 0; if(nsects != 0){ header.ncmds += 1; header.sizeofcmds += sizeof(segment_command_t) + nsects * sizeof(section_t); } if(nsyms != 0){ header.ncmds += 1; header.sizeofcmds += sizeof(struct symtab_command); if(flagseen['k']){ header.ncmds += 1; header.sizeofcmds += sizeof(struct dysymtab_command); } } else strsize = 0; header.flags = 0; if(subsections_via_symbols == TRUE) header.flags |= MH_SUBSECTIONS_VIA_SYMBOLS; #ifdef ARCH64 header.reserved = 0; #endif /* fill in the segment command */ memset(&reloc_segment, '\0', sizeof(segment_command_t)); reloc_segment.cmd = LC_SEGMENT_VALUE; reloc_segment.cmdsize = sizeof(segment_command_t) + nsects * sizeof(section_t); /* leave reloc_segment.segname full of zeros */ reloc_segment.vmaddr = 0; reloc_segment.vmsize = 0; reloc_segment.filesize = 0; offset = header.sizeofcmds + sizeof(mach_header_t); reloc_segment.fileoff = offset; reloc_segment.maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; reloc_segment.initprot= VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; reloc_segment.nsects = nsects; reloc_segment.flags = 0; /* * Set the offsets to the contents of the sections (for non-zerofill * sections) and set the filesize and vmsize of the segment. This is * complicated by the fact that all the zerofill sections have addresses * after the non-zerofill sections and that the alignment of sections * produces gaps that are not in any section. For the vmsize we rely on * the fact the the sections start at address 0 so it is just the last * zerofill section or the last not-zerofill section. */ for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){ if((frchainP->frch_section.flags & SECTION_TYPE) == S_ZEROFILL) continue; for(p = frchainP->frch_next; p != NULL; p = p->frch_next) if((p->frch_section.flags & SECTION_TYPE) != S_ZEROFILL) break; if(p != NULL) i = p->frch_section.addr - frchainP->frch_section.addr; else i = frchainP->frch_section.size; reloc_segment.filesize += i; frchainP->frch_section.offset = offset; offset += i; reloc_segment.vmsize = frchainP->frch_section.addr + frchainP->frch_section.size; } for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){ if((frchainP->frch_section.flags & SECTION_TYPE) != S_ZEROFILL) continue; reloc_segment.vmsize = frchainP->frch_section.addr + frchainP->frch_section.size; } offset = round(offset, sizeof(int32_t)); /* * Count the number of relocation entries for each section. */ for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){ frchainP->frch_section.nreloc = 0; for(fixP = frchainP->frch_fix_root; fixP; fixP = fixP->fx_next){ frchainP->frch_section.nreloc += nrelocs_for_fix(fixP); } } /* * Fill in the offset to the relocation entries of the sections. */ offset = round(offset, sizeof(int32_t)); reloff = offset; nrelocs = 0; for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){ if(frchainP->frch_section.nreloc == 0) frchainP->frch_section.reloff = 0; else frchainP->frch_section.reloff = offset; offset += frchainP->frch_section.nreloc * sizeof(struct relocation_info); nrelocs += frchainP->frch_section.nreloc; } if(flagseen['k']){ /* fill in the fields of the dysymtab_command */ dynamic_symbol_table.cmd = LC_DYSYMTAB; dynamic_symbol_table.cmdsize = sizeof(struct dysymtab_command); dynamic_symbol_table.ilocalsym = ilocalsym; dynamic_symbol_table.nlocalsym = nlocalsym; dynamic_symbol_table.iextdefsym = iextdefsym; dynamic_symbol_table.nextdefsym = nextdefsym; dynamic_symbol_table.iundefsym = iundefsym; dynamic_symbol_table.nundefsym = nundefsym; if(nindirectsyms == 0){ dynamic_symbol_table.nindirectsyms = 0; dynamic_symbol_table.indirectsymoff = 0; } else{ dynamic_symbol_table.nindirectsyms = nindirectsyms; dynamic_symbol_table.indirectsymoff = offset; offset += nindirectsyms * sizeof(uint32_t); } dynamic_symbol_table.tocoff = 0; dynamic_symbol_table.ntoc = 0; dynamic_symbol_table.modtaboff = 0; dynamic_symbol_table.nmodtab = 0; dynamic_symbol_table.extrefsymoff = 0; dynamic_symbol_table.nextrefsyms = 0; dynamic_symbol_table.extreloff = 0; dynamic_symbol_table.nextrel = 0; dynamic_symbol_table.locreloff = 0; dynamic_symbol_table.nlocrel = 0; } /* fill in the fields of the symtab_command (except the string table) */ symbol_table.cmd = LC_SYMTAB; symbol_table.cmdsize = sizeof(struct symtab_command); if(nsyms == 0) symbol_table.symoff = 0; else symbol_table.symoff = offset; symbol_table.nsyms = nsyms; offset += symbol_table.nsyms * sizeof(nlist_t); /* fill in the string table fields of the symtab_command */ if(strsize == 0) symbol_table.stroff = 0; else symbol_table.stroff = offset; symbol_table.strsize = round(strsize, sizeof(uint32_t)); offset += round(strsize, sizeof(uint32_t)); /* * The second group of things to do is now with the size of everything * known the object file and the offsets set in the various structures * the contents of the object file can be created. */ /* * Create the buffer to copy the parts of the output file into. */ output_size = offset; if((r = vm_allocate(mach_task_self(), (vm_address_t *)&output_addr, output_size, TRUE)) != KERN_SUCCESS) as_fatal("can't vm_allocate() buffer for output file of size %u", output_size); /* put the headers in the output file's buffer */ host_byte_sex = get_host_byte_sex(); offset = 0; /* put the mach_header in the buffer */ memcpy(output_addr + offset, &header, sizeof(mach_header_t)); if(host_byte_sex != md_target_byte_sex) swap_mach_header_t((mach_header_t *)(output_addr + offset), md_target_byte_sex); offset += sizeof(mach_header_t); /* put the segment_command in the buffer */ if(nsects != 0){ memcpy(output_addr + offset, &reloc_segment, sizeof(segment_command_t)); if(host_byte_sex != md_target_byte_sex) swap_segment_command_t((segment_command_t *) (output_addr + offset), md_target_byte_sex); offset += sizeof(segment_command_t); } /* put the segment_command's section structures in the buffer */ for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){ memcpy(output_addr + offset, &(frchainP->frch_section), sizeof(section_t)); if(host_byte_sex != md_target_byte_sex) swap_section_t((section_t *)(output_addr + offset), 1, md_target_byte_sex); offset += sizeof(section_t); } /* put the symbol_command in the buffer */ if(nsyms != 0){ memcpy(output_addr + offset, &symbol_table, sizeof(struct symtab_command)); if(host_byte_sex != md_target_byte_sex) swap_symtab_command((struct symtab_command *) (output_addr + offset), md_target_byte_sex); offset += sizeof(struct symtab_command); } if(flagseen['k']){ /* put the dysymbol_command in the buffer */ if(nsyms != 0){ memcpy(output_addr + offset, &dynamic_symbol_table, sizeof(struct dysymtab_command)); if(host_byte_sex != md_target_byte_sex) swap_dysymtab_command((struct dysymtab_command *) (output_addr + offset), md_target_byte_sex); offset += sizeof(struct dysymtab_command); } } /* put the section contents (frags) in the buffer */ for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){ offset = frchainP->frch_section.offset; for(fragP = frchainP->frch_root; fragP; fragP = fragP->fr_next){ know(fragP->fr_type == rs_fill); /* put the fixed part of the frag in the buffer */ memcpy(output_addr + offset, fragP->fr_literal, fragP->fr_fix); offset += fragP->fr_fix; /* put the variable repeated part of the frag in the buffer */ fill_literal = fragP->fr_literal + fragP->fr_fix; fill_size = fragP->fr_var; num_bytes = fragP->fr_offset * fragP->fr_var; for(count = 0; count < num_bytes; count += fill_size){ memcpy(output_addr + offset, fill_literal, fill_size); offset += fill_size; } } } /* put the symbols in the output file's buffer */ offset = symbol_table.symoff; for(symbolP = symbol_rootP; symbolP; symbolP = symbolP->sy_next){ if((symbolP->sy_type & N_EXT) == 0){ symbol_name = symbolP->sy_name; symbolP->sy_nlist.n_un.n_strx = symbolP->sy_name_offset; if(symbolP->expression != 0) { expressionS *exp; exp = (expressionS *)symbolP->expression; if((exp->X_add_symbol->sy_type & N_TYPE) == N_UNDF) as_fatal("undefined symbol `%s' in operation setting " "`%s'", exp->X_add_symbol->sy_name, symbol_name); if((exp->X_subtract_symbol->sy_type & N_TYPE) == N_UNDF) as_fatal("undefined symbol `%s' in operation setting " "`%s'", exp->X_subtract_symbol->sy_name, symbol_name); if(exp->X_add_symbol->sy_other != exp->X_subtract_symbol->sy_other) as_fatal("invalid sections for operation on `%s' and " "`%s' setting `%s'",exp->X_add_symbol->sy_name, exp->X_subtract_symbol->sy_name, symbol_name); symbolP->sy_nlist.n_value += exp->X_add_symbol->sy_value - exp->X_subtract_symbol->sy_value; } memcpy(output_addr + offset, (char *)(&symbolP->sy_nlist), sizeof(nlist_t)); symbolP->sy_name = symbol_name; offset += sizeof(nlist_t); } } for(i = 0; i < nextdefsym; i++){ symbol_name = extdefsyms[i]->sy_name; extdefsyms[i]->sy_nlist.n_un.n_strx = extdefsyms[i]->sy_name_offset; memcpy(output_addr + offset, (char *)(&extdefsyms[i]->sy_nlist), sizeof(nlist_t)); extdefsyms[i]->sy_name = symbol_name; offset += sizeof(nlist_t); } for(j = 0; j < nundefsym; j++){ symbol_name = undefsyms[j]->sy_name; undefsyms[j]->sy_nlist.n_un.n_strx = undefsyms[j]->sy_name_offset; memcpy(output_addr + offset, (char *)(&undefsyms[j]->sy_nlist), sizeof(nlist_t)); undefsyms[j]->sy_name = symbol_name; offset += sizeof(nlist_t); } if(host_byte_sex != md_target_byte_sex) swap_nlist_t((nlist_t *)(output_addr + symbol_table.symoff), symbol_table.nsyms, md_target_byte_sex); /* * Put the relocation entries for each section in the buffer. */ for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){ offset = frchainP->frch_section.reloff; for(fixP = frchainP->frch_fix_root; fixP; fixP = fixP->fx_next){ offset += fix_to_relocation_entries( fixP, frchainP->frch_section.addr, (struct relocation_info *)(output_addr + offset), frchainP->frch_section.flags & S_ATTR_DEBUG); } } if(host_byte_sex != md_target_byte_sex) swap_relocation_info((struct relocation_info *) (output_addr + reloff), nrelocs, md_target_byte_sex); if(flagseen['k']){ /* put the indirect symbol table in the buffer */ offset = dynamic_symbol_table.indirectsymoff; for(frchainP = frchain_root; frchainP != NULL; frchainP = frchainP->frch_next){ section_type = frchainP->frch_section.flags & SECTION_TYPE; if(section_type == S_NON_LAZY_SYMBOL_POINTERS || section_type == S_LAZY_SYMBOL_POINTERS || section_type == S_SYMBOL_STUBS){ /* * For each indirect symbol put out the symbol number. */ for(isymbolP = frchainP->frch_isym_root; isymbolP != NULL; isymbolP = isymbolP->isy_next){ /* * If this is a non-lazy pointer symbol section and * if the symbol is a local symbol then put out * INDIRECT_SYMBOL_LOCAL as the indirect symbol table * entry. This is used with code gen for fix-n-continue * where the compiler generates indirection for static * data references. See the comments at the end of * fixup_section() that explains the assembly code used. */ if(section_type == S_NON_LAZY_SYMBOL_POINTERS && (isymbolP->isy_symbol->sy_nlist.n_type & N_EXT) != N_EXT){ local = INDIRECT_SYMBOL_LOCAL; if((isymbolP->isy_symbol->sy_nlist.n_type & N_TYPE) == N_ABS) local |= INDIRECT_SYMBOL_ABS; memcpy(output_addr + offset, (char *)(&local), sizeof(uint32_t)); } else{ memcpy(output_addr + offset, (char *)(&isymbolP->isy_symbol->sy_number), sizeof(uint32_t)); } offset += sizeof(uint32_t); } } } if(host_byte_sex != md_target_byte_sex){ indirect_symbols = (uint32_t *)(output_addr + dynamic_symbol_table.indirectsymoff); swap_indirect_symbols(indirect_symbols, nindirectsyms, md_target_byte_sex); } } /* put the strings in the output file's buffer */ offset = symbol_table.stroff; if(symbol_table.strsize != 0){ zero = 0; memcpy(output_addr + offset, (char *)&zero, sizeof(char)); offset += sizeof(char); } for(symbolP = symbol_rootP; symbolP; symbolP = symbolP->sy_next){ /* Ordinary case: not .stabd. */ if(symbolP->sy_name != NULL){ if((symbolP->sy_type & N_EXT) != 0){ memcpy(output_addr + offset, symbolP->sy_name, strlen(symbolP->sy_name) + 1); offset += strlen(symbolP->sy_name) + 1; } } } for(symbolP = symbol_rootP; symbolP; symbolP = symbolP->sy_next){ /* Ordinary case: not .stabd. */ if(symbolP->sy_name != NULL){ if((symbolP->sy_type & N_EXT) == 0){ memcpy(output_addr + offset, symbolP->sy_name, strlen(symbolP->sy_name) + 1); offset += strlen(symbolP->sy_name) + 1; } } } /* * Create the output file. The unlink() is done to handle the problem * when the out_file_name is not writable but the directory allows the * file to be removed (since the file may not be there the return code * of the unlink() is ignored). */ if(bad_error != 0) return; /* * Avoid doing the unlink() on special files, just unlink regular files * that exist. */ if(stat(out_file_name, &stat_buf) != -1){ if(stat_buf.st_mode & S_IFREG) (void)unlink(out_file_name); } if((fd = open(out_file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) as_fatal("can't create output file: %s", out_file_name); if(write(fd, output_addr, output_size) != (int)output_size) as_fatal("can't write output file"); if(close(fd) == -1) as_fatal("can't close output file"); }
int main(int argc, char * argv[]) { ToolError err; int i, fd; const char * output_name = NULL; uint32_t zero = 0, num_files = 0; uint32_t filenum; uint32_t strx, strtabsize, strtabpad; struct symbol * import_symbols; struct symbol * export_symbols; uint32_t num_import_syms, num_export_syms; uint32_t result_count, num_removed_syms; uint32_t import_idx, export_idx; const NXArchInfo * host_arch; const NXArchInfo * target_arch; boolean_t require_imports = true; boolean_t diff = false; struct file { vm_offset_t mapped; vm_size_t mapped_size; uint32_t nsyms; boolean_t import; const char * path; }; struct file files[64]; host_arch = NXGetLocalArchInfo(); target_arch = host_arch; for( i = 1; i < argc; i += 2) { boolean_t import; if (!strcmp("-sect", argv[i])) { require_imports = false; i--; continue; } if (!strcmp("-diff", argv[i])) { require_imports = false; diff = true; i--; continue; } if (i == (argc - 1)) { fprintf(stderr, "bad arguments: %s\n", argv[i]); exit(1); } if (!strcmp("-arch", argv[i])) { target_arch = DL_NXGetArchInfoFromName(argv[i + 1]); if (!target_arch) { fprintf(stderr, "unknown architecture name: %s\n", argv[i+1]); exit(1); } continue; } if (!strcmp("-output", argv[i])) { output_name = argv[i+1]; continue; } if (!strcmp("-import", argv[i])) import = true; else if (!strcmp("-export", argv[i])) import = false; else { fprintf(stderr, "unknown option: %s\n", argv[i]); exit(1); } err = readFile(argv[i+1], &files[num_files].mapped, &files[num_files].mapped_size); if (kErrorNone != err) exit(1); if (files[num_files].mapped && files[num_files].mapped_size) { files[num_files].import = import; files[num_files].path = argv[i+1]; num_files++; } } if (!output_name) { fprintf(stderr, "no output file\n"); exit(1); } num_import_syms = 0; num_export_syms = 0; for (filenum = 0; filenum < num_files; filenum++) { files[filenum].nsyms = count_symbols((char *) files[filenum].mapped, files[filenum].mapped_size); if (files[filenum].import) num_import_syms += files[filenum].nsyms; else num_export_syms += files[filenum].nsyms; } if (!num_export_syms) { fprintf(stderr, "no export names\n"); exit(1); } import_symbols = calloc(num_import_syms, sizeof(struct symbol)); export_symbols = calloc(num_export_syms, sizeof(struct symbol)); import_idx = 0; export_idx = 0; for (filenum = 0; filenum < num_files; filenum++) { if (files[filenum].import) { store_symbols((char *) files[filenum].mapped, files[filenum].mapped_size, import_symbols, import_idx, num_import_syms); import_idx += files[filenum].nsyms; } else { store_symbols((char *) files[filenum].mapped, files[filenum].mapped_size, export_symbols, export_idx, num_export_syms); export_idx += files[filenum].nsyms; } if (false && !files[filenum].nsyms) { fprintf(stderr, "warning: file %s contains no names\n", files[filenum].path); } } qsort(import_symbols, num_import_syms, sizeof(struct symbol), &qsort_cmp); qsort(export_symbols, num_export_syms, sizeof(struct symbol), &qsort_cmp); result_count = 0; num_removed_syms = 0; strtabsize = 4; if (num_import_syms) { for (export_idx = 0; export_idx < num_export_syms; export_idx++) { struct symbol * result; char * name; size_t len; boolean_t wild; name = export_symbols[export_idx].indirect; len = export_symbols[export_idx].indirect_len; if (!name) { name = export_symbols[export_idx].name; len = export_symbols[export_idx].name_len; } wild = ((len > 2) && ('*' == name[len-=2])); if (wild) { struct bsearch_key key; key.name = name; key.name_len = len; result = bsearch(&key, import_symbols, num_import_syms, sizeof(struct symbol), &bsearch_cmp_prefix); if (result) { struct symbol * first; struct symbol * last; strtabsize += (result->name_len + result->indirect_len); first = result; while (--first >= &import_symbols[0]) { if (bsearch_cmp_prefix(&key, first)) break; strtabsize += (first->name_len + first->indirect_len); } first++; last = result; while (++last < (&import_symbols[0] + num_import_syms)) { if (bsearch_cmp_prefix(&key, last)) break; strtabsize += (last->name_len + last->indirect_len); } result_count += last - first; result = first; export_symbols[export_idx].list = first; export_symbols[export_idx].list_count = last - first; export_symbols[export_idx].flags |= kExported; } } else result = bsearch(name, import_symbols, num_import_syms, sizeof(struct symbol), &bsearch_cmp); if (!result && require_imports) { int status; char * demangled_result = __cxa_demangle(export_symbols[export_idx].name + 1, NULL, NULL, &status); fprintf(stderr, "exported name not in import list: %s\n", demangled_result ? demangled_result : export_symbols[export_idx].name); // fprintf(stderr, " : %s\n", export_symbols[export_idx].name); if (demangled_result) { free(demangled_result); } num_removed_syms++; } if (diff) { if (!result) result = &export_symbols[export_idx]; else result = NULL; } if (result && !wild) { export_symbols[export_idx].flags |= kExported; strtabsize += (export_symbols[export_idx].name_len + export_symbols[export_idx].indirect_len); result_count++; export_symbols[export_idx].list = &export_symbols[export_idx]; export_symbols[export_idx].list_count = 1; } } } strtabpad = (strtabsize + 3) & ~3; if (require_imports && num_removed_syms) { err = kError; goto finish; } fd = open(output_name, O_WRONLY|O_CREAT|O_TRUNC, 0755); if (-1 == fd) { perror("couldn't write output"); err = kErrorFileAccess; goto finish; } struct symtab_command symcmd; struct uuid_command uuidcmd; symcmd.cmd = LC_SYMTAB; symcmd.cmdsize = sizeof(symcmd); symcmd.symoff = sizeof(symcmd) + sizeof(uuidcmd); symcmd.nsyms = result_count; symcmd.strsize = strtabpad; uuidcmd.cmd = LC_UUID; uuidcmd.cmdsize = sizeof(uuidcmd); uuid_generate(uuidcmd.uuid); if (CPU_ARCH_ABI64 & target_arch->cputype) { struct mach_header_64 hdr; hdr.magic = MH_MAGIC_64; hdr.cputype = target_arch->cputype; hdr.cpusubtype = target_arch->cpusubtype; hdr.filetype = MH_KEXT_BUNDLE; hdr.ncmds = 2; hdr.sizeofcmds = sizeof(symcmd) + sizeof(uuidcmd); hdr.flags = MH_INCRLINK; symcmd.symoff += sizeof(hdr); symcmd.stroff = result_count * sizeof(struct nlist_64) + symcmd.symoff; if (target_arch->byteorder != host_arch->byteorder) swap_mach_header_64(&hdr, target_arch->byteorder); err = writeFile(fd, &hdr, sizeof(hdr)); } else { struct mach_header hdr; hdr.magic = MH_MAGIC; hdr.cputype = target_arch->cputype; hdr.cpusubtype = target_arch->cpusubtype; hdr.filetype = (target_arch->cputype == CPU_TYPE_I386) ? MH_OBJECT : MH_KEXT_BUNDLE; hdr.ncmds = 2; hdr.sizeofcmds = sizeof(symcmd) + sizeof(uuidcmd); hdr.flags = MH_INCRLINK; symcmd.symoff += sizeof(hdr); symcmd.stroff = result_count * sizeof(struct nlist) + symcmd.symoff; if (target_arch->byteorder != host_arch->byteorder) swap_mach_header(&hdr, target_arch->byteorder); err = writeFile(fd, &hdr, sizeof(hdr)); } if (kErrorNone != err) goto finish; if (target_arch->byteorder != host_arch->byteorder) { swap_symtab_command(&symcmd, target_arch->byteorder); swap_uuid_command(&uuidcmd, target_arch->byteorder); } err = writeFile(fd, &symcmd, sizeof(symcmd)); if (kErrorNone != err) goto finish; err = writeFile(fd, &uuidcmd, sizeof(uuidcmd)); if (kErrorNone != err) goto finish; strx = 4; for (export_idx = 0; export_idx < num_export_syms; export_idx++) { if (!export_symbols[export_idx].name) continue; if (!(kExported & export_symbols[export_idx].flags)) continue; if (export_idx && export_symbols[export_idx - 1].name && !strcmp(export_symbols[export_idx - 1].name, export_symbols[export_idx].name)) { fprintf(stderr, "duplicate export: %s\n", export_symbols[export_idx - 1].name); err = kErrorDuplicate; goto finish; } for (import_idx = 0; import_idx < export_symbols[export_idx].list_count; import_idx++) { if (export_symbols[export_idx].list != &export_symbols[export_idx]) { printf("wild: %s, %s\n", export_symbols[export_idx].name, export_symbols[export_idx].list[import_idx].name); } if (CPU_ARCH_ABI64 & target_arch->cputype) { struct nlist_64 nl; nl.n_sect = 0; nl.n_desc = 0; nl.n_un.n_strx = strx; strx += export_symbols[export_idx].list[import_idx].name_len; if (export_symbols[export_idx].flags & kObsolete) { nl.n_desc |= N_DESC_DISCARDED; } if (export_symbols[export_idx].list[import_idx].indirect) { nl.n_type = N_INDR | N_EXT; nl.n_value = strx; strx += export_symbols[export_idx].list[import_idx].indirect_len; } else { nl.n_type = N_UNDF | N_EXT; nl.n_value = 0; } if (target_arch->byteorder != host_arch->byteorder) swap_nlist_64(&nl, 1, target_arch->byteorder); err = writeFile(fd, &nl, sizeof(nl)); } else { struct nlist nl; nl.n_sect = 0; nl.n_desc = 0; nl.n_un.n_strx = strx; strx += export_symbols[export_idx].list[import_idx].name_len; if (export_symbols[export_idx].flags & kObsolete) { nl.n_desc |= N_DESC_DISCARDED; } if (export_symbols[export_idx].list[import_idx].indirect) { nl.n_type = N_INDR | N_EXT; nl.n_value = strx; strx += export_symbols[export_idx].list[import_idx].indirect_len; } else { nl.n_type = N_UNDF | N_EXT; nl.n_value = 0; } if (target_arch->byteorder != host_arch->byteorder) swap_nlist(&nl, 1, target_arch->byteorder); err = writeFile(fd, &nl, sizeof(nl)); } } if (kErrorNone != err) goto finish; } strx = sizeof(uint32_t); err = writeFile(fd, &zero, strx); if (kErrorNone != err) goto finish; for (export_idx = 0; export_idx < num_export_syms; export_idx++) { if (!export_symbols[export_idx].name) continue; for (import_idx = 0; import_idx < export_symbols[export_idx].list_count; import_idx++) { err = writeFile(fd, export_symbols[export_idx].list[import_idx].name, export_symbols[export_idx].list[import_idx].name_len); if (kErrorNone != err) goto finish; if (export_symbols[export_idx].list[import_idx].indirect) { err = writeFile(fd, export_symbols[export_idx].list[import_idx].indirect, export_symbols[export_idx].list[import_idx].indirect_len); if (kErrorNone != err) goto finish; } } } err = writeFile(fd, &zero, strtabpad - strtabsize); if (kErrorNone != err) goto finish; close(fd); finish: for (filenum = 0; filenum < num_files; filenum++) { // unmap file if (files[filenum].mapped_size) { munmap((caddr_t)files[filenum].mapped, files[filenum].mapped_size); files[filenum].mapped = 0; files[filenum].mapped_size = 0; } } if (kErrorNone != err) { if (output_name) unlink(output_name); exit(1); } else exit(0); return(0); }
static int register_mach_header(const char* build, const char* project, const char* path, struct fat_arch* fa, int fd, int* isMachO) { ssize_t res; uint32_t magic; int swap = 0; struct mach_header* mh = NULL; struct mach_header_64* mh64 = NULL; if (isMachO) *isMachO = 0; res = read(fd, &magic, sizeof(uint32_t)); if (res < sizeof(uint32_t)) { return 0; } // // 32-bit, read the rest of the header // if (magic == MH_MAGIC || magic == MH_CIGAM) { if (isMachO) *isMachO = 1; mh = malloc(sizeof(struct mach_header)); if (mh == NULL) return -1; memset(mh, 0, sizeof(struct mach_header)); mh->magic = magic; res = read(fd, &mh->cputype, sizeof(struct mach_header) - sizeof(uint32_t)); if (res < sizeof(struct mach_header) - sizeof(uint32_t)) { return 0; } if (magic == MH_CIGAM) { swap = 1; swap_mach_header(mh, NXHostByteOrder()); } // // 64-bit, read the rest of the header // } else if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) { if (isMachO) *isMachO = 1; mh64 = malloc(sizeof(struct mach_header_64)); if (mh64 == NULL) return -1; memset(mh64, 0, sizeof(struct mach_header_64)); mh64->magic = magic; res = read(fd, &mh64->cputype, sizeof(struct mach_header_64) - sizeof(uint32_t)); if (res < sizeof(struct mach_header_64) - sizeof(uint32_t)) { return 0; } if (magic == MH_CIGAM_64) { swap = 1; swap_mach_header_64(mh64, NXHostByteOrder()); } // // Not a Mach-O // } else { return 0; } switch (mh64 ? mh64->filetype : mh->filetype) { case MH_EXECUTE: case MH_DYLIB: case MH_BUNDLE: break; case MH_OBJECT: default: return 0; } res = SQL("INSERT INTO mach_o_objects (magic, type, cputype, cpusubtype, flags, build, project, path) VALUES (%u, %u, %u, %u, %u, %Q, %Q, %Q)", mh64 ? mh64->magic : mh->magic, mh64 ? mh64->filetype : mh->filetype, mh64 ? mh64->cputype : mh->cputype, mh64 ? mh64->cpusubtype : mh->cpusubtype, mh64 ? mh64->flags : mh->flags, build, project, path); uint64_t serial = sqlite3_last_insert_rowid((sqlite3*)_DBPluginGetDataStorePtr()); // // Information needed to parse the symbol table // int count_nsect = 0; unsigned char text_nsect = NO_SECT; unsigned char data_nsect = NO_SECT; unsigned char bss_nsect = NO_SECT; uint32_t nsyms = 0; uint8_t *symbols = NULL; uint32_t strsize = 0; uint8_t *strings = NULL; int i; uint32_t ncmds = mh64 ? mh64->ncmds : mh->ncmds; for (i = 0; i < ncmds; ++i) { // // Read a generic load command into memory. // At first, we only know it has a type and size. // struct load_command lctmp; ssize_t res = read(fd, &lctmp, sizeof(struct load_command)); if (res < sizeof(struct load_command)) { return 0; } uint32_t cmd = swap ? OSSwapInt32(lctmp.cmd) : lctmp.cmd; uint32_t cmdsize = swap ? OSSwapInt32(lctmp.cmdsize) : lctmp.cmdsize; if (cmdsize == 0) continue; struct load_command* lc = malloc(cmdsize); if (lc == NULL) { return 0; } memset(lc, 0, cmdsize); memcpy(lc, &lctmp, sizeof(lctmp)); // Read the remainder of the load command. res = read(fd, (uint8_t*)lc + sizeof(struct load_command), cmdsize - sizeof(struct load_command)); if (res < (cmdsize - sizeof(struct load_command))) { free(lc); return 0; } // // LC_LOAD_DYLIB and LC_LOAD_WEAK_DYLIB // Add dylibs as unresolved "lib" dependencies. // if (cmd == LC_LOAD_DYLIB || cmd == LC_LOAD_WEAK_DYLIB) { struct dylib_command *dylib = (struct dylib_command*)lc; if (swap) swap_dylib_command(dylib, NXHostByteOrder()); // sections immediately follow the dylib_command structure, and are // reflected in the cmdsize. int strsize = dylib->cmdsize - sizeof(struct dylib_command); char* str = malloc(strsize+1); strncpy(str, (char*)((uint8_t*)dylib + dylib->dylib.name.offset), strsize); str[strsize] = 0; // NUL-terminate res = SQL("INSERT INTO unresolved_dependencies (build,project,type,dependency) VALUES (%Q,%Q,%Q,%Q)", build, project, "lib", str); free(str); // // LC_LOAD_DYLINKER // Add the dynamic linker (usually dyld) as an unresolved "lib" dependency. // } else if (cmd == LC_LOAD_DYLINKER) { struct dylinker_command *dylinker = (struct dylinker_command*)lc; if (swap) swap_dylinker_command(dylinker, NXHostByteOrder()); // sections immediately follow the dylib_command structure, and are // reflected in the cmdsize. int strsize = dylinker->cmdsize - sizeof(struct dylinker_command); char* str = malloc(strsize+1); strncpy(str, (char*)((uint8_t*)dylinker + dylinker->name.offset), strsize); str[strsize] = 0; // NUL-terminate res = SQL("INSERT INTO unresolved_dependencies (build,project,type,dependency) VALUES (%Q,%Q,%Q,%Q)", build, project, "lib", str); free(str); // // LC_SYMTAB // Read the symbol table into memory, we'll process it after we're // done with the load commands. // } else if (cmd == LC_SYMTAB && symbols == NULL) { struct symtab_command *symtab = (struct symtab_command*)lc; if (swap) swap_symtab_command(symtab, NXHostByteOrder()); nsyms = symtab->nsyms; uint32_t symsize = nsyms * (mh64 ? sizeof(struct nlist_64) : sizeof(struct nlist)); symbols = malloc(symsize); strsize = symtab->strsize; // XXX: check strsize != 0 strings = malloc(strsize); off_t save = lseek(fd, 0, SEEK_CUR); off_t origin = fa ? fa->offset : 0; lseek(fd, (off_t)symtab->symoff + origin, SEEK_SET); res = read(fd, symbols, symsize); if (res < symsize) { /* XXX: leaks */ return 0; } lseek(fd, (off_t)symtab->stroff + origin, SEEK_SET); res = read(fd, strings, strsize); if (res < strsize) { /* XXX: leaks */ return 0; } lseek(fd, save, SEEK_SET); // // LC_SEGMENT // We're looking for the section number of the text, data, and bss segments // in order to parse symbols. // } else if (cmd == LC_SEGMENT) { struct segment_command* seg = (struct segment_command*)lc; if (swap) swap_segment_command(seg, NXHostByteOrder()); // sections immediately follow the segment_command structure, and are // reflected in the cmdsize. int k; for (k = 0; k < seg->nsects; ++k) { struct section* sect = (struct section*)((uint8_t*)seg + sizeof(struct segment_command) + k * sizeof(struct section)); if (swap) swap_section(sect, 1, NXHostByteOrder()); if (strcmp(sect->sectname, SECT_TEXT) == 0 && strcmp(sect->segname, SEG_TEXT) == 0) { text_nsect = ++count_nsect; } else if (strcmp(sect->sectname, SECT_DATA) == 0 && strcmp(sect->segname, SEG_DATA) == 0) { data_nsect = ++count_nsect; } else if (strcmp(sect->sectname, SECT_BSS) == 0 && strcmp(sect->segname, SEG_DATA) == 0) { bss_nsect = ++count_nsect; } else { ++count_nsect; } } // // LC_SEGMENT_64 // Same as LC_SEGMENT, but for 64-bit binaries. // } else if (lc->cmd == LC_SEGMENT_64) { struct segment_command_64* seg = (struct segment_command_64*)lc; if (swap) swap_segment_command_64(seg, NXHostByteOrder()); // sections immediately follow the segment_command structure, and are // reflected in the cmdsize. int k; for (k = 0; k < seg->nsects; ++k) { struct section_64* sect = (struct section_64*)((uint8_t*)seg + sizeof(struct segment_command_64) + k * sizeof(struct section_64)); if (swap) swap_section_64(sect, 1, NXHostByteOrder()); if (strcmp(sect->sectname, SECT_TEXT) == 0 && strcmp(sect->segname, SEG_TEXT) == 0) { text_nsect = ++count_nsect; } else if (strcmp(sect->sectname, SECT_DATA) == 0 && strcmp(sect->segname, SEG_DATA) == 0) { data_nsect = ++count_nsect; } else if (strcmp(sect->sectname, SECT_BSS) == 0 && strcmp(sect->segname, SEG_DATA) == 0) { bss_nsect = ++count_nsect; } else { ++count_nsect; } } } free(lc); } // // Finished processing the load commands, now insert symbols into the database. // int j; for (j = 0; j < nsyms; ++j) { struct nlist_64 symbol; if (mh64) { memcpy(&symbol, (symbols + j * sizeof(struct nlist_64)), sizeof(struct nlist_64)); if (swap) swap_nlist_64(&symbol, 1, NXHostByteOrder()); } else { symbol.n_value = 0; memcpy(&symbol, (symbols + j * sizeof(struct nlist)), sizeof(struct nlist)); if (swap) swap_nlist_64(&symbol, 1, NXHostByteOrder()); // we copied a 32-bit nlist into a 64-bit one, adjust the value accordingly // all other fields are identical sizes symbol.n_value >>= 32; } char type = '?'; switch (symbol.n_type & N_TYPE) { case N_UNDF: case N_PBUD: type = 'u'; if (symbol.n_value != 0) { type = 'c'; } break; case N_ABS: type = 'a'; break; case N_SECT: if (symbol.n_sect == text_nsect) { type = 't'; } else if (symbol.n_sect == data_nsect) { type = 'd'; } else if (symbol.n_sect == bss_nsect) { type = 'b'; } else { type = 's'; } break; case N_INDR: type = 'i'; break; } // uppercase indicates an externally visible symbol if ((symbol.n_type & N_EXT) && type != '?') { type = toupper(type); } if (type != '?' && type != 'u' && type != 'c') { const uint8_t* name = (const uint8_t*)""; if (symbol.n_un.n_strx != 0) { name = (uint8_t*)(strings + symbol.n_un.n_strx); } res = SQL("INSERT INTO mach_o_symbols VALUES (%lld, \'%c\', %lld, %Q)", serial, type, symbol.n_value, name); } } return 0; }