/* * Copyright (c) 1999 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@ */ /* * The strip(1) and nmedit(l) program. This understands only Mach-O format * files (with the restriction the symbol table is at the end of the file) and * fat files with Mach-O files in them. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "stuff/breakout.h" #include "stuff/allocate.h" #include "stuff/errors.h" #include "stuff/rnd.h" #include "stuff/reloc.h" #include "stuff/reloc.h" #include "stuff/symbol_list.h" #include "stuff/unix_standard_mode.h" #include "stuff/execute.h" #ifdef TRIE_SUPPORT #include #endif /* TRIE_SUPPORT */ /* These are set from the command line arguments */ __private_extern__ char *progname = NULL; /* name of the program for error messages (argv[0]) */ static char *output_file;/* name of the output file */ static char *sfile; /* filename of global symbol names to keep */ static char *Rfile; /* filename of global symbol names to remove */ static uint32_t Aflag; /* save only absolute symbols with non-zero value and .objc_class_name_* symbols */ static uint32_t iflag; /* -i ignore symbols in -s file not in object */ #ifdef NMEDIT static uint32_t pflag; /* make all defined global symbols private extern */ #else /* !defined(NMEDIT) */ static char *dfile; /* filename of filenames of debugger symbols to keep */ static uint32_t uflag; /* save undefined symbols */ static uint32_t rflag; /* save symbols referenced dynamically */ static uint32_t nflag; /* save N_SECT global symbols */ static uint32_t Sflag; /* -S strip only debugger symbols N_STAB */ static uint32_t xflag; /* -x strip non-globals */ static uint32_t Xflag; /* -X strip local symbols with 'L' names */ static uint32_t cflag; /* -c strip section contents from dynamic libraries files to create stub libraries */ static uint32_t no_uuid;/* -no_uuid strip LC_UUID load commands */ static uint32_t vflag; /* -v for verbose debugging ld -r executions */ static uint32_t lflag; /* -l do ld -r executions even if it has bugs */ static uint32_t strip_all = 1; /* * This is set on an object by object basis if the strip_all flag is still set * and the object is an executable that is for use with the dynamic linker. * This has the same effect as -r and -u. */ static enum bool default_dyld_executable = FALSE; #endif /* NMEDIT */ /* * Data structures to perform selective stripping of symbol table entries. * save_symbols is the names of the symbols from the -s argument. * remove_symbols is the names of the symbols from the -R argument. */ static struct symbol_list *save_symbols = NULL; static uint32_t nsave_symbols = 0; static struct symbol_list *remove_symbols = NULL; static uint32_t nremove_symbols = 0; /* * saves points to an array of uint32_t's that is allocated. This array is a * map of old symbol indexes to new symbol indexes. The new symbol indexes are * plus 1 and zero value means that old symbol is not in the new symbol table. * ref_saves is used in the same way but for the reference table. * nmedits is an array and indexed by the symbol index the value indicates if * the symbol was edited and turned into a non-global. */ static int32_t *saves = NULL; #ifndef NMEDIT static int32_t *ref_saves = NULL; #else static enum bool *nmedits = NULL; #endif /* * These hold pointers to the symbol, string and indirect tables being worked on * by strip_object and strip_symtab() from an input object file or possiblity * changed to an ld -r (-S or -x) file by make_ld_r_object(). */ static struct nlist *symbols = NULL; static struct nlist_64 *symbols64 = NULL; static uint32_t nsyms = 0; static char *strings = NULL; static uint32_t strsize = 0; static uint32_t *indirectsyms = NULL; static uint32_t nindirectsyms = 0; /* * These hold the new symbol and string table created by strip_symtab() * and the new counts of local, defined external and undefined symbols. */ static struct nlist *new_symbols = NULL; static struct nlist_64 *new_symbols64 = NULL; static uint32_t new_nsyms = 0; static char *new_strings = NULL; static uint32_t new_strsize = 0; static uint32_t new_nlocalsym = 0; static uint32_t new_nextdefsym = 0; static uint32_t new_nundefsym = 0; #if defined(TRIE_SUPPORT) && !defined(NMEDIT) /* * The index into the new symbols where the defined external start. */ static uint32_t inew_nextdefsym = 0; #endif /* * These hold the new table of contents, reference table and module table for * dylibs. */ static struct dylib_table_of_contents *new_tocs = NULL; static uint32_t new_ntoc = 0; static struct dylib_reference *new_refs = NULL; static uint32_t new_nextrefsyms = 0; #ifdef NMEDIT static struct dylib_module *new_mods = NULL; static struct dylib_module_64 *new_mods64 = NULL; static uint32_t new_nmodtab = 0; #endif #ifndef NMEDIT /* * The list of file names to save debugging symbols from. */ static char **debug_filenames = NULL; static uint32_t ndebug_filenames = 0; struct undef_map { uint32_t index; struct nlist symbol; }; struct undef_map64 { uint32_t index; struct nlist_64 symbol64; }; static char *qsort_strings = NULL; #endif /* !defined(NMEDIT) */ /* Internal routines */ static void usage( void); static void strip_file( char *input_file, struct arch_flag *arch_flags, uint32_t narch_flags, enum bool all_archs); static void strip_arch( struct arch *archs, uint32_t narchs, struct arch_flag *arch_flags, uint32_t narch_flags, enum bool all_archs); static void strip_object( struct arch *arch, struct member *member, struct object *object); static uint32_t get_starting_syminfo_offset( struct object *object); static void check_object_relocs( struct arch *arch, struct member *member, struct object *object, char *segname, char *sectname, uint64_t sectsize, char *contents, struct relocation_info *relocs, uint32_t nreloc, struct nlist *symbols, struct nlist_64 *symbols64, uint32_t nsyms, char *strings, int32_t *missing_reloc_symbols, enum byte_sex host_byte_sex); static void check_indirect_symtab( struct arch *arch, struct member *member, struct object *object, uint32_t nitems, uint32_t reserved1, uint32_t section_type, char *contents, struct nlist *symbols, struct nlist_64 *symbols64, uint32_t nsyms, char *strings, int32_t *missing_reloc_symbols, enum byte_sex host_byte_sex); #ifndef NMEDIT static enum bool strip_symtab( struct arch *arch, struct member *member, struct object *object, struct dylib_table_of_contents *tocs, uint32_t ntoc, struct dylib_module *mods, struct dylib_module_64 *mods64, uint32_t nmodtab, struct dylib_reference *refs, uint32_t nextrefsyms); #ifdef TRIE_SUPPORT static int prune( const char *name); #endif /* TRIE_SUPPORT */ static void make_ld_r_object( struct arch *arch, struct member *member, struct object *object); static void strip_LC_UUID_commands( struct arch *arch, struct member *member, struct object *object); #ifndef NMEDIT static void strip_LC_CODE_SIGNATURE_commands( struct arch *arch, struct member *member, struct object *object); #endif /* !(NMEDIT) */ static enum bool private_extern_reference_by_module( uint32_t symbol_index, struct dylib_reference *refs, uint32_t nextrefsyms); static enum bool symbol_pointer_used( uint32_t symbol_index, uint32_t *indirectsyms, uint32_t nindirectsyms); static int cmp_qsort_undef_map( const struct undef_map *sym1, const struct undef_map *sym2); static int cmp_qsort_undef_map_64( const struct undef_map64 *sym1, const struct undef_map64 *sym2); #endif /* !defined(NMEDIT) */ #ifdef NMEDIT static enum bool edit_symtab( struct arch *arch, struct member *member, struct object *object, struct nlist *symbols, struct nlist_64 *symbols64, uint32_t nsyms, char *strings, uint32_t strsize, struct dylib_table_of_contents *tocs, uint32_t ntoc, struct dylib_module *mods, struct dylib_module_64 *mods64, uint32_t nmodtab, struct dylib_reference *refs, uint32_t nextrefsyms); #endif /* NMEDIT */ #ifndef NMEDIT static void setup_debug_filenames( char *dfile); static int cmp_qsort_filename( const char **name1, const char **name2); static int cmp_bsearch_filename( const char *name1, const char **name2); #endif /* NMEDIT */ #ifdef NMEDIT /* * This variable and routines are used for nmedit(1) only. */ static char *global_strings = NULL; static int cmp_qsort_global( const struct nlist **sym1, const struct nlist **sym2); static int cmp_qsort_global_64( const struct nlist_64 **sym1, const struct nlist_64 **sym2); static int cmp_bsearch_global_stab( const char *name, const struct nlist **sym); static int cmp_bsearch_global_stab_64( const char *name, const struct nlist_64 **sym); static int cmp_bsearch_global( const char *name, const struct nlist **sym); static int cmp_bsearch_global_64( const char *name, const struct nlist_64 **sym); #endif /* NMEDIT */ /* apple_version is created by the libstuff/Makefile */ extern char apple_version[]; char *version = apple_version; int main( int argc, char *argv[], char *envp[]) { int i; uint32_t j, args_left, files_specified; struct arch_flag *arch_flags; uint32_t narch_flags; enum bool all_archs; struct symbol_list *sp; progname = argv[0]; arch_flags = NULL; narch_flags = 0; all_archs = FALSE; files_specified = 0; args_left = 1; for (i = 1; i < argc; i++){ if(argv[i][0] == '-'){ if(argv[i][1] == '\0'){ args_left = 0; break; } 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], "-s") == 0){ if(i + 1 >= argc) fatal("-s requires an argument"); if(sfile != NULL) fatal("only one -s option allowed"); sfile = argv[i + 1]; i++; } else if(strcmp(argv[i], "-R") == 0){ if(i + 1 >= argc) fatal("-R requires an argument"); if(Rfile != NULL) fatal("only one -R option allowed"); Rfile = argv[i + 1]; i++; } #ifndef NMEDIT else if(strcmp(argv[i], "-d") == 0){ if(i + 1 >= argc) fatal("-d requires an argument"); if(dfile != NULL) fatal("only one -d option allowed"); dfile = argv[i + 1]; i++; } else if(strcmp(argv[i], "-no_uuid") == 0){ no_uuid = 1; } #endif /* !defined(NMEDIT) */ else if(strcmp(argv[i], "-arch") == 0){ if(i + 1 == argc){ error("missing argument(s) to %s option", argv[i]); usage(); } if(strcmp("all", argv[i+1]) == 0){ all_archs = TRUE; } else{ arch_flags = reallocate(arch_flags, (narch_flags + 1) * sizeof(struct arch_flag)); if(get_arch_from_flag(argv[i+1], arch_flags + narch_flags) == 0){ error("unknown architecture specification flag: " "%s %s", argv[i], argv[i+1]); arch_usage(); usage(); } for(j = 0; j < narch_flags; j++){ if(arch_flags[j].cputype == arch_flags[narch_flags].cputype && (arch_flags[j].cpusubtype & ~CPU_SUBTYPE_MASK) == (arch_flags[narch_flags].cpusubtype & ~CPU_SUBTYPE_MASK) && strcmp(arch_flags[j].name, arch_flags[narch_flags].name) == 0) break; } if(j == narch_flags) narch_flags++; } i++; } else{ for(j = 1; argv[i][j] != '\0'; j++){ switch(argv[i][j]){ #ifdef NMEDIT case 'p': pflag = 1; break; #else /* !defined(NMEDIT) */ case 'S': Sflag = 1; strip_all = 0; break; case 'X': Xflag = 1; strip_all = 0; break; case 'x': xflag = 1; strip_all = 0; break; case 'i': iflag = 1; break; case 'u': uflag = 1; strip_all = 0; break; case 'r': rflag = 1; strip_all = 0; break; case 'n': nflag = 1; strip_all = 0; break; #endif /* !defined(NMEDIT) */ case 'A': Aflag = 1; #ifndef NMEDIT strip_all = 0; #endif /* !defined(NMEDIT) */ break; #ifndef NMEDIT case 'c': cflag = 1; strip_all = 0; break; case 'v': vflag = 1; break; case 'l': lflag = 1; break; #endif /* NMEDIT */ default: error("unrecognized option: %s", argv[i]); usage(); } } } } else files_specified++; } if(args_left == 0) files_specified += argc - (i + 1); if(files_specified > 1 && output_file != NULL){ error("-o can only be used when one file is specified"); usage(); } if(sfile){ setup_symbol_list(sfile, &save_symbols, &nsave_symbols); } #ifdef NMEDIT else{ if(Rfile == NULL && pflag == 0){ error("-s , -R or -p argument required"); usage(); } } #endif /* NMEDIT */ if(Rfile){ setup_symbol_list(Rfile, &remove_symbols, &nremove_symbols); if(sfile){ for(j = 0; j < nremove_symbols ; j++){ sp = bsearch(remove_symbols[j].name, save_symbols, nsave_symbols, sizeof(struct symbol_list), (int (*)(const void *, const void *)) symbol_list_bsearch); if(sp != NULL){ error("symbol name: %s is listed in both -s %s and -R " "%s files (can't be both saved and removed)", remove_symbols[j].name, sfile, Rfile); } } if(errors) exit(EXIT_FAILURE); } } /* the default when no -arch flags is present is to strip all archs */ if(narch_flags == 0) all_archs = TRUE; #ifndef NMEDIT if(dfile){ setup_debug_filenames(dfile); } #endif /* !defined(NMEDIT) */ files_specified = 0; args_left = 1; for (i = 1; i < argc; i++) { if(args_left && argv[i][0] == '-'){ if(argv[i][1] == '\0') args_left = 0; else if(strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "-R") == 0 || #ifndef NMEDIT strcmp(argv[i], "-d") == 0 || #endif /* !defined(NMEDIT) */ strcmp(argv[i], "-arch") == 0) i++; } else{ char resolved_path[PATH_MAX + 1]; if(realpath(argv[i], resolved_path) == NULL) strip_file(argv[i], arch_flags, narch_flags, all_archs); else strip_file(resolved_path, arch_flags,narch_flags,all_archs); files_specified++; } } if(files_specified == 0) fatal("no files specified"); if(errors) return(EXIT_FAILURE); else return(EXIT_SUCCESS); } static void usage( void) { #ifndef NMEDIT fprintf(stderr, "Usage: %s [-AnuSXx] [-] [-d filename] [-s filename] " "[-R filename] [-o output] file [...] \n", progname); #else /* defined(NMEDIT) */ fprintf(stderr, "Usage: %s -s filename [-R filename] [-p] [-A] [-] " "[-o output] file [...] \n", progname); #endif /* NMEDIT */ exit(EXIT_FAILURE); } static void strip_file( char *input_file, struct arch_flag *arch_flags, uint32_t narch_flags, enum bool all_archs) { struct ofile *ofile; struct arch *archs; uint32_t narchs; struct stat stat_buf; uint32_t previous_errors; enum bool unix_standard_mode; int cwd_fd; char *rename_file; #ifndef NMEDIT char *p; #endif archs = NULL; narchs = 0; previous_errors = errors; errors = 0; /* breakout the file for processing */ ofile = breakout(input_file, &archs, &narchs, FALSE); if(errors) return; /* checkout the file for symbol table replacement processing */ checkout(archs, narchs); /* process the symbols in the input file */ strip_arch(archs, narchs, arch_flags, narch_flags, all_archs); if(errors){ free_archs(archs, narchs); ofile_unmap(ofile); return; } /* create the output file */ if(stat(input_file, &stat_buf) == -1) system_error("can't stat input file: %s", input_file); if(output_file != NULL){ writeout(archs, narchs, output_file, stat_buf.st_mode & 0777, TRUE, FALSE, FALSE, NULL); } else{ unix_standard_mode = get_unix_standard_mode(); rename_file = NULL; cwd_fd = -1; #ifdef NMEDIT output_file = makestr(input_file, ".nmedit", NULL); #else /* !defined(NMEDIT) */ /* * In UNIX standard conformance mode we are not allowed to replace * a file that is not writeable. */ if(unix_standard_mode == TRUE && access(input_file, W_OK) == -1){ system_error("file: %s is not writable", input_file); goto strip_file_return; } output_file = makestr(input_file, ".strip", NULL); /* * The UNIX standard conformance test suite expects files of * MAXPATHLEN to work. */ if(strlen(output_file) >= MAXPATHLEN){ /* * If there is a directory path in the name try to change * the current working directory to that path. */ if((p = rindex(output_file, '/')) != NULL){ if((cwd_fd = open(".", O_RDONLY, 0)) == -1){ system_error("can't open current working directory"); goto strip_file_return; } *p = '\0'; if(chdir(output_file) == -1){ system_error("can't change current working directory " "to: %s", output_file); goto strip_file_return; } p = rindex(input_file, '/'); rename_file = makestr(p + 1, NULL); } /* * Create what might be a short enough name. */ free(output_file); output_file = makestr("strip.XXXXXX", NULL); output_file = mktemp(output_file); } #endif /* NMEDIT */ writeout(archs, narchs, output_file, stat_buf.st_mode & 0777, TRUE, FALSE, FALSE, NULL); if(rename_file != NULL){ if(rename(output_file, rename_file) == -1) system_error("can't move temporary file: %s to file: %s", output_file, rename_file); free(rename_file); } else{ if(rename(output_file, input_file) == -1) system_error("can't move temporary file: %s to input " "file: %s", output_file, input_file); } free(output_file); output_file = NULL; /* * If we changed the current working directory change back to * the previous working directory. */ if(cwd_fd != -1){ if(fchdir(cwd_fd) == -1) system_error("can't change back to previous working " "directory"); if(close(cwd_fd) == -1) system_error("can't close previous working directory"); } } #ifndef NMEDIT strip_file_return: #endif /* !defined(NMEDIT) */ /* clean-up data structures */ free_archs(archs, narchs); ofile_unmap(ofile); errors += previous_errors; } static void strip_arch( struct arch *archs, uint32_t narchs, struct arch_flag *arch_flags, uint32_t narch_flags, enum bool all_archs) { uint32_t i, j, k, offset, size, missing_syms; cpu_type_t cputype; cpu_subtype_t cpusubtype; struct arch_flag host_arch_flag; enum bool arch_process, any_processing, *arch_flag_processed, family; const struct arch_flag *family_arch_flag; /* * Using the specified arch_flags process specified objects for those * architecures. */ any_processing = FALSE; arch_flag_processed = NULL; if(narch_flags != 0) arch_flag_processed = allocate(narch_flags * sizeof(enum bool)); memset(arch_flag_processed, '\0', narch_flags * sizeof(enum bool)); for(i = 0; i < narchs; i++){ /* * Determine the architecture (cputype and cpusubtype) of arch[i] */ cputype = 0; cpusubtype = 0; if(archs[i].type == OFILE_ARCHIVE){ for(j = 0; j < archs[i].nmembers; j++){ if(archs[i].members[j].type == OFILE_Mach_O){ cputype = archs[i].members[j].object->mh_cputype; cpusubtype = archs[i].members[j].object->mh_cpusubtype; break; } } } else if(archs[i].type == OFILE_Mach_O){ cputype = archs[i].object->mh_cputype; cpusubtype = archs[i].object->mh_cpusubtype; } else if(archs[i].fat_arch != NULL){ cputype = archs[i].fat_arch->cputype; cpusubtype = archs[i].fat_arch->cpusubtype; } arch_process = FALSE; if(all_archs == TRUE){ arch_process = TRUE; } else if(narch_flags != 0){ family = FALSE; if(narch_flags == 1){ family_arch_flag = get_arch_family_from_cputype(arch_flags[0].cputype); if(family_arch_flag != NULL) family = (enum bool) ((family_arch_flag->cpusubtype & ~CPU_SUBTYPE_MASK) == (arch_flags[0].cpusubtype & ~CPU_SUBTYPE_MASK)); } for(j = 0; j < narch_flags; j++){ if(arch_flags[j].cputype == cputype && ((arch_flags[j].cpusubtype & ~CPU_SUBTYPE_MASK) == (cpusubtype & ~CPU_SUBTYPE_MASK) || family == TRUE)){ arch_process = TRUE; arch_flag_processed[j] = TRUE; break; } } } else{ (void)get_arch_from_host(&host_arch_flag, NULL); if(host_arch_flag.cputype == cputype && (host_arch_flag.cpusubtype & ~CPU_SUBTYPE_MASK) == (cpusubtype & ~CPU_SUBTYPE_MASK)) arch_process = TRUE; } if(narchs != 1 && arch_process == FALSE) continue; any_processing = TRUE; /* * Now this arch[i] has been selected to be processed so process it * according to its type. */ if(archs[i].type == OFILE_ARCHIVE){ for(j = 0; j < archs[i].nmembers; j++){ if(archs[i].members[j].type == OFILE_Mach_O){ strip_object(archs + i, archs[i].members + j, archs[i].members[j].object); } } missing_syms = 0; if(iflag == 0){ for(k = 0; k < nsave_symbols; k++){ if(save_symbols[k].seen == FALSE){ if(missing_syms == 0){ error_arch(archs + i, NULL, "symbols names " "listed in: %s not in: ", sfile); missing_syms = 1; } fprintf(stderr, "%s\n", save_symbols[k].name); } } } for(k = 0; k < nsave_symbols; k++){ save_symbols[k].seen = FALSE; } missing_syms = 0; if(iflag == 0){ for(k = 0; k < nremove_symbols; k++){ if(remove_symbols[k].seen == FALSE){ if(missing_syms == 0){ error_arch(archs + i, NULL, "symbols names " "listed in: %s not defined in: ", Rfile); missing_syms = 1; } fprintf(stderr, "%s\n", remove_symbols[k].name); } } } for(k = 0; k < nremove_symbols; k++){ remove_symbols[k].seen = FALSE; } /* * Reset the library offsets and size. */ offset = 0; for(j = 0; j < archs[i].nmembers; j++){ archs[i].members[j].offset = offset; size = 0; if(archs[i].members[j].member_long_name == TRUE){ size = rnd(archs[i].members[j].member_name_size, 8) + (rnd(sizeof(struct ar_hdr), 8) - sizeof(struct ar_hdr)); archs[i].toc_long_name = TRUE; } if(archs[i].members[j].object != NULL){ size += rnd(archs[i].members[j].object->object_size - archs[i].members[j].object->input_sym_info_size + archs[i].members[j].object->output_sym_info_size, 8); sprintf(archs[i].members[j].ar_hdr->ar_size, "%-*ld", (int)sizeof(archs[i].members[j].ar_hdr->ar_size), (long)(size)); /* * This has to be done by hand because sprintf puts a * null at the end of the buffer. */ memcpy(archs[i].members[j].ar_hdr->ar_fmag, ARFMAG, (int)sizeof(archs[i].members[j].ar_hdr->ar_fmag)); } else{ size += archs[i].members[j].unknown_size; } offset += sizeof(struct ar_hdr) + size; } archs[i].library_size = offset; } else if(archs[i].type == OFILE_Mach_O){ strip_object(archs + i, NULL, archs[i].object); } else { warning_arch(archs + i, NULL, "can't process non-object and " "non-archive file: "); return; } } if(all_archs == FALSE && narch_flags != 0){ for(i = 0; i < narch_flags; i++){ if(arch_flag_processed[i] == FALSE) error("file: %s does not contain architecture: %s", archs[0].file_name, arch_flags[i].name); } free(arch_flag_processed); } if(any_processing == FALSE) fatal("no processing done on input file: %s (specify a -arch flag)", archs[0].file_name); } static void strip_object( struct arch *arch, struct member *member, struct object *object) { enum byte_sex host_byte_sex; uint32_t offset; struct dylib_table_of_contents *tocs; uint32_t ntoc; struct dylib_module *mods; struct dylib_module_64 *mods64; uint32_t nmodtab; struct dylib_reference *refs; uint32_t nextrefsyms; uint32_t i, j; struct load_command *lc; struct segment_command *sg; struct segment_command_64 *sg64; struct section *s; struct section_64 *s64; struct relocation_info *relocs; struct scattered_relocation_info *sreloc; int32_t missing_reloc_symbols; uint32_t stride, section_type, nitems; char *contents; uint32_t dyld_info_start; uint32_t dyld_info_end; #ifndef NMEDIT uint32_t flags; uint32_t k; #endif uint32_t ncmds; host_byte_sex = get_host_byte_sex(); /* Don't do anything to stub dylibs which have no load commands. */ if(object->mh_filetype == MH_DYLIB_STUB){ if((object->mh != NULL && object->mh->ncmds == 0) || (object->mh64 != NULL && object->mh64->ncmds == 0)){ return; } } if(object->mh_filetype == MH_DSYM) fatal_arch(arch, member, "can't process dSYM companion file: "); if(object->st == NULL || object->st->nsyms == 0){ warning_arch(arch, member, "input object file stripped: "); return; } nsyms = object->st->nsyms; if(object->mh != NULL){ symbols = (struct nlist *) (object->object_addr + object->st->symoff); if(object->object_byte_sex != host_byte_sex) swap_nlist(symbols, nsyms, host_byte_sex); symbols64 = NULL; } else{ symbols = NULL; symbols64 = (struct nlist_64 *) (object->object_addr + object->st->symoff); if(object->object_byte_sex != host_byte_sex) swap_nlist_64(symbols64, nsyms, host_byte_sex); } strings = object->object_addr + object->st->stroff; strsize = object->st->strsize; #ifndef NMEDIT if(object->mh != NULL) flags = object->mh->flags; else flags = object->mh64->flags; if(object->mh_filetype == MH_DYLIB && (flags & MH_PREBOUND) != MH_PREBOUND){ arch->dont_update_LC_ID_DYLIB_timestamp = TRUE; } if(object->mh_filetype != MH_DYLIB && cflag) fatal_arch(arch, member, "-c can't be used on non-dynamic " "library: "); #endif /* !(NMEDIT) */ if(object->mh_filetype == MH_DYLIB_STUB) fatal_arch(arch, member, "dynamic stub library can't be changed " "once created: "); if(object->mh_filetype == MH_DYLIB){ tocs = (struct dylib_table_of_contents *) (object->object_addr + object->dyst->tocoff); ntoc = object->dyst->ntoc; nmodtab = object->dyst->nmodtab; if(object->mh != NULL){ mods = (struct dylib_module *) (object->object_addr + object->dyst->modtaboff); if(object->object_byte_sex != host_byte_sex) swap_dylib_module(mods, nmodtab, host_byte_sex); mods64 = NULL; } else{ mods = NULL; mods64 = (struct dylib_module_64 *) (object->object_addr + object->dyst->modtaboff); if(object->object_byte_sex != host_byte_sex) swap_dylib_module_64(mods64, nmodtab, host_byte_sex); } refs = (struct dylib_reference *) (object->object_addr + object->dyst->extrefsymoff); nextrefsyms = object->dyst->nextrefsyms; if(object->object_byte_sex != host_byte_sex){ swap_dylib_table_of_contents(tocs, ntoc, host_byte_sex); swap_dylib_reference(refs, nextrefsyms, host_byte_sex); } #ifndef NMEDIT /* * In the -c flag is specified then strip the section contents of * this dynamic library and change it into a stub library. When * creating a stub library the timestamp is not changed. */ if(cflag){ arch->dont_update_LC_ID_DYLIB_timestamp = TRUE; lc = object->load_commands; if(object->mh != NULL){ ncmds = object->mh->ncmds; object->mh_filetype = MH_DYLIB_STUB; object->mh->filetype = MH_DYLIB_STUB; } else{ ncmds = object->mh64->ncmds; object->mh_filetype = MH_DYLIB_STUB; object->mh64->filetype = MH_DYLIB_STUB; } for(i = 0; i < ncmds; i++){ if(lc->cmd == LC_SEGMENT){ sg = (struct segment_command *)lc; if(strcmp(sg->segname, SEG_LINKEDIT) != 0){ /* * Zero out the section offset, reloff, and size * fields as the section contents are being removed. */ s = (struct section *) ((char *)sg + sizeof(struct segment_command)); for(j = 0; j < sg->nsects; j++){ /* * For section types with indirect tables we * do not zero out the section size in a stub * library. As the section size is needed to * know now many indirect table entries the * section has. This is a bit odd but programs * dealing with MH_DYLIB_STUB filetypes special * case this. */ section_type = s[j].flags & SECTION_TYPE; if(section_type != S_SYMBOL_STUBS && section_type != S_LAZY_SYMBOL_POINTERS && section_type != S_LAZY_DYLIB_SYMBOL_POINTERS && section_type != S_NON_LAZY_SYMBOL_POINTERS){ s[j].size = 0; } s[j].addr = 0; s[j].offset = 0; s[j].reloff = 0; } /* zero out file offset and size in the segment */ sg->fileoff = 0; sg->filesize = 0; } } else if(lc->cmd == LC_SEGMENT_64){ sg64 = (struct segment_command_64 *)lc; if(strcmp(sg64->segname, SEG_LINKEDIT) != 0){ /* * Zero out the section offset, reloff, and size * fields as the section contents are being removed. */ s64 = (struct section_64 *) ((char *)sg64 + sizeof(struct segment_command_64)); for(j = 0; j < sg64->nsects; j++){ /* * For section types with indirect tables we * do not zero out the section size in a stub * library. As the section size is needed to * know now many indirect table entries the * section has. This is a bit odd but programs * dealing with MH_DYLIB_STUB filetypes special * case this. */ section_type = s64[j].flags & SECTION_TYPE; if(section_type != S_SYMBOL_STUBS && section_type != S_LAZY_SYMBOL_POINTERS && section_type != S_LAZY_DYLIB_SYMBOL_POINTERS && section_type != S_NON_LAZY_SYMBOL_POINTERS){ s64[j].size = 0; } s64[j].addr = 0; s64[j].offset = 0; s64[j].reloff = 0; } /* zero out file offset and size in the segment */ sg64->fileoff = 0; sg64->filesize = 0; } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } /* * To get the right amount of the file copied out by writeout() * for the case when we are stripping out the section contents * we reduce the object size by the size of the section contents * including the padding after the load commands. Then this * size minus the size of the input symbolic information is * copied out. */ if(object->mh != NULL){ object->object_size -= (object->seg_linkedit->fileoff - (sizeof(struct mach_header) + object->mh->sizeofcmds)); /* * Set the file offset to the link edit information to be * right after the load commands. */ object->seg_linkedit->fileoff = sizeof(struct mach_header) + object->mh->sizeofcmds; } else{ object->object_size -= (object->seg_linkedit64->fileoff - (sizeof(struct mach_header_64) + object->mh64->sizeofcmds)); /* * Set the file offset to the link edit information to be * right after the load commands. */ object->seg_linkedit64->fileoff = sizeof(struct mach_header_64) + object->mh64->sizeofcmds; } } #endif /* !(NMEDIT) */ } else{ tocs = NULL; ntoc = 0; mods = NULL; mods64 = NULL; nmodtab = 0; refs = NULL; nextrefsyms = 0; } /* * coalesced symbols can be stripped only if they are not used via an * symbol pointer. So to know that strip_symtab() needs to be passed * the indirect symbol table. */ if(object->dyst != NULL && object->dyst->nindirectsyms != 0){ nindirectsyms = object->dyst->nindirectsyms; indirectsyms = (uint32_t *) (object->object_addr + object->dyst->indirectsymoff); if(object->object_byte_sex != host_byte_sex) swap_indirect_symbols(indirectsyms, nindirectsyms, host_byte_sex); } else{ indirectsyms = NULL; nindirectsyms = 0; } if(object->mh != NULL) object->input_sym_info_size = nsyms * sizeof(struct nlist) + strsize; else object->input_sym_info_size = nsyms * sizeof(struct nlist_64) + strsize; #ifndef NMEDIT if(object->mh != NULL) flags = object->mh->flags; else flags = object->mh64->flags; if(strip_all && (flags & MH_DYLDLINK) == MH_DYLDLINK && object->mh_filetype == MH_EXECUTE) default_dyld_executable = TRUE; else default_dyld_executable = FALSE; #endif /* !defined(NMEDIT) */ #ifndef NMEDIT if(sfile != NULL || Rfile != NULL || dfile != NULL || Aflag || uflag || Sflag || xflag || Xflag || nflag || rflag || default_dyld_executable || object->mh_filetype == MH_DYLIB || object->mh_filetype == MH_DYLINKER) #endif /* !defined(NMEDIT) */ { #ifdef NMEDIT if(edit_symtab(arch, member, object, symbols, symbols64, nsyms, strings, strsize, tocs, ntoc, mods, mods64, nmodtab, refs, nextrefsyms) == FALSE) return; #else /* !defined(NMEDIT) */ if(strip_symtab(arch, member, object, tocs, ntoc, mods, mods64, nmodtab, refs, nextrefsyms) == FALSE) return; if(no_uuid == TRUE) strip_LC_UUID_commands(arch, member, object); #endif /* !defined(NMEDIT) */ /* * The parts that make up output_sym_info_size must be added up in * the output order so that when the sizes of things are rounded up * before parts that must be aligned the final output_sym_info_size * is correct. * * Also the parts that make up input_sym_info_size must be added up * in the same way. And must be done here as the input file may * have been changed to and "ld -r" file and may not be the * the original input file. */ object->output_sym_info_size = 0; object->input_sym_info_size = 0; if(object->dyld_info != NULL){ /* there are five parts to the dyld info, but strip does not alter them, so copy as a block */ dyld_info_start = 0; if (object->dyld_info->rebase_off != 0) dyld_info_start = object->dyld_info->rebase_off; else if (object->dyld_info->bind_off != 0) dyld_info_start = object->dyld_info->bind_off; else if (object->dyld_info->weak_bind_off != 0) dyld_info_start = object->dyld_info->weak_bind_off; else if (object->dyld_info->lazy_bind_off != 0) dyld_info_start = object->dyld_info->lazy_bind_off; else if (object->dyld_info->export_off != 0) dyld_info_start = object->dyld_info->export_off; dyld_info_end = 0; if (object->dyld_info->export_size != 0) dyld_info_end = object->dyld_info->export_off + object->dyld_info->export_size; else if (object->dyld_info->lazy_bind_size != 0) dyld_info_end = object->dyld_info->lazy_bind_off + object->dyld_info->lazy_bind_size; else if (object->dyld_info->weak_bind_size != 0) dyld_info_end = object->dyld_info->weak_bind_off + object->dyld_info->weak_bind_size; else if (object->dyld_info->bind_size != 0) dyld_info_end = object->dyld_info->bind_off + object->dyld_info->bind_size; else if (object->dyld_info->rebase_size != 0) dyld_info_end = object->dyld_info->rebase_off + object->dyld_info->rebase_size; object->output_dyld_info = object->object_addr +dyld_info_start; object->output_dyld_info_size = dyld_info_end - dyld_info_start; object->output_sym_info_size += object->output_dyld_info_size; /* * Warn about strip -s or -R on a final linked image with * dyld_info. */ if(nsave_symbols != 0){ warning_arch(arch, NULL, "removing global symbols from a " "final linked no longer supported. Use " "-exported_symbols_list at link time when " "building: "); } 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; } if(object->dyst != NULL){ #ifndef NMEDIT /* * When stripping out the section contents to create a * dynamic library stub the relocation info also gets * stripped. */ if(!cflag) #endif /* !(NMEDIT) */ { object->output_sym_info_size += object->dyst->nlocrel * sizeof(struct relocation_info); } object->input_sym_info_size += object->dyst->nlocrel * sizeof(struct relocation_info); } 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->mh != NULL){ object->input_sym_info_size += nsyms * sizeof(struct nlist); object->output_symbols = new_symbols; object->output_sym_info_size += new_nsyms * sizeof(struct nlist); } else{ object->input_sym_info_size += nsyms * sizeof(struct nlist_64); object->output_symbols64 = new_symbols64; object->output_sym_info_size += new_nsyms * sizeof(struct nlist_64); } object->output_nsymbols = new_nsyms; object->st->nsyms = new_nsyms; if(object->hints_cmd != NULL){ 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->dyst != NULL){ #ifndef NMEDIT /* * When stripping out the section contents to create a * dynamic library stub the relocation info also gets * stripped. */ if(!cflag) #endif /* !(NMEDIT) */ { object->output_sym_info_size += object->dyst->nextrel * sizeof(struct relocation_info); } object->input_sym_info_size += object->dyst->nextrel * sizeof(struct relocation_info); } if(object->dyst != NULL){ object->output_sym_info_size += object->dyst->nindirectsyms * sizeof(uint32_t) + object->input_indirectsym_pad; if(object->mh != NULL){ object->input_sym_info_size += object->dyst->nindirectsyms * sizeof(uint32_t); } else{ object->input_sym_info_size += object->dyst->nindirectsyms * sizeof(uint32_t) + object->input_indirectsym_pad; } } if(object->dyst != NULL){ object->output_sym_info_size += new_ntoc * sizeof(struct dylib_table_of_contents); object->input_sym_info_size += object->dyst->ntoc * sizeof(struct dylib_table_of_contents); } if(object->dyst != NULL){ if(object->mh != NULL){ object->output_sym_info_size += object->dyst->nmodtab * sizeof(struct dylib_module); object->input_sym_info_size += object->dyst->nmodtab * sizeof(struct dylib_module); } else{ object->output_sym_info_size += object->dyst->nmodtab * sizeof(struct dylib_module_64); object->input_sym_info_size += object->dyst->nmodtab * sizeof(struct dylib_module_64); } } if(object->dyst != NULL){ object->output_sym_info_size += new_nextrefsyms * sizeof(struct dylib_reference); object->input_sym_info_size += object->dyst->nextrefsyms * sizeof(struct dylib_reference); } object->output_strings = new_strings; object->output_strings_size = new_strsize; object->output_sym_info_size += new_strsize; object->input_sym_info_size += strsize; object->st->strsize = new_strsize; if(object->code_sig_cmd != NULL){ #ifndef NMEDIT if(!cflag) #endif /* !(NMEDIT) */ { 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; #ifndef NMEDIT if(cflag){ strip_LC_CODE_SIGNATURE_commands(arch, member, object); } else #endif /* !(NMEDIT) */ { object->output_sym_info_size = rnd(object->output_sym_info_size, 16); object->output_sym_info_size += object->code_sig_cmd->datasize; } } if(object->dyst != NULL){ object->dyst->ilocalsym = 0; object->dyst->nlocalsym = new_nlocalsym; object->dyst->iextdefsym = new_nlocalsym; object->dyst->nextdefsym = new_nextdefsym; object->dyst->iundefsym = new_nlocalsym + new_nextdefsym; object->dyst->nundefsym = new_nundefsym; if(object->dyst->nindirectsyms != 0){ object->output_indirect_symtab = indirectsyms; if(object->object_byte_sex != host_byte_sex) swap_indirect_symbols(indirectsyms, nindirectsyms, object->object_byte_sex); } /* * If the -c option is specified the object's filetype will * have been changed from MH_DYLIB to MH_DYLIB_STUB above. */ if(object->mh_filetype == MH_DYLIB || object->mh_filetype == MH_DYLIB_STUB){ object->output_tocs = new_tocs; object->output_ntoc = new_ntoc; #ifdef NMEDIT if(object->mh != NULL) object->output_mods = new_mods; else object->output_mods64 = new_mods64; object->output_nmodtab = new_nmodtab; #else object->output_mods = mods; object->output_nmodtab = nmodtab; #endif object->output_refs = new_refs; object->output_nextrefsyms = new_nextrefsyms; if(object->object_byte_sex != host_byte_sex){ swap_dylib_table_of_contents(new_tocs, new_ntoc, object->object_byte_sex); #ifdef NMEDIT if(object->mh != NULL) swap_dylib_module(new_mods, new_nmodtab, object->object_byte_sex); else swap_dylib_module_64(new_mods64, new_nmodtab, object->object_byte_sex); #else if(object->mh != NULL) swap_dylib_module(mods, nmodtab, object->object_byte_sex); else swap_dylib_module_64(mods64, nmodtab, object->object_byte_sex); #endif swap_dylib_reference(new_refs, new_nextrefsyms, object->object_byte_sex); } } object->dyst->ntoc = new_ntoc; object->dyst->nextrefsyms = new_nextrefsyms; offset = get_starting_syminfo_offset(object); if(object->dyld_info != 0){ if (object->dyld_info->rebase_off != 0){ object->dyld_info->rebase_off = offset; offset += object->dyld_info->rebase_size; } if (object->dyld_info->bind_off != 0){ object->dyld_info->bind_off = offset; offset += object->dyld_info->bind_size; } if (object->dyld_info->weak_bind_off != 0){ object->dyld_info->weak_bind_off = offset; offset += object->dyld_info->weak_bind_size; } if (object->dyld_info->lazy_bind_off != 0){ object->dyld_info->lazy_bind_off = offset; offset += object->dyld_info->lazy_bind_size; } if (object->dyld_info->export_off != 0){ object->dyld_info->export_off = offset; offset += object->dyld_info->export_size; } } if(object->dyst->nlocrel != 0){ object->output_loc_relocs = (struct relocation_info *) (object->object_addr + object->dyst->locreloff); #ifndef NMEDIT /* * When stripping out the section contents to create a * dynamic library stub the relocation info also gets * stripped. */ if(cflag){ object->dyst->nlocrel = 0; object->dyst->locreloff = 0; } else #endif /* defined(NMEDIT) */ { object->dyst->locreloff = offset; offset += object->dyst->nlocrel * sizeof(struct relocation_info); } } else object->dyst->locreloff = 0; if(object->split_info_cmd != NULL){ object->split_info_cmd->dataoff = offset; offset += object->split_info_cmd->datasize; } if(object->func_starts_info_cmd != NULL){ object->func_starts_info_cmd->dataoff = offset; offset += object->func_starts_info_cmd->datasize; } if(object->data_in_code_cmd != NULL){ object->data_in_code_cmd->dataoff = offset; offset += object->data_in_code_cmd->datasize; } if(object->code_sign_drs_cmd != NULL){ object->code_sign_drs_cmd->dataoff = offset; offset += object->code_sign_drs_cmd->datasize; } if(object->st->nsyms != 0){ object->st->symoff = offset; if(object->mh != NULL) offset += object->st->nsyms * sizeof(struct nlist); else offset += object->st->nsyms * sizeof(struct nlist_64); } else object->st->symoff = 0; if(object->hints_cmd != NULL){ if(object->hints_cmd->nhints != 0){ object->output_hints = (struct twolevel_hint *) (object->object_addr + object->hints_cmd->offset); object->hints_cmd->offset = offset; offset += object->hints_cmd->nhints * sizeof(struct twolevel_hint); } else object->hints_cmd->offset = 0; } if(object->dyst->nextrel != 0){ object->output_ext_relocs = (struct relocation_info *) (object->object_addr + object->dyst->extreloff); #ifndef NMEDIT /* * When stripping out the section contents to create a * dynamic library stub the relocation info also gets * stripped. */ if(cflag){ object->dyst->nextrel = 0; object->dyst->extreloff = 0; } else #endif /* defined(NMEDIT) */ { object->dyst->extreloff = offset; offset += object->dyst->nextrel * sizeof(struct relocation_info); } } else object->dyst->extreloff = 0; if(object->dyst->nindirectsyms != 0){ object->dyst->indirectsymoff = offset; offset += object->dyst->nindirectsyms * sizeof(uint32_t) + object->input_indirectsym_pad; } else object->dyst->indirectsymoff = 0;; if(object->dyst->ntoc != 0){ object->dyst->tocoff = offset; offset += object->dyst->ntoc * sizeof(struct dylib_table_of_contents); } else object->dyst->tocoff = 0; if(object->dyst->nmodtab != 0){ #ifndef NMEDIT /* * When stripping out the section contents to create a * dynamic library stub zero out the fields in the module * table for the sections and relocation information and * clear Objective-C address and size from modules. */ if(cflag){ if(object->mh != NULL){ for(k = 0; k < object->dyst->nmodtab; k++){ mods[k].iinit_iterm = 0; mods[k].ninit_nterm = 0; mods[k].iextrel = 0; mods[k].nextrel = 0; mods[k].objc_module_info_addr = 0; mods[k].objc_module_info_size = 0; } } else{ for(k = 0; k < object->dyst->nmodtab; k++){ mods64[k].iinit_iterm = 0; mods64[k].ninit_nterm = 0; mods64[k].iextrel = 0; mods64[k].nextrel = 0; mods64[k].objc_module_info_addr = 0; mods64[k].objc_module_info_size = 0; } } } #endif /* !(NMEDIT) */ object->dyst->modtaboff = offset; if(object->mh != NULL) offset += object->dyst->nmodtab * sizeof(struct dylib_module); else offset += object->dyst->nmodtab * sizeof(struct dylib_module_64); } else object->dyst->modtaboff = 0; if(object->dyst->nextrefsyms != 0){ object->dyst->extrefsymoff = offset; offset += object->dyst->nextrefsyms * sizeof(struct dylib_reference); } else object->dyst->extrefsymoff = 0; if(object->st->strsize != 0){ object->st->stroff = offset; offset += object->st->strsize; } else object->st->stroff = 0; if(object->code_sig_cmd != NULL){ offset = rnd(offset, 16); object->code_sig_cmd->dataoff = offset; offset += object->code_sig_cmd->datasize; } } else{ if(new_strsize != 0){ if(object->mh != NULL) object->st->stroff = object->st->symoff + new_nsyms * sizeof(struct nlist); else object->st->stroff = object->st->symoff + new_nsyms * sizeof(struct nlist_64); } else object->st->stroff = 0; if(new_nsyms == 0) object->st->symoff = 0; } } #ifndef NMEDIT else{ /* * Here we are doing a full symbol strip. In some cases it may * leave the local relocation entries as well as LOCAL indirect * symbol table entries. */ if(saves != NULL) free(saves); saves = (int32_t *)allocate(object->st->nsyms * sizeof(int32_t)); bzero(saves, object->st->nsyms * sizeof(int32_t)); /* * Account for the symbolic info in the input file. */ if(object->dyst != NULL){ object->input_sym_info_size += object->dyst->nlocrel * sizeof(struct relocation_info) + object->dyst->nextrel * sizeof(struct relocation_info) + object->dyst->ntoc * sizeof(struct dylib_table_of_contents)+ object->dyst->nextrefsyms * sizeof(struct dylib_reference); if(object->mh != NULL){ object->input_sym_info_size += object->dyst->nmodtab * sizeof(struct dylib_module) + object->dyst->nindirectsyms * sizeof(uint32_t); } else{ object->input_sym_info_size += object->dyst->nmodtab * sizeof(struct dylib_module_64) + object->dyst->nindirectsyms * sizeof(uint32_t) + object->input_indirectsym_pad; } } /* * Determine the offset where the remaining symbolic info will start * in the output file (if any). */ offset = get_starting_syminfo_offset(object); /* * For a full symbol strip all these values in the output file are * set to zero. */ object->st->symoff = 0; object->st->nsyms = 0; object->st->stroff = 0; object->st->strsize = 0; if(object->dyst != NULL){ object->dyst->ilocalsym = 0; object->dyst->nlocalsym = 0; object->dyst->iextdefsym = 0; object->dyst->nextdefsym = 0; object->dyst->iundefsym = 0; object->dyst->nundefsym = 0; } /* * This will accumulate any remaining symbolic info size in the * output file. */ object->output_sym_info_size = 0; /* * We set these so that checking can be done below to report the * symbols that can't be stripped because of relocation entries * or indirect symbol table entries. Normally if these table have a * non-zero number of entries it will be an error as we are trying * to strip everything. But it maybe that there are only LOCAL * indirect entries which is odd but will be OK. */ if(object->dyst != NULL){ if(object->dyst->nextrel != 0){ object->output_ext_relocs = (struct relocation_info *) (object->object_addr + object->dyst->extreloff); } /* * Since this file has a dynamic symbol table and if this file * has local relocation entries on input make sure they are * there on output. This is a rare case that it will not have * external relocs or indirect symbols but can happen as is the * case with the dynamic linker itself. */ if(object->dyst->nlocrel != 0){ object->output_loc_relocs = (struct relocation_info *) (object->object_addr + object->dyst->locreloff); object->output_sym_info_size += object->dyst->nlocrel * sizeof(struct relocation_info); object->dyst->locreloff = offset; offset += object->dyst->nlocrel * sizeof(struct relocation_info); } if(object->dyst->nindirectsyms != 0){ object->output_indirect_symtab = (uint32_t *) (object->object_addr + object->dyst->indirectsymoff); if(object->object_byte_sex != host_byte_sex) swap_indirect_symbols( object->output_indirect_symtab, object->dyst->nindirectsyms, object->object_byte_sex); object->output_sym_info_size += object->dyst->nindirectsyms * sizeof(uint32_t) + object->input_indirectsym_pad; object->dyst->indirectsymoff = offset; offset += object->dyst->nindirectsyms * sizeof(uint32_t) + object->input_indirectsym_pad; } } if(no_uuid == TRUE) strip_LC_UUID_commands(arch, member, object); } #endif /* !defined(NMEDIT) */ /* * Always clear the prebind checksum if any when creating a new file. */ if(object->cs != NULL) object->cs->cksum = 0; if(object->seg_linkedit != NULL){ object->seg_linkedit->filesize += object->output_sym_info_size - object->input_sym_info_size; object->seg_linkedit->vmsize = object->seg_linkedit->filesize; } else if(object->seg_linkedit64 != NULL){ /* Do this in two steps to avoid 32/64-bit casting problems. */ object->seg_linkedit64->filesize -= object->input_sym_info_size; object->seg_linkedit64->filesize += object->output_sym_info_size; object->seg_linkedit64->vmsize = object->seg_linkedit64->filesize; } /* * Check and update the external relocation entries to make sure * referenced symbols are not stripped and refer to the new symbol * table indexes. * * The external relocation entries can be located in one of two places, * first off of the sections or second off of the dynamic symtab. */ missing_reloc_symbols = 0; lc = object->load_commands; if(object->mh != NULL) ncmds = object->mh->ncmds; else ncmds = object->mh64->ncmds; for(i = 0; i < ncmds; i++){ if(lc->cmd == LC_SEGMENT && object->seg_linkedit != (struct segment_command *)lc){ sg = (struct segment_command *)lc; s = (struct section *)((char *)sg + sizeof(struct segment_command)); for(j = 0; j < sg->nsects; j++){ if(s->nreloc != 0){ if(s->reloff + s->nreloc * sizeof(struct relocation_info) > object->object_size){ fatal_arch(arch, member, "truncated or malformed " "object (relocation entries for section (%.16s," "%.16s) extends past the end of the file)", s->segname, s->sectname); } relocs = (struct relocation_info *) (object->object_addr + s->reloff); if(object->object_byte_sex != host_byte_sex) swap_relocation_info(relocs, s->nreloc, host_byte_sex); if(s->offset + s->size > object->object_size){ fatal_arch(arch, member, "truncated or malformed " "object (contents of section (%.16s," "%.16s) extends past the end of the file)", s->segname, s->sectname); } contents = object->object_addr + s->offset; check_object_relocs(arch, member, object, s->segname, s->sectname, s->size, contents, relocs, s->nreloc, symbols, symbols64, nsyms, strings, &missing_reloc_symbols, host_byte_sex); if(object->object_byte_sex != host_byte_sex) swap_relocation_info(relocs, s->nreloc, object->object_byte_sex); } s++; } } else if(lc->cmd == LC_SEGMENT_64 && object->seg_linkedit64 != (struct segment_command_64 *)lc){ sg64 = (struct segment_command_64 *)lc; s64 = (struct section_64 *)((char *)sg64 + sizeof(struct segment_command_64)); for(j = 0; j < sg64->nsects; j++){ if(s64->nreloc != 0){ if(s64->reloff + s64->nreloc * sizeof(struct relocation_info) > object->object_size){ fatal_arch(arch, member, "truncated or malformed " "object (relocation entries for section (%.16s," "%.16s) extends past the end of the file)", s64->segname, s64->sectname); } relocs = (struct relocation_info *) (object->object_addr + s64->reloff); if(object->object_byte_sex != host_byte_sex) swap_relocation_info(relocs, s64->nreloc, host_byte_sex); if(s64->offset + s64->size > object->object_size){ fatal_arch(arch, member, "truncated or malformed " "object (contents of section (%.16s," "%.16s) extends past the end of the file)", s64->segname, s64->sectname); } contents = object->object_addr + s64->offset; check_object_relocs(arch, member, object, s64->segname, s64->sectname, s64->size, contents, relocs, s64->nreloc, symbols, symbols64, nsyms, strings, &missing_reloc_symbols, host_byte_sex); if(object->object_byte_sex != host_byte_sex) swap_relocation_info(relocs, s64->nreloc, object->object_byte_sex); } s64++; } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } if(object->dyst != NULL && object->dyst->nextrel != 0){ relocs = object->output_ext_relocs; if(object->object_byte_sex != host_byte_sex) swap_relocation_info(relocs, object->dyst->nextrel, host_byte_sex); for(i = 0; i < object->dyst->nextrel; i++){ if((relocs[i].r_address & R_SCATTERED) == 0 && relocs[i].r_extern == 1){ if(relocs[i].r_symbolnum > nsyms){ fatal_arch(arch, member, "bad r_symbolnum for external " "relocation entry %d in: ", i); } if(saves[relocs[i].r_symbolnum] == 0){ if(missing_reloc_symbols == 0){ error_arch(arch, member, "symbols referenced by " "relocation entries that can't be stripped in: "); missing_reloc_symbols = 1; } if(object->mh != NULL){ fprintf(stderr, "%s\n", strings + symbols [relocs[i].r_symbolnum].n_un.n_strx); } else { fprintf(stderr, "%s\n", strings + symbols64 [relocs[i].r_symbolnum].n_un.n_strx); } saves[relocs[i].r_symbolnum] = -1; } if(saves[relocs[i].r_symbolnum] != -1){ relocs[i].r_symbolnum = saves[relocs[i].r_symbolnum] - 1; } } else{ fatal_arch(arch, member, "bad external relocation entry " "%d (not external) in: ", i); } if((relocs[i].r_address & R_SCATTERED) == 0){ if(reloc_has_pair(object->mh_cputype, relocs[i].r_type)) i++; } else{ sreloc = (struct scattered_relocation_info *)relocs + i; if(reloc_has_pair(object->mh_cputype, sreloc->r_type)) i++; } } if(object->object_byte_sex != host_byte_sex) swap_relocation_info(relocs, object->dyst->nextrel, object->object_byte_sex); } /* * Check and update the indirect symbol table entries to make sure * referenced symbols are not stripped and refer to the new symbol * table indexes. */ if(object->dyst != NULL && object->dyst->nindirectsyms != 0){ if(object->object_byte_sex != host_byte_sex) swap_indirect_symbols(object->output_indirect_symtab, object->dyst->nindirectsyms, host_byte_sex); lc = object->load_commands; if(object->mh != NULL) ncmds = object->mh->ncmds; else ncmds = object->mh64->ncmds; for(i = 0; i < ncmds; i++){ if(lc->cmd == LC_SEGMENT && object->seg_linkedit != (struct segment_command *)lc){ 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_LAZY_SYMBOL_POINTERS || section_type == S_LAZY_DYLIB_SYMBOL_POINTERS || section_type == S_NON_LAZY_SYMBOL_POINTERS) stride = 4; else if(section_type == S_SYMBOL_STUBS) stride = s->reserved2; else{ s++; continue; } nitems = s->size / stride; contents = object->object_addr + s->offset; check_indirect_symtab(arch, member, object, nitems, s->reserved1, section_type, contents, symbols, symbols64, nsyms, strings, &missing_reloc_symbols, host_byte_sex); s++; } } else if(lc->cmd == LC_SEGMENT_64 && object->seg_linkedit64 != (struct segment_command_64 *)lc){ sg64 = (struct segment_command_64 *)lc; s64 = (struct section_64 *)((char *)sg64 + sizeof(struct segment_command_64)); for(j = 0; j < sg64->nsects; j++){ section_type = s64->flags & SECTION_TYPE; if(section_type == S_LAZY_SYMBOL_POINTERS || section_type == S_LAZY_DYLIB_SYMBOL_POINTERS || section_type == S_NON_LAZY_SYMBOL_POINTERS) stride = 8; else if(section_type == S_SYMBOL_STUBS) stride = s64->reserved2; else{ s64++; continue; } nitems = s64->size / stride; contents = object->object_addr + s64->offset; check_indirect_symtab(arch, member, object, nitems, s64->reserved1, section_type, contents, symbols, symbols64, nsyms, strings, &missing_reloc_symbols, host_byte_sex); s64++; } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } if(object->object_byte_sex != host_byte_sex) swap_indirect_symbols(object->output_indirect_symtab, object->dyst->nindirectsyms, object->object_byte_sex); } /* * Issue a warning if object file has a code signature that the * operation will invalidate it. */ if(object->code_sig_cmd != NULL) warning_arch(arch, member, "changes being made to the file will " "invalidate the code signature in: "); } /* * get_starting_syminfo_offset() returns the starting offset of the symbolic * info in the object file. */ static uint32_t get_starting_syminfo_offset( struct object *object) { uint32_t offset; if(object->seg_linkedit != NULL || object->seg_linkedit64 != NULL){ if(object->mh != NULL) offset = object->seg_linkedit->fileoff; else offset = object->seg_linkedit64->fileoff; } else{ offset = UINT_MAX; if(object->dyst != NULL && object->dyst->nlocrel != 0 && object->dyst->locreloff < offset) offset = object->dyst->locreloff; if(object->func_starts_info_cmd != NULL && object->func_starts_info_cmd->datasize != 0 && object->func_starts_info_cmd->dataoff < offset) offset = object->func_starts_info_cmd->dataoff; if(object->data_in_code_cmd != NULL && object->data_in_code_cmd->datasize != 0 && object->data_in_code_cmd->dataoff < offset) offset = object->data_in_code_cmd->dataoff; if(object->st->nsyms != 0 && object->st->symoff < offset) offset = object->st->symoff; if(object->dyst != NULL && object->dyst->nextrel != 0 && object->dyst->extreloff < offset) offset = object->dyst->extreloff; if(object->dyst != NULL && object->dyst->nindirectsyms != 0 && object->dyst->indirectsymoff < offset) offset = object->dyst->indirectsymoff; if(object->dyst != NULL && object->dyst->ntoc != 0 && object->dyst->tocoff < offset) offset = object->dyst->tocoff; if(object->dyst != NULL && object->dyst->nmodtab != 0 && object->dyst->modtaboff < offset) offset = object->dyst->modtaboff; if(object->dyst != NULL && object->dyst->nextrefsyms != 0 && object->dyst->extrefsymoff < offset) offset = object->dyst->extrefsymoff; if(object->st->strsize != 0 && object->st->stroff < offset) offset = object->st->stroff; } return(offset); } /* * check_object_relocs() is used to check and update the external relocation * entries from a section in an object file, to make sure referenced symbols * are not stripped and are changed to refer to the new symbol table indexes. */ static void check_object_relocs( struct arch *arch, struct member *member, struct object *object, char *segname, char *sectname, uint64_t sectsize, char *contents, struct relocation_info *relocs, uint32_t nreloc, struct nlist *symbols, struct nlist_64 *symbols64, uint32_t nsyms, char *strings, int32_t *missing_reloc_symbols, enum byte_sex host_byte_sex) { uint32_t k, n_strx; uint64_t n_value; #ifdef NMEDIT uint32_t value, n_ext; uint64_t value64; #endif struct scattered_relocation_info *sreloc; for(k = 0; k < nreloc; k++){ if((relocs[k].r_address & R_SCATTERED) == 0 && relocs[k].r_extern == 1){ if(relocs[k].r_symbolnum > nsyms){ fatal_arch(arch, member, "bad r_symbolnum for relocation " "entry %d in section (%.16s,%.16s) in: ", k, segname, sectname); } if(object->mh != NULL){ n_strx = symbols[relocs[k].r_symbolnum].n_un.n_strx; n_value = symbols[relocs[k].r_symbolnum].n_value; } else{ n_strx = symbols64[relocs[k].r_symbolnum].n_un.n_strx; n_value = symbols64[relocs[k].r_symbolnum].n_value; } #ifndef NMEDIT if(saves[relocs[k].r_symbolnum] == 0){ if(*missing_reloc_symbols == 0){ error_arch(arch, member, "symbols referenced by " "relocation entries that can't be stripped in: "); *missing_reloc_symbols = 1; } fprintf(stderr, "%s\n", strings + n_strx); saves[relocs[k].r_symbolnum] = -1; } #else /* defined(NMEDIT) */ /* * We are letting nmedit change global coalesed symbols into * statics in MH_OBJECT file types only. Relocation entries to * global coalesced symbols are external relocs. */ if(object->mh != NULL) n_ext = new_symbols[saves[relocs[k].r_symbolnum] - 1]. n_type & N_EXT; else n_ext = new_symbols64[saves[relocs[k].r_symbolnum] - 1]. n_type & N_EXT; if(n_ext != N_EXT && object->mh_cputype != CPU_TYPE_X86_64){ /* * We need to do the relocation for this external relocation * entry so the item to be relocated is correct for a local * relocation entry. We don't need to do this for x86-64. */ if(relocs[k].r_address + sizeof(int32_t) > sectsize){ fatal_arch(arch, member, "truncated or malformed " "object (r_address of relocation entry %u of " "section (%.16s,%.16s) extends past the end " "of the section)", k, segname, sectname); } if(object->mh != NULL){ value = *(uint32_t *) (contents + relocs[k].r_address); if(object->object_byte_sex != host_byte_sex) value = SWAP_INT(value); /* * We handle a very limited form here. Only VANILLA * (r_type == 0) long (r_length==2) absolute or pcrel * that won't need a scattered relocation entry. */ if(relocs[k].r_type != 0 || relocs[k].r_length != 2){ fatal_arch(arch, member, "don't have " "code to convert external relocation " "entry %d in section (%.16s,%.16s) " "for global coalesced symbol: %s " "in: ", k, segname, sectname, strings + n_strx); } value += n_value; if(object->object_byte_sex != host_byte_sex) value = SWAP_INT(value); *(uint32_t *)(contents + relocs[k].r_address) = value; } else{ value64 = *(uint64_t *)(contents + relocs[k].r_address); if(object->object_byte_sex != host_byte_sex) value64 = SWAP_LONG_LONG(value64); /* * We handle a very limited form here. Only VANILLA * (r_type == 0) quad (r_length==3) absolute or pcrel * that won't need a scattered relocation entry. */ if(relocs[k].r_type != 0 || relocs[k].r_length != 3){ fatal_arch(arch, member, "don't have " "code to convert external relocation " "entry %d in section (%.16s,%.16s) " "for global coalesced symbol: %s " "in: ", k, segname, sectname, strings + n_strx); } value64 += n_value; if(object->object_byte_sex != host_byte_sex) value64 = SWAP_LONG_LONG(value64); *(uint64_t *)(contents + relocs[k].r_address) = value64; } /* * Turn the extern reloc into a local. */ if(object->mh != NULL) relocs[k].r_symbolnum = new_symbols[saves[relocs[k].r_symbolnum] - 1].n_sect; else relocs[k].r_symbolnum = new_symbols64[saves[relocs[k].r_symbolnum] - 1].n_sect; relocs[k].r_extern = 0; } #endif /* NMEDIT */ if(relocs[k].r_extern == 1 && saves[relocs[k].r_symbolnum] != -1){ relocs[k].r_symbolnum = saves[relocs[k].r_symbolnum] - 1; } } if((relocs[k].r_address & R_SCATTERED) == 0){ if(reloc_has_pair(object->mh_cputype, relocs[k].r_type) == TRUE) k++; } else{ sreloc = (struct scattered_relocation_info *)relocs + k; if(reloc_has_pair(object->mh_cputype, sreloc->r_type) == TRUE) k++; } } } /* * check_indirect_symtab() checks and updates the indirect symbol table entries * to make sure referenced symbols are not stripped and refer to the new symbol * table indexes. */ static void check_indirect_symtab( struct arch *arch, struct member *member, struct object *object, uint32_t nitems, uint32_t reserved1, uint32_t section_type, char *contents, struct nlist *symbols, struct nlist_64 *symbols64, uint32_t nsyms, char *strings, int32_t *missing_reloc_symbols, enum byte_sex host_byte_sex) { uint32_t k, index; uint8_t n_type; uint32_t n_strx, value; uint64_t value64; enum bool made_local; for(k = 0; k < nitems; k++){ made_local = FALSE; index = object->output_indirect_symtab[reserved1 + k]; if(index == INDIRECT_SYMBOL_LOCAL || index == INDIRECT_SYMBOL_ABS || index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) continue; if(index > nsyms) fatal_arch(arch, member,"indirect symbol table entry %d (past " "the end of the symbol table) in: ", reserved1 + k); #ifdef NMEDIT if(pflag == 0 && nmedits[index] == TRUE && saves[index] != -1) #else if(saves[index] == 0) #endif { /* * Indirect symbol table entries for defined symbols in a * non-lazy pointer section that are not saved are changed to * INDIRECT_SYMBOL_LOCAL which their values just have to be * slid if the are not absolute symbols. */ if(object->mh != NULL){ n_type = symbols[index].n_type; n_strx = symbols[index].n_un.n_strx; } else{ n_type = symbols64[index].n_type; n_strx = symbols64[index].n_un.n_strx; } if((n_type & N_TYPE) != N_UNDF && (n_type & N_TYPE) != N_PBUD && section_type == S_NON_LAZY_SYMBOL_POINTERS){ object->output_indirect_symtab[reserved1 + k] = INDIRECT_SYMBOL_LOCAL; if((n_type & N_TYPE) == N_ABS) object->output_indirect_symtab[reserved1 + k] |= INDIRECT_SYMBOL_ABS; made_local = TRUE; /* * When creating a stub shared library the section contents * are not updated since they will be stripped. */ if(object->mh_filetype != MH_DYLIB_STUB){ if(object->mh != NULL){ value = symbols[index].n_value; if (symbols[index].n_desc & N_ARM_THUMB_DEF) value |= 1; if(object->object_byte_sex != host_byte_sex) value = SWAP_INT(value); *(uint32_t *)(contents + k * 4) = value; } else{ value64 = symbols64[index].n_value; if(object->object_byte_sex != host_byte_sex) value64 = SWAP_LONG_LONG(value64); *(uint64_t *)(contents + k * 8) = value64; } } } #ifdef NMEDIT else { object->output_indirect_symtab[reserved1 + k] = saves[index] - 1; } #else /* !defined(NMEDIT) */ else{ if(*missing_reloc_symbols == 0){ error_arch(arch, member, "symbols referenced by " "indirect symbol table entries that can't be " "stripped in: "); *missing_reloc_symbols = 1; } fprintf(stderr, "%s\n", strings + n_strx); saves[index] = -1; } #endif /* !defined(NMEDIT) */ } #ifdef NMEDIT else #else /* !defined(NMEDIT) */ if(made_local == FALSE && saves[index] != -1) #endif /* !defined(NMEDIT) */ { object->output_indirect_symtab[reserved1+k] = saves[index] - 1; } } } #ifndef NMEDIT /* * This is called if there is a -d option specified. It reads the file with * the strings in it and places them in the array debug_filenames and sorts * them by name. The file that contains the file names must have names one * per line with no white space (except the newlines). */ static void setup_debug_filenames( char *dfile) { int fd, i, strings_size; struct stat stat_buf; char *strings, *p; if((fd = open(dfile, O_RDONLY)) < 0){ system_error("can't open: %s", dfile); return; } if(fstat(fd, &stat_buf) == -1){ system_error("can't stat: %s", dfile); close(fd); return; } strings_size = stat_buf.st_size; strings = (char *)allocate(strings_size + 1); strings[strings_size] = '\0'; if(read(fd, strings, strings_size) != strings_size){ system_error("can't read: %s", dfile); close(fd); return; } p = strings; for(i = 0; i < strings_size; i++){ if(*p == '\n'){ *p = '\0'; ndebug_filenames++; } p++; } debug_filenames = (char **)allocate(ndebug_filenames * sizeof(char *)); p = strings; for(i = 0; i < ndebug_filenames; i++){ debug_filenames[i] = p; p += strlen(p) + 1; } qsort(debug_filenames, ndebug_filenames, sizeof(char *), (int (*)(const void *, const void *))cmp_qsort_filename); #ifdef DEBUG printf("Debug filenames:\n"); for(i = 0; i < ndebug_filenames; i++){ printf("filename = %s\n", debug_filenames[i]); } #endif /* DEBUG */ } /* * Strip the symbol table to the level specified by the command line arguments. * The new symbol table is built and new_symbols is left pointing to it. The * number of new symbols is left in new_nsyms, the new string table is built * and new_stings is left pointing to it and new_strsize is left containing it. * This routine returns zero if successfull and non-zero otherwise. */ static enum bool strip_symtab( struct arch *arch, struct member *member, struct object *object, struct dylib_table_of_contents *tocs, uint32_t ntoc, struct dylib_module *mods, struct dylib_module_64 *mods64, uint32_t nmodtab, struct dylib_reference *refs, uint32_t nextrefsyms) { uint32_t i, j, k, n, inew_syms, save_debug, missing_syms; uint32_t missing_symbols; char *p, *q, **pp, *basename; struct symbol_list *sp; uint32_t new_ext_strsize, len, *changes, inew_undefsyms; unsigned char nsects; struct load_command *lc; struct segment_command *sg; struct segment_command_64 *sg64; struct section *s, **sections; struct section_64 *s64, **sections64; uint32_t ncmds, mh_flags, s_flags, n_strx; struct nlist *sym; struct undef_map *undef_map; struct undef_map64 *undef_map64; uint8_t n_type, n_sect; uint16_t n_desc; uint64_t n_value; uint32_t module_name, iextdefsym, nextdefsym, ilocalsym, nlocalsym; uint32_t irefsym, nrefsym; enum bool has_dwarf, hack_5614542; save_debug = 0; if(saves != NULL) free(saves); changes = NULL; for(i = 0; i < nsave_symbols; i++) save_symbols[i].sym = NULL; for(i = 0; i < nremove_symbols; i++) remove_symbols[i].sym = NULL; if(member == NULL){ for(i = 0; i < nsave_symbols; i++) save_symbols[i].seen = FALSE; for(i = 0; i < nremove_symbols; i++) remove_symbols[i].seen = FALSE; } new_nsyms = 0; if(object->mh != NULL) new_strsize = sizeof(int32_t); else new_strsize = sizeof(int64_t); new_nlocalsym = 0; new_nextdefsym = 0; new_nundefsym = 0; new_ext_strsize = 0; /* * If this an object file that has DWARF debugging sections to strip * then we have to run ld -r on it. We also have to do this for * ARM objects because thumb symbols can't be stripped as they are * needed for proper linking in .o files. */ if(object->mh_filetype == MH_OBJECT && (Sflag || xflag)){ has_dwarf = FALSE; lc = object->load_commands; if(object->mh != NULL) ncmds = object->mh->ncmds; else ncmds = object->mh64->ncmds; for(i = 0; i < ncmds && has_dwarf == FALSE; 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++){ if(s->flags & S_ATTR_DEBUG){ has_dwarf = TRUE; break; } s++; } } else if(lc->cmd == LC_SEGMENT_64){ sg64 = (struct segment_command_64 *)lc; s64 = (struct section_64 *)((char *)sg64 + sizeof(struct segment_command_64)); for(j = 0; j < sg64->nsects; j++){ if(s64->flags & S_ATTR_DEBUG){ has_dwarf = TRUE; break; } s64++; } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } /* * If the file has dwarf symbols or is an ARM object then have * ld(1) do the "stripping" and make an ld -r version of the object. */ if(has_dwarf == TRUE || object->mh_cputype == CPU_TYPE_ARM) make_ld_r_object(arch, member, object); } /* * Because of the "design" of 64-bit object files and the lack of * local relocation entries it is not possible for strip(1) to do its * job without becoming a static link editor. The "design" does not * actually strip the symbols it simply renames them to things like * "l1000". And they become static symbols but still have external * relocation entries. Thus can never actually be stripped. Also some * symbols, *.eh, symbols are not even changed to these names if there * corresponding global symbol is not stripped. So strip(1) only * recourse is to use the unified linker to create an ld -r object then * save all resulting symbols (both static and global) and hope the user * does not notice the stripping is not what they asked for. */ if(object->mh_filetype == MH_OBJECT && (object->mh64 != NULL && object->ld_r_ofile == NULL)) make_ld_r_object(arch, member, object); /* * Since make_ld_r_object() may create an object with more symbols * this has to be done after make_ld_r_object() and nsyms is updated. */ saves = (int32_t *)allocate(nsyms * sizeof(int32_t)); bzero(saves, nsyms * sizeof(int32_t)); /* * Gather an array of section struct pointers so we can later determine * if we run into a global symbol in a coalesced section and not strip * those symbols. * statics. */ nsects = 0; lc = object->load_commands; if(object->mh != NULL) ncmds = object->mh->ncmds; else ncmds = object->mh64->ncmds; for(i = 0; i < ncmds; i++){ if(lc->cmd == LC_SEGMENT){ sg = (struct segment_command *)lc; nsects += sg->nsects; } else if(lc->cmd == LC_SEGMENT_64){ sg64 = (struct segment_command_64 *)lc; nsects += sg64->nsects; } lc = (struct load_command *)((char *)lc + lc->cmdsize); } if(object->mh != NULL){ sections = allocate(nsects * sizeof(struct section *)); sections64 = NULL; } else{ sections = NULL; sections64 = allocate(nsects * sizeof(struct section_64 *)); } nsects = 0; lc = object->load_commands; for(i = 0; i < 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++) sections[nsects++] = s++; } else if(lc->cmd == LC_SEGMENT_64){ sg64 = (struct segment_command_64 *)lc; s64 = (struct section_64 *)((char *)sg64 + sizeof(struct segment_command_64)); for(j = 0; j < sg64->nsects; j++) sections64[nsects++] = s64++; } lc = (struct load_command *)((char *)lc + lc->cmdsize); } for(i = 0; i < nsyms; i++){ s_flags = 0; if(object->mh != NULL){ mh_flags = object->mh->flags; n_strx = symbols[i].n_un.n_strx; n_type = symbols[i].n_type; n_sect = symbols[i].n_sect; if((n_type & N_TYPE) == N_SECT){ if(n_sect == 0 || n_sect > nsects){ error_arch(arch, member, "bad n_sect for symbol " "table entry %d in: ", i); return(FALSE); } s_flags = sections[n_sect - 1]->flags; } n_desc = symbols[i].n_desc; n_value = symbols[i].n_value; } else{ mh_flags = object->mh64->flags; n_strx = symbols64[i].n_un.n_strx; n_type = symbols64[i].n_type; n_sect = symbols64[i].n_sect; if((n_type & N_TYPE) == N_SECT){ if(n_sect == 0 || n_sect > nsects){ error_arch(arch, member, "bad n_sect for symbol " "table entry %d in: ", i); return(FALSE); } s_flags = sections64[n_sect - 1]->flags; } n_desc = symbols64[i].n_desc; n_value = symbols64[i].n_value; } if(n_strx != 0){ if(n_strx > strsize){ error_arch(arch, member, "bad string index for symbol " "table entry %d in: ", i); return(FALSE); } } if((n_type & N_TYPE) == N_INDR){ if(n_value != 0){ if(n_value > strsize){ error_arch(arch, member, "bad string index for " "indirect symbol table entry %d in: ", i); return(FALSE); } } } if((n_type & N_EXT) == 0){ /* local symbol */ /* * For x86_64 .o or ARM files we have run ld -r on them * we keeping all resulting symbols. */ if((object->mh_cputype == CPU_TYPE_X86_64 || object->mh_cputype == CPU_TYPE_ARM) && object->mh_filetype == MH_OBJECT){ if(n_strx != 0) new_strsize += strlen(strings + n_strx) + 1; new_nlocalsym++; new_nsyms++; saves[i] = new_nsyms; } /* * The cases a local symbol might be saved is with -X -S or * with -d filename. */ else if((!strip_all && (Xflag || Sflag)) || dfile){ if(n_type & N_STAB){ /* debug symbol */ if(dfile && n_type == N_SO){ if(n_strx != 0){ basename = strrchr(strings + n_strx, '/'); if(basename != NULL) basename++; else basename = strings + n_strx; pp = bsearch(basename, debug_filenames, ndebug_filenames, sizeof(char *), (int (*)(const void *, const void *) )cmp_bsearch_filename); /* * Save the bracketing N_SO. For each N_SO that * has a filename there is an N_SO that has a * name of "" which ends the stabs for that file */ if(*basename != '\0'){ if(pp != NULL) save_debug = 1; else save_debug = 0; } else{ /* * This is a bracketing SO so if we are * currently saving debug symbols save this * last one and turn off saving debug syms. */ if(save_debug){ if(n_strx != 0) new_strsize += strlen(strings + n_strx) + 1; new_nlocalsym++; new_nsyms++; saves[i] = new_nsyms; } save_debug = 0; } } else{ save_debug = 0; } } if(saves[i] == 0 && (!Sflag || save_debug)){ if(n_strx != 0) new_strsize += strlen(strings + n_strx) + 1; new_nlocalsym++; new_nsyms++; saves[i] = new_nsyms; } } else{ /* non-debug local symbol */ if(xflag == 0 && (Sflag || Xflag)){ if(Xflag == 0 || (n_strx != 0 && strings[n_strx] != 'L')){ /* * If this file is a for the dynamic linker and * this symbol is in a section marked so that * static symbols are stripped then don't * keep this symbol. */ if((mh_flags & MH_DYLDLINK) != MH_DYLDLINK || (n_type & N_TYPE) != N_SECT || (s_flags & S_ATTR_STRIP_STATIC_SYMS) != S_ATTR_STRIP_STATIC_SYMS){ new_strsize += strlen(strings + n_strx) + 1; new_nlocalsym++; new_nsyms++; saves[i] = new_nsyms; } } } /* * Treat a local symbol that was a private extern as if * were global if it is referenced by a module and save * it. */ if((n_type & N_PEXT) == N_PEXT){ if(saves[i] == 0 && private_extern_reference_by_module( i, refs ,nextrefsyms) == TRUE){ if(n_strx != 0) new_strsize += strlen(strings + n_strx) + 1; new_nlocalsym++; new_nsyms++; saves[i] = new_nsyms; } /* * We need to save symbols that were private externs * that are used with indirect symbols. */ if(saves[i] == 0 && symbol_pointer_used(i, indirectsyms, nindirectsyms) == TRUE){ if(n_strx != 0){ len = strlen(strings + n_strx) + 1; new_strsize += len; } new_nlocalsym++; new_nsyms++; saves[i] = new_nsyms; } } } } /* * Treat a local symbol that was a private extern as if were * global if it is not referenced by a module. */ else if((n_type & N_PEXT) == N_PEXT){ if(saves[i] == 0 && sfile){ sp = bsearch(strings + n_strx, save_symbols, nsave_symbols, sizeof(struct symbol_list), (int (*)(const void *, const void *)) symbol_list_bsearch); if(sp != NULL){ if(sp->sym == NULL){ if(object->mh != NULL) sp->sym = &(symbols[i]); else sp->sym = &(symbols64[i]); sp->seen = TRUE; } if(n_strx != 0) new_strsize += strlen(strings + n_strx) + 1; new_nlocalsym++; new_nsyms++; saves[i] = new_nsyms; } } if(saves[i] == 0 && private_extern_reference_by_module( i, refs ,nextrefsyms) == TRUE){ if(n_strx != 0) new_strsize += strlen(strings + n_strx) + 1; new_nlocalsym++; new_nsyms++; saves[i] = new_nsyms; } /* * We need to save symbols that were private externs that * are used with indirect symbols. */ if(saves[i] == 0 && symbol_pointer_used(i, indirectsyms, nindirectsyms) == TRUE){ if(n_strx != 0){ len = strlen(strings + n_strx) + 1; new_strsize += len; } new_nlocalsym++; new_nsyms++; saves[i] = new_nsyms; } } } else{ /* global symbol */ /* * strip -R on an x86_64 .o file should do nothing. */ if(Rfile && (object->mh != NULL || object->mh64->cputype != CPU_TYPE_X86_64 || object->mh64->filetype != MH_OBJECT)){ sp = bsearch(strings + n_strx, remove_symbols, nremove_symbols, sizeof(struct symbol_list), (int (*)(const void *, const void *)) symbol_list_bsearch); if(sp != NULL){ if((n_type & N_TYPE) == N_UNDF || (n_type & N_TYPE) == N_PBUD){ error_arch(arch, member, "symbol: %s undefined" " and can't be stripped from: ", sp->name); } else if(sp->sym != NULL){ sym = (struct nlist *)sp->sym; if((sym->n_type & N_PEXT) != N_PEXT) error_arch(arch, member, "more than one symbol " "for: %s found in: ", sp->name); } else{ if(object->mh != NULL) sp->sym = &(symbols[i]); else sp->sym = &(symbols64[i]); sp->seen = TRUE; } if(n_desc & REFERENCED_DYNAMICALLY){ error_arch(arch, member, "symbol: %s is dynamically" " referenced and can't be stripped " "from: ", sp->name); } if((n_type & N_TYPE) == N_SECT && (s_flags & SECTION_TYPE) == S_COALESCED){ error_arch(arch, member, "symbol: %s is a global " "coalesced symbol and can't be " "stripped from: ", sp->name); } /* don't save this symbol */ continue; } } if(Aflag && (n_type & N_TYPE) == N_ABS && (n_value != 0 || (n_strx != 0 && strncmp(strings + n_strx, ".objc_class_name_", sizeof(".objc_class_name_") - 1) == 0))){ len = strlen(strings + n_strx) + 1; new_strsize += len; new_ext_strsize += len; new_nextdefsym++; new_nsyms++; saves[i] = new_nsyms; } if(saves[i] == 0 && (uflag || default_dyld_executable) && ((((n_type & N_TYPE) == N_UNDF) && n_value == 0) || (n_type & N_TYPE) == N_PBUD)){ if(n_strx != 0){ len = strlen(strings + n_strx) + 1; new_strsize += len; new_ext_strsize += len; } new_nundefsym++; new_nsyms++; saves[i] = new_nsyms; } if(saves[i] == 0 && nflag && (n_type & N_TYPE) == N_SECT){ if(n_strx != 0){ len = strlen(strings + n_strx) + 1; new_strsize += len; new_ext_strsize += len; } new_nextdefsym++; new_nsyms++; saves[i] = new_nsyms; } if(saves[i] == 0 && sfile){ sp = bsearch(strings + n_strx, save_symbols, nsave_symbols, sizeof(struct symbol_list), (int (*)(const void *, const void *)) symbol_list_bsearch); if(sp != NULL){ if(sp->sym != NULL){ sym = (struct nlist *)sp->sym; if((sym->n_type & N_PEXT) != N_PEXT) error_arch(arch, member, "more than one symbol " "for: %s found in: ", sp->name); } else{ if(object->mh != NULL) sp->sym = &(symbols[i]); else sp->sym = &(symbols64[i]); sp->seen = TRUE; len = strlen(strings + n_strx) + 1; new_strsize += len; new_ext_strsize += len; if((n_type & N_TYPE) == N_UNDF || (n_type & N_TYPE) == N_PBUD) new_nundefsym++; else new_nextdefsym++; new_nsyms++; saves[i] = new_nsyms; } } } /* * We only need to save coalesced symbols that are used as * indirect symbols in 32-bit applications. * * In 64-bit applications, we only need to save coalesced * symbols that are used as weak definitions. */ if(object->mh != NULL && saves[i] == 0 && (n_type & N_TYPE) == N_SECT && (s_flags & SECTION_TYPE) == S_COALESCED && symbol_pointer_used(i, indirectsyms, nindirectsyms) == TRUE){ if(n_strx != 0){ len = strlen(strings + n_strx) + 1; new_strsize += len; new_ext_strsize += len; } new_nextdefsym++; new_nsyms++; saves[i] = new_nsyms; } if(saves[i] == 0 && (n_type & N_TYPE) == N_SECT && (n_desc & N_WEAK_DEF) != 0){ if(n_strx != 0){ len = strlen(strings + n_strx) + 1; new_strsize += len; new_ext_strsize += len; } new_nextdefsym++; new_nsyms++; saves[i] = new_nsyms; } if(saves[i] == 0 && ((Xflag || Sflag || xflag) || ((rflag || default_dyld_executable) && n_desc & REFERENCED_DYNAMICALLY))){ len = strlen(strings + n_strx) + 1; new_strsize += len; new_ext_strsize += len; if((n_type & N_TYPE) == N_INDR){ len = strlen(strings + n_value) + 1; new_strsize += len; new_ext_strsize += len; } if((n_type & N_TYPE) == N_UNDF || (n_type & N_TYPE) == N_PBUD) new_nundefsym++; else new_nextdefsym++; new_nsyms++; saves[i] = new_nsyms; } /* * For x86_64 .o files we have run ld -r on them and are stuck * keeping all resulting symbols. */ if(saves[i] == 0 && object->mh == NULL && object->mh64->cputype == CPU_TYPE_X86_64 && object->mh64->filetype == MH_OBJECT){ len = strlen(strings + n_strx) + 1; new_strsize += len; new_ext_strsize += len; if((n_type & N_TYPE) == N_INDR){ len = strlen(strings + n_value) + 1; new_strsize += len; new_ext_strsize += len; } if((n_type & N_TYPE) == N_UNDF || (n_type & N_TYPE) == N_PBUD) new_nundefsym++; else new_nextdefsym++; new_nsyms++; saves[i] = new_nsyms; } } } /* * The module table's module names are placed with the external strings. * So size them and add this to the external string size. */ for(i = 0; i < nmodtab; i++){ if(object->mh != NULL) module_name = mods[i].module_name; else module_name = mods64[i].module_name; if(module_name == 0 || module_name > strsize){ error_arch(arch, member, "bad string index for module_name " "of module table entry %d in: ", i); return(FALSE); } len = strlen(strings + module_name) + 1; new_strsize += len; new_ext_strsize += len; } /* * Updating the reference table may require a symbol not yet listed as * as saved to be present in the output file. If a defined external * symbol is removed and there is a undefined reference to it in the * reference table an undefined symbol needs to be created for it in * the output file. If this happens the number of new symbols and size * of the new strings are adjusted. And the array changes[] is set to * map the old symbol index to the new symbol index for the symbol that * is changed to an undefined symbol. */ missing_symbols = 0; if(ref_saves != NULL) free(ref_saves); ref_saves = (int32_t *)allocate(nextrefsyms * sizeof(int32_t)); bzero(ref_saves, nextrefsyms * sizeof(int32_t)); changes = (uint32_t *)allocate(nsyms * sizeof(int32_t)); bzero(changes, nsyms * sizeof(int32_t)); new_nextrefsyms = 0; for(i = 0; i < nextrefsyms; i++){ if(refs[i].isym > nsyms){ error_arch(arch, member, "bad symbol table index for " "reference table entry %d in: ", i); return(FALSE); } if(saves[refs[i].isym]){ new_nextrefsyms++; ref_saves[i] = new_nextrefsyms; } else{ if(refs[i].flags == REFERENCE_FLAG_UNDEFINED_NON_LAZY || refs[i].flags == REFERENCE_FLAG_UNDEFINED_LAZY){ if(changes[refs[i].isym] == 0){ if(object->mh != NULL) n_strx = symbols[refs[i].isym].n_un.n_strx; else n_strx = symbols64[refs[i].isym].n_un.n_strx; len = strlen(strings + n_strx) + 1; new_strsize += len; new_ext_strsize += len; new_nundefsym++; new_nsyms++; changes[refs[i].isym] = new_nsyms; new_nextrefsyms++; ref_saves[i] = new_nextrefsyms; } } else{ if(refs[i].flags == REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY || refs[i].flags == REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY){ if(missing_symbols == 0){ error_arch(arch, member, "private extern symbols " "referenced by modules can't be stripped in: "); missing_symbols = 1; } if(object->mh != NULL) n_strx = symbols[refs[i].isym].n_un.n_strx; else n_strx = symbols64[refs[i].isym].n_un.n_strx; fprintf(stderr, "%s\n", strings + n_strx); saves[refs[i].isym] = -1; } } } } if(missing_symbols == 1) return(FALSE); if(member == NULL){ missing_syms = 0; if(iflag == 0){ for(i = 0; i < nsave_symbols; i++){ if(save_symbols[i].sym == NULL){ if(missing_syms == 0){ error_arch(arch, member, "symbols names listed " "in: %s not in: ", sfile); missing_syms = 1; } fprintf(stderr, "%s\n", save_symbols[i].name); } } } missing_syms = 0; /* * strip -R on an x86_64 .o file should do nothing. */ if(iflag == 0 && (object->mh != NULL || object->mh64->cputype != CPU_TYPE_X86_64 || object->mh64->filetype != MH_OBJECT)){ for(i = 0; i < nremove_symbols; i++){ if(remove_symbols[i].sym == NULL){ if(missing_syms == 0){ error_arch(arch, member, "symbols names listed " "in: %s not in: ", Rfile); missing_syms = 1; } fprintf(stderr, "%s\n", remove_symbols[i].name); } } } } /* * If there is a chance that we could end up with an indirect symbol * with an index of zero we need to avoid that due to a work around * in the dynamic linker for a bug it is working around that was in * the old classic static linker. See radar bug 5614542 and the * related bugs 3685312 and 3534709. * * A reasonable way to do this to know that local symbols are first in * the symbol table. So if we have any local symbols this won't happen * and if there are no indirect symbols it will also not happen. Past * that we'll just add a local symbol so it will end up at symbol index * zero and avoid any indirect symbol having that index. * * If one really wanted they could build up the new symbol table then * look at all the indirect symbol table entries to see if any of them * have an index of zero then in that case throw that new symbol table * away and rebuild the symbol and string table once again after adding * a local symbol. This seems not all that resonable to save one symbol * table entry and a few bytes in the string table for the complexity it * would add and what it would save. */ if(new_nlocalsym == 0 && nindirectsyms != 0){ len = strlen("radr://5614542") + 1; new_strsize += len; new_nlocalsym++; new_nsyms++; hack_5614542 = TRUE; } else{ hack_5614542 = FALSE; } if(object->mh != NULL){ new_symbols = (struct nlist *) allocate(new_nsyms * sizeof(struct nlist)); new_symbols64 = NULL; } else{ new_symbols = NULL; new_symbols64 = (struct nlist_64 *) allocate(new_nsyms * sizeof(struct nlist_64)); } if(object->mh != NULL) new_strsize = rnd(new_strsize, sizeof(int32_t)); else new_strsize = rnd(new_strsize, sizeof(int64_t)); new_strings = (char *)allocate(new_strsize); if(object->mh != NULL){ new_strings[new_strsize - 3] = '\0'; new_strings[new_strsize - 2] = '\0'; new_strings[new_strsize - 1] = '\0'; } else{ new_strings[new_strsize - 7] = '\0'; new_strings[new_strsize - 6] = '\0'; new_strings[new_strsize - 5] = '\0'; new_strings[new_strsize - 4] = '\0'; new_strings[new_strsize - 3] = '\0'; new_strings[new_strsize - 2] = '\0'; new_strings[new_strsize - 1] = '\0'; } memset(new_strings, '\0', sizeof(int32_t)); p = new_strings + sizeof(int32_t); q = p + new_ext_strsize; /* * If all strings were stripped set the size to zero but only for 32-bit * because the unified linker seems to set the filesize of empty .o * files to include the string table. */ if(object->mh != NULL && new_strsize == sizeof(int32_t)) new_strsize = 0; /* * Now create a symbol table and string table in this order * symbol table * local symbols * external defined symbols * undefined symbols * string table * external strings * local strings */ inew_syms = 0; /* * If we are doing the hack for radar bug 5614542 (see above) add the * one local symbol and string. * * We use an N_OPT stab which should be safe to use and not mess any * thing up. In Mac OS X, gcc(1) writes one N_OPT stab saying the file * is compiled with gcc(1). Then gdb(1) looks for that stab, but it * also looks at the name. If the name string is "gcc_compiled" or * "gcc2_compiled" gdb(1) sets its "compiled by gcc flag. If the N_OPT * is emitted INSIDE an N_SO section, then gdb(1) thinks that object * module was compiled by Sun's compiler, which apparently sticks one * outermost N_LBRAC/N_RBRAC pair, which gdb(1) strips off. But if the * N_OPT comes before any N_SO stabs, then gdb(1) will just ignore it. * Since this N_OPT is the first local symbol, it will always come * before any N_SO stabs that might be around and should be fine. */ if(hack_5614542 == TRUE){ if(object->mh != NULL){ new_symbols[inew_syms].n_type = N_OPT; new_symbols[inew_syms].n_sect = NO_SECT; new_symbols[inew_syms].n_desc = 0; new_symbols[inew_syms].n_value = 0x05614542; } else{ new_symbols64[inew_syms].n_type = N_OPT; new_symbols64[inew_syms].n_sect = NO_SECT; new_symbols64[inew_syms].n_desc = 0; new_symbols64[inew_syms].n_value = 0x05614542; } strcpy(q, "radr://5614542"); if(object->mh != NULL) new_symbols[inew_syms].n_un.n_strx = q - new_strings; else new_symbols64[inew_syms].n_un.n_strx = q - new_strings; q += strlen(q) + 1; inew_syms++; } for(i = 0; i < nsyms; i++){ if(saves[i]){ if(object->mh != NULL){ n_strx = symbols[i].n_un.n_strx; n_type = symbols[i].n_type; } else{ n_strx = symbols64[i].n_un.n_strx; n_type = symbols64[i].n_type; } if((n_type & N_EXT) == 0){ if(object->mh != NULL) new_symbols[inew_syms] = symbols[i]; else new_symbols64[inew_syms] = symbols64[i]; if(n_strx != 0){ strcpy(q, strings + n_strx); if(object->mh != NULL) new_symbols[inew_syms].n_un.n_strx = q - new_strings; else new_symbols64[inew_syms].n_un.n_strx = q - new_strings; q += strlen(q) + 1; } inew_syms++; saves[i] = inew_syms; } } } #ifdef TRIE_SUPPORT inew_nextdefsym = inew_syms; #endif /* TRIE_SUPPORT */ for(i = 0; i < nsyms; i++){ if(saves[i]){ if(object->mh != NULL){ n_strx = symbols[i].n_un.n_strx; n_type = symbols[i].n_type; n_value = symbols[i].n_value; } else{ n_strx = symbols64[i].n_un.n_strx; n_type = symbols64[i].n_type; n_value = symbols64[i].n_value; } if((n_type & N_EXT) == N_EXT && ((n_type & N_TYPE) != N_UNDF && (n_type & N_TYPE) != N_PBUD)){ if(object->mh != NULL) new_symbols[inew_syms] = symbols[i]; else new_symbols64[inew_syms] = symbols64[i]; if(n_strx != 0){ strcpy(p, strings + n_strx); if(object->mh != NULL) new_symbols[inew_syms].n_un.n_strx = p - new_strings; else new_symbols64[inew_syms].n_un.n_strx = p - new_strings; p += strlen(p) + 1; } if((n_type & N_TYPE) == N_INDR){ if(n_value != 0){ strcpy(p, strings + n_value); if(object->mh != NULL) new_symbols[inew_syms].n_value = p - new_strings; else new_symbols64[inew_syms].n_value = p - new_strings; p += strlen(p) + 1; } } inew_syms++; saves[i] = inew_syms; } } } /* * Build the new undefined symbols into a map and sort it. */ inew_undefsyms = 0; if(object->mh != NULL){ undef_map = (struct undef_map *)allocate(new_nundefsym * sizeof(struct undef_map)); undef_map64 = NULL; } else{ undef_map = NULL; undef_map64 = (struct undef_map64 *)allocate(new_nundefsym * sizeof(struct undef_map64)); } for(i = 0; i < nsyms; i++){ if(saves[i]){ if(object->mh != NULL){ n_strx = symbols[i].n_un.n_strx; n_type = symbols[i].n_type; } else{ n_strx = symbols64[i].n_un.n_strx; n_type = symbols64[i].n_type; } if((n_type & N_EXT) == N_EXT && ((n_type & N_TYPE) == N_UNDF || (n_type & N_TYPE) == N_PBUD)){ if(object->mh != NULL) undef_map[inew_undefsyms].symbol = symbols[i]; else undef_map64[inew_undefsyms].symbol64 = symbols64[i]; if(n_strx != 0){ strcpy(p, strings + n_strx); if(object->mh != NULL) undef_map[inew_undefsyms].symbol.n_un.n_strx = p - new_strings; else undef_map64[inew_undefsyms].symbol64.n_un.n_strx = p - new_strings; p += strlen(p) + 1; } if(object->mh != NULL) undef_map[inew_undefsyms].index = i; else undef_map64[inew_undefsyms].index = i; inew_undefsyms++; } } } for(i = 0; i < nsyms; i++){ if(changes[i]){ if(object->mh != NULL) n_strx = symbols[i].n_un.n_strx; else n_strx = symbols64[i].n_un.n_strx; if(n_strx != 0){ strcpy(p, strings + n_strx); if(object->mh != NULL) undef_map[inew_undefsyms].symbol.n_un.n_strx = p - new_strings; else undef_map64[inew_undefsyms].symbol64.n_un.n_strx = p - new_strings; p += strlen(p) + 1; } if(object->mh != NULL){ undef_map[inew_undefsyms].symbol.n_type = N_UNDF | N_EXT; undef_map[inew_undefsyms].symbol.n_sect = NO_SECT; undef_map[inew_undefsyms].symbol.n_desc = 0; undef_map[inew_undefsyms].symbol.n_value = 0; undef_map[inew_undefsyms].index = i; } else{ undef_map64[inew_undefsyms].symbol64.n_type = N_UNDF |N_EXT; undef_map64[inew_undefsyms].symbol64.n_sect = NO_SECT; undef_map64[inew_undefsyms].symbol64.n_desc = 0; undef_map64[inew_undefsyms].symbol64.n_value = 0; undef_map64[inew_undefsyms].index = i; } inew_undefsyms++; } } /* Sort the undefined symbols by name */ qsort_strings = new_strings; if(object->mh != NULL) qsort(undef_map, new_nundefsym, sizeof(struct undef_map), (int (*)(const void *, const void *))cmp_qsort_undef_map); else qsort(undef_map64, new_nundefsym, sizeof(struct undef_map64), (int (*)(const void *, const void *))cmp_qsort_undef_map_64); /* Copy the symbols now in sorted order into new_symbols */ for(i = 0; i < new_nundefsym; i++){ if(object->mh != NULL){ new_symbols[inew_syms] = undef_map[i].symbol; inew_syms++; saves[undef_map[i].index] = inew_syms; } else{ new_symbols64[inew_syms] = undef_map64[i].symbol64; inew_syms++; saves[undef_map64[i].index] = inew_syms; } } /* * Fixup the module table's module name strings adding them to the * string table. Also fix the indexes into the symbol table for * external and local symbols. And fix up the indexes into the * reference table. */ for(i = 0; i < nmodtab; i++){ if(object->mh != NULL){ strcpy(p, strings + mods[i].module_name); mods[i].module_name = p - new_strings; iextdefsym = mods[i].iextdefsym; nextdefsym = mods[i].nextdefsym; ilocalsym = mods[i].ilocalsym; nlocalsym = mods[i].nlocalsym; irefsym = mods[i].irefsym; nrefsym = mods[i].nrefsym; } else{ strcpy(p, strings + mods64[i].module_name); mods64[i].module_name = p - new_strings; iextdefsym = mods64[i].iextdefsym; nextdefsym = mods64[i].nextdefsym; ilocalsym = mods64[i].ilocalsym; nlocalsym = mods64[i].nlocalsym; irefsym = mods64[i].irefsym; nrefsym = mods64[i].nrefsym; } p += strlen(p) + 1; if(iextdefsym > nsyms){ error_arch(arch, member, "bad index into externally defined " "symbols of module table entry %d in: ", i); return(FALSE); } if(iextdefsym + nextdefsym > nsyms){ error_arch(arch, member, "bad number of externally defined " "symbols of module table entry %d in: ", i); return(FALSE); } for(j = iextdefsym; j < iextdefsym + nextdefsym; j++){ if(saves[j] != 0 && changes[j] == 0) break; } n = 0; for(k = j; k < iextdefsym + nextdefsym; k++){ if(saves[k] != 0 && changes[k] == 0) n++; } if(n == 0){ if(object->mh != NULL){ mods[i].iextdefsym = 0; mods[i].nextdefsym = 0; } else{ mods64[i].iextdefsym = 0; mods64[i].nextdefsym = 0; } } else{ if(object->mh != NULL){ mods[i].iextdefsym = saves[j] - 1; mods[i].nextdefsym = n; } else{ mods64[i].iextdefsym = saves[j] - 1; mods64[i].nextdefsym = n; } } if(ilocalsym > nsyms){ error_arch(arch, member, "bad index into symbols for local " "symbols of module table entry %d in: ", i); return(FALSE); } if(ilocalsym + nlocalsym > nsyms){ error_arch(arch, member, "bad number of local " "symbols of module table entry %d in: ", i); return(FALSE); } for(j = ilocalsym; j < ilocalsym + nlocalsym; j++){ if(saves[j] != 0) break; } n = 0; for(k = j; k < ilocalsym + nlocalsym; k++){ if(saves[k] != 0) n++; } if(n == 0){ if(object->mh != NULL){ mods[i].ilocalsym = 0; mods[i].nlocalsym = 0; } else{ mods64[i].ilocalsym = 0; mods64[i].nlocalsym = 0; } } else{ if(object->mh != NULL){ mods[i].ilocalsym = saves[j] - 1; mods[i].nlocalsym = n; } else{ mods64[i].ilocalsym = saves[j] - 1; mods64[i].nlocalsym = n; } } if(irefsym > nextrefsyms){ error_arch(arch, member, "bad index into reference table " "of module table entry %d in: ", i); return(FALSE); } if(irefsym + nrefsym > nextrefsyms){ error_arch(arch, member, "bad number of reference table " "entries of module table entry %d in: ", i); return(FALSE); } for(j = irefsym; j < irefsym + nrefsym; j++){ if(ref_saves[j] != 0) break; } n = 0; for(k = j; k < irefsym + nrefsym; k++){ if(ref_saves[k] != 0) n++; } if(n == 0){ if(object->mh != NULL){ mods[i].irefsym = 0; mods[i].nrefsym = 0; } else{ mods64[i].irefsym = 0; mods64[i].nrefsym = 0; } } else{ if(object->mh != NULL){ mods[i].irefsym = ref_saves[j] - 1; mods[i].nrefsym = n; } else{ mods64[i].irefsym = ref_saves[j] - 1; mods64[i].nrefsym = n; } } } /* * Create a new reference table. */ new_refs = allocate(new_nextrefsyms * sizeof(struct dylib_reference)); j = 0; for(i = 0; i < nextrefsyms; i++){ if(ref_saves[i]){ if(saves[refs[i].isym]){ new_refs[j].isym = saves[refs[i].isym] - 1; new_refs[j].flags = refs[i].flags; } else{ if(refs[i].flags == REFERENCE_FLAG_UNDEFINED_NON_LAZY || refs[i].flags == REFERENCE_FLAG_UNDEFINED_LAZY){ new_refs[j].isym = changes[refs[i].isym] - 1; new_refs[j].flags = refs[i].flags; } } j++; } } /* * Create a new dylib table of contents. */ new_ntoc = 0; for(i = 0; i < ntoc; i++){ if(tocs[i].symbol_index >= nsyms){ error_arch(arch, member, "bad symbol index for table of " "contents table entry %d in: ", i); return(FALSE); } if(saves[tocs[i].symbol_index] != 0 && changes[tocs[i].symbol_index] == 0) new_ntoc++; } new_tocs = allocate(new_ntoc * sizeof(struct dylib_table_of_contents)); j = 0; for(i = 0; i < ntoc; i++){ if(saves[tocs[i].symbol_index] != 0 && changes[tocs[i].symbol_index] == 0){ new_tocs[j].symbol_index = saves[tocs[i].symbol_index] - 1; new_tocs[j].module_index = tocs[i].module_index; j++; } } #ifdef TRIE_SUPPORT /* * Update the export trie if it has one but only call the the * prune_trie() routine when we are removing global symbols as is * done with default stripping of a dyld executable or with the -s * or -R options. */ if(object->dyld_info != NULL && object->dyld_info->export_size != 0 && (default_dyld_executable || sfile != NULL || Rfile != NULL)){ const char *error_string; uint32_t trie_new_size; error_string = prune_trie((uint8_t *)(object->object_addr + object->dyld_info->export_off), object->dyld_info->export_size, prune, &trie_new_size); if(error_string != NULL){ error_arch(arch, member, "%s", error_string); return(FALSE); } } #endif /* TRIE_SUPPORT */ if(undef_map != NULL) free(undef_map); if(undef_map64 != NULL) free(undef_map64); if(changes != NULL) free(changes); if(sections != NULL) free(sections); if(sections64 != NULL) free(sections64); if(errors == 0) return(TRUE); else return(FALSE); } #ifdef TRIE_SUPPORT /* * prune() is called by prune_trie() and passed a name of an external symbol * in the trie. It returns 1 if the symbols is to be pruned out and 0 if the * symbol is to be kept. * * Note that it may seem like a linear search of the new symbols would not be * the best approach but in 10.6 the only defined global symbol left in a * stripped executable is __mh_execute_header and new_nextdefsym is usually 1 * so this never actually loops in practice. */ static int prune( const char *name) { uint32_t i; for(i = 0; i < new_nextdefsym; i++){ if(new_symbols != NULL){ if(strcmp(name, new_strings + new_symbols[inew_nextdefsym + i] .n_un.n_strx) == 0) return(0); } else{ if(strcmp(name, new_strings + new_symbols64[inew_nextdefsym + i] .n_un.n_strx) == 0) return(0); } } return(1); } #endif /* TRIE_SUPPORT */ /* * make_ld_r_object() takes the object file contents referenced by the passed * data structures, writes that to a temporary file, runs "ld -r" plus the * specified stripping option creating a second temporary file, reads that file * in and replaces the object file contents with that and resets the variables * pointing to the symbol, string and indirect tables. */ static void make_ld_r_object( struct arch *arch, struct member *member, struct object *object) { enum byte_sex host_byte_sex; char *input_file, *output_file; int fd; struct ofile *ld_r_ofile; struct arch *ld_r_archs; uint32_t ld_r_narchs, save_errors; host_byte_sex = get_host_byte_sex(); /* * Swap the object file back into its bytesex before writing it to the * temporary file if needed. */ if(object->object_byte_sex != host_byte_sex){ if(object->mh != NULL){ if(swap_object_headers(object->mh, object->load_commands) == FALSE) fatal("internal error: swap_object_headers() failed"); swap_nlist(symbols, nsyms, object->object_byte_sex); } else{ if(swap_object_headers(object->mh64, object->load_commands) == FALSE) fatal("internal error: swap_object_headers() failed"); swap_nlist_64(symbols64, nsyms, object->object_byte_sex); } swap_indirect_symbols(indirectsyms, nindirectsyms, object->object_byte_sex); } /* * Create an input object file for the ld -r command from the bytes * of this arch's object file. */ input_file = makestr("/tmp/strip.XXXXXX", NULL); input_file = mktemp(input_file); if((fd = open(input_file, O_WRONLY|O_CREAT, 0600)) < 0) system_fatal("can't open temporary file: %s", input_file); if(write(fd, object->object_addr, object->object_size) != object->object_size) system_fatal("can't write temporary file: %s", input_file); if(close(fd) == -1) system_fatal("can't close temporary file: %s", input_file); /* * Create a temporary name for the output file of the ld -r */ output_file = makestr("/tmp/strip.XXXXXX", NULL); output_file = mktemp(output_file); /* * Create the ld -r command line and execute it. */ reset_execute_list(); add_execute_list_with_prefix("ld"); add_execute_list("-keep_private_externs"); add_execute_list("-r"); if(Sflag) add_execute_list("-S"); if(xflag) add_execute_list("-x"); add_execute_list(input_file); add_execute_list("-o"); add_execute_list(output_file); if(sfile != NULL){ add_execute_list("-x"); add_execute_list("-exported_symbols_list"); add_execute_list(sfile); } if(Rfile != NULL){ add_execute_list("-unexported_symbols_list"); add_execute_list(Rfile); } if(execute_list(vflag) == 0) fatal("internal link edit command failed"); save_errors = errors; errors = 0; /* breakout the output file of the ld -f for processing */ ld_r_ofile = breakout(output_file, &ld_r_archs, &ld_r_narchs, FALSE); if(errors) goto make_ld_r_object_cleanup; /* checkout the file for symbol table replacement processing */ checkout(ld_r_archs, ld_r_narchs); /* * Make sure the output of the ld -r is an object file with one arch. */ if(ld_r_narchs != 1 || ld_r_archs->type != OFILE_Mach_O || ld_r_archs->object == NULL || ld_r_archs->object->mh_filetype != MH_OBJECT) fatal("internal link edit command failed to produce a thin Mach-O " "object file"); /* * Now reset all the data of the input object with the ld -r output * object file. */ nsyms = ld_r_archs->object->st->nsyms; if(ld_r_archs->object->mh != NULL){ symbols = (struct nlist *) (ld_r_archs->object->object_addr + ld_r_archs->object->st->symoff); if(ld_r_archs->object->object_byte_sex != host_byte_sex) swap_nlist(symbols, nsyms, host_byte_sex); symbols64 = NULL; } else{ symbols = NULL; symbols64 = (struct nlist_64 *) (ld_r_archs->object->object_addr + ld_r_archs->object->st->symoff); if(ld_r_archs->object->object_byte_sex != host_byte_sex) swap_nlist_64(symbols64, nsyms, host_byte_sex); } strings = ld_r_archs->object->object_addr + ld_r_archs->object->st->stroff; strsize = ld_r_archs->object->st->strsize; if(ld_r_archs->object->dyst != NULL && ld_r_archs->object->dyst->nindirectsyms != 0){ nindirectsyms = ld_r_archs->object->dyst->nindirectsyms; indirectsyms = (uint32_t *) (ld_r_archs->object->object_addr + ld_r_archs->object->dyst->indirectsymoff); if(ld_r_archs->object->object_byte_sex != host_byte_sex) swap_indirect_symbols(indirectsyms, nindirectsyms, host_byte_sex); } else{ indirectsyms = NULL; nindirectsyms = 0; } if(ld_r_archs->object->mh != NULL) ld_r_archs->object->input_sym_info_size = nsyms * sizeof(struct nlist) + strsize; else ld_r_archs->object->input_sym_info_size = nsyms * sizeof(struct nlist_64) + strsize; /* * Copy over the object struct from the ld -r object file onto the * input object file. */ *object = *ld_r_archs->object; /* * Save the ofile struct for the ld -r output so it can be umapped when * we are done. And free up the ld_r_archs now that we are done with * them. */ object->ld_r_ofile = ld_r_ofile; free_archs(ld_r_archs, ld_r_narchs); make_ld_r_object_cleanup: errors += save_errors; /* * Remove the input and output files and clean up. */ if(unlink(input_file) == -1) system_fatal("can't remove temporary file: %s", input_file); if(unlink(output_file) == -1) system_fatal("can't remove temporary file: %s", output_file); free(input_file); free(output_file); } /* * strip_LC_UUID_commands() is called when -no_uuid is specified to remove any * LC_UUID load commands from the object's load commands. */ static void strip_LC_UUID_commands( struct arch *arch, struct member *member, struct object *object) { uint32_t i, ncmds, nuuids, mh_sizeofcmds, sizeofcmds; struct load_command *lc1, *lc2, *new_load_commands; struct segment_command *sg; /* * See if there are any LC_UUID load commands. */ nuuids = 0; lc1 = arch->object->load_commands; if(arch->object->mh != NULL){ ncmds = arch->object->mh->ncmds; mh_sizeofcmds = arch->object->mh->sizeofcmds; } else{ ncmds = arch->object->mh64->ncmds; mh_sizeofcmds = arch->object->mh64->sizeofcmds; } for(i = 0; i < ncmds; i++){ if(lc1->cmd == LC_UUID){ nuuids++; } lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize); } /* if no LC_UUID load commands just return */ if(nuuids == 0) return; /* * Allocate space for the new load commands as zero it out so any holes * will be zero bytes. */ new_load_commands = allocate(mh_sizeofcmds); memset(new_load_commands, '\0', mh_sizeofcmds); /* * Copy all the load commands except the LC_UUID load commands into the * allocated space for the new load commands. */ lc1 = arch->object->load_commands; lc2 = new_load_commands; sizeofcmds = 0; for(i = 0; i < ncmds; i++){ if(lc1->cmd != LC_UUID){ memcpy(lc2, lc1, lc1->cmdsize); sizeofcmds += lc2->cmdsize; lc2 = (struct load_command *)((char *)lc2 + lc2->cmdsize); } lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize); } /* * Finally copy the updated load commands over the existing load * commands. */ memcpy(arch->object->load_commands, new_load_commands, sizeofcmds); if(mh_sizeofcmds > sizeofcmds){ memset((char *)arch->object->load_commands + sizeofcmds, '\0', (mh_sizeofcmds - sizeofcmds)); } ncmds -= nuuids; if(arch->object->mh != NULL) { arch->object->mh->sizeofcmds = sizeofcmds; arch->object->mh->ncmds = ncmds; } else { arch->object->mh64->sizeofcmds = 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_SPLIT_INFO: object->split_info_cmd = (struct linkedit_data_command *)lc1; break; case LC_FUNCTION_STARTS: object->func_starts_info_cmd = (struct linkedit_data_command *)lc1; break; case LC_DATA_IN_CODE: object->data_in_code_cmd = (struct linkedit_data_command *)lc1; break; case LC_DYLIB_CODE_SIGN_DRS: object->code_sign_drs_cmd = (struct linkedit_data_command *)lc1; break; case LC_CODE_SIGNATURE: object->code_sig_cmd = (struct linkedit_data_command *)lc1; break; case LC_DYLD_INFO_ONLY: case LC_DYLD_INFO: object->dyld_info = (struct dyld_info_command *)lc1; } lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize); } } #ifndef NMEDIT /* * strip_LC_CODE_SIGNATURE_commands() is called when -c is specified to remove * any LC_CODE_SIGNATURE load commands from the object's load commands. */ static void strip_LC_CODE_SIGNATURE_commands( struct arch *arch, struct member *member, struct object *object) { uint32_t i, ncmds, mh_sizeofcmds, sizeofcmds; struct load_command *lc1, *lc2, *new_load_commands; struct segment_command *sg; /* * See if there is an LC_CODE_SIGNATURE load command and if no command * just return. */ if(object->code_sig_cmd == NULL) return; /* * Allocate space for the new load commands and zero it out so any holes * will be zero bytes. */ if(arch->object->mh != NULL){ ncmds = arch->object->mh->ncmds; mh_sizeofcmds = arch->object->mh->sizeofcmds; } else{ ncmds = arch->object->mh64->ncmds; mh_sizeofcmds = arch->object->mh64->sizeofcmds; } new_load_commands = allocate(mh_sizeofcmds); memset(new_load_commands, '\0', mh_sizeofcmds); /* * Copy all the load commands except the LC_CODE_SIGNATURE load commands * into the allocated space for the new load commands. */ lc1 = arch->object->load_commands; lc2 = new_load_commands; sizeofcmds = 0; for(i = 0; i < ncmds; i++){ if(lc1->cmd != LC_CODE_SIGNATURE){ memcpy(lc2, lc1, lc1->cmdsize); sizeofcmds += lc2->cmdsize; lc2 = (struct load_command *)((char *)lc2 + lc2->cmdsize); } lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize); } /* * Finally copy the updated load commands over the existing load * commands. */ memcpy(arch->object->load_commands, new_load_commands, sizeofcmds); if(mh_sizeofcmds > sizeofcmds){ memset((char *)arch->object->load_commands + sizeofcmds, '\0', (mh_sizeofcmds - sizeofcmds)); } ncmds -= 1; if(arch->object->mh != NULL) { arch->object->mh->sizeofcmds = sizeofcmds; arch->object->mh->ncmds = ncmds; } else { arch->object->mh64->sizeofcmds = sizeofcmds; arch->object->mh64->ncmds = ncmds; } free(new_load_commands); /* reset the pointers into the load commands */ object->code_sig_cmd = NULL; 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_SPLIT_INFO: object->split_info_cmd = (struct linkedit_data_command *)lc1; break; case LC_FUNCTION_STARTS: object->func_starts_info_cmd = (struct linkedit_data_command *)lc1; break; case LC_DATA_IN_CODE: object->data_in_code_cmd = (struct linkedit_data_command *)lc1; break; case LC_DYLIB_CODE_SIGN_DRS: object->code_sign_drs_cmd = (struct linkedit_data_command *)lc1; break; } lc1 = (struct load_command *)((char *)lc1 + lc1->cmdsize); } /* * To get the right amount of the file copied out by writeout() for the * case when we are stripping out the section contents we already reduce * the object size by the size of the section contents including the * padding after the load commands. So here we need to further reduce * it by the load command for the LC_CODE_SIGNATURE (a struct * linkedit_data_command) we are removing. */ object->object_size -= sizeof(struct linkedit_data_command); /* * Then this size minus the size of the input symbolic information is * what is copied out from the file by writeout(). Which in this case * is just the new headers. */ /* * Finally for -c the file offset to the link edit information is to be * right after the load commands. So reset this for the updated size * of the load commands without the LC_CODE_SIGNATURE. */ if(object->mh != NULL) object->seg_linkedit->fileoff = sizeof(struct mach_header) + sizeofcmds; else object->seg_linkedit64->fileoff = sizeof(struct mach_header_64) + sizeofcmds; } #endif /* !(NMEDIT) */ /* * private_extern_reference_by_module() is passed a symbol_index of a private * extern symbol and the module table. If the symbol_index appears in the * module symbol table this returns TRUE else it returns FALSE. */ static enum bool private_extern_reference_by_module( uint32_t symbol_index, struct dylib_reference *refs, uint32_t nextrefsyms) { uint32_t i; for(i = 0; i < nextrefsyms; i++){ if(refs[i].isym == symbol_index){ if(refs[i].flags == REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY || refs[i].flags == REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY){ return(TRUE); } } } return(FALSE); } /* * symbol_pointer_used() is passed a symbol_index and the indirect table. If * the symbol_index appears in the indirect symbol table this returns TRUE else * it returns FALSE. */ static enum bool symbol_pointer_used( uint32_t symbol_index, uint32_t *indirectsyms, uint32_t nindirectsyms) { uint32_t i; for(i = 0; i < nindirectsyms; i++){ if(indirectsyms[i] == symbol_index) return(TRUE); } return(FALSE); } /* * Function for qsort for comparing undefined map entries. */ static int cmp_qsort_undef_map( const struct undef_map *sym1, const struct undef_map *sym2) { return(strcmp(qsort_strings + sym1->symbol.n_un.n_strx, qsort_strings + sym2->symbol.n_un.n_strx)); } static int cmp_qsort_undef_map_64( const struct undef_map64 *sym1, const struct undef_map64 *sym2) { return(strcmp(qsort_strings + sym1->symbol64.n_un.n_strx, qsort_strings + sym2->symbol64.n_un.n_strx)); } #endif /* !defined(NMEDIT) */ #ifndef NMEDIT /* * Function for qsort for comparing object names. */ static int cmp_qsort_filename( const char **name1, const char **name2) { return(strcmp(*name1, *name2)); } /* * Function for bsearch for finding a object name. */ static int cmp_bsearch_filename( const char *name1, const char **name2) { return(strcmp(name1, *name2)); } #endif /* !defined(NMEDIT) */ #ifdef NMEDIT static enum bool edit_symtab( struct arch *arch, struct member *member, struct object *object, struct nlist *symbols, struct nlist_64 *symbols64, uint32_t nsyms, char *strings, uint32_t strsize, struct dylib_table_of_contents *tocs, uint32_t ntoc, struct dylib_module *mods, struct dylib_module_64 *mods64, uint32_t nmodtab, struct dylib_reference *refs, uint32_t nextrefsyms) { uint32_t i, j, k; unsigned char data_n_sect, nsects; struct load_command *lc; struct segment_command *sg; struct segment_command_64 *sg64; struct section *s, **sections; struct section_64 *s64, **sections64; uint32_t missing_syms; struct symbol_list *sp; struct nlist **global_symbol; struct nlist_64 **global_symbol64; enum bool global_symbol_found; char *global_name, save_char; enum bool dwarf_debug_map; enum byte_sex host_byte_sex; int32_t missing_reloc_symbols; enum bool edit_symtab_return; char *p, *q; uint32_t new_ext_strsize, len, inew_syms; struct nlist **changed_globals; struct nlist_64 **changed_globals64; uint32_t nchanged_globals; uint32_t ncmds, s_flags, n_strx, module_name, ilocalsym, nlocalsym; uint32_t iextdefsym, nextdefsym; uint8_t n_type, n_sect, global_symbol_n_sect; uint64_t n_value; enum bool warned_about_global_coalesced_symbols; edit_symtab_return = TRUE; host_byte_sex = get_host_byte_sex(); missing_reloc_symbols = 0; warned_about_global_coalesced_symbols = FALSE; if(nmedits != NULL) free(nmedits); nmedits = allocate(nsyms * sizeof(enum bool)); for(i = 0; i < nsyms; i++) nmedits[i] = FALSE; /* * If nmedit is operating on a dynamic library then symbols are turned * into private externs with the extern bit off not into static symbols. */ if(object->mh_filetype == MH_DYLIB && pflag == TRUE){ error_arch(arch, member, "can't use -p with dynamic libraries"); return(FALSE); } /* * As part of the MAJOR guess for the second pass to fix stabs for the * globals symbols that get turned into non-global symbols. We need to * change the stabs. To do this we to know if a N_GSYM is for a data * symbol or not to know to turn it into an N_STSYM or a N_FUN. * This logic as determined by compiling test cases with and without * the key word 'static' and looking at the difference between the STABS * the compiler generates and trying to match that here. * * We also use this loop and the next to gather an array of section * struct pointers so we can later determine if we run into a global * symbol in a coalesced section and not turn those symbols into * statics. */ j = 0; nsects = 0; n_sect = 1; data_n_sect = NO_SECT; lc = object->load_commands; if(object->mh != NULL) ncmds = object->mh->ncmds; else ncmds = object->mh64->ncmds; for(i = 0; i < ncmds; i++){ if(lc->cmd == LC_SEGMENT){ sg = (struct segment_command *)lc; s = (struct section *)((char *)sg + sizeof(struct segment_command)); nsects += sg->nsects; for(j = 0; j < sg->nsects; j++){ if(strcmp(s->segname, SEG_DATA) == 0 && strcmp(s->sectname, SECT_DATA) == 0 && data_n_sect == NO_SECT){ data_n_sect = n_sect; break; } n_sect++; s++; } } else if(lc->cmd == LC_SEGMENT_64){ sg64 = (struct segment_command_64 *)lc; s64 = (struct section_64 *)((char *)sg64 + sizeof(struct segment_command_64)); nsects += sg64->nsects; for(j = 0; j < sg64->nsects; j++){ if(strcmp(s64->segname, SEG_DATA) == 0 && strcmp(s64->sectname, SECT_DATA) == 0 && data_n_sect == NO_SECT){ data_n_sect = n_sect; break; } n_sect++; s64++; } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } if(object->mh != NULL){ sections = allocate(nsects * sizeof(struct section *)); sections64 = NULL; } else{ sections = NULL; sections64 = allocate(nsects * sizeof(struct section_64 *)); } nsects = 0; lc = object->load_commands; for(i = 0; i < 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++){ sections[nsects++] = s++; } } else if(lc->cmd == LC_SEGMENT_64){ sg64 = (struct segment_command_64 *)lc; s64 = (struct section_64 *)((char *)sg64 + sizeof(struct segment_command_64)); for(j = 0; j < sg64->nsects; j++){ sections64[nsects++] = s64++; } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } /* * Zero out the saved symbols so they can be recorded for this file. */ for(i = 0; i < nsave_symbols; i++) save_symbols[i].sym = NULL; for(i = 0; i < nremove_symbols; i++) remove_symbols[i].sym = NULL; if(member == NULL){ for(i = 0; i < nsave_symbols; i++) save_symbols[i].seen = FALSE; for(i = 0; i < nremove_symbols; i++) remove_symbols[i].seen = FALSE; } nchanged_globals = 0; if(object->mh != NULL){ changed_globals = allocate(nsyms * sizeof(struct nlist *)); changed_globals64 = NULL; for(i = 0; i < nsyms; i++) changed_globals[i] = NULL; } else{ changed_globals = NULL; changed_globals64 = allocate(nsyms * sizeof(struct nlist_64 *)); for(i = 0; i < nsyms; i++) changed_globals64[i] = NULL; } /* * These are the variables for the new symbol table and new string * table. Since this routine only turns globals into non-globals the * number of symbols does not change. But the count of local, defined * external symbols does change. */ new_nsyms = nsyms; new_nlocalsym = 0; new_nextdefsym = 0; new_nundefsym = 0; new_strsize = sizeof(int32_t); new_ext_strsize = 0; /* * First pass: turn the globals symbols into non-global symbols. */ for(i = 0; i < nsyms; i++){ len = 0; s_flags = 0; if(object->mh != NULL){ n_strx = symbols[i].n_un.n_strx; n_type = symbols[i].n_type; n_sect = symbols[i].n_sect; if((n_type & N_TYPE) == N_SECT) s_flags = sections[n_sect - 1]->flags; n_value = symbols[i].n_value; } else{ n_strx = symbols64[i].n_un.n_strx; n_type = symbols64[i].n_type; n_sect = symbols64[i].n_sect; if((n_type & N_TYPE) == N_SECT) s_flags = sections64[n_sect - 1]->flags; n_value = symbols64[i].n_value; } if(n_strx != 0){ if(n_strx > strsize){ error_arch(arch, member, "bad string index for symbol " "table entry %u in: ", i); return(FALSE); } len = strlen(strings + n_strx) + 1; } if(n_type & N_EXT){ if((n_type & N_TYPE) != N_UNDF && (n_type & N_TYPE) != N_PBUD){ if((n_type & N_TYPE) == N_SECT){ if(n_sect > nsects){ error_arch(arch, member, "bad n_sect for symbol " "table entry %u in: ", i); return(FALSE); } if(((s_flags & SECTION_TYPE) == S_COALESCED) && pflag == FALSE && object->mh_filetype != MH_OBJECT){ /* this remains a global defined symbol */ if(warned_about_global_coalesced_symbols == FALSE){ warning_arch(arch, member, "can't make global " "coalesced symbols (like %s) into static " "symbols (use ld(1)'s " "-exported_symbols_list option) in a final " "linked image: ", strings + n_strx); warned_about_global_coalesced_symbols = TRUE; } new_nextdefsym++; new_ext_strsize += len; new_strsize += len; sp = bsearch(strings + n_strx, remove_symbols, nremove_symbols, sizeof(struct symbol_list), (int (*)(const void *, const void *)) symbol_list_bsearch); if(sp != NULL){ if(sp->sym != NULL){ error_arch(arch, member, "more than one " "symbol for: %s found in: ", sp->name); return(FALSE); } else{ if(object->mh != NULL) sp->sym = &(symbols[i]); else sp->sym = &(symbols64[i]); sp->seen = TRUE; warning_arch(arch, member, "can't make " "global coalesced symbol: %s into a " "static symbol in: ", sp->name); } } /* * In case the user has listed this coalesced * symbol in the save list look for it and mark it * as seen so we don't complain about not seeing it. */ sp = bsearch(strings + n_strx, save_symbols, nsave_symbols, sizeof(struct symbol_list), (int (*)(const void *, const void *)) symbol_list_bsearch); if(sp != NULL){ if(sp->sym != NULL){ error_arch(arch, member, "more than one " "symbol for: %s found in: ", sp->name); return(FALSE); } else{ if(object->mh != NULL) sp->sym = &(symbols[i]); else sp->sym = &(symbols64[i]); sp->seen = TRUE; } } continue; /* leave this symbol unchanged */ } } sp = bsearch(strings + n_strx, remove_symbols, nremove_symbols, sizeof(struct symbol_list), (int (*)(const void *, const void *)) symbol_list_bsearch); if(sp != NULL){ if(sp->sym != NULL){ error_arch(arch, member, "more than one symbol " "for: %s found in: ", sp->name); return(FALSE); } else{ if(object->mh != NULL) sp->sym = &(symbols[i]); else sp->sym = &(symbols64[i]); sp->seen = TRUE; goto change_symbol; } } else{ /* * If there is no list of saved symbols, then all * symbols will be saved unless listed in the remove * list. */ if(sfile == NULL){ /* * There is no save list, so if there is also no * remove list but the -p flag is specified or it is * a dynamic library then change all symbols. */ if((pflag || object->mh_filetype == MH_DYLIB) && nremove_symbols == 0) goto change_symbol; /* this remains a global defined symbol */ new_nextdefsym++; new_ext_strsize += len; new_strsize += len; continue; /* leave this symbol unchanged */ } } sp = bsearch(strings + n_strx, save_symbols, nsave_symbols, sizeof(struct symbol_list), (int (*)(const void *, const void *)) symbol_list_bsearch); if(sp != NULL){ if(sp->sym != NULL){ error_arch(arch, member, "more than one symbol " "for: %s found in: ", sp->name); return(FALSE); } else{ if(object->mh != NULL) sp->sym = &(symbols[i]); else sp->sym = &(symbols64[i]); sp->seen = TRUE; /* this remains a global defined symbol */ new_nextdefsym++; new_ext_strsize += len; new_strsize += len; } } else{ if(Aflag && n_type == (N_EXT | N_ABS) && (n_value != 0 || (n_strx != 0 && strncmp(strings + n_strx, ".objc_class_name_", sizeof(".objc_class_name_") - 1) == 0))){ /* this remains a global defined symbol */ new_nextdefsym++; new_ext_strsize += len; new_strsize += len; } else{ change_symbol: if((n_type & N_TYPE) != N_INDR){ nmedits[i] = TRUE; if(object->mh != NULL) changed_globals[nchanged_globals++] = symbols + i; else changed_globals64[nchanged_globals++] = symbols64 + i; if(pflag){ /* this remains a global defined symbol */ new_nextdefsym++; new_ext_strsize += len; new_strsize += len; } else{ /* this will become a non-global symbol */ new_nlocalsym++; new_strsize += len; } } else{ /* this remains a global defined symbol */ new_nextdefsym++; new_ext_strsize += len; new_strsize += len; } } } } else{ /* this is an undefined symbol */ new_nundefsym++; new_ext_strsize += len; new_strsize += len; } } else{ /* this is a local symbol */ new_nlocalsym++; new_strsize += len; } } /* * The module table's module names are placed with the external * strings. So size them and add this to the external string size. */ for(i = 0; i < nmodtab; i++){ if(object->mh != NULL) module_name = mods[i].module_name; else module_name = mods64[i].module_name; if(module_name == 0 || module_name > strsize){ error_arch(arch, member, "bad string index for module_name " "of module table entry %d in: ", i); return(FALSE); } len = strlen(strings + module_name) + 1; new_strsize += len; new_ext_strsize += len; } /* * Warn about symbols to be saved that were missing. */ if(member == NULL){ missing_syms = 0; if(iflag == 0){ for(i = 0; i < nsave_symbols; i++){ if(save_symbols[i].sym == NULL){ if(missing_syms == 0){ error_arch(arch, member, "symbols names listed " "in: %s not in: ", sfile); missing_syms = 1; } fprintf(stderr, "%s\n", save_symbols[i].name); } } for(i = 0; i < nremove_symbols; i++){ if(remove_symbols[i].sym == NULL){ if(missing_syms == 0){ error_arch(arch, member, "symbols names listed " "in: %s not in: ", Rfile); missing_syms = 1; } fprintf(stderr, "%s\n", remove_symbols[i].name); } } } } /* * Second pass: fix stabs for the globals symbols that got turned into * non-global symbols. This is a MAJOR guess. The specific changes * to do here were determined by compiling test cases with and without * the key word 'static' and looking at the difference between the STABS * the compiler generates and trying to match that here. */ global_strings = strings; if(object->mh != NULL) qsort(changed_globals, nchanged_globals, sizeof(struct nlist *), (int (*)(const void *, const void *))cmp_qsort_global); else qsort(changed_globals64, nchanged_globals,sizeof(struct nlist_64 *), (int (*)(const void *, const void *))cmp_qsort_global_64); dwarf_debug_map = FALSE; for(i = 0; i < nsyms; i++){ uint16_t n_desc; if(object->mh != NULL){ n_strx = symbols[i].n_un.n_strx; n_type = symbols[i].n_type; n_desc = symbols[i].n_desc; } else{ n_strx = symbols64[i].n_un.n_strx; n_type = symbols64[i].n_type; n_desc = symbols64[i].n_desc; } if(n_type == N_SO) dwarf_debug_map = FALSE; else if (n_type == N_OSO) dwarf_debug_map = n_desc != 0; else if (dwarf_debug_map && n_type == N_GSYM){ global_name = strings + n_strx; if(object->mh != NULL){ global_symbol = bsearch(global_name, changed_globals, nchanged_globals,sizeof(struct nlist *), (int (*)(const void *, const void *)) cmp_bsearch_global); if(global_symbol != NULL){ symbols[i].n_type = N_STSYM; symbols[i].n_sect = (*global_symbol)->n_sect; symbols[i].n_value = (*global_symbol)->n_value; } } else{ global_symbol64 = bsearch(global_name, changed_globals64, nchanged_globals, sizeof(struct nlist_64 *), (int (*)(const void *, const void *)) cmp_bsearch_global_64); if(global_symbol64 != NULL){ symbols64[i].n_type = N_STSYM; symbols64[i].n_sect = (*global_symbol64)->n_sect; symbols64[i].n_value = (*global_symbol64)->n_value; } } } else if(! dwarf_debug_map && (n_type == N_GSYM || n_type == N_FUN) && (n_strx != 0 && strings[n_strx] != '\0')){ global_name = strings + n_strx; if((global_name[0] == '+' || global_name[0] == '-') && global_name[1] == '['){ j = 2; while(j + n_strx < strsize && global_name[j] != ']') j++; if(j + n_strx < strsize && global_name[j] == ']') j++; } else j = 0; while(j + n_strx < strsize && global_name[j] != ':') j++; if(j + n_strx >= strsize){ error_arch(arch, member, "bad N_STAB symbol name for entry " "%u (does not contain ':' separating name from type) " "in: ", i); return(FALSE); } save_char = global_name[j]; global_name[j] = '\0'; global_symbol_found = FALSE; global_symbol_n_sect = 0; if(object->mh != NULL){ global_symbol = bsearch(global_name, changed_globals, nchanged_globals,sizeof(struct nlist *), (int (*)(const void *, const void *)) cmp_bsearch_global_stab); global_symbol64 = NULL; if(global_symbol != NULL){ global_symbol_found = TRUE; global_symbol_n_sect = (*global_symbol)->n_sect; } } else{ global_symbol64 = bsearch(global_name, changed_globals64, nchanged_globals, sizeof(struct nlist_64 *), (int (*)(const void *, const void *)) cmp_bsearch_global_stab_64); global_symbol = NULL; if(global_symbol64 != NULL){ global_symbol_found = TRUE; global_symbol_n_sect = (*global_symbol64)->n_sect; } } global_name[j] = save_char; if(global_symbol_found == TRUE){ if(n_type == N_GSYM){ if(global_symbol_n_sect == data_n_sect){ if(object->mh != NULL) symbols[i].n_type = N_STSYM; else symbols64[i].n_type = N_STSYM; } else{ if(object->mh != NULL) symbols[i].n_type = N_FUN; else symbols64[i].n_type = N_FUN; } if(object->mh != NULL){ symbols[i].n_sect = (*global_symbol)->n_sect; symbols[i].n_value = (*global_symbol)->n_value; symbols[i].n_desc = (*global_symbol)->n_desc; } else{ symbols64[i].n_sect = (*global_symbol64)->n_sect; symbols64[i].n_value = (*global_symbol64)->n_value; symbols64[i].n_desc = (*global_symbol64)->n_desc; } if(j + 1 + n_strx >= strsize || global_name[j+1] != 'G'){ error_arch(arch, member, "bad N_GSYM symbol name " "for entry %u (does not have type 'G' after " "':' in name) in: ", i); return(FALSE); } global_name[j+1] = 'S'; } else{ /* n_type == N_FUN */ if(j + 1 + n_strx >= strsize || global_name[j+1] == 'F'){ global_name[j+1] = 'f'; } } } } } global_strings = NULL; /* * Now what needs to be done is to create the new symbol table moving * those global symbols being changed into non-globals into the areas * in the symbol table for local symbols. The symbol table and string * table must be in this order: * * symbol table * local symbols * external defined symbols * undefined symbols * string table * external strings * local strings */ if(saves != NULL) free(saves); saves = (int32_t *)allocate(nsyms * sizeof(int32_t)); bzero(saves, nsyms * sizeof(int32_t)); if(object->mh != NULL){ new_symbols = (struct nlist *) allocate(new_nsyms * sizeof(struct nlist)); new_symbols64 = NULL; } else{ new_symbols = NULL; new_symbols64 = (struct nlist_64 *) allocate(new_nsyms * sizeof(struct nlist_64)); } new_strsize = rnd(new_strsize, sizeof(int32_t)); new_strings = (char *)allocate(new_strsize); new_strings[new_strsize - 3] = '\0'; new_strings[new_strsize - 2] = '\0'; new_strings[new_strsize - 1] = '\0'; memset(new_strings, '\0', sizeof(int32_t)); p = new_strings + sizeof(int32_t); q = p + new_ext_strsize; /* * If this is a dynamic library the movement of the symbols has to be * done with respect to the modules. As the local symbols, and external * defined symbols are grouped together for each module. Then a new * module table needs to be created with the new indexes into the symbol * table for each module. */ new_nmodtab = nmodtab; new_ntoc = ntoc; new_nextrefsyms = nextrefsyms; if(object->mh_filetype == MH_DYLIB && nmodtab != 0){ if(object->mh != NULL){ new_mods = allocate(nmodtab * sizeof(struct dylib_module)); new_mods64 = NULL; } else{ new_mods = NULL; new_mods64 = allocate(nmodtab * sizeof(struct dylib_module_64)); } inew_syms = 0; /* * This first loop through the module table sets the index and * counts of the local symbols for each module. */ for(i = 0; i < nmodtab; i++){ /* * First put the existing local symbols into the new symbol * table. */ if(object->mh != NULL){ new_mods[i].ilocalsym = inew_syms; new_mods[i].nlocalsym = 0; ilocalsym = mods[i].ilocalsym; nlocalsym = mods[i].nlocalsym; } else{ new_mods64[i].ilocalsym = inew_syms; new_mods64[i].nlocalsym = 0; ilocalsym = mods64[i].ilocalsym; nlocalsym = mods64[i].nlocalsym; } for(j = ilocalsym; j < ilocalsym + nlocalsym; j++){ if(object->mh != NULL){ n_strx = symbols[j].n_un.n_strx; n_type = symbols[j].n_type; } else{ n_strx = symbols64[j].n_un.n_strx; n_type = symbols64[j].n_type; } if((n_type & N_EXT) == 0){ if(object->mh != NULL) new_symbols[inew_syms] = symbols[j]; else new_symbols64[inew_syms] = symbols64[j]; if(n_strx != 0){ strcpy(q, strings + n_strx); if(object->mh != NULL) new_symbols[inew_syms].n_un.n_strx = q - new_strings; else new_symbols64[inew_syms].n_un.n_strx = q - new_strings; q += strlen(q) + 1; } inew_syms++; saves[j] = inew_syms; if(object->mh != NULL) new_mods[i].nlocalsym++; else new_mods64[i].nlocalsym++; } } /* * Next put the global symbols that were changed into * non-global symbols into the new symbol table and moved their * counts to the local symbol counts. */ if(object->mh != NULL){ iextdefsym = mods[i].iextdefsym; nextdefsym = mods[i].nextdefsym; } else{ iextdefsym = mods64[i].iextdefsym; nextdefsym = mods64[i].nextdefsym; } for(j = iextdefsym; j < iextdefsym + nextdefsym; j++){ if(object->mh != NULL){ n_strx = symbols[j].n_un.n_strx; n_type = symbols[j].n_type; } else{ n_strx = symbols64[j].n_un.n_strx; n_type = symbols64[j].n_type; } if((n_type & N_EXT) != 0){ if(nmedits[j] == TRUE){ /* * Change the new symbol to a private extern symbol * with the extern bit off. */ if(object->mh != NULL){ new_symbols[inew_syms] = symbols[j]; new_symbols[inew_syms].n_type |= N_PEXT; new_symbols[inew_syms].n_type &= ~N_EXT; } else{ new_symbols64[inew_syms] = symbols64[j]; new_symbols64[inew_syms].n_type |= N_PEXT; new_symbols64[inew_syms].n_type &= ~N_EXT; } if(n_strx != 0){ strcpy(q, strings + n_strx); if(object->mh != NULL) new_symbols[inew_syms].n_un.n_strx = q - new_strings; else new_symbols64[inew_syms].n_un.n_strx = q - new_strings; q += strlen(q) + 1; } inew_syms++; saves[j] = inew_syms; if(object->mh != NULL) new_mods[i].nlocalsym++; else new_mods64[i].nlocalsym++; } } } } /* * Next put the unchanged defined global symbols into the new * symbol table. */ for(i = 0; i < nmodtab; i++){ if(object->mh != NULL){ new_mods[i].iextdefsym = inew_syms; new_mods[i].nextdefsym = 0; iextdefsym = mods[i].iextdefsym; nextdefsym = mods[i].nextdefsym; } else{ new_mods64[i].iextdefsym = inew_syms; new_mods64[i].nextdefsym = 0; iextdefsym = mods64[i].iextdefsym; nextdefsym = mods64[i].nextdefsym; } for(j = iextdefsym; j < iextdefsym + nextdefsym; j++){ if(object->mh != NULL){ n_strx = symbols[j].n_un.n_strx; n_type = symbols[j].n_type; } else{ n_strx = symbols64[j].n_un.n_strx; n_type = symbols64[j].n_type; } if((n_type & N_EXT) != 0){ if(nmedits[j] == FALSE){ if(object->mh != NULL) new_symbols[inew_syms] = symbols[j]; else new_symbols64[inew_syms] = symbols64[j]; if(n_strx != 0){ strcpy(p, strings + n_strx); if(object->mh != NULL) new_symbols[inew_syms].n_un.n_strx = p - new_strings; else new_symbols64[inew_syms].n_un.n_strx = p - new_strings; p += strlen(p) + 1; } inew_syms++; saves[j] = inew_syms; if(object->mh != NULL) new_mods[i].nextdefsym++; else new_mods64[i].nextdefsym++; } } } } /* * Last put the undefined symbols into the new symbol table. */ for(i = 0; i < nsyms; i++){ if(object->mh != NULL){ n_strx = symbols[i].n_un.n_strx; n_type = symbols[i].n_type; } else{ n_strx = symbols64[i].n_un.n_strx; n_type = symbols64[i].n_type; } if((n_type & N_EXT) != 0 && ((n_type & N_TYPE) == N_UNDF || (n_type & N_TYPE) == N_PBUD)){ if(object->mh != NULL) new_symbols[inew_syms] = symbols[i]; else new_symbols64[inew_syms] = symbols64[i]; if(n_strx != 0){ strcpy(p, strings + n_strx); if(object->mh != NULL) new_symbols[inew_syms].n_un.n_strx = p - new_strings; else new_symbols64[inew_syms].n_un.n_strx = p - new_strings; p += strlen(p) + 1; } inew_syms++; saves[i] = inew_syms; } } /* * Place the module table's module names with the external strings * and set the names in the new module table. And then copy the * other unchanged fields. */ for(i = 0; i < nmodtab; i++){ if(object->mh != NULL){ strcpy(p, strings + mods[i].module_name); new_mods[i].module_name = p - new_strings; p += strlen(p) + 1; new_mods[i].irefsym = mods[i].irefsym; new_mods[i].nrefsym = mods[i].nrefsym; new_mods[i].iextrel = mods[i].iextrel; new_mods[i].nextrel = mods[i].nextrel; new_mods[i].iinit_iterm = mods[i].iinit_iterm; new_mods[i].ninit_nterm = mods[i].ninit_nterm; new_mods[i].objc_module_info_addr = mods[i].objc_module_info_addr; new_mods[i].objc_module_info_size = mods[i].objc_module_info_size; } else{ strcpy(p, strings + mods64[i].module_name); new_mods64[i].module_name = p - new_strings; p += strlen(p) + 1; new_mods64[i].irefsym = mods64[i].irefsym; new_mods64[i].nrefsym = mods64[i].nrefsym; new_mods64[i].iextrel = mods64[i].iextrel; new_mods64[i].nextrel = mods64[i].nextrel; new_mods64[i].iinit_iterm = mods64[i].iinit_iterm; new_mods64[i].ninit_nterm = mods64[i].ninit_nterm; new_mods64[i].objc_module_info_addr = mods64[i].objc_module_info_addr; new_mods64[i].objc_module_info_size = mods64[i].objc_module_info_size; } } /* * Update the reference table with the new symbol indexes for all * entries and change type of reference (the flags field) for those * symbols that got changed from globals to non-globals. */ new_nextrefsyms = nextrefsyms; new_refs = allocate(new_nextrefsyms * sizeof(struct dylib_reference)); j = 0; for(i = 0; i < nextrefsyms; i++){ if(nmedits[refs[i].isym] == TRUE){ if(refs[i].flags == REFERENCE_FLAG_DEFINED) new_refs[i].flags = REFERENCE_FLAG_PRIVATE_DEFINED; else if(refs[i].flags == REFERENCE_FLAG_UNDEFINED_NON_LAZY) new_refs[i].flags = REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY; else if(refs[i].flags == REFERENCE_FLAG_UNDEFINED_LAZY) new_refs[i].flags = REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY; else new_refs[i].flags = refs[i].flags; } else{ new_refs[i].flags = refs[i].flags; } new_refs[i].isym = saves[refs[i].isym] - 1; } /* * Create a new dylib table of contents without the global symbols * that got turned into non-globals. */ new_ntoc = ntoc - nchanged_globals; new_tocs = allocate(new_ntoc * sizeof(struct dylib_table_of_contents)); k = 0; for(i = 0; i < ntoc; i++){ if(tocs[i].symbol_index >= nsyms){ error_arch(arch, member, "bad symbol index for table of " "contents table entry %d in: ", i); return(FALSE); } if(nmedits[tocs[i].symbol_index] == FALSE){ new_tocs[k].symbol_index = saves[tocs[i].symbol_index] - 1; new_tocs[k].module_index = tocs[i].module_index; k++; } } } /* * If is not a dynamic library so all global symbols changed into * statics can be moved to the end of the local symbols. If the pflag * is set then the changed symbols remain global and just get the * private extern bit set. */ else{ /* * First put the existing local symbols into the new symbol table. */ inew_syms = 0; for(i = 0; i < nsyms; i++){ if(object->mh != NULL){ n_strx = symbols[i].n_un.n_strx; n_type = symbols[i].n_type; } else{ n_strx = symbols64[i].n_un.n_strx; n_type = symbols64[i].n_type; } if((n_type & N_EXT) == 0){ if(object->mh != NULL) new_symbols[inew_syms] = symbols[i]; else new_symbols64[inew_syms] = symbols64[i]; if(n_strx != 0){ strcpy(q, strings + n_strx); if(object->mh != NULL) new_symbols[inew_syms].n_un.n_strx = q - new_strings; else new_symbols64[inew_syms].n_un.n_strx = q - new_strings; q += strlen(q) + 1; } inew_syms++; saves[i] = inew_syms; } } /* * Next put the global symbols that were changed into statics * symbols into the new symbol table. */ if(pflag == FALSE){ for(i = 0; i < nsyms; i++){ if(object->mh != NULL){ n_strx = symbols[i].n_un.n_strx; n_type = symbols[i].n_type; } else{ n_strx = symbols64[i].n_un.n_strx; n_type = symbols64[i].n_type; } if((n_type & N_EXT) != 0){ if(nmedits[i] == TRUE){ /* * Change the new symbol to not be an extern symbol * by turning off the extern bit. */ if(object->mh != NULL){ new_symbols[inew_syms] = symbols[i]; new_symbols[inew_syms].n_type &= ~N_EXT; new_symbols[inew_syms].n_desc &= ~N_WEAK_DEF; } else{ new_symbols64[inew_syms] = symbols64[i]; new_symbols64[inew_syms].n_type &= ~N_EXT; new_symbols64[inew_syms].n_desc &= ~N_WEAK_DEF; } if(n_strx != 0){ strcpy(q, strings + n_strx); if(object->mh != NULL) new_symbols[inew_syms].n_un.n_strx = q - new_strings; else new_symbols64[inew_syms].n_un.n_strx = q - new_strings; q += strlen(q) + 1; } inew_syms++; saves[i] = inew_syms; } } } } /* * Last put the unchanged global symbols into the new symbol table * and symbols changed into private externs. */ for(i = 0; i < nsyms; i++){ if(object->mh != NULL){ n_strx = symbols[i].n_un.n_strx; n_type = symbols[i].n_type; } else{ n_strx = symbols64[i].n_un.n_strx; n_type = symbols64[i].n_type; } if((n_type & N_EXT) != 0){ if(nmedits[i] == FALSE || pflag == TRUE){ if(object->mh != NULL) new_symbols[inew_syms] = symbols[i]; else new_symbols64[inew_syms] = symbols64[i]; if(nmedits[i] == TRUE && pflag == TRUE){ /* * Change the new symbol to be a private extern * symbol by turning on the private extern bit. */ if(object->mh != NULL) new_symbols[inew_syms].n_type |= N_PEXT; else new_symbols64[inew_syms].n_type |= N_PEXT; } if(n_strx != 0){ strcpy(p, strings + n_strx); if(object->mh != NULL) new_symbols[inew_syms].n_un.n_strx = p - new_strings; else new_symbols64[inew_syms].n_un.n_strx = p - new_strings; p += strlen(p) + 1; } inew_syms++; saves[i] = inew_syms; } } } } if(sections != NULL) free(sections); if(sections64 != NULL) free(sections64); if(errors == 0) return(TRUE); else return(FALSE); } /* * Function for qsort for comparing global symbol names. */ static int cmp_qsort_global( const struct nlist **sym1, const struct nlist **sym2) { return(strcmp(global_strings + (*sym1)->n_un.n_strx, global_strings + (*sym2)->n_un.n_strx)); } static int cmp_qsort_global_64( const struct nlist_64 **sym1, const struct nlist_64 **sym2) { return(strcmp(global_strings + (*sym1)->n_un.n_strx, global_strings + (*sym2)->n_un.n_strx)); } /* * Function for bsearch for finding a global symbol that matches a stab name. */ static int cmp_bsearch_global_stab( const char *name, const struct nlist **sym) { /* * The +1 is for the '_' on the global symbol that is not on the * stab string that is trying to be matched. */ return(strcmp(name, global_strings + (*sym)->n_un.n_strx + 1)); } static int cmp_bsearch_global_stab_64( const char *name, const struct nlist_64 **sym) { /* * The +1 is for the '_' on the global symbol that is not on the * stab string that is trying to be matched. */ return(strcmp(name, global_strings + (*sym)->n_un.n_strx + 1)); } /* * Function for bsearch for finding a global symbol that matches a stab name * in the debug map. */ static int cmp_bsearch_global( const char *name, const struct nlist **sym) { return(strcmp(name, global_strings + (*sym)->n_un.n_strx)); } static int cmp_bsearch_global_64( const char *name, const struct nlist_64 **sym) { return(strcmp(name, global_strings + (*sym)->n_un.n_strx)); } #endif /* defined(NMEDIT) */