/*
 * Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include "stuff/errors.h"
#include "stuff/breakout.h"
#include "stuff/rnd.h"
#include "stuff/allocate.h"

/* used by error routines as the name of the program */
char *progname = NULL;

static void usage(
    void);

static void process(
    struct arch *archs,
    uint32_t narchs);

static void write_on_input(
    struct arch *archs,
    uint32_t narchs,
    char *input);

static void setup_object_symbolic_info(
    struct object *object);

static void update_load_commands(
    struct arch *arch,
    uint32_t *header_size);

/* the argument to the -id option */
static char *id = NULL;

/* the arguments to the -change options */
struct changes {
    char *old;
    char *new;
};
static struct changes *changes = NULL;
static uint32_t nchanges = 0;

/* the arguments to the -rpath options */
struct rpaths {
    char *old;
    char *new;
    enum bool found;
};
static struct rpaths *rpaths = NULL;
static uint32_t nrpaths = 0;

/* the arguments to the -add_rpath options */
struct add_rpaths {
    char *new;
};
static struct add_rpaths *add_rpaths = NULL;
static uint32_t nadd_rpaths = 0;

/* the arguments to the -delete_rpath options */
struct delete_rpaths {
    char *old;
    enum bool found;
};
static struct delete_rpaths *delete_rpaths = NULL;
static uint32_t ndelete_rpaths = 0;

/*
 * This is a pointer to an array of the original header sizes (mach header and
 * load commands) for each architecture which is used when we are writing on the
 * input file.
 */
static uint32_t *arch_header_sizes = NULL;

/*
 * The -o output option is not enabled as it is not needed and has the
 * unintended side effect of changing the time stamps in LC_ID_DYLIB commands
 * which is not the desired functionality of this command.
 */
#undef OUTPUT_OPTION

/* apple_version is created by the libstuff/Makefile */
extern char apple_version[];
char *version = apple_version;

/*
 * The install_name_tool allow the dynamic shared library install names of a
 * Mach-O binary to be changed.  For this tool to work when the install names
 * are larger the binary should be built with the ld(1)
 * -headerpad_max_install_names option.
 *
 *    Usage: install_name_tool [-change old new] ... [-rpath old new] ...
 * 	[-add_rpath new] ... [-delete_rpath old] ... [-id name] input
 *
 * The "-change old new" option changes the "old" install name to the "new"
 * install name if found in the binary.
 * 
 * The "-rpath old new" option changes the "old" path name in the rpath to
 * the "new" path name in an LC_RPATH load command in the binary.
 *
 * The "-add_rpath new" option adds an LC_RPATH load command.
 *
 * The "-delete_rpath old" option deletes the LC_RPATH load command with the
 * "old" path name in the binary.
 *
 * The "-id name" option changes the install name in the LC_ID_DYLIB load
 * command for a dynamic shared library.
 */
