/* * 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@ */ #include #include #include #include #include #include #include #include #include "libgcc.h" #include "hack_libgcc.h" #include "stuff/bool.h" #include "stuff/allocate.h" #include "stuff/breakout.h" #include "stuff/hash_string.h" #include "stuff/errors.h" #include "stuff/round.h" #undef DEBUG #define NEW_LIBCGG_PREFIX "__libgcc_private_extern" #define OLD_LIBCGG_PREFIX "__old_libgcc" __private_extern__ char *progname = NULL; /* name of the program for error messages (argv[0]) */ /* file name of the specification input file */ char *spec_filename = NULL; static struct arch_flag arch_flag = { 0 }; /* the -arch flag */ /* * Where the information for each object file is stored. See hack_libgcc.h * for the structure definition. */ struct object_info objects = { 0 }; /* * This will be set to true if a symbol name is changed. If it is not set then * the input file does not have to be written out. */ static enum bool input_changed = FALSE; /* * If -p is specified then this is set to TRUE and the new libgcc defined * symbols will have their private extern bit (N_PEXT) set. */ static enum bool set_private_extern = FALSE; /* * Data structures to hold the symbol names (both the prefixed and non-prefixed * symbol names) from the libgcc defined symbols. */ struct symbol { char *name; /* name of the symbol */ char *prefixed_name; /* prefixed name of the symbol */ struct symbol *next; /* next symbol in the hash chain */ }; /* The symbol hash table is hashed on the name field */ #define SYMBOL_HASH_SIZE 250 static struct symbol *new_symbol_hash[SYMBOL_HASH_SIZE]; static struct symbol *old_symbol_hash[SYMBOL_HASH_SIZE]; /* * The string table maintained by start_string_table(), add_to_string_table() * and end_string_table(); */ #define INITIAL_STRING_TABLE_SIZE 40960 static struct { char *strings; unsigned long size; unsigned long index; } string_table; static void usage( void); static void process( char *filename, enum bool first_pass, enum libgcc libgcc); static void translate_input( struct arch *archs, unsigned long narchs, enum libgcc libgcc); static void translate_object( struct arch *arch, struct member *member, struct object *object, enum libgcc libgcc); static char * lookup_new_symbol( char *name, enum bool add); static char * lookup_old_symbol( char *name, enum bool add); static void start_string_table( void); static long add_to_string_table( char *p); static void end_string_table( void); /* * The hack_libgcc(l) program takes the following options: * * % hack_libgcc [-p] -arch -filelist filename[,dirname] ... * * % hack_libgcc -arch -s spec_file * * It is used to build a 4.2mach and later System framework (and libsys) that is * built with a newer gcc than was used in 4.1mach and previous releases. In * 4.2mach and into the future, libgcc that matches the compiler will be linked * into each image (executable, shared library, and bundle). This libgcc will * be a static archive (objects compiled -dynamic) and all defined symbols being * __private_extern__ . This allows only the code in the image to use the * parts of libgcc copied into it. * * There is a wrinkle. System framework when compiled with the the new gcc is * linked with the new libgcc. But it must also export the old libgcc functions * to maintain compatibility with 4.1mach and older programs. This is where * this program comes in. It takes the new libgcc and records all the names * of the defined symbols. Then it changes the symbol names in the new libgcc * and all of the other objects with the prefix "_System." * * For System framework, the first -filelist argument is assumed to be for the * new libgcc objects. The rest of the -filelist arguments are assumed to be * the other objects that make up System framework. The old libgcc objects * should not ever be specified as an argument. * * For libsys, the spec_file has the new and old libgcc functions marked. */ int main( int argc, char *argv[], char *envp[]) { int i; unsigned long j, nfiles; char *p, *filelist, *dirname, *addr; int fd; struct stat stat_buf; enum libgcc libgcc; kern_return_t r; /* set the name of the program for error messages */ progname = argv[0]; /* the first filelist argument is assumed to be for libgcc */ libgcc = NEW_LIBGCC; /* parse the command line arguments */ for(i = 1; i < argc; i++){ if(strcmp(argv[i], "-arch") == 0){ if(arch_flag.name != NULL) fatal("only one -arch can be specified"); if(i + 1 >= argc){ error("missing argument(s) to %s option", argv[i]); usage(); } if(strcmp("all", argv[i+1]) == 0) fatal("-arch all can't be specified"); if(get_arch_from_flag(argv[i+1], &arch_flag) == 0){ error("unknown architecture specification flag: " "%s %s", argv[i], argv[i+1]); arch_usage(); usage(); } i++; } else if(strcmp(argv[i], "-filelist") == 0){ if(spec_filename != NULL){ error("can't specify both -s and -filelist " "arguments"); usage(); } if(i + 1 == argc){ error("missing argument to: %s option", argv[i]); usage(); } filelist = argv[i + 1]; dirname = strrchr(filelist, ','); if(dirname != NULL){ *dirname = '\0'; dirname++; } else dirname = ""; if((fd = open(filelist, O_RDONLY, 0)) == -1) system_fatal("can't open file list file: %s", filelist); if(fstat(fd, &stat_buf) == -1) system_fatal("can't stat file list file: %s", filelist); /* * For some reason mapping files with zero size fails * so it has to be handled specially. */ if(stat_buf.st_size != 0){ if((r = map_fd((int)fd, (vm_offset_t)0, (vm_offset_t *)&(addr), (boolean_t)TRUE, (vm_size_t)stat_buf.st_size)) != KERN_SUCCESS) mach_fatal(r, "can't map file list file: %s", filelist); } else{ fatal("file list file: %s is empty", filelist); } if(*dirname != '\0') dirname[-1] = ','; close(fd); /* * Count the number of files listed in this filelist. * Note since the file can't be zero length and does not need * to end in a '\n' there is always at least one file. */ nfiles = 0; for(j = 0; j < stat_buf.st_size; j++){ if(addr[j] == '\n') nfiles++; } if(addr[stat_buf.st_size - 1] != '\n') nfiles++; /* * Allocate some space to put the file names which includes the * dirname prepended to them. And space to store info about * each object. */ p = allocate((strlen(dirname) + 1) * nfiles + stat_buf.st_size + 1); objects.names = reallocate(objects.names, sizeof(char *) * (objects.nobjects + nfiles)); objects.filelists = reallocate(objects.filelists, sizeof(char *) * (objects.nobjects + nfiles)); objects.libgcc = reallocate(objects.libgcc, sizeof(enum libgcc) * (objects.nobjects + nfiles)); objects.names[objects.nobjects] = p; objects.filelists[objects.nobjects] = filelist; objects.libgcc[objects.nobjects] = libgcc; objects.nobjects++; if(*dirname != '\0'){ strcpy(p, dirname); p += strlen(dirname); *p++ = '/'; } for(j = 0; j < stat_buf.st_size; j++){ if(addr[j] != '\n') *p++ = addr[j]; else{ *p++ = '\0'; if(j != stat_buf.st_size - 1){ objects.names[objects.nobjects] = p; objects.filelists[objects.nobjects] = filelist; objects.libgcc[objects.nobjects] = libgcc; objects.nobjects++; if(*dirname != '\0'){ strcpy(p, dirname); p += strlen(dirname); *p++ = '/'; } } } } if(addr[stat_buf.st_size - 1] != '\n') *p = '\0'; /* skip the filelist argument */ i++; /* all other filelists will be assumed not to be libgcc */ libgcc = NOT_LIBGCC; } else if(strcmp(argv[i], "-s") == 0){ if(objects.nobjects != 0){ error("can't specify both -s and -filelist " "arguments"); usage(); } if(i + 1 == argc){ error("missing argument to: %s option", argv[i]); usage(); } if(spec_filename != NULL){ error("only one -s argument can be specified"); } spec_filename = argv[i + 1]; i++; } else if(strcmp(argv[i], "-p") == 0){ set_private_extern = TRUE; } else{ error("unknown argument: %s", argv[i]); usage(); } } /* * If we are using the -s option parse the spec file and * create objects for each one. */ if(spec_filename != NULL){ load_objects_from_specfile(); } /* * Make sure the needed arguments are specified. */ if(arch_flag.name == NULL){ error("-arch must be specified"); usage(); } if(objects.nobjects == 0){ error("no -filelist arguments or -s with #objects " "specified"); usage(); } /* * Process the objects. Two passes are required over the libgcc objects * to make sure all the symbols are picked up and all the referenced * symbols are changed. In the first pass no objects are written. * Once all the libgcc objects have been processed then all the other * objects are processed. */ for(j = 0; j < objects.nobjects; j++){ if(objects.libgcc[j] == NEW_LIBGCC || objects.libgcc[j] == OLD_LIBGCC) process(objects.names[j], TRUE, objects.libgcc[j]); } for(j = 0; j < objects.nobjects; j++){ if(objects.libgcc[j] == NEW_LIBGCC || objects.libgcc[j] == OLD_LIBGCC) process(objects.names[j], FALSE, objects.libgcc[j]); } for(j = 0; j < objects.nobjects; j++){ if(objects.libgcc[j] == NOT_LIBGCC) process(objects.names[j], FALSE, objects.libgcc[j]); } if(errors) return(EXIT_FAILURE); else return(EXIT_SUCCESS); } /* * usage() prints the current usage message and exits indicating failure. */ static void usage( void) { fprintf(stderr, "Usage: %s [-p] -arch " "-filelist filename[,dirname] ... OR\n", progname); fprintf(stderr, "Usage: %s -arch -s \n",progname); exit(EXIT_FAILURE); } /* * process() takes an object file name and drives the process to make sure the * libgcc symbols are prefixed. If the first_pass parameter is true then no * objects are written (this is used to scan all the libgcc objects and pick * up the symbol names). The parameter tells if this object comes from the * new or old libgcc or neither. */ static void process( char *filename, enum bool first_pass, enum libgcc libgcc) { struct arch *archs; unsigned long narchs; struct stat stat_buf; #ifdef DEBUG printf("process(%s,",filename); switch(libgcc){ case NOT_LIBGCC: printf("NOT_LIBGCC)\n"); break; case NEW_LIBGCC: printf("NEW_LIBGCC)\n"); break; case OLD_LIBGCC: printf("OLD_LIBGCC)\n"); break; } #endif archs = NULL; narchs = 0; input_changed = FALSE; /* breakout the input file for processing */ breakout(filename, &archs, &narchs, FALSE); if(errors) exit(EXIT_FAILURE); /* checkout the input file for symbol table replacement processing */ checkout(archs, narchs); /* translate the symbols in the input file */ translate_input(archs, narchs, libgcc); if(errors) exit(EXIT_FAILURE); if(first_pass == FALSE && input_changed == TRUE){ #ifdef DEBUG #endif printf("process(%s) was changed\n", filename); /* create the output file */ if(stat(filename, &stat_buf) == -1) system_error("can't stat file: %s", filename); writeout(archs, narchs, filename, stat_buf.st_mode & 0777, TRUE, FALSE, FALSE, NULL); } if(errors) exit(EXIT_FAILURE); } /* * translate_input() is passed a set of archs. It digs through the archs and * if one matches the arch_flag specified it will cause that arch to translated. * If the arch comes from new libgcc then libgcc parameter is NEW_LIBGCC, from * the old libgcc OLD_LIBGCC and neither then NOT_LIBGCC. */ static void translate_input( struct arch *archs, unsigned long narchs, enum libgcc libgcc) { unsigned long i, j, offset, size; cpu_type_t cputype; cpu_subtype_t cpusubtype; /* * Using the specified arch_flag process specified objects for this * architecure. */ for(i = 0; i < narchs; i++){ #ifdef DEBUG printf("translate_input(arch = %lu, %s)\n", i, archs[i].file_name); #endif /* * 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; } if(arch_flag.cputype != cputype || arch_flag.cpusubtype != cpusubtype) continue; /* * Now this arch[i] has been selected to be processed so process it * according to it's type. */ if(archs[i].type == OFILE_ARCHIVE){ for(j = 0; j < archs[i].nmembers; j++){ if(archs[i].members[j].type == OFILE_Mach_O){ translate_object(archs + i, archs[i].members + j, archs[i].members[j].object, libgcc); } } /* * Reset the library offsets and size. */ offset = 0; for(j = 0; j < archs[i].nmembers; j++){ archs[i].members[j].offset = offset; if(archs[i].members[j].object != NULL){ size = 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; 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){ translate_object(archs + i, NULL, archs[i].object, libgcc); } else { fatal_arch(archs + i, NULL, "can't process non-object and " "non-archive file: "); } } } /* * translate_object() is passed an object to be have it's libgcc symbols * translated to ones with suffixes. The symbol may have already had their * libgcc symbols suffixed. If the object comes from new libgcc then libgcc * parameter is NEW_LIBGCC, from the old libgcc OLD_LIBGCC and neither then * NOT_LIBGCC. */ static void translate_object( struct arch *arch, struct member *member, struct object *object, enum libgcc libgcc) { unsigned long i; enum byte_sex host_byte_sex; struct nlist *symbols, *nlistp; unsigned long nsyms, offset; char *strings, *p; unsigned long strings_size; #ifdef DEBUG printf("translate_object(%s)\n", arch->file_name); #endif if(object->mh->filetype != MH_OBJECT){ fatal_arch(arch, member,"is not an MH_OBJECT file (invalid input)"); return; } host_byte_sex = get_host_byte_sex(); if(object->st == NULL || object->st->nsyms == 0) return; symbols = (struct nlist *)(object->object_addr + object->st->symoff); nsyms = object->st->nsyms; if(object->object_byte_sex != host_byte_sex) swap_nlist(symbols, nsyms, host_byte_sex); strings = object->object_addr + object->st->stroff; strings_size = object->st->strsize; object->output_symbols = symbols; object->output_nsymbols = nsyms; object->input_sym_info_size = nsyms * sizeof(struct nlist) + object->st->strsize; if(object->dyst != NULL){ object->input_sym_info_size += object->dyst->nindirectsyms * sizeof(unsigned long); } start_string_table(); nlistp = symbols; for(i = 0; i < nsyms; i++){ if(nlistp->n_type & N_EXT){ if(nlistp->n_un.n_strx){ if(nlistp->n_un.n_strx > 0 && (unsigned long)nlistp->n_un.n_strx < strings_size){ if(libgcc == OLD_LIBGCC){ p = lookup_old_symbol(strings + nlistp->n_un.n_strx, ((nlistp->n_type & N_TYPE) != N_UNDF)); if(p != NULL){ nlistp->n_un.n_strx = add_to_string_table(p); } else{ nlistp->n_un.n_strx = add_to_string_table( strings + nlistp->n_un.n_strx); } } else{ p = lookup_new_symbol(strings + nlistp->n_un.n_strx, libgcc == NEW_LIBGCC && ((nlistp->n_type & N_TYPE) != N_UNDF)); if(p != NULL){ nlistp->n_un.n_strx = add_to_string_table(p); } else{ nlistp->n_un.n_strx = add_to_string_table( strings + nlistp->n_un.n_strx); } if(libgcc == NEW_LIBGCC && set_private_extern == TRUE && (nlistp->n_type & N_TYPE) != N_UNDF && (nlistp->n_type & N_PEXT) != N_PEXT){ nlistp->n_type |= N_PEXT; input_changed = TRUE; #ifdef DEBUG printf("translate_object(%s) setting N_PEXT on " "%s\n", arch->file_name, strings + nlistp->n_un.n_strx); #endif } } } else fatal_arch(arch, member, "bad string table " "index in symbol %lu in: ", i); } } else{ if(nlistp->n_un.n_strx){ if(nlistp->n_un.n_strx > 0 && (unsigned long)nlistp->n_un.n_strx < strings_size) nlistp->n_un.n_strx = add_to_string_table( strings + nlistp->n_un.n_strx); else fatal_arch(arch, member, "bad string table " "index in symbol %lu in: ", i); } } nlistp++; } end_string_table(); object->output_strings = allocate(string_table.index); memcpy(object->output_strings, string_table.strings,string_table.index); object->output_strings_size = string_table.index; object->output_sym_info_size = nsyms * sizeof(struct nlist) + string_table.index; if(object->dyst != NULL){ object->output_sym_info_size += object->dyst->nindirectsyms * sizeof(unsigned long); } 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; } if(object->dyst != NULL){ object->st->nsyms = nsyms; object->st->strsize = string_table.index; offset = ULONG_MAX; if(object->st->nsyms != 0 && object->st->symoff < offset) offset = object->st->symoff; if(object->dyst->nindirectsyms != 0 && object->dyst->indirectsymoff < offset) offset = object->dyst->indirectsymoff; if(object->st->strsize != 0 && object->st->stroff < offset) offset = object->st->stroff; if(object->st->nsyms != 0){ object->st->symoff = offset; offset += object->st->nsyms * sizeof(struct nlist); } else object->st->symoff = 0; if(object->dyst->nindirectsyms != 0){ object->output_indirect_symtab = (unsigned long *) (object->object_addr + object->dyst->indirectsymoff); object->dyst->indirectsymoff = offset; offset += object->dyst->nindirectsyms * sizeof(unsigned long); } else object->dyst->indirectsymoff = 0;; if(object->st->strsize != 0){ object->st->stroff = offset; offset += object->st->strsize; } else object->st->stroff = 0; } else{ object->st->nsyms = nsyms; object->st->stroff = object->st->symoff + nsyms * sizeof(struct nlist); object->st->strsize = string_table.index; } } /* * lookup_new_symbol() takes a symbol name which could possibly the name of a * new libgcc symbol. If the symbol is a new libgcc function it returns the * prefixed name for the symbol. The symbol may already have the prefix when * passed in. If this symbol is from new libgcc and not undefined then the * parameter add is TRUE and it is added to the hash table if not found. */ static char * lookup_new_symbol( char *name, enum bool add) { char *p, *q; long hash_key; struct symbol *sp; #ifdef DEBUG printf("lookup_new_symbol(%s, %s)\n", name, add == TRUE ? "TRUE" : "FALSE"); #endif if(strncmp(name, NEW_LIBCGG_PREFIX, strlen(NEW_LIBCGG_PREFIX)) == 0) p = name + strlen(NEW_LIBCGG_PREFIX); else p = name; hash_key = hash_string(p) % SYMBOL_HASH_SIZE; sp = new_symbol_hash[hash_key]; while(sp != NULL){ if(strcmp(p, sp->name) == 0){ if(p == name) input_changed = TRUE; return(sp->prefixed_name); } sp = sp->next; } if(add == TRUE){ #ifdef DEBUG printf("lookup_new_symbol adding %s\n", name); #endif sp = (struct symbol *)allocate(sizeof(struct symbol)); q = allocate(strlen(NEW_LIBCGG_PREFIX) + strlen(p) + 1); strcpy(q, NEW_LIBCGG_PREFIX); strcat(q, p); sp->name = q + strlen(NEW_LIBCGG_PREFIX); sp->prefixed_name = q; sp->next = new_symbol_hash[hash_key]; new_symbol_hash[hash_key] = sp; if(p == name) input_changed = TRUE; return(sp->prefixed_name); } return(NULL); } /* * lookup_old_symbol() takes a symbol name which could possibly the name of an * old libgcc symbol. If the symbol is an old libgcc function it returns the * prefixed name for the symbol. The symbol may already have the prefix when * passed in. If this symbol is from old libgcc and not undefined then the * parameter add is TRUE and it is added to the hash table if not found. */ static char * lookup_old_symbol( char *name, enum bool add) { char *p, *q; long hash_key; struct symbol *sp; #ifdef DEBUG printf("lookup_old_symbol(%s, %s)\n", name, add == TRUE ? "TRUE" : "FALSE"); #endif if(strncmp(name, OLD_LIBCGG_PREFIX, strlen(OLD_LIBCGG_PREFIX)) == 0) p = name + strlen(OLD_LIBCGG_PREFIX); else p = name; hash_key = hash_string(p) % SYMBOL_HASH_SIZE; sp = old_symbol_hash[hash_key]; while(sp != NULL){ if(strcmp(p, sp->name) == 0){ if(p == name) input_changed = TRUE; return(sp->prefixed_name); } sp = sp->next; } if(add == TRUE){ #ifdef DEBUG printf("lookup_old_symbol adding %s\n", name); #endif sp = (struct symbol *)allocate(sizeof(struct symbol)); q = allocate(strlen(OLD_LIBCGG_PREFIX) + strlen(p) + 1); strcpy(q, OLD_LIBCGG_PREFIX); strcat(q, p); sp->name = q + strlen(OLD_LIBCGG_PREFIX); sp->prefixed_name = q; sp->next = old_symbol_hash[hash_key]; old_symbol_hash[hash_key] = sp; if(p == name) input_changed = TRUE; return(sp->prefixed_name); } return(NULL); } /* * This routine is called before calls to add_to_string_table() are made to * setup or reset the string table structure. The first four bytes string * table are zeroed and the first string is placed after that (this was for * the string table length in a 4.3bsd a.out along time ago). The first four * bytes are kept zero even thought only the first byte can't be used as valid * string offset (because that is defined to be a NULL string) but to avoid * breaking programs that don't know this the first byte is left zero and the * first 4 bytes are not stuffed with the size because on a little endian * machine that first byte is likely to be non-zero. */ static void start_string_table() { if(string_table.size == 0){ string_table.size = INITIAL_STRING_TABLE_SIZE; string_table.strings = (char *)allocate(string_table.size); } memset(string_table.strings, '\0', sizeof(long)); string_table.index = sizeof(long); } /* * This routine adds the specified string to the string table structure and * returns the index of the string in the table. */ static long add_to_string_table( char *p) { long len, index; len = strlen(p) + 1; if(string_table.size < string_table.index + len){ string_table.strings = (char *)reallocate(string_table.strings, string_table.size * 2); string_table.size *= 2; } index = string_table.index; strcpy(string_table.strings + string_table.index, p); string_table.index += len; return(index); } /* * This routine is called after all calls to add_to_string_table() are made * to round off the size of the string table. It zeros the rounded bytes. */ static void end_string_table() { unsigned long length; length = round(string_table.index, sizeof(unsigned long)); memset(string_table.strings + string_table.index, '\0', length - string_table.index); string_table.index = length; }