/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. 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 1.1 (the "License"). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource 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 OR NON- INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * The redo_prebinding(1) program. This redoes the prebinding of an executable * or dynamic library. * * redo_prebinding [-c|-p|-d] [-i] [-r rootdir] [-o output_file] input_file * -c check only and return status * -p check only for prebound files and return status * -d check only for dylibs and return status * -i ignore non-prebound files * -r prepend the next argument to dependent libraries * With no -c, -p or -d it exits 0 if sucessful and 2 means it could not be * done for reasons like a dependent library is missing. An exit of 3 is for * the specific case when the dependent libraries are out of date with respect * to each other. * * If -c, check only, is specified a 0 exit means the file's prebinding is * uptodate, 1 means it needs to be redone and 2 means it could not be checked * for reasons like a dependent library is missing. * * If -p, check only for prebound files, is specified 1 exit means the file is * a Mach-O that could be prebound and is not otherwise the exit is 0. * * If -d, check only for dylib files, is specified a 0 exit means the file is a * dylib, 1 means the file is not a dylib and 2 means there is some mix in * the architectures. * * Other possible options to consider implementing: * -seg1addr (for dylibs only) slide library to new seg1addr */ #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #define U_ABS(l) (((long)(l))<0 ? (unsigned long)(-(l)) : (l)) /* name of the program for error messages (argv[0]) */ char *progname; /* -c option, only check and return status */ enum bool check_only = FALSE; /* -i option, ignore non-prebound files */ enum bool ignore_non_prebound = FALSE; /* -p option, check for non-prebound files */ enum bool check_for_non_prebound = FALSE; /* -d option, check for dynamic library files */ enum bool check_for_dylibs = FALSE; enum bool seen_a_dylib = FALSE; enum bool seen_a_non_dylib = FALSE; /* -r option's argument, root directory to prepend to dependent libraries */ char *root_dir = NULL; /* -debug turn on debugging printf()'s */ enum bool debug = FALSE; /* * If some architecture was processed then the output file needs to be built * otherwise no output file is written. */ enum bool arch_processed = FALSE; /* the link state of each module */ enum link_state { UNLINKED, LINKED }; /* * These are set to the current arch's symbolic info. */ static struct arch *arch = NULL; static struct arch_flag arch_flag = { 0 }; static enum bool arch_swapped; static char *arch_name = NULL; static struct nlist *arch_symbols = NULL; static unsigned long arch_nsyms = 0; static char *arch_strings = NULL; static unsigned long arch_strsize = 0; static struct dylib_table_of_contents *arch_tocs = NULL; static unsigned long arch_ntoc = 0; static struct dylib_module *arch_mods = NULL; static unsigned long arch_nmodtab = 0; static struct dylib_reference *arch_refs = NULL; static unsigned long arch_nextrefsyms = 0; static enum link_state arch_state = LINKED; static unsigned long arch_seg1addr; static unsigned long arch_segs_read_write_addr; static enum bool arch_split_segs; static struct relocation_info *arch_extrelocs = NULL; static unsigned long arch_nextrel = 0; static unsigned long *arch_indirect_symtab = NULL; static unsigned long arch_nindirectsyms = 0; /* * These hold the dependent libraries for the arch currently being processed. * Their link edit information is used to update the arch currently being * processed. */ struct lib { char *dylib_name; char *file_name; struct ofile *ofile; struct symtab_command *st; struct dysymtab_command *dyst; struct routines_command *rc; struct nlist *symbols; unsigned long nsyms; char *strings; unsigned long strsize; struct dylib_table_of_contents *tocs; unsigned long ntoc; struct dylib_module *mods; unsigned long nmodtab; struct dylib_reference *refs; unsigned long nextrefsyms; enum link_state *module_states; enum bool LC_PREBOUND_DYLIB_found; unsigned long LC_PREBOUND_DYLIB_size; }; struct lib *libs = NULL; unsigned long nlibs = 0; /* * This is used by check_for_overlapping_segments() to create a list of segment * for overlap checking. */ struct segment { char *file_name; struct segment_command *sg; }; static void usage( void); static void process_archs( struct arch *archs, unsigned long narchs); static void process_arch(void); static void load_archs_libraries(void); static void load_library( char *file_name, struct dylib_command *dl_load, enum bool time_stamps_must_match); static void load_dependent_libraries(void); static void check_for_overlapping_segments(void); static void check_overlap( struct segment *s1, struct segment *s2); static void setup_symbolic_info(void); static void swap_arch_for_output(void); static void check_symbolic_info_tables( char *file_name, struct symtab_command *st, struct dysymtab_command *dyst, struct nlist *symbols, unsigned long nsyms, char *strings, unsigned long strsize, struct dylib_table_of_contents *tocs, unsigned long ntoc, struct dylib_module *mods, unsigned long nmodtab, struct dylib_reference *refs, unsigned long nextrefsyms); static void check_for_dylib_override_symbols(void); static void check_dylibs_for_definition( char *file_name, char *symbol_name); static enum bool check_dylibs_for_reference( char *symbol_name); /* these two variables are used by the bsearch routines */ static char *bsearch_strings = NULL; static struct nlist *bsearch_symbols = NULL; static int dylib_bsearch( const char *symbol_name, const struct dylib_table_of_contents *toc); static int nlist_bsearch( const char *symbol_name, const struct nlist *symbol); static void setup_initial_undefined_list(void); static void link_in_need_modules(void); /* * The structure of an element in a symbol list. */ struct symbol_list { char *name; /* name of the symbol */ struct symbol_list *prev; /* previous in the chain */ struct symbol_list *next; /* next in the chain */ }; /* * The head of the undefined list. This is a circular list so it can be * searched from start to end and so new items can be put on the end. This * structure never has its name filled in but they only serve as head and tail * of the list. */ static struct symbol_list undefined_list = { NULL, &undefined_list, &undefined_list }; static void add_to_undefined_list( char *name); static void link_library_module( enum link_state *module_state, struct lib *lib); struct indr_loop_list { struct nlist *symbol; struct indr_loop_list *next; }; #define NO_INDR_LOOP ((struct indr_loop_list *)1) static void lookup_symbol( char *name, struct nlist **symbol, enum link_state **module_state, struct lib **lib, struct indr_loop_list *indr_loop); static void build_new_symbol_table( void); static void update_external_relocs( void); static void update_generic_external_relocs( void); static void update_hppa_external_relocs( void); static void update_sparc_external_relocs( void); static void update_ppc_external_relocs( void); static char *contents_pointer_for_vmaddr( unsigned long vmaddr, unsigned long size); static void update_symbol_pointers( void); static void update_load_commands( void); /* * These routines are used to get/set values that might not be aligned correctly * which are being relocated. */ static inline long get_arch_long( void *addr) { long l; memcpy(&l, addr, sizeof(long)); if(arch_swapped == TRUE) return(SWAP_LONG(l)); else return(l); } static inline short get_arch_short( void *addr) { short s; memcpy(&s, addr, sizeof(short)); if(arch_swapped == TRUE) return(SWAP_SHORT(s)); else return(s); } static inline char get_arch_byte( char *addr) { return(*addr); } static inline void set_arch_long( void *addr, long value) { if(arch_swapped == TRUE) value = SWAP_LONG(value); memcpy(addr, &value, sizeof(long)); } static inline void set_arch_short( void *addr, short value) { if(arch_swapped == TRUE) value = SWAP_SHORT(value); memcpy(addr, &value, sizeof(short)); } static inline void set_arch_byte( char *addr, char value) { *addr = value; } /* * main() see top of file for program's description and options. */ int main( int argc, char *argv[], char *envp[]) { unsigned long i; char *input_file, *output_file; struct arch *archs; unsigned long narchs; struct stat stat_buf; enum bool verbose; unsigned short mode; uid_t uid; gid_t gid; input_file = NULL; output_file = NULL; archs = NULL; narchs = 0; errors = 0; verbose = FALSE; progname = argv[0]; for(i = 1; i < argc; i++){ if(argv[i][0] == '-'){ if(strcmp(argv[i], "-o") == 0){ if(i + 1 >= argc) fatal("-o requires an argument"); if(output_file != NULL) fatal("only one -o option allowed"); output_file = argv[i + 1]; i++; } else if(strcmp(argv[i], "-r") == 0){ if(i + 1 >= argc) fatal("-r requires an argument"); if(root_dir != NULL) fatal("only one -r option allowed"); root_dir = argv[i + 1]; i++; } else if(strcmp(argv[i], "-c") == 0){ check_only = TRUE; } else if(strcmp(argv[i], "-i") == 0){ ignore_non_prebound = TRUE; } else if(strcmp(argv[i], "-p") == 0){ check_for_non_prebound = TRUE; } else if(strcmp(argv[i], "-d") == 0){ check_for_dylibs = TRUE; } else if(strcmp(argv[i], "-debug") == 0){ debug = TRUE; } else if(strcmp(argv[i], "-v") == 0){ verbose = TRUE; } else{ fprintf(stderr, "%s: unknown option: %s\n", progname, argv[i]); usage(); } } else{ if(input_file != NULL) fatal("only one input file allowed"); input_file = argv[i]; } } if(input_file == NULL){ fprintf(stderr, "%s no input file specified\n", progname); usage(); } if(check_only + check_for_non_prebound + check_for_dylibs > 1){ fprintf(stderr, "%s only one of -c, -p or -d can be specified\n", progname); usage(); } /* breakout the file for processing */ breakout(input_file, &archs, &narchs); if(errors) exit(2); /* checkout the file for processing */ checkout(archs, narchs); /* process the input file */ process_archs(archs, narchs); if(errors) exit(2); /* * If we are checking for dylibs and get back from process_archs() we * either have all dylibs or all non-dylibs. So exit with 0 for dylibs * an 1 for non-dylibs. */ if(check_for_dylibs == TRUE){ if(seen_a_dylib == TRUE) exit(0); exit(1); } /* * If we are checking for non-prebound files and get back from * process_archs() we don't have any Mach-O's that were not prebound * so indicate this with an exit status of 0. */ if(check_for_non_prebound == TRUE) exit(0); /* * Create an output file if we processed any of the archs and we are * not doing checking only. */ if(arch_processed == TRUE){ if(check_only == TRUE) exit(1); if(stat(input_file, &stat_buf) == -1) system_error("can't stat input file: %s", input_file); mode = stat_buf.st_mode & 06777; uid = stat_buf.st_uid; gid = stat_buf.st_gid; if(output_file != NULL){ writeout(archs, narchs, output_file, mode, TRUE, FALSE, FALSE); } else{ output_file = makestr(input_file, ".redo_prebinding", NULL); writeout(archs, narchs, output_file, mode, TRUE, FALSE, FALSE); if(rename(output_file, input_file) == 1) system_error("can't move temporary file: %s to input " "file: %s\n", output_file, input_file); free(output_file); output_file = NULL; } /* * Run /usr/bin/objcunique on the output. */ if(stat("/usr/bin/objcunique", &stat_buf) != -1){ reset_execute_list(); add_execute_list("/usr/bin/objcunique"); if(output_file != NULL) add_execute_list(output_file); else add_execute_list(input_file); add_execute_list("-prebind"); if(ignore_non_prebound == TRUE) add_execute_list("-i"); if(root_dir != NULL){ add_execute_list("-r"); add_execute_list(root_dir); } if(execute_list(verbose) == 0) fatal("internal /usr/bin/objcunique command failed"); } /* * Call chmod(2) to insure set-uid, set-gid and sticky bits get set. * Then call chown to insure the file has the same owner and group * as the original file. */ if(output_file != NULL){ if(chmod(output_file, mode) == -1) system_error("can't set permissions on file: %s", output_file); if(chown(output_file, uid, gid) == -1) system_error("can't set owner and group on file: %s", output_file); } else{ if(chmod(input_file, mode) == -1) system_error("can't set permissions on file: %s", input_file); if(chown(input_file, uid, gid) == -1) system_error("can't set owner and group on file: %s", input_file); } } else{ if(check_only == TRUE) exit(0); } /* clean-up data structures */ free_archs(archs, narchs); if(errors) return(2); else return(0); } /* * usage() prints the current usage message. */ static void usage( void) { fprintf(stderr, "Usage: %s [-c|-p|-d] [-i] [-r rootdir] " "[-o output_file] input_file\n", progname); exit(EXIT_FAILURE); } /* * process_archs() is passed the broken out arch's and processes each of them * checking to make sure they are prebound and either executables or dylibs. */ static void process_archs( struct arch *archs, unsigned long narchs) { unsigned long i; for(i = 0; i < narchs; i++){ arch = archs + i; if(arch->type != OFILE_Mach_O){ if(check_for_dylibs == TRUE){ if(seen_a_dylib == TRUE) exit(2); seen_a_non_dylib = TRUE; continue; } else if(check_only == TRUE || ignore_non_prebound == TRUE || check_for_non_prebound == TRUE) continue; else fatal_arch(arch, NULL, "file is not a Mach-O file: "); } if(arch->object->mh->filetype != MH_EXECUTE && arch->object->mh->filetype != MH_DYLIB){ if(check_for_dylibs == TRUE){ if(seen_a_dylib == TRUE) exit(2); seen_a_non_dylib = TRUE; } else if(check_only == TRUE || ignore_non_prebound == TRUE || check_for_non_prebound == TRUE) continue; else fatal_arch(arch, NULL, "file is not a Mach-O " "executable or dynamic shared library file: "); } if(check_for_dylibs == TRUE){ if(arch->object->mh->filetype == MH_DYLIB){ if(seen_a_non_dylib == TRUE) exit(2); seen_a_dylib = TRUE; } else{ if(seen_a_dylib == TRUE) exit(2); seen_a_non_dylib = TRUE; } continue; } if((arch->object->mh->flags & MH_PREBOUND) != MH_PREBOUND){ if(check_for_non_prebound == TRUE){ if((arch->object->mh->flags & MH_DYLDLINK) == MH_DYLDLINK) exit(1); continue; } else if(check_only == TRUE || ignore_non_prebound == TRUE) continue; else fatal_arch(arch, NULL, "file is not prebound: "); } if(check_for_non_prebound == TRUE) continue; /* Now redo the prebinding for this arch[i] */ process_arch(); } } /* * process_arch() takes one arch which is a prebound executable or dylib and * redoes the prebinding. */ static void process_arch( void) { /* * Clear out any libraries loaded for the previous arch that was * processed. */ if(libs != NULL) free(libs); libs = NULL; nlibs = 0; /* set up an arch_flag for this arch's object */ arch_flag.cputype = arch->object->mh->cputype; arch_flag.cpusubtype = arch->object->mh->cpusubtype; set_arch_flag_name(&arch_flag); arch_name = arch_flag.name; if(debug == TRUE) printf("%s: processing file: %s (for architecture %s)\n", progname, arch->file_name, arch_name); /* * First load the dynamic libraries this arch directly depends on * allowing the time stamps not to match since we are redoing the * prebinding for this arch. */ load_archs_libraries(); /* * Now load the dependent libraries who's time stamps much match. */ load_dependent_libraries(); /* * To deal with libsys, in that it has no dependent libs and it's * prebinding can't be redone as indr(1) has been run on it and it has * undefineds for __NXArgc, __NXArgv, and __environ from crt code, * we return if the arch has no dependent libraries. */ if(nlibs == 0) return; /* * Before we use the symbolic information we may need to swap everything * into the host byte sex and check to see if it is all valid. */ setup_symbolic_info(); /* * Check for overlaping segments in case a library now overlaps this * arch. We assume that the checks for the dependent libraries made * by the link editor when the where prebound is still vaild. */ check_for_overlapping_segments(); /* * Check to make sure symbols are not overridden in dependent dylibs * when so prebinding can be redone. */ check_for_dylib_override_symbols(); /* * Setup the initial list of undefined symbols from the arch being * processed. */ setup_initial_undefined_list(); /* * Link in the needed modules from the dependent libraries based on the * undefined symbols setup above. This will check for multiply defined * symbols. Then allow undefined symbols to be checked for. */ link_in_need_modules(); /* * If check_only is set then load_library() checked the time stamps and * did an exit(1) if they did not match. So if we get here this arch * has been checked so just return so the other archs can be checked. */ if(check_only == TRUE) return; /* * Now that is possible to redo the prebinding as all the above checks * have been done. So this arch will be processed so set arch_processed * to indicate an output file needs to be created. */ arch_processed = TRUE; /* * Now that is possible to redo the prebinding build a new symbol table * using the new values for prebound undefined symbols. */ build_new_symbol_table(); /* * Using the new and old symbol table update the external relocation * entries. */ update_external_relocs(); /* * Using the new and old symbol table update the symbol pointers. */ update_symbol_pointers(); /* * Update the time stamps in the LC_LOAD_DYLIB commands and update * the LC_PREBOUND_DYLIB is this is an excutable. */ update_load_commands(); /* * If the arch is swapped swap it back for output. */ if(arch_swapped == TRUE) swap_arch_for_output(); } /* * load_archs_libraries() loads the libraries referenced by the image arch. */ static void load_archs_libraries( void) { unsigned long i; struct load_command *lc; struct dylib_command *dl_load; lc = arch->object->load_commands; for(i = 0; i < arch->object->mh->ncmds; i++){ if(lc->cmd == LC_LOAD_DYLIB){ dl_load = (struct dylib_command *)lc; load_library(arch->file_name, dl_load, FALSE); } if(lc->cmd == LC_ID_DYLIB && check_only == TRUE){ dl_load = (struct dylib_command *)lc; load_library(arch->file_name, dl_load, TRUE); } lc = (struct load_command *)((char *)lc + lc->cmdsize); } } /* * load_dependent_libraries() now that the libraries of the arch being are * loaded now load the dependent libraries who's time stamps much match. */ static void load_dependent_libraries( void) { unsigned long i, j; struct load_command *lc; struct dylib_command *dl_load; for(i = 0; i < nlibs; i++){ if(debug == TRUE) printf("%s: loading libraries for library %s\n", progname, libs[i].file_name); lc = libs[i].ofile->load_commands; for(j = 0; j < libs[i].ofile->mh->ncmds; j++){ if(lc->cmd == LC_LOAD_DYLIB){ dl_load = (struct dylib_command *)lc; load_library(libs[i].ofile->file_name, dl_load, TRUE); } lc = (struct load_command *)((char *)lc + lc->cmdsize); } } } /* * load_library() loads the library for the dl_load command for the current * architecture being processed in indicated by arch_flag. This library is * being loaded because file_name depends in it. If time_stamps_must_match is * TRUE then this library is not a direct dependent of what we are redoing the * prebinding for it must be correct. Since we are now processing a valid * file any errors in loading the library are fatal. */ static void load_library( char *file_name, struct dylib_command *dl_load, enum bool time_stamps_must_match) { unsigned long i; char *dylib_name; struct ofile *ofile; struct fat_arch *best_fat_arch; struct load_command *lc; struct dylib_command *dl_id; enum bool already_loaded; /* get the name of the library from the load command */ dylib_name = (char *)dl_load + dl_load->dylib.name.offset; /* if this library is already loaded just return */ already_loaded = FALSE; ofile = NULL; for(i = 0; i < nlibs; i++){ if(strcmp(libs[i].dylib_name, dylib_name) == 0){ if(time_stamps_must_match == FALSE) return; already_loaded = TRUE; ofile = libs[i].ofile; dylib_name = libs[i].file_name; break; } } if(already_loaded == FALSE){ if(debug == TRUE) printf("%s: loading library: %s\n", progname, dylib_name); /* * If a root_dir option is used prepend the directory for rooted * names. */ if(root_dir != NULL && *dylib_name == '/') dylib_name = makestr(root_dir, dylib_name, NULL); ofile = allocate(sizeof(struct ofile)); /* now map in the library for this architecture */ if(ofile_map(dylib_name, NULL, NULL, ofile, FALSE) == FALSE) exit(2); } /* * Check to make sure the ofile is a dynamic library and for the * the correct architecture. */ if(ofile->file_type == OFILE_FAT){ best_fat_arch = cpusubtype_findbestarch( arch_flag.cputype, arch_flag.cpusubtype, ofile->fat_archs, ofile->fat_header->nfat_arch); if(best_fat_arch == NULL){ error("dynamic shared library file: %s does not contain an " "architecture that can be used with %s (architecture %s)", dylib_name, file_name, arch_name); exit(2); } (void)ofile_first_arch(ofile); do{ if(best_fat_arch != ofile->fat_archs + ofile->narch) continue; if(ofile->arch_type == OFILE_ARCHIVE){ error("file: %s (for architecture %s) is an archive (not " "a Mach-O dynamic shared library)", dylib_name, ofile->arch_flag.name); exit(2); } else if(ofile->arch_type == OFILE_Mach_O){ if(ofile->mh->filetype != MH_DYLIB){ error("file: %s (for architecture %s) is not a Mach-O " "dynamic shared library", dylib_name, arch_name); exit(2); } goto good; } else if(ofile->arch_type == OFILE_UNKNOWN){ error("file: %s (for architecture %s) is not a Mach-O " "dynamic shared library", dylib_name, arch_name); exit(2); } }while(ofile_next_arch(ofile) == TRUE); } else if(ofile->file_type == OFILE_ARCHIVE){ error("file: %s is an archive (not a Mach-O dynamic shared " "library)", dylib_name); exit(2); } else if(ofile->file_type == OFILE_Mach_O){ if(arch_flag.cputype != ofile->mh->cputype){ error("dynamic shared library: %s has the wrong CPU type for: " "%s (architecture %s)", dylib_name, file_name, arch_name); exit(2); } if(cpusubtype_combine(arch_flag.cputype, arch_flag.cpusubtype, ofile->mh->cpusubtype) == -1){ error("dynamic shared library: %s has the wrong CPU subtype " "for: %s (architecture %s)", dylib_name, file_name, arch_name); exit(2); } } else{ /* ofile->file_type == OFILE_UNKNOWN */ error("file: %s is not a Mach-O dynamic shared library", dylib_name); exit(2); } good: /* * At this point ofile is infact a dynamic library and the right part * of the ofile selected for the arch passed into here. */ /* * If the time stamps must match check for matching time stamps. */ if(time_stamps_must_match == TRUE){ lc = ofile->load_commands; for(i = 0; i < ofile->mh->ncmds; i++){ if(lc->cmd == LC_ID_DYLIB){ dl_id = (struct dylib_command *)lc; if(dl_load->dylib.timestamp != dl_id->dylib.timestamp){ if(dl_load->cmd == LC_ID_DYLIB){ error("library: %s (architecture %s) prebinding " "not uptodate with installed dynamic shared " "library: %s", file_name, arch_name, dylib_name); exit(1); } else{ error("library: %s (architecture %s) prebinding " "not uptodate with dependent dynamic shared " "library: %s", file_name, arch_name, dylib_name); exit(3); } } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } } /* * When the time stamps don't need to match we are processing the * arch we are trying to redo the prebinding for. So if we are just * checking then see if the time stamps are out of date and if so * exit(1) to indicate this needs to have it's prepinding redone. */ else if(check_only == TRUE){ lc = ofile->load_commands; for(i = 0; i < ofile->mh->ncmds; i++){ if(lc->cmd == LC_ID_DYLIB){ dl_id = (struct dylib_command *)lc; if(dl_load->dylib.timestamp != dl_id->dylib.timestamp){ exit(1); } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } } /* * To allow the check_only to check the installed library load_library() * can be called with an LC_ID_DYLIB for the install library to check * it time stamp. This is not put into the list however as it would * overlap. */ if(already_loaded == FALSE && dl_load->cmd == LC_LOAD_DYLIB){ /* * Add this library's ofile to the list of libraries the current * arch depends on. */ libs = reallocate(libs, (nlibs + 1) * sizeof(struct lib)); memset(libs + nlibs, '\0', sizeof(struct lib)); libs[nlibs].dylib_name = (char *)dl_load + dl_load->dylib.name.offset; libs[nlibs].file_name = dylib_name; libs[nlibs].ofile = ofile; nlibs++; } } /* * check_for_overlapping_segments() checks to make sure the segments in the * arch and all the dependent libraries do not overlap. If they do the * prebinding can't be redone. */ static void check_for_overlapping_segments( void) { unsigned long i, j; struct segment *segments; unsigned long nsegments; struct load_command *lc; struct segment_command *sg; segments = NULL; nsegments = 0; /* put each segment of the arch in the segment list */ lc = arch->object->load_commands; for(i = 0; i < arch->object->mh->ncmds; i++){ if(lc->cmd == LC_SEGMENT){ sg = (struct segment_command *)lc; segments = reallocate(segments, (nsegments + 1) * sizeof(struct segment)); segments[nsegments].file_name = arch->file_name; segments[nsegments].sg = sg; nsegments++; } lc = (struct load_command *)((char *)lc + lc->cmdsize); } /* put each segment of each library in the segment list */ for(i = 0; i < nlibs; i++){ lc = libs[i].ofile->load_commands; for(j = 0; j < libs[i].ofile->mh->ncmds; j++){ if(lc->cmd == LC_SEGMENT){ sg = (struct segment_command *)lc; segments = reallocate(segments, (nsegments + 1) * sizeof(struct segment)); segments[nsegments].file_name = libs[i].file_name; segments[nsegments].sg = sg; nsegments++; } lc = (struct load_command *)((char *)lc + lc->cmdsize); } } /* check each segment against all others */ for(i = 0; i < nsegments; i++){ for(j = i + 1; j < nsegments; j++){ check_overlap(segments + i, segments + j); } } } /* * check_overlap() checks if the two segments passed to it overlap and if so * prints an error message and exit with a value of 2 indicating the prebinding * can't be redone. */ static void check_overlap( struct segment *s1, struct segment *s2) { if(s1->sg->vmsize == 0 || s2->sg->vmsize == 0) return; if(s1->sg->vmaddr > s2->sg->vmaddr){ if(s2->sg->vmaddr + s2->sg->vmsize <= s1->sg->vmaddr) return; } else{ if(s1->sg->vmaddr + s1->sg->vmsize <= s2->sg->vmaddr) return; } error("prebinding can't be redone because %.16s segment (address = 0x%x" " size = 0x%x) of %s overlaps with %.16s segment (address = 0x%x " "size = 0x%x) of %s (for architecture %s)", s1->sg->segname, (unsigned int)(s1->sg->vmaddr), (unsigned int)(s1->sg->vmsize), s1->file_name, s2->sg->segname, (unsigned int)(s2->sg->vmaddr), (unsigned int)(s2->sg->vmsize), s2->file_name, arch_name); exit(2); } /* * setup_symbolic_info() sets up all the symbolic info in the arch and loaded * libraries by swapping it into the host bytesex if needed and checking it to * be valid. */ static void setup_symbolic_info( void) { unsigned long i, j; enum byte_sex host_byte_sex; struct load_command *lc; host_byte_sex = get_host_byte_sex(); arch_swapped = arch->object->object_byte_sex != host_byte_sex; if(arch->object->st == NULL){ error("malformed file: %s (no LC_SYMTAB load command) (for" " architecture %s)", arch->file_name, arch_name); exit(2); } if(arch->object->dyst == NULL){ error("malformed file: %s (no LC_DYSYMTAB load command) (for" " architecture %s)", arch->file_name, arch_name); exit(2); } arch_symbols = (struct nlist *)(arch->object->object_addr + arch->object->st->symoff); arch_nsyms = arch->object->st->nsyms; if(arch_swapped == TRUE) swap_nlist(arch_symbols, arch_nsyms, host_byte_sex); arch_strings = arch->object->object_addr + arch->object->st->stroff; arch_strsize = arch->object->st->strsize; arch_extrelocs = (struct relocation_info *) (arch->object->object_addr + arch->object->dyst->extreloff); arch_nextrel = arch->object->dyst->nextrel; if(arch_swapped == TRUE) swap_relocation_info(arch_extrelocs, arch_nextrel, host_byte_sex); arch_indirect_symtab = (unsigned long *) (arch->object->object_addr + arch->object->dyst->indirectsymoff); arch_nindirectsyms = arch->object->dyst->nindirectsyms; if(arch_swapped == TRUE) swap_indirect_symbols(arch_indirect_symtab, arch_nindirectsyms, host_byte_sex); if(arch->object->mh->filetype == MH_DYLIB){ arch_tocs = (struct dylib_table_of_contents *) (arch->object->object_addr + arch->object->dyst->tocoff); arch_ntoc = arch->object->dyst->ntoc; arch_mods = (struct dylib_module *) (arch->object->object_addr + arch->object->dyst->modtaboff); arch_nmodtab = arch->object->dyst->nmodtab; arch_refs = (struct dylib_reference *) (arch->object->object_addr + arch->object->dyst->extrefsymoff); arch_nextrefsyms = arch->object->dyst->nextrefsyms; if(arch_swapped == TRUE){ swap_dylib_table_of_contents( arch_tocs, arch_ntoc, host_byte_sex); swap_dylib_module( arch_mods, arch_nmodtab, host_byte_sex); swap_dylib_reference( arch_refs, arch_nextrefsyms, host_byte_sex); } } else{ arch_tocs = NULL; arch_ntoc = 0;; arch_mods = NULL; arch_nmodtab = 0;; arch_refs = NULL; arch_nextrefsyms = 0;; } check_symbolic_info_tables( arch->file_name, arch->object->st, arch->object->dyst, arch_symbols, arch_nsyms, arch_strings, arch_strsize, arch_tocs, arch_ntoc, arch_mods, arch_nmodtab, arch_refs, arch_nextrefsyms); /* * Get all the symbolic info for the libraries the arch uses in the * correct byte sex. */ for(i = 0; i < nlibs; i++){ libs[i].st = NULL; libs[i].dyst = NULL; lc = libs[i].ofile->load_commands; for(j = 0; j < libs[i].ofile->mh->ncmds; j++){ if(lc->cmd == LC_SYMTAB){ if(libs[i].st != NULL){ error("malformed library: %s (more than one LC_SYMTAB " "load command) (for architecture %s)", libs[i].file_name, arch_name); exit(2); } libs[i].st = (struct symtab_command *)lc; } else if(lc->cmd == LC_DYSYMTAB){ if(libs[i].dyst != NULL){ error("malformed library: %s (more than one LC_DYSYMTAB" " load command) (for architecture %s)", libs[i].file_name, arch_name); exit(2); } libs[i].dyst = (struct dysymtab_command *)lc; } else if(lc->cmd == LC_ROUTINES){ if(libs[i].rc != NULL){ error("malformed library: %s (more than one LC_ROUTINES" " load command) (for architecture %s)", libs[i].file_name, arch_name); exit(2); } libs[i].rc = (struct routines_command *)lc; } lc = (struct load_command *)((char *)lc + lc->cmdsize); } if(libs[i].st == NULL){ error("malformed file: %s (no LC_SYMTAB load command) (for" " architecture %s)", libs[i].file_name, arch_name); exit(2); } if(libs[i].dyst == NULL){ error("malformed file: %s (no LC_DYSYMTAB load command) (for" " architecture %s)", libs[i].file_name, arch_name); exit(2); } libs[i].symbols = (struct nlist *)(libs[i].ofile->object_addr + libs[i].st->symoff); libs[i].nsyms = libs[i].st->nsyms; libs[i].strings = libs[i].ofile->object_addr + libs[i].st->stroff; libs[i].strsize = libs[i].st->strsize; libs[i].tocs = (struct dylib_table_of_contents *) (libs[i].ofile->object_addr + libs[i].dyst->tocoff); libs[i].ntoc = libs[i].dyst->ntoc; libs[i].mods = (struct dylib_module *) (libs[i].ofile->object_addr + libs[i].dyst->modtaboff); libs[i].nmodtab = libs[i].dyst->nmodtab; libs[i].refs = (struct dylib_reference *) (libs[i].ofile->object_addr + libs[i].dyst->extrefsymoff); libs[i].nextrefsyms = libs[i].dyst->nextrefsyms; if(arch_swapped == TRUE){ swap_nlist( libs[i].symbols, libs[i].nsyms, host_byte_sex); swap_dylib_table_of_contents( libs[i].tocs, libs[i].ntoc, host_byte_sex); swap_dylib_module( libs[i].mods, libs[i].nmodtab, host_byte_sex); swap_dylib_reference( libs[i].refs, libs[i].nextrefsyms, host_byte_sex); } check_symbolic_info_tables( libs[i].file_name, libs[i].st, libs[i].dyst, libs[i].symbols, libs[i].nsyms, libs[i].strings, libs[i].strsize, libs[i].tocs, libs[i].ntoc, libs[i].mods, libs[i].nmodtab, libs[i].refs, libs[i].nextrefsyms); libs[i].module_states = allocate(libs[i].nmodtab * sizeof(enum link_state)); memset(libs[i].module_states, '\0', libs[i].nmodtab * sizeof(enum link_state)); } } static void swap_arch_for_output( void) { swap_nlist(arch_symbols, arch_nsyms, arch->object->object_byte_sex); swap_relocation_info(arch_extrelocs, arch_nextrel, arch->object->object_byte_sex); swap_indirect_symbols(arch_indirect_symtab, arch_nindirectsyms, arch->object->object_byte_sex); swap_dylib_table_of_contents(arch_tocs, arch_ntoc, arch->object->object_byte_sex); swap_dylib_module(arch_mods, arch_nmodtab, arch->object->object_byte_sex); swap_dylib_reference(arch_refs, arch_nextrefsyms, arch->object->object_byte_sex); } /* * check_symbolic_info_tables() checks to see that the parts of the symbolic * info used to redo the prebinding is valid. */ static void check_symbolic_info_tables( char *file_name, struct symtab_command *st, struct dysymtab_command *dyst, struct nlist *symbols, unsigned long nsyms, char *strings, unsigned long strsize, struct dylib_table_of_contents *tocs, unsigned long ntoc, struct dylib_module *mods, unsigned long nmodtab, struct dylib_reference *refs, unsigned long nextrefsyms) { unsigned long i; /* check the symbol table's offsets into the string table */ for(i = 0; i < nsyms; i++){ if(symbols[i].n_un.n_strx > strsize){ error("mallformed file: %s (bad string table index (%ld) for " "symbol %lu) (for architecture %s)", file_name, symbols[i].n_un.n_strx, i, arch_name); exit(2); } if((symbols[i].n_type & N_TYPE) == N_INDR && symbols[i].n_value > strsize){ error("mallformed file: %s (bad string table index (%ld) for " "N_INDR symbol %lu) (for architecture %s)", file_name, symbols[i].n_value, i, arch_name); exit(2); } } /* check toc's symbol and module indexes */ for(i = 0; i < ntoc; i++){ if(tocs[i].symbol_index > nsyms){ error("mallformed file: %s (bad symbol table index (%ld) for " "table of contents entry %lu) (for architecture %s)", file_name, tocs[i].symbol_index, i, arch_name); exit(2); } if(tocs[i].module_index > nmodtab){ error("mallformed file: %s (bad module table index (%ld) for " "table of contents entry %lu) (for architecture %s)", file_name, tocs[i].module_index, i, arch_name); exit(2); } } /* check module table's string index for module names */ for(i = 0; i < nmodtab; i++){ if(mods[i].module_name > strsize){ error("mallformed file: %s (bad string table index (%ld) for " "module_name in module table entry %lu ) (for " "architecture %s)", file_name, mods[i].module_name, i, arch_name); exit(2); } if(mods[i].nextdefsym != 0 && (mods[i].iextdefsym < dyst->iextdefsym || mods[i].iextdefsym >= dyst->iextdefsym + dyst->nextdefsym)){ error("mallformed file: %s (bad external symbol table index for" " for module table entry %lu) (for architecture %s)", file_name, i, arch_name); exit(2); } if(mods[i].nextdefsym != 0 && mods[i].iextdefsym + mods[i].nextdefsym > dyst->iextdefsym + dyst->nextdefsym){ error("mallformed file: %s (bad number of external symbol table" " entries for module table entry %lu) (for architecture " "%s)", file_name, i, arch_name); exit(2); } } /* check refernce table's symbol indexes */ for(i = 0; i < nextrefsyms; i++){ if(refs[i].isym > nsyms){ error("mallformed file: %s (bad external symbol table index " "reference table entry %lu) (for architecture %s)", file_name, i, arch_name); exit(2); } } } /* * check_for_dylib_override_symbols() checks to make sure symbols in this arch * are not overriding symbols in dependent dylibs which the dependent library * also uses. This is to verify prebinding can be redone. */ static void check_for_dylib_override_symbols( void) { unsigned long i; for(i = arch->object->dyst->iextdefsym; i < arch->object->dyst->iextdefsym + arch->object->dyst->nextdefsym; i++){ check_dylibs_for_definition( arch->file_name, arch_strings + arch_symbols[i].n_un.n_strx); } } /* * check_dylibs_for_definition() checks to see if the symbol name is defined * in any of the dependent dynamic shared libraries. If it is a an error * message is printed and exit(2) is done to indicate the prebinding can't be * redone. */ static void check_dylibs_for_definition( char *file_name, char *symbol_name) { unsigned long i; struct dylib_table_of_contents *toc; for(i = 0; i < nlibs; i++){ bsearch_strings = libs[i].strings; bsearch_symbols = libs[i].symbols; toc = bsearch(symbol_name, libs[i].tocs, libs[i].ntoc, sizeof(struct dylib_table_of_contents), (int (*)(const void *, const void *))dylib_bsearch); if(toc != NULL){ /* * There is a module that defineds this symbol. If this * symbol is also referenced by the libraries then we * can't redo the prebindng. */ if(check_dylibs_for_reference(symbol_name) == TRUE){ error("prebinding can't be redone because of symbols " "overridded in dependent dynamic shared libraries (%s " "defined in: %s and in %s(%s)) (for architecture %s)", symbol_name, file_name, libs[i].file_name, libs[i].strings + libs[i].mods[toc->module_index].module_name, arch_name); exit(2); } } } } /* * check_dylibs_for_reference() checks the dependent dynamic shared libraries * to see if the specified merged symbol is referenced. If it is TRUE is * returned else FALSE is returned. */ static enum bool check_dylibs_for_reference( char *symbol_name) { unsigned long i, j, symbol_index; struct dylib_table_of_contents *toc; struct nlist *symbol; for(i = 0; i < nlibs; i++){ /* * See if this symbol appears at all (defined or undefined) * in this library. */ bsearch_strings = libs[i].strings; bsearch_symbols = libs[i].symbols; toc = bsearch(symbol_name, libs[i].tocs, libs[i].ntoc, sizeof(struct dylib_table_of_contents), (int (*)(const void *, const void *))dylib_bsearch); if(toc != NULL){ symbol_index = toc->symbol_index; } else{ symbol = bsearch(symbol_name, libs[i].symbols + libs[i].dyst->iundefsym, libs[i].dyst->nundefsym, sizeof(struct nlist), (int (*)(const void *,const void *))nlist_bsearch); if(symbol == NULL) continue; symbol_index = symbol - libs[i].symbols; } /* * The symbol appears in this library. Now see if it is * referenced by a module in the library. */ for(j = 0; j < libs[i].nextrefsyms; j++){ if(libs[i].refs[j].isym == symbol_index && (libs[i].refs[j].flags == REFERENCE_FLAG_UNDEFINED_NON_LAZY || libs[i].refs[j].flags == REFERENCE_FLAG_UNDEFINED_LAZY)) return(TRUE); } } return(FALSE); } /* * Function for bsearch() for finding a symbol name in a dylib table of * contents. */ static int dylib_bsearch( const char *symbol_name, const struct dylib_table_of_contents *toc) { return(strcmp(symbol_name, bsearch_strings + bsearch_symbols[toc->symbol_index].n_un.n_strx)); } /* * Function for bsearch() for finding a symbol name in the sorted list of * undefined symbols. */ static int nlist_bsearch( const char *symbol_name, const struct nlist *symbol) { return(strcmp(symbol_name, bsearch_strings + symbol->n_un.n_strx)); } /* * setup_initial_undefined_list() builds the initial list of undefined symbol * references based on the arch's undefined symbols. */ static void setup_initial_undefined_list( void) { unsigned long i; for(i = arch->object->dyst->iundefsym; i < arch->object->dyst->iundefsym + arch->object->dyst->nundefsym; i++){ add_to_undefined_list( arch_strings + arch_symbols[i].n_un.n_strx); } } /* * link_in_need_modules() causes any needed modules to be linked into the * program. */ static void link_in_need_modules( void) { struct symbol_list *undefined, *next_undefined; struct nlist *symbol; enum link_state *module_state; struct lib *lib; for(undefined = undefined_list.next; undefined != &undefined_list; /* no increment expression */){ /* * Look up the symbol, if is not found we can't redo the prebinding. * So leave it on the undefined list and if there are undefined * symbols on the list after all the undefined symbols have been * searched for a message will be printed and exit(2) will be done * to indicate this. */ lookup_symbol(undefined->name, &symbol, &module_state, &lib, NO_INDR_LOOP); if(symbol != NULL){ /* * The symbol was found so remove it from the undefined_list. * Then if the module that defined this symbol is unlinked * then link it in checking for multiply defined symbols. */ /* take this off the undefined list */ next_undefined = undefined->next; undefined->prev->next = undefined->next; undefined->next->prev = undefined->prev; undefined = next_undefined; if(*module_state == UNLINKED) link_library_module(module_state, lib); if(undefined == &undefined_list && undefined->next != &undefined_list) undefined = undefined->next; } else{ undefined = undefined->next; } } if(undefined_list.next != &undefined_list){ printf("%s: prebinding can't be redone for: %s (for architecture " "%s) because of undefined symbols:\n", progname, arch->file_name, arch_name); for(undefined = undefined_list.next; undefined != &undefined_list; undefined = undefined->next){ printf("%s\n", undefined->name); } exit(2); } } /* * link_library_module() links in the specified library module. It checks the * module for symbols that are already defined and reports multiply defined * errors. Then it adds it's undefined symbols to the undefined list. */ static void link_library_module( enum link_state *module_state, struct lib *lib) { unsigned long i, j, module_index; struct dylib_module *dylib_module; char *name; struct nlist *prev_symbol; enum link_state *prev_module_state; struct lib *prev_lib; struct nlist *ref_symbol; enum link_state *ref_module_state; struct lib *ref_lib; module_index = module_state - lib->module_states; dylib_module = lib->mods + module_index; /* * For each defined symbol check to see if it is not defined in a module * that is already linked (or being linked). */ for(i = dylib_module->iextdefsym; i < dylib_module->iextdefsym + dylib_module->nextdefsym; i++){ name = lib->strings + lib->symbols[i].n_un.n_strx; lookup_symbol(name, &prev_symbol, &prev_module_state, &prev_lib, NO_INDR_LOOP); if(prev_symbol != NULL && module_state != prev_module_state && *prev_module_state != UNLINKED){ printf("%s: prebinding can't be redone for: %s (for " "architecture %s) because of multiply defined " "symbol: %s\n", progname, arch->file_name, arch_name, name); if(prev_module_state == &arch_state) printf("%s definition of %s\n", arch->file_name, name); else printf("%s(%s) definition of %s\n", prev_lib->file_name, prev_lib->strings + prev_lib->mods[ prev_module_state - prev_lib->module_states]. module_name, name); if(module_state == &arch_state) printf("%s definition of %s\n", arch->file_name, name); else printf("%s(%s) definition of %s\n", lib->file_name, lib->strings + dylib_module->module_name, name); exit(2); } } /* * For each reference to an undefined symbol look it up to see if it is * defined in an already linked module. If it is not then add it to * the undefined list. */ for(i = dylib_module->irefsym; i < dylib_module->irefsym + dylib_module->nrefsym; i++){ if(lib->refs[i].flags == REFERENCE_FLAG_UNDEFINED_NON_LAZY || lib->refs[i].flags == REFERENCE_FLAG_UNDEFINED_LAZY){ name = lib->strings + lib->symbols[lib->refs[i].isym].n_un.n_strx; lookup_symbol(name, &ref_symbol, &ref_module_state, &ref_lib, NO_INDR_LOOP); if(ref_symbol != NULL){ if(*ref_module_state == UNLINKED) add_to_undefined_list(name); } else{ add_to_undefined_list(name); } } else{ /* * If this is a reference to a private extern make sure the * module that defineds it is linked and if not set cause it * to be linked. References to private externs in a library * only are resolved to symbols in the same library and modules * in a library that have private externs can't have any global * symbols (this is done by the static link editor). The reason * this is done at all is so that module is marked as linked. */ if(lib->refs[i].flags == REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY || lib->refs[i].flags == REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY){ for(j = 0; j < lib->nmodtab; j++){ if(lib->refs[i].isym >= lib->mods[j].ilocalsym && lib->refs[i].isym < lib->mods[j].ilocalsym + lib->mods[j].nlocalsym) break; } if(j < lib->nmodtab){ if(lib->module_states[j] == UNLINKED) lib->module_states[j] = LINKED; } } } } *module_state = LINKED; /* * If this library has a shared library initialization routine then * make sure this module is linked in. If not link it in. */ if(lib->rc != NULL && lib->module_states[lib->rc->init_module] == UNLINKED){ link_library_module(lib->module_states + lib->rc->init_module, lib); } } /* * add_to_undefined_list() adds an item to the list of undefined symbols. */ static void add_to_undefined_list( char *name) { struct symbol_list *undefined, *new; for(undefined = undefined_list.next; undefined != &undefined_list; undefined = undefined->next){ if(undefined->name == name) return; } /* get a new symbol list entry */ new = allocate(sizeof(struct symbol_list)); /* fill in the pointers for the undefined symbol */ new->name = name; /* put this at the end of the undefined list */ new->prev = undefined_list.prev; new->next = &undefined_list; undefined_list.prev->next = new; undefined_list.prev = new; } /* * lookup_symbol() is passed a name of a symbol. The name is looked up in the * current arch and the libs. If found symbol, module_state and lib is set * to indicate where the symbol is defined. */ static void lookup_symbol( char *name, struct nlist **symbol, enum link_state **module_state, struct lib **lib, struct indr_loop_list *indr_loop) { unsigned long i; struct dylib_table_of_contents *toc; struct nlist *s; struct indr_loop_list new_indr_loop, *loop; /* * First search the current arch for the named symbol as a defined * external symbol. If the current arch is a dylib look in the table * of contents else look in the sorted external symbols. */ if(arch->object->mh->filetype == MH_DYLIB){ bsearch_strings = arch_strings; bsearch_symbols = arch_symbols; toc = bsearch(name, arch_tocs, arch_ntoc, sizeof(struct dylib_table_of_contents), (int (*)(const void *, const void *))dylib_bsearch); if(toc != NULL){ *symbol = arch_symbols + toc->symbol_index; if(((*symbol)->n_type & N_TYPE) == N_INDR){ name = (*symbol)->n_value + arch_strings; goto indr; } *module_state = &arch_state; *lib = NULL; return; } } else{ bsearch_strings = arch_strings; s = bsearch(name, arch_symbols + arch->object->dyst->iextdefsym, arch->object->dyst->nextdefsym, sizeof(struct nlist), (int (*)(const void *,const void *))nlist_bsearch); if(s != NULL){ *symbol = s; if(((*symbol)->n_type & N_TYPE) == N_INDR){ name = (*symbol)->n_value + arch_strings; goto indr; } *module_state = &arch_state; *lib = NULL; return; } } /* * The symbol was not found in the current arch so look through the * dependent libs for the a definition of the named symbol. */ for(i = 0; i < nlibs; i++){ bsearch_strings = libs[i].strings; bsearch_symbols = libs[i].symbols; toc = bsearch(name, libs[i].tocs, libs[i].ntoc, sizeof(struct dylib_table_of_contents), (int (*)(const void *, const void *))dylib_bsearch); if(toc != NULL){ *symbol = libs[i].symbols + toc->symbol_index; if(((*symbol)->n_type & N_TYPE) == N_INDR){ name = (*symbol)->n_value + libs[i].strings; goto indr; } *module_state = libs[i].module_states + toc->module_index; *lib = libs + i; return; } } *symbol = NULL; *module_state = NULL; *lib = NULL; return; indr: if(indr_loop != NO_INDR_LOOP){ for(loop = indr_loop; loop != NULL; loop = loop->next){ if(loop->symbol == *symbol){ /* this is an indirect loop */ *symbol = NULL; *module_state = NULL; *lib = NULL; return; } } } new_indr_loop.symbol = *symbol; new_indr_loop.next = indr_loop; lookup_symbol(name, symbol, module_state, lib, &new_indr_loop); return; } /* * build_new_symbol_table() builds a new symbol table for the current arch * using the new values for prebound undefined symbols from the dependent * libraries. */ static void build_new_symbol_table( void) { unsigned long i, sym_info_size; char *symbol_name; struct nlist *new_symbols; struct nlist *symbol; enum link_state *module_state; struct lib *lib; /* the size of the symbol table will not change just the contents */ sym_info_size = arch_nextrel * sizeof(struct relocation_info) + arch->object->dyst->nlocrel * sizeof(struct relocation_info) + arch_nindirectsyms * sizeof(unsigned long *) + arch_ntoc * sizeof(struct dylib_table_of_contents) + arch_nmodtab * sizeof(struct dylib_module) + arch_nextrefsyms * sizeof(struct dylib_reference) + arch_nsyms * sizeof(struct nlist) + arch_strsize; arch->object->input_sym_info_size = sym_info_size; arch->object->output_sym_info_size = sym_info_size; arch->object->output_nsymbols = arch_nsyms; arch->object->output_strings_size = arch_strsize; arch->object->output_ilocalsym = arch->object->dyst->ilocalsym; arch->object->output_nlocalsym = arch->object->dyst->nlocalsym; arch->object->output_iextdefsym = arch->object->dyst->iextdefsym; arch->object->output_nextdefsym = arch->object->dyst->nextdefsym; arch->object->output_iundefsym = arch->object->dyst->iundefsym; arch->object->output_nundefsym = arch->object->dyst->nundefsym; arch->object->output_loc_relocs = (struct relocation_info *) (arch->object->object_addr + arch->object->dyst->locreloff); arch->object->output_ext_relocs = arch_extrelocs; arch->object->output_indirect_symtab = arch_indirect_symtab; arch->object->output_tocs = arch_tocs; arch->object->output_ntoc = arch_ntoc; arch->object->output_mods = arch_mods; arch->object->output_nmodtab = arch_nmodtab; arch->object->output_refs = arch_refs; arch->object->output_nextrefsyms = arch_nextrefsyms; /* * The new symbol table is just a copy of the old symbol table with * the n_value's of the prebound undefined symbols updated. */ new_symbols = allocate(arch_nsyms * sizeof(struct nlist)); memcpy(new_symbols, arch_symbols, arch_nsyms * sizeof(struct nlist)); for(i = arch->object->dyst->iundefsym; i < arch->object->dyst->iundefsym + arch->object->dyst->nundefsym; i++){ symbol_name = arch_strings + arch_symbols[i].n_un.n_strx; lookup_symbol(symbol_name, &symbol, &module_state, &lib, NO_INDR_LOOP); new_symbols[i].n_value = symbol->n_value; } arch->object->output_symbols = new_symbols; /* the strings don't change so just use the existing string table */ arch->object->output_strings = arch_strings; } /* * update_external_relocs() drives the updating of the items with external * relocation entries for the current arch. */ static void update_external_relocs( void) { unsigned long i; struct load_command *lc; struct segment_command *sg; /* figure out what this arch's seg1addr or segs_read_write_addr is */ arch_seg1addr = ULONG_MAX; arch_segs_read_write_addr = ULONG_MAX; if((arch->object->mh->flags & MH_SPLIT_SEGS) == MH_SPLIT_SEGS) arch_split_segs = TRUE; else arch_split_segs = FALSE; lc = arch->object->load_commands; for(i = 0; i < arch->object->mh->ncmds; i++){ if(lc->cmd == LC_SEGMENT){ sg = (struct segment_command *)lc; if(sg->vmaddr < arch_seg1addr) arch_seg1addr = sg->vmaddr; /* * Pickup the address of the first read-write segment for * MH_SPLIT_SEGS images. */ if((sg->initprot & VM_PROT_WRITE) == VM_PROT_WRITE && sg->vmaddr < arch_segs_read_write_addr) arch_segs_read_write_addr = sg->vmaddr; } lc = (struct load_command *)((char *)lc + lc->cmdsize); } switch(arch->object->mh->cputype){ case CPU_TYPE_MC680x0: update_generic_external_relocs(); break; case CPU_TYPE_I386: update_generic_external_relocs(); break; case CPU_TYPE_HPPA: update_hppa_external_relocs(); break; case CPU_TYPE_SPARC: update_sparc_external_relocs(); break; case CPU_TYPE_POWERPC: update_ppc_external_relocs(); break; default: error("can't redo prebinding for: %s (for architecture %s) because " "of unknown cputype", arch->file_name, arch_name); } } /* * update_generic_external_relocs() updates of the items with external * relocation entries for the architectures that use generic relocation entries * (the i386 and m68k architectures). It only deals with external relocation * entries that are using prebound undefined symbols. */ static void update_generic_external_relocs( void) { unsigned long i, value; char *name, *p; struct nlist *defined_symbol, *arch_symbol; enum link_state *module_state; struct lib *lib; for(i = 0; i < arch_nextrel; i++){ /* check the r_symbolnum field */ if(arch_extrelocs[i].r_symbolnum > arch_nsyms){ error("mallformed file: %s (bad symbol table index for " "external relocation entry %lu) (for architecture %s)", arch->file_name, i, arch_name); exit(2); } /* * If the symbol this relocation entry is refering to is not a * prebound undefined symbol then skip it. */ arch_symbol = arch_symbols + arch_extrelocs[i].r_symbolnum; if((arch_symbol->n_type & N_TYPE) != N_PBUD) continue; /* * Look up the symbol being referenced by this relocation entry to * get the defined symbol's value to be used. */ name = arch_strings + arch_symbol->n_un.n_strx; lookup_symbol(name, &defined_symbol, &module_state, &lib, NO_INDR_LOOP); p = contents_pointer_for_vmaddr(arch_extrelocs[i].r_address + (arch_split_segs == TRUE ? arch_segs_read_write_addr : arch_seg1addr), 1 << arch_extrelocs[i].r_length); if(p == NULL){ error("mallformed file: %s (for architecture %s) (bad r_address" " field for external relocation entry %lu)", arch->file_name, arch_name, i); exit(2); } switch(arch_extrelocs[i].r_length){ case 0: /* byte */ value = get_arch_byte(p); value = (value - arch_symbol->n_value) + defined_symbol->n_value; if( (value & 0xffffff00) && ((value & 0xffffff80) != 0xffffff80)){ error("prebinding can't be redone for: %s (for architecture" " %s) because of relocation overflow (external " "relocation for symbol %s does not fit in 1 byte)", arch->file_name, arch_name, name); exit(2); } set_arch_byte(p, value); break; case 1: /* word (2 byte) */ value = get_arch_short(p); value = (value - arch_symbol->n_value) + defined_symbol->n_value; if( (value & 0xffff0000) && ((value & 0xffff8000) != 0xffff8000)){ error("prebinding can't be redone for: %s (for architecture" " %s) because of relocation overflow (external " "relocation for symbol %s does not fit in 2 bytes)", arch->file_name, arch_name, name); exit(2); } set_arch_short(p, value); break; case 2: /* long (4 byte) */ value = get_arch_long(p); value = (value - arch_symbol->n_value) + defined_symbol->n_value; set_arch_long(p, value); break; default: error("mallformed file: %s (external relocation entry " "%lu has bad r_length) (for architecture %s)", arch->file_name, i, arch_name); exit(2); } } } /* * update_hppa_external_relocs() updates of the items with external relocation * entries for the hppa architecture. It only deals with external relocation * entries that are using prebound undefined symbols. */ static void update_hppa_external_relocs( void) { unsigned long i, value; char *name, *p; struct nlist *defined_symbol, *arch_symbol; enum link_state *module_state; struct lib *lib; unsigned long instruction, immediate; unsigned long other_half; unsigned long hi21, lo14; unsigned long w, w1, w2; for(i = 0; i < arch_nextrel; i++){ /* check the r_symbolnum field */ if(arch_extrelocs[i].r_symbolnum > arch_nsyms){ error("mallformed file: %s (bad symbol table index for " "external relocation entry %lu) (for architecture %s)", arch->file_name, i, arch_name); exit(2); } /* check to see if it needs a pair and has a correct one */ if(arch_extrelocs[i].r_type == HPPA_RELOC_HI21 || arch_extrelocs[i].r_type == HPPA_RELOC_LO14 || arch_extrelocs[i].r_type == HPPA_RELOC_BR17){ if(i + 1 == arch_nextrel){ error("mallformed file: %s (missing pair external " "relocation entry for entry %lu) (for architecture " "%s)", arch->file_name, i, arch_name); exit(2); } if(arch_extrelocs[i + 1].r_type != HPPA_RELOC_PAIR){ error("mallformed file: %s (pair external relocation entry " "for entry %lu is not of r_type HPPA_RELOC_PAIR) (for" " architecture %s)", arch->file_name, i, arch_name); exit(2); } } /* * If the symbol this relocation entry is refering to is not a * prebound undefined symbol then skip it. */ arch_symbol = arch_symbols + arch_extrelocs[i].r_symbolnum; if((arch_symbol->n_type & N_TYPE) != N_PBUD) goto next; /* * Look up the symbol being referenced by this relocation entry to * get the defined symbol's value to be used. */ name = arch_strings + arch_symbol->n_un.n_strx; lookup_symbol(name, &defined_symbol, &module_state, &lib, NO_INDR_LOOP); p = contents_pointer_for_vmaddr(arch_extrelocs[i].r_address + (arch_split_segs == TRUE ? arch_segs_read_write_addr : arch_seg1addr), 1 << arch_extrelocs[i].r_length); if(p == NULL){ error("mallformed file: %s (for architecture %s) (bad r_address" " field for external relocation entry %lu)", arch->file_name, arch_name, i); exit(2); } if(arch_extrelocs[i].r_type == HPPA_RELOC_VANILLA){ switch(arch_extrelocs[i].r_length){ case 0: /* byte */ value = get_arch_byte(p); value = (value - arch_symbol->n_value) + defined_symbol->n_value; if( (value & 0xffffff00) && ((value & 0xffffff80) != 0xffffff80)){ error("prebinding can't be redone for: %s (for " "architecture %s) because of relocation overflow " "(external relocation for symbol %s does not fit " "in 1 byte)", arch->file_name, arch_name, name); exit(2); } set_arch_byte(p, value); break; case 1: /* word (2 byte) */ value = get_arch_short(p); value = (value - arch_symbol->n_value) + defined_symbol->n_value; if( (value & 0xffff0000) && ((value & 0xffff8000) != 0xffff8000)){ error("prebinding can't be redone for: %s (for " "architecture %s) because of relocation overflow " "(external relocation for symbol %s does not fit " "in 2 bytes)", arch->file_name, arch_name, name); exit(2); } set_arch_short(p, value); break; case 2: /* long (4 byte) */ value = get_arch_long(p); value = (value - arch_symbol->n_value) + defined_symbol->n_value; set_arch_long(p, value); break; default: error("mallformed file: %s (external relocation entry " "%lu has bad r_length) (for architecture %s)", arch->file_name, i, arch_name); exit(2); } } /* * Do hppa specific relocation based on the r_type. */ else{ instruction = get_arch_long(p); switch(arch_extrelocs[i].r_type){ case HPPA_RELOC_HI21: other_half = arch_extrelocs[i + 1].r_address; immediate = sign_ext(other_half, 14) + (assemble_21(instruction & 0x1fffff) << 11); calc_hppa_HILO(-arch_symbol->n_value + defined_symbol->n_value + immediate, 0, &hi21, &lo14); instruction = (instruction & 0xffe00000) | dis_assemble_21(hi21 >> 11); arch_extrelocs[i + 1].r_address = lo14 & 0x3fff; break; case HPPA_RELOC_LO14: other_half = arch_extrelocs[i + 1].r_address; immediate = low_sign_ext(instruction & 0x3fff, 14) + (other_half << 11); calc_hppa_HILO(-arch_symbol->n_value + defined_symbol->n_value + immediate, 0, &hi21, &lo14); lo14 = low_sign_unext(lo14, 14); instruction = (instruction & 0xffffc000) | (lo14 & 0x3fff); arch_extrelocs[i + 1].r_address = hi21 >> 11; break; case HPPA_RELOC_BR17: other_half = arch_extrelocs[i + 1].r_address; immediate = assemble_17((instruction & 0x1f0000) >> 16, (instruction & 0x1ffc) >> 2, instruction & 1); immediate = (sign_ext(immediate, 17) << 2) + (other_half << 11); calc_hppa_HILO(-arch_symbol->n_value + defined_symbol->n_value + immediate, 0, &hi21, &lo14); lo14 >>= 2; dis_assemble_17(lo14, &w1, &w2, &w); instruction = (instruction & 0xffe0e002) | (w1 << 16) | (w2 << 2) | w; arch_extrelocs[i + 1].r_address = hi21 >> 11; break; case HPPA_RELOC_BL17: immediate = assemble_17((instruction & 0x1f0000) >> 16, (instruction & 0x1ffc) >> 2, instruction & 1); if((immediate & 0x10000) != 0) immediate |= 0xfffe0000; immediate <<= 2; immediate -= arch_symbol->n_value; immediate += defined_symbol->n_value; if(U_ABS(immediate) > 0x3ffff){ error("prebinding can't be redone for: %s (for " "architecture %s) because of relocation overflow " "(external relocation for symbol %s displacement " "too large to fit)", arch->file_name, arch_name, name); exit(2); } immediate >>= 2; dis_assemble_17(immediate, &w1, &w2, &w); instruction = (instruction & 0xffe0e002) | (w1 << 16) | (w2 << 2) | w; break; default: error("mallformed file: %s (external relocation entry " "%lu has unknown r_type) (for architecture %s)", arch->file_name, i, arch_name); exit(2); } set_arch_long(p, instruction); } next: /* * If the relocation entry had a pair step over it. */ if(arch_extrelocs[i].r_type == HPPA_RELOC_HI21 || arch_extrelocs[i].r_type == HPPA_RELOC_LO14 || arch_extrelocs[i].r_type == HPPA_RELOC_BR17) i++; } } /* * update_sparc_external_relocs() updates of the items with external relocation * entries for the sparc architecture. It only deals with external relocation * entries that are using prebound undefined symbols. */ static void update_sparc_external_relocs( void) { unsigned long i, value; char *name, *p; struct nlist *defined_symbol, *arch_symbol; enum link_state *module_state; struct lib *lib; unsigned long instruction, immediate; unsigned long other_half; for(i = 0; i < arch_nextrel; i++){ /* check the r_symbolnum field */ if(arch_extrelocs[i].r_symbolnum > arch_nsyms){ error("mallformed file: %s (bad symbol table index for " "external relocation entry %lu) (for architecture %s)", arch->file_name, i, arch_name); exit(2); } /* check to see if it needs a pair and has a correct one */ if(arch_extrelocs[i].r_type == SPARC_RELOC_LO10 || arch_extrelocs[i].r_type == SPARC_RELOC_HI22){ if(i + 1 == arch_nextrel){ error("mallformed file: %s (missing pair external " "relocation entry for entry %lu) (for architecture " "%s)", arch->file_name, i, arch_name); exit(2); } if(arch_extrelocs[i + 1].r_type != SPARC_RELOC_PAIR){ error("mallformed file: %s (pair external relocation entry " "for entry %lu is not of r_type SPARC_RELOC_PAIR) " "(for architecture %s)", arch->file_name, i, arch_name); exit(2); } } /* * If the symbol this relocation entry is refering to is not a * prebound undefined symbol then skip it. */ arch_symbol = arch_symbols + arch_extrelocs[i].r_symbolnum; if((arch_symbol->n_type & N_TYPE) != N_PBUD) goto next; /* * Look up the symbol being referenced by this relocation entry to * get the defined symbol's value to be used. */ name = arch_strings + arch_symbol->n_un.n_strx; lookup_symbol(name, &defined_symbol, &module_state, &lib, NO_INDR_LOOP); p = contents_pointer_for_vmaddr(arch_extrelocs[i].r_address + (arch_split_segs == TRUE ? arch_segs_read_write_addr : arch_seg1addr), 1 << arch_extrelocs[i].r_length); if(p == NULL){ error("mallformed file: %s (for architecture %s) (bad r_address" " field for external relocation entry %lu)", arch->file_name, arch_name, i); exit(2); } if(arch_extrelocs[i].r_type == SPARC_RELOC_VANILLA){ switch(arch_extrelocs[i].r_length){ case 0: /* byte */ value = get_arch_byte(p); value = (value - arch_symbol->n_value) + defined_symbol->n_value; if( (value & 0xffffff00) && ((value & 0xffffff80) != 0xffffff80)){ error("prebinding can't be redone for: %s (for " "architecture %s) because of relocation overflow " "(external relocation for symbol %s does not fit " "in 1 byte)", arch->file_name, arch_name, name); exit(2); } set_arch_byte(p, value); break; case 1: /* word (2 byte) */ value = get_arch_short(p); value = (value - arch_symbol->n_value) + defined_symbol->n_value; if( (value & 0xffff0000) && ((value & 0xffff8000) != 0xffff8000)){ error("prebinding can't be redone for: %s (for " "architecture %s) because of relocation overflow " "(external relocation for symbol %s does not fit " "in 2 bytes)", arch->file_name, arch_name, name); exit(2); } set_arch_short(p, value); break; case 2: /* long (4 byte) */ value = get_arch_long(p); value = (value - arch_symbol->n_value) + defined_symbol->n_value; set_arch_long(p, value); break; default: error("mallformed file: %s (external relocation entry " "%lu has bad r_length) (for architecture %s)", arch->file_name, i, arch_name); exit(2); } } /* * Do SPARC specific relocation based on the r_type. */ else{ instruction = get_arch_long(p); switch(arch_extrelocs[i].r_type){ case SPARC_RELOC_HI22: other_half = (arch_extrelocs[i + 1].r_address) & 0x3ff; immediate = ((instruction & 0x3fffff) << 10) | other_half; immediate -= arch_symbol->n_value; immediate += defined_symbol->n_value; instruction = (instruction & 0xffc00000) | ((immediate >> 10) & 0x3fffff); arch_extrelocs[i + 1].r_address = immediate & 0x3ff; break; case SPARC_RELOC_LO10: other_half = ((arch_extrelocs[i + 1].r_address) >> 10) & 0x3fffff; immediate = (instruction & 0x3ff) | (other_half << 10); immediate -= arch_symbol->n_value; immediate += defined_symbol->n_value; instruction = (instruction & 0xfffffc00) | (immediate & 0x3ff); arch_extrelocs[i + 1].r_address = (immediate >> 10) & 0x3fffff; break; case SPARC_RELOC_WDISP22: immediate = (instruction & 0x3fffff); if ((immediate & 0x200000) != 0) immediate |= 0xffc00000; immediate <<= 2; immediate -= arch_symbol->n_value; immediate += defined_symbol->n_value; if ((immediate & 0xff800000) != 0xff800000 && (immediate & 0xff800000) != 0x00) { error("prebinding can't be redone for: %s (for " "architecture %s) because of relocation overflow " "(external relocation for symbol %s displacement " "too large to fit)", arch->file_name, arch_name, name); exit(2); } immediate >>= 2; instruction = (instruction & 0xffc00000) | (immediate & 0x3fffff); break; case SPARC_RELOC_WDISP30: immediate = (instruction & 0x3fffffff); immediate <<= 2; immediate -= arch_symbol->n_value; immediate += defined_symbol->n_value; immediate >>= 2; instruction = (instruction & 0xc0000000) | (immediate & 0x3fffffff); break; default: error("mallformed file: %s (external relocation entry " "%lu has unknown r_type) (for architecture %s)", arch->file_name, i, arch_name); exit(2); } set_arch_long(p, instruction); } next: /* * If the relocation entry had a pair step over it. */ if(arch_extrelocs[i].r_type == SPARC_RELOC_LO10 || arch_extrelocs[i].r_type == SPARC_RELOC_HI22) i++; } } /* * update_ppc_external_relocs() updates of the items with external relocation * entries for the ppc architecture. It only deals with external relocation * entries that are using prebound undefined symbols. */ static void update_ppc_external_relocs( void) { unsigned long i, value; char *name, *p; struct nlist *defined_symbol, *arch_symbol; enum link_state *module_state; struct lib *lib; unsigned long instruction, immediate; unsigned long other_half, br14_disp_sign; for(i = 0; i < arch_nextrel; i++){ /* check the r_symbolnum field */ if(arch_extrelocs[i].r_symbolnum > arch_nsyms){ error("mallformed file: %s (bad symbol table index for " "external relocation entry %lu) (for architecture %s)", arch->file_name, i, arch_name); exit(2); } /* check to see if it needs a pair and has a correct one */ if(arch_extrelocs[i].r_type == PPC_RELOC_HI16 || arch_extrelocs[i].r_type == PPC_RELOC_LO16 || arch_extrelocs[i].r_type == PPC_RELOC_HA16 || arch_extrelocs[i].r_type == PPC_RELOC_LO14){ if(i + 1 == arch_nextrel){ error("mallformed file: %s (missing pair external " "relocation entry for entry %lu) (for architecture " "%s)", arch->file_name, i, arch_name); exit(2); } if(arch_extrelocs[i + 1].r_type != PPC_RELOC_PAIR){ error("mallformed file: %s (pair external relocation entry " "for entry %lu is not of r_type PPC_RELOC_PAIR) " "(for architecture %s)", arch->file_name, i, arch_name); exit(2); } } /* * If the symbol this relocation entry is refering to is not a * prebound undefined symbol then skip it. */ arch_symbol = arch_symbols + arch_extrelocs[i].r_symbolnum; if((arch_symbol->n_type & N_TYPE) != N_PBUD) goto next; /* * Look up the symbol being referenced by this relocation entry to * get the defined symbol's value to be used. */ name = arch_strings + arch_symbol->n_un.n_strx; lookup_symbol(name, &defined_symbol, &module_state, &lib, NO_INDR_LOOP); p = contents_pointer_for_vmaddr(arch_extrelocs[i].r_address + (arch_split_segs == TRUE ? arch_segs_read_write_addr : arch_seg1addr), 1 << arch_extrelocs[i].r_length); if(p == NULL){ error("mallformed file: %s (for architecture %s) (bad r_address" " field for external relocation entry %lu)", arch->file_name, arch_name, i); exit(2); } if(arch_extrelocs[i].r_type == PPC_RELOC_VANILLA){ switch(arch_extrelocs[i].r_length){ case 0: /* byte */ value = get_arch_byte(p); value = (value - arch_symbol->n_value) + defined_symbol->n_value; if( (value & 0xffffff00) && ((value & 0xffffff80) != 0xffffff80)){ error("prebinding can't be redone for: %s (for " "architecture %s) because of relocation overflow " "(external relocation for symbol %s does not fit " "in 1 byte)", arch->file_name, arch_name, name); exit(2); } set_arch_byte(p, value); break; case 1: /* word (2 byte) */ value = get_arch_short(p); value = (value - arch_symbol->n_value) + defined_symbol->n_value; if( (value & 0xffff0000) && ((value & 0xffff8000) != 0xffff8000)){ error("prebinding can't be redone for: %s (for " "architecture %s) because of relocation overflow " "(external relocation for symbol %s does not fit " "in 2 bytes)", arch->file_name, arch_name, name); exit(2); } set_arch_short(p, value); break; case 2: /* long (4 byte) */ value = get_arch_long(p); value = (value - arch_symbol->n_value) + defined_symbol->n_value; set_arch_long(p, value); break; default: error("mallformed file: %s (external relocation entry " "%lu has bad r_length) (for architecture %s)", arch->file_name, i, arch_name); exit(2); } } /* * Do PPC specific relocation based on the r_type. */ else{ instruction = get_arch_long(p); switch(arch_extrelocs[i].r_type){ case PPC_RELOC_HI16: other_half = (arch_extrelocs[i + 1].r_address) & 0xffff; immediate = ((instruction & 0xffff) << 16) | other_half; immediate -= arch_symbol->n_value; immediate += defined_symbol->n_value; instruction = (instruction & 0xffff0000) | ((immediate >> 16) & 0xffff); arch_extrelocs[i + 1].r_address = immediate & 0xffff; break; case PPC_RELOC_LO16: other_half = (arch_extrelocs[i + 1].r_address) & 0xffff; immediate = (other_half << 16) | (instruction & 0xffff); immediate -= arch_symbol->n_value; immediate += defined_symbol->n_value; instruction = (instruction & 0xffff0000) | (immediate & 0xffff); arch_extrelocs[i + 1].r_address = (immediate >> 16) & 0xffff; break; case PPC_RELOC_HA16: other_half = (arch_extrelocs[i + 1].r_address) & 0xffff; immediate = ((instruction & 0xffff) << 16) | other_half; immediate -= arch_symbol->n_value; immediate += defined_symbol->n_value; if((immediate & 0x00008000) != 0) instruction = (instruction & 0xffff0000) | (((immediate + 0x00008000) >> 16) & 0xffff); else instruction = (instruction & 0xffff0000) | ((immediate >> 16) & 0xffff); arch_extrelocs[i + 1].r_address = immediate & 0xffff; break; case PPC_RELOC_LO14: other_half = (arch_extrelocs[i + 1].r_address) & 0xffff; immediate = (other_half << 16) | (instruction & 0xfffc); immediate -= arch_symbol->n_value; immediate += defined_symbol->n_value; if((immediate & 0x3) != 0){ error("prebinding can't be redone for: %s (for " "architecture %s) because of relocated value " "not a multiple of 4 bytes", arch->file_name, arch_name); exit(2); } instruction = (instruction & 0xffff0003) | (immediate & 0xfffc); arch_extrelocs[i + 1].r_address = (immediate >> 16) & 0xffff; break; case PPC_RELOC_BR14: br14_disp_sign = (instruction & 0x8000); immediate = instruction & 0xfffc; if((immediate & 0x8000) != 0) immediate |= 0xffff0000; immediate -= arch_symbol->n_value; immediate += defined_symbol->n_value; if((immediate & 0x3) != 0){ error("prebinding can't be redone for: %s (for " "architecture %s) because of relocated value " "not a multiple of 4 bytes", arch->file_name, arch_name); exit(2); } if((immediate & 0xfffe0000) != 0xfffe0000 && (immediate & 0xfffe0000) != 0x00000000){ error("prebinding can't be redone for: %s (for " "architecture %s) because of relocation overflow " "(external relocation for symbol %s displacement " "too large to fit)", arch->file_name, arch_name, name); exit(2); } instruction = (instruction & 0xffff0003) | (immediate & 0xfffc); /* * If this is a branch conditional B-form where * the branch condition is not branch always and * the sign of the displacement is different * after relocation then flip the Y-bit to * preserve the sense of the branch prediction. */ if((instruction & 0xfc000000) == 0x40000000 && (instruction & 0x03e00000) != 0x02800000 && (instruction & 0x00008000) != br14_disp_sign) instruction ^= (1 << 21); break; case PPC_RELOC_BR24: immediate = instruction & 0x03fffffc; if((immediate & 0x02000000) != 0) immediate |= 0xfc000000; immediate -= arch_symbol->n_value; immediate += defined_symbol->n_value; if((immediate & 0x3) != 0){ error("prebinding can't be redone for: %s (for " "architecture %s) because of relocated value " "not a multiple of 4 bytes", arch->file_name, arch_name); exit(2); } if((immediate & 0xfe000000) != 0xfe000000 && (immediate & 0xfe000000) != 0x00000000){ error("prebinding can't be redone for: %s (for " "architecture %s) because of relocation overflow " "(external relocation for symbol %s displacement " "too large to fit)", arch->file_name, arch_name, name); exit(2); exit(2); } instruction = (instruction & 0xfc000003) | (immediate & 0x03fffffc); break; default: error("mallformed file: %s (external relocation entry " "%lu has unknown r_type) (for architecture %s)", arch->file_name, i, arch_name); exit(2); } set_arch_long(p, instruction); } next: /* * If the relocation entry had a pair step over it. */ if(arch_extrelocs[i].r_type == PPC_RELOC_HI16 || arch_extrelocs[i].r_type == PPC_RELOC_LO16 || arch_extrelocs[i].r_type == PPC_RELOC_HA16 || arch_extrelocs[i].r_type == PPC_RELOC_LO14) i++; } } /* * contents_pointer_for_vmaddr() returns a pointer in memory for the vmaddr * of the current arch. If the vmaddr is out of range return NULL. */ static char * contents_pointer_for_vmaddr( unsigned long vmaddr, unsigned long size) { unsigned long i, offset; struct load_command *lc; struct segment_command *sg; lc = arch->object->load_commands; for(i = 0; i < arch->object->mh->ncmds; i++){ if(lc->cmd == LC_SEGMENT){ sg = (struct segment_command *)lc; if(vmaddr >= sg->vmaddr && vmaddr + size < sg->vmaddr + sg->vmsize){ offset = vmaddr - sg->vmaddr; if(offset + size <= sg->filesize) return(arch->object->object_addr + sg->fileoff + offset); return(NULL); } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } return(NULL); } /* * update_symbol_pointers() updates the symbol pointers using the new and old * symbol table. */ static void update_symbol_pointers( void) { unsigned long i, j, k, section_type; struct load_command *lc; struct segment_command *sg; struct section *s; struct nlist *arch_symbol, *defined_symbol; char *name, *p; enum link_state *module_state; struct lib *lib; /* * For each symbol pointer section update the symbol pointers using * prebound undefined symbols to their new values. */ lc = arch->object->load_commands; for(i = 0; i < arch->object->mh->ncmds; i++){ if(lc->cmd == LC_SEGMENT){ sg = (struct segment_command *)lc; s = (struct section *) ((char *)sg + sizeof(struct segment_command)); for(j = 0 ; j < sg->nsects ; j++){ section_type = s->flags & SECTION_TYPE; if(section_type == S_NON_LAZY_SYMBOL_POINTERS || section_type == S_LAZY_SYMBOL_POINTERS){ if(s->reserved1 + s->size / sizeof(unsigned long) > arch_nindirectsyms){ error("mallformed file: %s (for architecture %s) " "(indirect symbol table entries for section " "(%.16s,%.16s) extends past the end of the " "indirect symbol table)", arch->file_name, arch_name, s->segname, s->sectname); exit(2); } for(k = 0; k < s->size / sizeof(unsigned long); k++){ /* * If this indirect symbol table entry is for a * non-lazy symbol pointer section for a defined * symbol which strip(1) has removed skip it. */ if(section_type == S_NON_LAZY_SYMBOL_POINTERS && (arch_indirect_symtab[s->reserved1 + k] & INDIRECT_SYMBOL_LOCAL) == INDIRECT_SYMBOL_LOCAL) continue; /* check symbol index of indirect symbol table */ if(arch_indirect_symtab[s->reserved1 + k] > arch_nsyms){ error("mallformed file: %s (for architecture " "%s) (bad indirect symbol table entry %lu)", arch->file_name, arch_name, i); exit(2); } /* * If the symbol this indirect symbol table entry is * refering to is not a prebound undefined symbol * then skip it. */ arch_symbol = arch_symbols + arch_indirect_symtab[s->reserved1 + k]; if((arch_symbol->n_type & N_TYPE) != N_PBUD) continue; /* * Look up the symbol being referenced by this * indirect symbol table entry to get the defined * symbol's value to be used. */ name = arch_strings + arch_symbol->n_un.n_strx; lookup_symbol(name, &defined_symbol, &module_state, &lib, NO_INDR_LOOP); p = contents_pointer_for_vmaddr( s->addr + (k * sizeof(long)), sizeof(long)); if(p == NULL){ error("mallformed file: %s (for architecture " "%s) (bad indirect section (%.16s,%.16s))", arch->file_name, arch_name, s->segname, s->sectname); exit(2); } set_arch_long(p, defined_symbol->n_value); } } s++; } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } } /* * update_load_commands() updates the time stamps in the LC_LOAD_DYLIB commands * and updates (and adds) the LC_PREBOUND_DYLIB commands if this is an * excutable. */ static void update_load_commands( void) { unsigned long i, j, k, nmodules, size, sizeofcmds, ncmds, low_fileoff; struct load_command *lc1, *lc2, *new_load_commands; struct dylib_command *dl_load, *dl_id; struct prebound_dylib_command *pbdylib1, *pbdylib2; struct segment_command *sg; struct section *s; char *dylib_name, *linked_modules; /* * First copy the time stamps for the dependent libraries from the * library's ID commands to the arch's load_command. Also size the * non LC_PREBOUND_DYLIB commands. */ ncmds = 0; sizeofcmds = 0; lc1 = arch->object->load_commands; for(i = 0; i < arch->object->mh->ncmds; i++){ if(lc1->cmd == LC_LOAD_DYLIB){ dl_load = (struct dylib_command *)lc1; dylib_name = (char *)dl_load + dl_load->dylib.name.offset; for(j = 0; j < nlibs; j++){ if(strcmp(libs[j].dylib_name, dylib_name) == 0){ lc2 = libs[j].ofile->load_commands; for(k = 0; k < libs[j].ofile->mh->ncmds; k++){ if(lc2->cmd == LC_ID_DYLIB){ dl_id = (struct dylib_command *)lc2; dl_load->dylib.timestamp = dl_id->dylib.timestamp; break; } lc2 = (struct load_command *) ((char *)lc2 + lc2->cmdsize); } break; } } } if(lc1->cmd != LC_PREBOUND_DYLIB){ ncmds += 1; sizeofcmds += lc1->cmdsize; } lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize); } /* * Only executables have LC_PREBOUND_DYLIB commands so if this is not * an executable (a library) then we are done here. */ if(arch->object->mh->filetype != MH_EXECUTE) return; /* * For each library the executable uses determine the size we need for * the LC_PREBOUND_DYLIB load command for it. If their is an exising * LC_PREBOUND_DYLIB command use it if there is enough space in the * command for the current number of modules. If not calculate the * size ld(1) would use for it. */ for(i = 0; i < nlibs; i++){ lc1 = arch->object->load_commands; for(j = 0; j < arch->object->mh->ncmds; j++){ if(lc1->cmd == LC_PREBOUND_DYLIB){ pbdylib1 = (struct prebound_dylib_command *)lc1; dylib_name = (char *)pbdylib1 + pbdylib1->name.offset; if(strcmp(libs[i].dylib_name, dylib_name) == 0){ libs[i].LC_PREBOUND_DYLIB_found = TRUE; if(libs[i].nmodtab <= pbdylib1->nmodules){ libs[i].LC_PREBOUND_DYLIB_size = pbdylib1->cmdsize; } else{ /* * Figure out the size left in the command for the * the linked_modules bit vector. When this is first * created by ld(1) extra space is left so this * this program can grow the vector if the library * changes. */ size = pbdylib1->cmdsize - (sizeof(struct prebound_dylib_command) + round(strlen(dylib_name) + 1, sizeof(long))); /* * Now see if the size left has enought space to fit * the linked_modules bit vector for the number of * modules this library currently has. */ if((libs[i].nmodtab + 7)/8 <= size){ libs[i].LC_PREBOUND_DYLIB_size = pbdylib1->cmdsize; } else{ /* * The existing space in not enough so calculate * the new size as ld(1) would. 125% of the * modules with a minimum size of 64 modules. */ nmodules = libs[i].nmodtab + (libs[i].nmodtab >> 2); if(nmodules < 64) nmodules = 64; size = sizeof(struct prebound_dylib_command) + round(strlen(dylib_name)+1, sizeof(long))+ round(nmodules / 8, sizeof(long)); libs[i].LC_PREBOUND_DYLIB_size = size; } } ncmds += 1; sizeofcmds += libs[i].LC_PREBOUND_DYLIB_size; break; } } lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize); } } /* * Make a pass through the libraries and pick up any of them that did * not appear in the load commands and then size their LC_PREBOUND_DYLIB * command. */ for(i = 0; i < nlibs; i++){ if(libs[i].LC_PREBOUND_DYLIB_found == FALSE){ /* * Calculate the size as ld(1) would. 125% of the * modules with a minimum size of 64 modules. */ nmodules = libs[i].nmodtab + (libs[i].nmodtab >> 2); if(nmodules < 64) nmodules = 64; size = sizeof(struct prebound_dylib_command) + round(strlen(libs[i].dylib_name) + 1, sizeof(long))+ round(nmodules / 8, sizeof(long)); libs[i].LC_PREBOUND_DYLIB_size = size; sizeofcmds += libs[i].LC_PREBOUND_DYLIB_size; ncmds++; } } /* * If the size of the load commands that includes the updated * LC_PREBOUND_DYLIB commands is larger than the existing load commands * then see if they can be fitted in before the contents of the first * section (or segment in the case of a LINKEDIT segment only file). */ if(sizeofcmds > arch->object->mh->sizeofcmds){ low_fileoff = ULONG_MAX; lc1 = arch->object->load_commands; for(i = 0; i < arch->object->mh->ncmds; i++){ if(lc1->cmd == 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->offset < low_fileoff) low_fileoff = s->offset; } } else{ if(sg->filesize != 0 && sg->fileoff < low_fileoff) low_fileoff = sg->fileoff; } } lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize); } if(sizeofcmds + sizeof(struct mach_header) > low_fileoff){ error("prebinding can't be redone for: %s (for architecture" " %s) because larger updated load commands do not fit " "(the program must be relinked)", arch->file_name, arch_name); exit(2); } } /* * Allocate space for the new load commands as zero it out so any holes * will be zero bytes. */ new_load_commands = allocate(sizeofcmds); memset(new_load_commands, '\0', sizeofcmds); /* * Fill in the new load commands by copying in the non-LC_PREBOUND_DYLIB * commands and updating the LC_PREBOUND_DYLIB commands. */ lc1 = arch->object->load_commands; lc2 = new_load_commands; for(i = 0; i < arch->object->mh->ncmds; i++){ if(lc1->cmd == LC_PREBOUND_DYLIB){ pbdylib1 = (struct prebound_dylib_command *)lc1; pbdylib2 = (struct prebound_dylib_command *)lc2; dylib_name = (char *)pbdylib1 + pbdylib1->name.offset; for(j = 0; j < nlibs; j++){ if(strcmp(libs[j].dylib_name, dylib_name) == 0){ pbdylib2->cmd = LC_PREBOUND_DYLIB; pbdylib2->cmdsize = libs[j].LC_PREBOUND_DYLIB_size; pbdylib2->name.offset = sizeof(struct prebound_dylib_command); strcpy(((char *)pbdylib2) + sizeof(struct prebound_dylib_command), dylib_name); pbdylib2->nmodules = libs[j].nmodtab; pbdylib2->linked_modules.offset = sizeof(struct prebound_dylib_command) + round(strlen(dylib_name) + 1, sizeof(long)); linked_modules = ((char *)pbdylib2) + sizeof(struct prebound_dylib_command) + round(strlen(dylib_name) + 1, sizeof(long)); for(k = 0; k < libs[j].nmodtab; k++){ if(libs[j].module_states[k] == LINKED) linked_modules[k / 8] |= 1 << k % 8; } lc2 = (struct load_command *) ((char *)lc2 + lc2->cmdsize); break; } } } else{ memcpy(lc2, lc1, lc1->cmdsize); lc2 = (struct load_command *)((char *)lc2 + lc2->cmdsize); } lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize); } /* * Add any new LC_PREBOUND_DYLIB load commands. */ for(i = 0; i < nlibs; i++){ if(libs[i].LC_PREBOUND_DYLIB_found == FALSE){ pbdylib2 = (struct prebound_dylib_command *)lc2; pbdylib2->cmd = LC_PREBOUND_DYLIB; pbdylib2->cmdsize = libs[i].LC_PREBOUND_DYLIB_size; pbdylib2->name.offset = sizeof(struct prebound_dylib_command); strcpy(((char *)pbdylib2) + sizeof(struct prebound_dylib_command), libs[i].dylib_name); pbdylib2->nmodules = libs[i].nmodtab; pbdylib2->linked_modules.offset = sizeof(struct prebound_dylib_command) + round(strlen(libs[i].dylib_name) + 1, sizeof(long)); linked_modules = ((char *)pbdylib2) + sizeof(struct prebound_dylib_command) + round(strlen(libs[i].dylib_name) + 1, sizeof(long)); for(j = 0; j < libs[i].nmodtab; j++){ if(libs[i].module_states[j] == LINKED) linked_modules[j / 8] |= 1 << j % 8; } lc2 = (struct load_command *) ((char *)lc2 + lc2->cmdsize); } } /* * Finally copy the updated load commands over the existing load * commands. */ memcpy(arch->object->load_commands, new_load_commands, sizeofcmds); arch->object->mh->sizeofcmds = sizeofcmds; arch->object->mh->ncmds = ncmds; free(new_load_commands); /* reset the pointers into the load commands */ lc1 = arch->object->load_commands; for(i = 0; i < arch->object->mh->ncmds; i++){ if(lc1->cmd == LC_SYMTAB){ arch->object->st = (struct symtab_command *)lc1; } else if(lc1->cmd == LC_DYSYMTAB){ arch->object->dyst = (struct dysymtab_command *)lc1; } lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize); } } /* * Print the warning message and the input file. */ __private_extern__ void warning_arch( struct arch *arch, struct member *member, char *format, ...) { va_list ap; va_start(ap, format); fprintf(stderr, "%s: ", progname); vfprintf(stderr, format, ap); va_end(ap); if(member != NULL){ fprintf(stderr, "%s(%.*s)", arch->file_name, (int)member->member_name_size, member->member_name); } else fprintf(stderr, "%s", arch->file_name); if(arch->fat_arch_name != NULL) fprintf(stderr, " (for architecture %s)\n", arch->fat_arch_name); else fprintf(stderr, "\n"); va_end(ap); } /* * Print the error message the input file and increment the error count */ __private_extern__ void error_arch( struct arch *arch, struct member *member, char *format, ...) { va_list ap; va_start(ap, format); fprintf(stderr, "%s: ", progname); vfprintf(stderr, format, ap); va_end(ap); if(member != NULL){ fprintf(stderr, "%s(%.*s)", arch->file_name, (int)member->member_name_size, member->member_name); } else fprintf(stderr, "%s", arch->file_name); if(arch->fat_arch_name != NULL) fprintf(stderr, " (for architecture %s)\n", arch->fat_arch_name); else fprintf(stderr, "\n"); va_end(ap); errors++; } /* * Print the fatal error message the input file and exit non-zero. */ __private_extern__ void fatal_arch( struct arch *arch, struct member *member, char *format, ...) { va_list ap; va_start(ap, format); fprintf(stderr, "%s: ", progname); vfprintf(stderr, format, ap); va_end(ap); if(member != NULL){ fprintf(stderr, "%s(%.*s)", arch->file_name, (int)member->member_name_size, member->member_name); } else fprintf(stderr, "%s", arch->file_name); if(arch->fat_arch_name != NULL) fprintf(stderr, " (for architecture %s)\n", arch->fat_arch_name); else fprintf(stderr, "\n"); va_end(ap); if(check_for_non_prebound == TRUE) exit(0); exit(2); }