int
main(
int argc,
char **argv,
char **envp)
{
    int i, j;
    struct arch *archs;
    uint32_t narchs;
    char *input;
    char *output;

	output = NULL;
	progname = argv[0];
	input = NULL;
	archs = NULL;
	narchs = 0;
	for(i = 1; i < argc; i++){
#ifdef OUTPUT_OPTION
	    if(strcmp(argv[i], "-o") == 0){
		if(i + 1 == argc){
		    error("missing argument to: %s option", argv[i]);
		    usage();
		}
		if(output != NULL){
		    error("more than one: %s option specified", argv[i]);
		    usage();
		}
		output = argv[i+1];
		i++;
	    }
	    else
#endif /* OUTPUT_OPTION */
	    if(strcmp(argv[i], "-id") == 0){
		if(i + 1 == argc){
		    error("missing argument to: %s option", argv[i]);
		    usage();
		}
		if(id != NULL){
		    error("more than one: %s option specified", argv[i]);
		    usage();
		}
		id = argv[i+1];
		i++;
	    }
	    else if(strcmp(argv[i], "-change") == 0){
		if(i + 2 >= argc){
		    error("missing argument(s) to: %s option", argv[i]);
		    usage();
		}
		changes = reallocate(changes,
				     sizeof(struct changes) * (nchanges + 1));
		changes[nchanges].old = argv[i+1];
		changes[nchanges].new = argv[i+2];
		nchanges += 1;
		i += 2;
	    }
	    else if(strcmp(argv[i], "-rpath") == 0){
		if(i + 2 >= argc){
		    error("missing argument(s) to: %s option", argv[i]);
		    usage();
		}
		for(j = 0; j < nrpaths; j++){
		    if(strcmp(rpaths[j].old, argv[i+1]) == 0){
		        if(strcmp(rpaths[j].new, argv[i+2]) == 0){
			    error("\"-rpath %s %s\" specified more than once",
				   argv[i+1], argv[i+2]);
			    usage();
			}
			error("can't specify both \"-rpath %s %s\" and "
			      "\"-rpath %s %s\"", rpaths[j].old, rpaths[j].new,
			      argv[i+1], argv[i+2]);
			usage();
		    }
		    if(strcmp(rpaths[j].new, argv[i+1]) == 0 ||
		       strcmp(rpaths[j].old, argv[i+2]) == 0 ||
		       strcmp(rpaths[j].new, argv[i+2]) == 0){
			error("can't specify both \"-rpath %s %s\" and "
			      "\"-rpath %s %s\"", rpaths[j].old, rpaths[j].new,
			      argv[i+1], argv[i+2]);
			usage();
		    }
		}
		for(j = 0; j < nadd_rpaths; j++){
		    if(strcmp(add_rpaths[j].new, argv[i+1]) == 0 ||
		       strcmp(add_rpaths[j].new, argv[i+2]) == 0){
			error("can't specify both \"-add_rpath %s\" "
			      "and \"-rpath %s %s\"", add_rpaths[j].new,
			      argv[i+1], argv[i+2]);
			usage();
		    }
		}
		for(j = 0; j < ndelete_rpaths; j++){
		    if(strcmp(delete_rpaths[j].old, argv[i+1]) == 0 ||
		       strcmp(delete_rpaths[j].old, argv[i+2]) == 0){
			error("can't specify both \"-delete_rpath %s\" "
			      "and \"-rpath %s %s\"", delete_rpaths[j].old,
			      argv[i+1], argv[i+2]);
			usage();
		    }
		}
		rpaths = reallocate(rpaths,
				    sizeof(struct rpaths) * (nrpaths + 1));
		rpaths[nrpaths].old = argv[i+1];
		rpaths[nrpaths].new = argv[i+2];
		rpaths[nrpaths].found = FALSE;
		nrpaths += 1;
		i += 2;
	    }
	    else if(strcmp(argv[i], "-add_rpath") == 0){
		if(i + 1 == argc){
		    error("missing argument(s) to: %s option", argv[i]);
		    usage();
		}
		for(j = 0; j < nadd_rpaths; j++){
		    if(strcmp(add_rpaths[j].new, argv[i+1]) == 0){
			error("\"-add_rpath %s\" specified more than once",
			      add_rpaths[j].new);
			usage();
		    }
		}
		for(j = 0; j < nrpaths; j++){
		    if(strcmp(rpaths[j].old, argv[i+1]) == 0 ||
		       strcmp(rpaths[j].new, argv[i+1]) == 0){
			error("can't specify both \"-rpath %s %s\" and "
			      "\"-add_rpath %s\"", rpaths[j].old, rpaths[j].new,
			      argv[i+1]);
			usage();
		    }
		}
		for(j = 0; j < ndelete_rpaths; j++){
		    if(strcmp(delete_rpaths[j].old, argv[i+1]) == 0){
			error("can't specify both \"-delete_rpath %s\" "
			      "and \"-add_rpath %s\"", delete_rpaths[j].old,
			      argv[i+1]);
			usage();
		    }
		}
		add_rpaths = reallocate(add_rpaths,
				sizeof(struct add_rpaths) * (nadd_rpaths + 1));
		add_rpaths[nadd_rpaths].new = argv[i+1];
		nadd_rpaths += 1;
		i += 1;
	    }
	    else if(strcmp(argv[i], "-delete_rpath") == 0){
		if(i + 1 == argc){
		    error("missing argument(s) to: %s option", argv[i]);
		    usage();
		}
		for(j = 0; j < ndelete_rpaths; j++){
		    if(strcmp(delete_rpaths[j].old, argv[i+1]) == 0){
			error("\"-delete_rpath %s\" specified more than once",
			      delete_rpaths[j].old);
			usage();
		    }
		}
		for(j = 0; j < nrpaths; j++){
		    if(strcmp(rpaths[j].old, argv[i+1]) == 0 ||
		       strcmp(rpaths[j].new, argv[i+1]) == 0){
			error("can't specify both \"-rpath %s %s\" and "
			      "\"-delete_rpath %s\"", rpaths[j].old,
			      rpaths[j].new, argv[i+1]);
			usage();
		    }
		}
		for(j = 0; j < nadd_rpaths; j++){
		    if(strcmp(add_rpaths[j].new, argv[i+1]) == 0){
			error("can't specify both \"-add_rpath %s\" "
			      "and \"-delete_rpath %s\"", add_rpaths[j].new,
			      argv[i+1]);
			usage();
		    }
		}
		delete_rpaths = reallocate(delete_rpaths,
				sizeof(struct delete_rpaths) *
					(ndelete_rpaths + 1));
		delete_rpaths[ndelete_rpaths].old = argv[i+1];
		delete_rpaths[ndelete_rpaths].found = FALSE;
		ndelete_rpaths += 1;
		i += 1;
	    }
	    else{
		if(input != NULL){
		    error("more than one input file specified (%s and %s)",
			  argv[i], input);
		    usage();
		}
		input = argv[i];
	    }
	}
	if(input == NULL || (id == NULL && nchanges == 0 && nrpaths == 0 &&
	   nadd_rpaths == 0 && ndelete_rpaths == 0))
	    usage();

	breakout(input, &archs, &narchs, FALSE);
	if(errors)
	    exit(EXIT_FAILURE);

	checkout(archs, narchs);
	if(errors)
	    exit(EXIT_FAILURE);

	arch_header_sizes = allocate(narchs * sizeof(uint32_t));
	process(archs, narchs);
	if(errors)
	    exit(EXIT_FAILURE);

	if(output != NULL)
	    writeout(archs, narchs, output, 0777, TRUE, FALSE, FALSE, NULL);
	else
	    write_on_input(archs, narchs, input);

	if(errors)
	    return(EXIT_FAILURE);
	else
	    return(EXIT_SUCCESS);
}

