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);
}
Beispiel #2
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);
}
Beispiel #3
0
/*
 * 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);
}
Beispiel #5
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;
}