/*
 * usage() prints the current usage message and exits indicating failure.
 */
static
void
usage(
void)
{
	fprintf(stderr, "Usage: %s [-change old new] ... [-rpath old new] ... "
			"[-add_rpath new] ... [-delete_rpath old] ... "
			"[-id name] input"
#ifdef OUTPUT_OPTION
		" [-o output]"
#endif /* OUTPUT_OPTION */
		"\n", progname);
	exit(EXIT_FAILURE);
}

static
void
process(
struct arch *archs,
uint32_t narchs)
{
    uint32_t i;
    struct object *object;

	for(i = 0; i < narchs; i++){
	    if(archs[i].type == OFILE_Mach_O){
		object = archs[i].object;
		if(object->mh_filetype == MH_DYLIB_STUB)
		    fatal("input file: %s is Mach-O dynamic shared library stub"
			  " file and can't be changed", archs[i].file_name);
		setup_object_symbolic_info(object);
		update_load_commands(archs + i, arch_header_sizes + i);
	    }
	    else{
		error("input file: %s is not a Mach-O file",archs[i].file_name);
		return;
	    }
	}
}

/*
 * write_on_input() takes the modified archs and writes the load commands
 * directly into the input file.
 */
static
void
write_on_input(
struct arch *archs,
uint32_t narchs,
char *input)
{
    int fd;
    uint32_t i, offset, size, headers_size;
    char *headers;
    struct mach_header *mh;
    struct mach_header_64 *mh64;
    struct load_command *lc;
    enum byte_sex host_byte_sex;

	host_byte_sex = get_host_byte_sex();

	fd = open(input, O_WRONLY, 0);
	if(fd == -1)
	    system_error("can't open input file: %s for writing", input);

	for(i = 0; i < narchs; i++){
	    if(archs[i].fat_arch != NULL)
		offset = archs[i].fat_arch->offset;
	    else
		offset = 0;
	    if(lseek(fd, offset, SEEK_SET) == -1)
		system_error("can't lseek to offset: %u in file: %s for "
			     "writing", offset, input);
	    /*
	     * Since the new headers may be smaller than the old headers and
	     * we want to make sure any old unused bytes are zero in the file
	     * we allocate the size of the original headers into a buffer and
	     * zero it out. Then copy the new headers into the buffer and write
	     * out the size of the original headers to the file.
	     */
	    if(archs[i].object->mh != NULL){
		headers_size = sizeof(struct mach_header) +
			       archs[i].object->mh->sizeofcmds;
	    }
	    else{
		headers_size = sizeof(struct mach_header_64) +
			       archs[i].object->mh64->sizeofcmds;
	    }
	    if(arch_header_sizes[i] > headers_size)
		size = arch_header_sizes[i];
	    else
		size = headers_size;
	    headers = allocate(size);
	    memset(headers, '\0', size);

	    if(archs[i].object->mh != NULL){
		mh = (struct mach_header *)headers;
		lc = (struct load_command *)(headers +
					     sizeof(struct mach_header));
		*mh = *(archs[i].object->mh);
		memcpy(lc, archs[i].object->load_commands, mh->sizeofcmds);
		if(archs[i].object->object_byte_sex != host_byte_sex)
		    if(swap_object_headers(mh, lc) == FALSE)
			fatal("internal error: swap_object_headers() failed");
	    }
	    else{
		mh64 = (struct mach_header_64 *)headers;
		lc = (struct load_command *)(headers +
					     sizeof(struct mach_header_64));
		*mh64 = *(archs[i].object->mh64);
		memcpy(lc, archs[i].object->load_commands, mh64->sizeofcmds);
		if(archs[i].object->object_byte_sex != host_byte_sex)
		    if(swap_object_headers(mh64, lc) == FALSE)
			fatal("internal error: swap_object_headers() failed");
	    }

	    if(write(fd, headers, size) != (int)size)
		system_error("can't write new headers in file: %s", input);

	    free(headers);
	}
	if(close(fd) == -1)
	    system_error("can't close written on input file: %s", input);
}

static
void
setup_object_symbolic_info(
struct object *object)
{
#ifdef OUTPUT_OPTION
	if(object->st != NULL && object->st->nsyms != 0){
	    object->output_symbols = (struct nlist *)
		(object->object_addr + object->st->symoff);
	    if(object->object_byte_sex != get_host_byte_sex())
		swap_nlist(object->output_symbols,
			   object->st->nsyms,
			   get_host_byte_sex());
	    object->output_nsymbols = object->st->nsyms;
	    object->output_strings =
		object->object_addr + object->st->stroff;
	    object->output_strings_size = object->st->strsize;
	    object->input_sym_info_size =
		object->st->nsyms * sizeof(struct nlist) +
		object->st->strsize;
	    object->output_sym_info_size =
		object->input_sym_info_size;
	}
	if(object->dyst != NULL){
	    object->output_ilocalsym = object->dyst->ilocalsym;
	    object->output_nlocalsym = object->dyst->nlocalsym;
	    object->output_iextdefsym = object->dyst->iextdefsym;
	    object->output_nextdefsym = object->dyst->nextdefsym;
	    object->output_iundefsym = object->dyst->iundefsym;
	    object->output_nundefsym = object->dyst->nundefsym;

	    object->output_loc_relocs = (struct relocation_info *)
		(object->object_addr + object->dyst->locreloff);
	    object->output_ext_relocs = (struct relocation_info *)
		(object->object_addr + object->dyst->extreloff);
	    object->output_indirect_symtab = (uint32_t *)
		(object->object_addr + object->dyst->indirectsymoff);
	    object->output_tocs =
		(struct dylib_table_of_contents *)
		(object->object_addr + object->dyst->tocoff);
	    object->output_ntoc = object->dyst->ntoc;
	    object->output_mods = (struct dylib_module *)
		(object->object_addr + object->dyst->modtaboff);
	    object->output_nmodtab = object->dyst->nmodtab;
	    object->output_refs = (struct dylib_reference *)
		(object->object_addr + object->dyst->extrefsymoff);
	    object->output_nextrefsyms = object->dyst->nextrefsyms;
	    if(object->dyld_info != NULL){
		object->input_sym_info_size += object->dyld_info->rebase_size
					    + object->dyld_info->bind_size
					    + object->dyld_info->weak_bind_size
					    + object->dyld_info->lazy_bind_size
					    + object->dyld_info->export_size;
	    }
	    object->input_sym_info_size +=
		object->dyst->nlocrel *
		    sizeof(struct relocation_info) +
		object->dyst->nextrel *
		    sizeof(struct relocation_info) +
		object->dyst->nindirectsyms *
		    sizeof(uint32_t) +
		object->dyst->ntoc *
		    sizeof(struct dylib_table_of_contents)+
		object->dyst->nmodtab *
		    sizeof(struct dylib_module) +
		object->dyst->nextrefsyms *
		    sizeof(struct dylib_reference);
	    object->output_sym_info_size +=
		object->dyst->nlocrel *
		    sizeof(struct relocation_info) +
		object->dyst->nextrel *
		    sizeof(struct relocation_info) +
		object->dyst->nindirectsyms *
		    sizeof(uint32_t) +
		object->dyst->ntoc *
		    sizeof(struct dylib_table_of_contents)+
		object->dyst->nmodtab *
		    sizeof(struct dylib_module) +
		object->dyst->nextrefsyms *
		    sizeof(struct dylib_reference);
	    if(object->split_info_cmd != NULL){
		object->output_split_info_data = 
		(object->object_addr + object->split_info_cmd->dataoff);
		object->output_split_info_data_size = 
		    object->split_info_cmd->datasize;
		object->input_sym_info_size +=
		    object->split_info_cmd->datasize;
		object->output_sym_info_size +=
		    object->split_info_cmd->datasize;
	    }
	    if(object->func_starts_info_cmd != NULL){
		object->output_func_start_info_data = 
		(object->object_addr + object->func_starts_info_cmd->dataoff);
		object->output_func_start_info_data_size = 
		    object->func_starts_info_cmd->datasize;
		object->input_sym_info_size +=
		    object->func_starts_info_cmd->datasize;
		object->output_sym_info_size +=
		    object->func_starts_info_cmd->datasize;
	    }
	    if(object->data_in_code_cmd != NULL){
		object->output_data_in_code_info_data = 
		(object->object_addr + object->data_in_code_cmd->dataoff);
		object->output_data_in_code_info_data_size = 
		    object->data_in_code_cmd->datasize;
		object->input_sym_info_size +=
		    object->data_in_code_cmd->datasize;
		object->output_sym_info_size +=
		    object->data_in_code_cmd->datasize;
	    }
	    if(object->code_sign_drs_cmd != NULL){
		object->output_code_sign_drs_info_data = 
		(object->object_addr + object->code_sign_drs_cmd->dataoff);
		object->output_code_sign_drs_info_data_size = 
		    object->code_sign_drs_cmd->datasize;
		object->input_sym_info_size +=
		    object->code_sign_drs_cmd->datasize;
		object->output_sym_info_size +=
		    object->code_sign_drs_cmd->datasize;
	    }
	    if(object->link_opt_hint_cmd != NULL){
		object->output_link_opt_hint_info_data = 
		(object->object_addr + object->link_opt_hint_cmd->dataoff);
		object->output_link_opt_hint_info_data_size = 
		    object->link_opt_hint_cmd->datasize;
		object->input_sym_info_size +=
		    object->link_opt_hint_cmd->datasize;
		object->output_sym_info_size +=
		    object->link_opt_hint_cmd->datasize;
	    }
	    if(object->hints_cmd != NULL){
		object->output_hints = (struct twolevel_hint *)
		    (object->object_addr +
		     object->hints_cmd->offset);
		object->input_sym_info_size +=
		    object->hints_cmd->nhints *
		    sizeof(struct twolevel_hint);
		object->output_sym_info_size +=
		    object->hints_cmd->nhints *
		    sizeof(struct twolevel_hint);
	    }
	    if(object->code_sig_cmd != NULL){
		object->output_code_sig_data = object->object_addr +
		    object->code_sig_cmd->dataoff;
		object->output_code_sig_data_size = 
		    object->code_sig_cmd->datasize;
		object->input_sym_info_size =
		    rnd(object->input_sym_info_size, 16);
		object->input_sym_info_size +=
		    object->code_sig_cmd->datasize;
		object->output_sym_info_size =
		    rnd(object->output_sym_info_size, 16);
		object->output_sym_info_size +=
		    object->code_sig_cmd->datasize;
	    }
	}
#endif /* OUTPUT_OPTION */
}

/*
 * update_load_commands() changes the install names the LC_LOAD_DYLIB,
 * LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and
 * LC_PREBOUND_DYLIB commands for the specified arch.
 */
static
void
update_load_commands(
struct arch *arch,
uint32_t *header_size)
{
    uint32_t i, j, new_sizeofcmds, new_size, linked_modules_size, ncmds,
	     sizeof_mach_header, cmd_round;
    uint64_t low_fileoff;
    struct load_command *lc1, *lc2, *new_load_commands;
    struct dylib_command *dl_load1, *dl_load2, *dl_id1, *dl_id2;
    struct prebound_dylib_command *pbdylib1, *pbdylib2;
    char *dylib_name1, *dylib_name2, *arch_name, *linked_modules1,
	 *linked_modules2, *path1, *path2;
    struct segment_command *sg;
    struct segment_command_64 *sg64;
    struct section *s;
    struct section_64 *s64;
    struct arch_flag arch_flag;
    struct rpath_command *rpath1, *rpath2;
    enum bool delete;

	/*
	 * Make a pass through the load commands and figure out what the new
	 * size of the the commands needs to be and how much room there is for
	 * them.
	 */
	if(arch->object->mh != NULL){
	    new_sizeofcmds = arch->object->mh->sizeofcmds;
	    ncmds = arch->object->mh->ncmds;
	    sizeof_mach_header = sizeof(struct mach_header);
	    cmd_round = 4;
	    arch_flag.cputype = arch->object->mh->cputype;
	    arch_flag.cpusubtype = arch->object->mh->cpusubtype;
	}
	else{
	    new_sizeofcmds = arch->object->mh64->sizeofcmds;
	    ncmds = arch->object->mh64->ncmds;
	    sizeof_mach_header = sizeof(struct mach_header_64);
	    cmd_round = 8;
	    arch_flag.cputype = arch->object->mh64->cputype;
	    arch_flag.cpusubtype = arch->object->mh64->cpusubtype;
	}
	set_arch_flag_name(&arch_flag);
	arch_name = arch_flag.name;

	low_fileoff = ULLONG_MAX;
	lc1 = arch->object->load_commands;
	for(i = 0; i < ncmds; i++){
	    switch(lc1->cmd){
	    case LC_ID_DYLIB:
		dl_id1 = (struct dylib_command *)lc1;
		dylib_name1 = (char *)dl_id1 + dl_id1->dylib.name.offset;
		if(id != NULL){
		    new_size = sizeof(struct dylib_command) +
			       rnd(strlen(id) + 1, cmd_round);
		    new_sizeofcmds += (new_size - dl_id1->cmdsize);
		}
		break;

	    case LC_LOAD_DYLIB:
	    case LC_LOAD_WEAK_DYLIB:
	    case LC_REEXPORT_DYLIB:
	    case LC_LOAD_UPWARD_DYLIB:
	    case LC_LAZY_LOAD_DYLIB:
		dl_load1 = (struct dylib_command *)lc1;
		dylib_name1 = (char *)dl_load1 + dl_load1->dylib.name.offset;
		for(j = 0; j < nchanges; j++){
		    if(strcmp(changes[j].old, dylib_name1) == 0){
			new_size = sizeof(struct dylib_command) +
				   rnd(strlen(changes[j].new) + 1,
					 cmd_round);
			new_sizeofcmds += (new_size - dl_load1->cmdsize);
			break;
		    }
		}
		break;

	    case LC_PREBOUND_DYLIB:
		pbdylib1 = (struct prebound_dylib_command *)lc1;
		dylib_name1 = (char *)pbdylib1 + pbdylib1->name.offset;
		for(j = 0; j < nchanges; j++){
		    if(strcmp(changes[j].old, dylib_name1) == 0){
			linked_modules_size = pbdylib1->cmdsize - (
				sizeof(struct prebound_dylib_command) +
				rnd(strlen(dylib_name1) + 1, cmd_round));
			new_size = sizeof(struct prebound_dylib_command) +
				   rnd(strlen(changes[j].new) + 1,
					 cmd_round) +
				   linked_modules_size;
			new_sizeofcmds += (new_size - pbdylib1->cmdsize);
			break;
		    }
		}
		break;

	    case LC_SEGMENT:
		sg = (struct segment_command *)lc1;
		s = (struct section *)
		    ((char *)sg + sizeof(struct segment_command));
		if(sg->nsects != 0){
		    for(j = 0; j < sg->nsects; j++){
			if(s->size != 0 &&
			   (s->flags & S_ZEROFILL) != S_ZEROFILL &&
			   (s->flags & S_THREAD_LOCAL_ZEROFILL) !=
				       S_THREAD_LOCAL_ZEROFILL &&
			   s->offset < low_fileoff)
			    low_fileoff = s->offset;
			s++;
		    }
		}
		else{
		    if(sg->filesize != 0 && sg->fileoff < low_fileoff)
			low_fileoff = sg->fileoff;
		}
		break;

	    case LC_SEGMENT_64:
		sg64 = (struct segment_command_64 *)lc1;
		s64 = (struct section_64 *)
		    ((char *)sg64 + sizeof(struct segment_command_64));
		if(sg64->nsects != 0){
		    for(j = 0; j < sg64->nsects; j++){
			if(s64->size != 0 &&
			   (s64->flags & S_ZEROFILL) != S_ZEROFILL &&
			   (s64->flags & S_THREAD_LOCAL_ZEROFILL) !=
					 S_THREAD_LOCAL_ZEROFILL &&
			   s64->offset < low_fileoff)
			    low_fileoff = s64->offset;
			s64++;
		    }
		}
		else{
		    if(sg64->filesize != 0 && sg64->fileoff < low_fileoff)
			low_fileoff = sg64->fileoff;
		}
		break;

	    case LC_RPATH:
		rpath1 = (struct rpath_command *)lc1;
		path1 = (char *)rpath1 + rpath1->path.offset;
		for(j = 0; j < nadd_rpaths; j++){
		    if(strcmp(add_rpaths[j].new, path1) == 0){
			error("for: %s (for architecture %s) option "
			      "\"-add_rpath %s\" would duplicate path, file "
			      "already has LC_RPATH for: %s", arch->file_name,
			      arch_name, add_rpaths[j].new, path1);
		    }
		}
		for(j = 0; j < nrpaths; j++){
		    if(strcmp(rpaths[j].old, path1) == 0){
			rpaths[j].found = TRUE;
			new_size = rnd(sizeof(struct rpath_command) +
				         strlen(rpaths[j].new) + 1,
					 cmd_round);
			new_sizeofcmds += (new_size - rpath1->cmdsize);
			break;
		    }
		}
		for(j = 0; j < ndelete_rpaths; j++){
		    if(strcmp(delete_rpaths[j].old, path1) == 0){
			delete_rpaths[j].found = TRUE;
			new_sizeofcmds -= rpath1->cmdsize;
			break;
		    }
		}
		break;
	    }
	    lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize);
	}

	for(i = 0; i < ndelete_rpaths; i++){
	    if(delete_rpaths[i].found == FALSE){
		error("no LC_RPATH load command with path: %s found in: "
		      "%s (for architecture %s), required for specified option "
		      "\"-delete_rpath %s\"", delete_rpaths[i].old,
		      arch->file_name, arch_name, delete_rpaths[i].old);
	    }
	}
	for(i = 0; i < nrpaths; i++){
	    if(rpaths[i].found == FALSE){
		error("no LC_RPATH load command with path: %s found in: "
		      "%s (for architecture %s), required for specified option "
		      "\"-rpath %s %s\"", rpaths[i].old, arch->file_name,
		      arch_name, rpaths[i].old, rpaths[i].new);
	    }
	}

	for(i = 0; i < nadd_rpaths; i++){
	    new_size = rnd(sizeof(struct rpath_command) +
			     strlen(add_rpaths[i].new) + 1, cmd_round);
	    new_sizeofcmds += new_size;
	}

	if(new_sizeofcmds + sizeof_mach_header > low_fileoff){
	    error("changing install names or rpaths can't be redone for: %s "
		  "(for architecture %s) because larger updated load commands "
		  "do not fit (the program must be relinked, and you may need "
		  "to use -headerpad or -headerpad_max_install_names)",
		  arch->file_name, arch_name);
	    return;
	}

	/*
	 * Allocate space for the new load commands and zero it out so any holes
	 * will be zero bytes.  Note this may be smaller than the original size
	 * of the load commands.
	 */
	new_load_commands = allocate(new_sizeofcmds);
	memset(new_load_commands, '\0', new_sizeofcmds);

	/*
	 * Fill in the new load commands by copying in the non-modified
	 * commands and updating ones with install name changes.
	 */
	lc1 = arch->object->load_commands;
	lc2 = new_load_commands;
	for(i = 0; i < ncmds; i++){
	    delete = FALSE;
	    switch(lc1->cmd){
	    case LC_ID_DYLIB:
		if(id != NULL){
		    memcpy(lc2, lc1, sizeof(struct dylib_command));
		    dl_id2 = (struct dylib_command *)lc2;
		    dl_id2->cmdsize = sizeof(struct dylib_command) +
				     rnd(strlen(id) + 1, cmd_round);
		    dl_id2->dylib.name.offset = sizeof(struct dylib_command);
		    dylib_name2 = (char *)dl_id2 + dl_id2->dylib.name.offset;
		    strcpy(dylib_name2, id);
		}
		else{
		    memcpy(lc2, lc1, lc1->cmdsize);
		}
		break;

	    case LC_LOAD_DYLIB:
	    case LC_LOAD_WEAK_DYLIB:
	    case LC_REEXPORT_DYLIB:
	    case LC_LOAD_UPWARD_DYLIB:
	    case LC_LAZY_LOAD_DYLIB:
		dl_load1 = (struct dylib_command *)lc1;
		dylib_name1 = (char *)dl_load1 + dl_load1->dylib.name.offset;
		for(j = 0; j < nchanges; j++){
		    if(strcmp(changes[j].old, dylib_name1) == 0){
			memcpy(lc2, lc1, sizeof(struct dylib_command));
			dl_load2 = (struct dylib_command *)lc2;
			dl_load2->cmdsize = sizeof(struct dylib_command) +
					    rnd(strlen(changes[j].new) + 1,
						  cmd_round);
			dl_load2->dylib.name.offset =
			    sizeof(struct dylib_command);
			dylib_name2 = (char *)dl_load2 +
				      dl_load2->dylib.name.offset;
			strcpy(dylib_name2, changes[j].new);
			break;
		    }
		}
		if(j >= nchanges){
		    memcpy(lc2, lc1, lc1->cmdsize);
		}
		break;

	    case LC_PREBOUND_DYLIB:
		pbdylib1 = (struct prebound_dylib_command *)lc1;
		dylib_name1 = (char *)pbdylib1 + pbdylib1->name.offset;
		for(j = 0; j < nchanges; j++){
		    if(strcmp(changes[j].old, dylib_name1) == 0){
			memcpy(lc2, lc1, sizeof(struct prebound_dylib_command));
			pbdylib2 = (struct prebound_dylib_command *)lc2;
			linked_modules_size = pbdylib1->cmdsize - (
			    sizeof(struct prebound_dylib_command) +
			    rnd(strlen(dylib_name1) + 1, cmd_round));
			pbdylib2->cmdsize =
			    sizeof(struct prebound_dylib_command) +
			    rnd(strlen(changes[j].new) + 1, cmd_round) +
			    linked_modules_size;

			pbdylib2->name.offset =
			    sizeof(struct prebound_dylib_command);
			dylib_name2 = (char *)pbdylib2 +
				      pbdylib2->name.offset;
			strcpy(dylib_name2, changes[j].new);
			
			pbdylib2->linked_modules.offset = 
			    sizeof(struct prebound_dylib_command) +
			    rnd(strlen(changes[j].new) + 1, cmd_round);
			linked_modules1 = (char *)pbdylib1 +
					  pbdylib1->linked_modules.offset;
			linked_modules2 = (char *)pbdylib2 +
					  pbdylib2->linked_modules.offset;
			memcpy(linked_modules2, linked_modules1,
			       linked_modules_size);
			break;
		    }
		}
		if(j >= nchanges){
		    memcpy(lc2, lc1, lc1->cmdsize);
		}
		break;

	    case LC_RPATH:
		rpath1 = (struct rpath_command *)lc1;
		path1 = (char *)rpath1 + rpath1->path.offset;
		for(j = 0; j < ndelete_rpaths; j++){
		    if(strcmp(delete_rpaths[j].old, path1) == 0){
			delete = TRUE;
			break;
		    }
		}
		if(delete == TRUE)
		    break;
		for(j = 0; j < nrpaths; j++){
		    if(strcmp(rpaths[j].old, path1) == 0){
			memcpy(lc2, lc1, sizeof(struct rpath_command));
			rpath2 = (struct rpath_command *)lc2;
			rpath2->cmdsize = rnd(sizeof(struct rpath_command) +
					        strlen(rpaths[j].new) + 1,
						cmd_round);
			rpath2->path.offset = sizeof(struct rpath_command);
			path2 = (char *)rpath2 + rpath2->path.offset;
			strcpy(path2, rpaths[j].new);
			break;
		    }
		}
		if(j >= nrpaths){
		    memcpy(lc2, lc1, lc1->cmdsize);
		}
		break;

	    default:
		memcpy(lc2, lc1, lc1->cmdsize);
		break;
	    }
	    lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize);
	    if(delete == FALSE)
		lc2 = (struct load_command *)((char *)lc2 + lc2->cmdsize);
	}
	/*
	 * Add the new rpath load commands.
	 */
	for(i = 0; i < nadd_rpaths; i++){
	    rpath2 = (struct rpath_command *)lc2;
	    rpath2->cmd = LC_RPATH;
	    rpath2->cmdsize = rnd(sizeof(struct rpath_command) +
				    strlen(add_rpaths[i].new) + 1,
				    cmd_round);
	    rpath2->path.offset = sizeof(struct rpath_command);
	    path2 = (char *)rpath2 + rpath2->path.offset;
	    strcpy(path2, add_rpaths[i].new);
	    lc2 = (struct load_command *)((char *)lc2 + lc2->cmdsize);
	}
	ncmds += nadd_rpaths;
	ncmds -= ndelete_rpaths;

	/*
	 * Finally copy the updated load commands over the existing load
	 * commands. Since the headers could be smaller we save away the old
	 * header_size (for use when writing on the input) and also put zero
	 * bytes on the part that is no longer used for headers.
	 */
	if(arch->object->mh != NULL){
	    *header_size = sizeof(struct mach_header) +
			   arch->object->mh->sizeofcmds;
	    if(new_sizeofcmds < arch->object->mh->sizeofcmds){
		memset(((char *)arch->object->load_commands) + new_sizeofcmds,
		       '\0', arch->object->mh->sizeofcmds - new_sizeofcmds);
	    }
	    memcpy(arch->object->load_commands, new_load_commands,
		   new_sizeofcmds);
	    arch->object->mh->sizeofcmds = new_sizeofcmds;
	    arch->object->mh->ncmds = ncmds;
	}
	else{
	    *header_size = sizeof(struct mach_header_64) +
			   arch->object->mh64->sizeofcmds;
	    if(new_sizeofcmds < arch->object->mh64->sizeofcmds){
		memset(((char *)arch->object->load_commands) + new_sizeofcmds,
		       '\0', arch->object->mh64->sizeofcmds - new_sizeofcmds);
	    }
	    memcpy(arch->object->load_commands, new_load_commands,
		   new_sizeofcmds);
	    arch->object->mh64->sizeofcmds = new_sizeofcmds;
	    arch->object->mh64->ncmds = ncmds;
	}

	free(new_load_commands);

	/* reset the pointers into the load commands */
	lc1 = arch->object->load_commands;
	for(i = 0; i < ncmds; i++){
	    switch(lc1->cmd){
	    case LC_SYMTAB:
		arch->object->st = (struct symtab_command *)lc1;
	        break;
	    case LC_DYSYMTAB:
		arch->object->dyst = (struct dysymtab_command *)lc1;
		break;
	    case LC_TWOLEVEL_HINTS:
		arch->object->hints_cmd = (struct twolevel_hints_command *)lc1;
		break;
	    case LC_PREBIND_CKSUM:
		arch->object->cs = (struct prebind_cksum_command *)lc1;
		break;
	    case LC_SEGMENT:
		sg = (struct segment_command *)lc1;
		if(strcmp(sg->segname, SEG_LINKEDIT) == 0)
		    arch->object->seg_linkedit = sg;
		break;
	    case LC_SEGMENT_64:
		sg64 = (struct segment_command_64 *)lc1;
		if(strcmp(sg64->segname, SEG_LINKEDIT) == 0)
		    arch->object->seg_linkedit64 = sg64;
		break;
	    case LC_CODE_SIGNATURE:
		arch->object->code_sig_cmd =
		    (struct linkedit_data_command *)lc1;
		break;
	    case LC_SEGMENT_SPLIT_INFO:
		arch->object->split_info_cmd =
		    (struct linkedit_data_command *)lc1;
		break;
	    case LC_FUNCTION_STARTS:
		arch->object->func_starts_info_cmd =
		    (struct linkedit_data_command *)lc1;
		break;
	    case LC_DATA_IN_CODE:
		arch->object->data_in_code_cmd =
		    (struct linkedit_data_command *)lc1;
		break;
	    case LC_DYLIB_CODE_SIGN_DRS:
		arch->object->code_sign_drs_cmd =
		    (struct linkedit_data_command *)lc1;
		break;
	    case LC_LINKER_OPTIMIZATION_HINT:
		arch->object->link_opt_hint_cmd =
		    (struct linkedit_data_command *)lc1;
		break;
	    }
	    lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize);
	}
}
