/* $NetBSD: crunchide.c,v 1.5 2006/10/02 19:11:13 joerg Exp $ */ /* NetBSD: crunchide.c,v 1.9 1999/01/11 22:40:00 kleink Exp */ /* * Copyright (c) 1997 Christopher G. Demetriou. All rights reserved. * Copyright (c) 1994 University of Maryland * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of U.M. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. U.M. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: James da Silva, Systems Design and Analysis Group * Computer Science Department * University of Maryland at College Park */ /* * crunchide.c - tiptoes through an a.out symbol table, hiding all defined * global symbols. Allows the user to supply a "keep list" of symbols * that are not to be hidden. This program relies on the use of the * linker's -dc flag to actually put global bss data into the file's * bss segment (rather than leaving it as undefined "common" data). * * The point of all this is to allow multiple programs to be linked * together without getting multiple-defined errors. * * For example, consider a program "foo.c". It can be linked with a * small stub routine, called "foostub.c", eg: * int foo_main(int argc, char **argv){ return main(argc, argv); } * like so: * cc -c foo.c foostub.c * ld -dc -r foo.o foostub.o -o foo.combined.o * crunchide -k _foo_main foo.combined.o * at this point, foo.combined.o can be linked with another program * and invoked with "foo_main(argc, argv)". foo's main() and any * other globals are hidden and will not conflict with other symbols. * * TODO: * - resolve the theoretical hanging reloc problem (see check_reloc() * below). I have yet to see this problem actually occur in any real * program. In what cases will gcc/gas generate code that needs a * relative reloc from a global symbol, other than PIC? The * solution is to not hide the symbol from the linker in this case, * but to generate some random name for it so that it doesn't link * with anything but holds the place for the reloc. * - arrange that all the BSS segments start at the same address, so * that the final crunched binary BSS size is the max of all the * component programs' BSS sizes, rather than their sum. */ #include #ifndef lint __RCSID("$NetBSD: crunchide.c,v 1.5 2006/10/02 19:11:13 joerg Exp $"); #endif #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; void usage __P((void)); void add_to_keep_list __P((char *)); void add_file_to_keep_list __P((char *)); int hide_syms __P((const char *, char *)); static void setup_section __P((bfd *, sec_ptr, PTR)); static void copy_section __P((bfd *, sec_ptr, PTR)); int verbose; struct keep { struct keep *next; char *sym; } *keep_list; struct bfd_cookie { bfd *bfd; asymbol **symtable; }; int main(argc, argv) int argc; char **argv; { int ch, errors; char *bfdname = NULL; while ((ch = getopt(argc, argv, "b:k:f:v")) != -1) switch (ch) { case 'b': bfdname = optarg; break; case 'k': add_to_keep_list(optarg); break; case 'f': add_file_to_keep_list(optarg); break; case 'v': verbose = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc == 0) usage(); errors = 0; while (argc) { if (hide_syms(*argv, bfdname)) errors = 1; argc--, argv++; } return errors; } void usage() { const char **list; fprintf(stderr, "Usage: %s [-b bfdname] [-k ] [-f ] ...\n", __progname); fprintf(stderr, "supported targets:"); for (list = bfd_target_list(); *list != NULL; list++) fprintf(stderr, " %s", *list); fprintf(stderr, "\n"); exit(1); } void add_to_keep_list(symbol) char *symbol; { struct keep *newp, *prevp, *curp; int cmp; cmp = 0; for (curp = keep_list, prevp = NULL; curp; prevp = curp, curp = curp->next) if ((cmp = strcmp(symbol, curp->sym)) <= 0) break; if (curp && cmp == 0) return; /* already in table */ newp = (struct keep *)malloc(sizeof (struct keep)); if (newp) newp->sym = strdup(symbol); if (newp == NULL || newp->sym == NULL) { fprintf(stderr, "%s: out of memory for keep list\n", __progname); exit(1); } newp->next = curp; if (prevp) prevp->next = newp; else keep_list = newp; } int in_keep_list(symbol) const char *symbol; { struct keep *curp; int cmp; cmp = 0; for (curp = keep_list; curp; curp = curp->next) if ((cmp = strcmp(symbol, curp->sym)) <= 0) break; return curp && cmp == 0; } void add_file_to_keep_list(filename) char *filename; { FILE *keepf; char symbol[1024]; int len; if ((keepf = fopen(filename, "r")) == NULL) { perror(filename); usage(); } while (fgets(symbol, 1024, keepf)) { len = strlen(symbol); if (len && symbol[len - 1] == '\n') symbol[len - 1] = '\0'; add_to_keep_list(symbol); } fclose(keepf); } int hide_syms(filename, bfdname) const char *filename; char *bfdname; { int i, n, rv = 0; bfd *org_bfd = NULL, *new_bfd = NULL; char tempname[16]; char **name; long storage_needed, number_of_symbols; size_t fn_size; asymbol **org_symtable, **new_symtable; fn_size = strlen(filename); bfd_init(); if ((org_bfd = bfd_openr(filename, bfdname)) == NULL) { bfd_perror(filename); return 1; } if (!bfd_check_format(org_bfd, bfd_object)) { bfd_perror(filename); goto err; } bfdname = bfd_get_target(org_bfd); strcpy(tempname, "tmp.XXXXXXX"); mktemp(tempname); if ((new_bfd = bfd_openw(tempname, bfdname)) == NULL) { bfd_perror(tempname); goto err; } if (!bfd_set_format(new_bfd, bfd_get_format(org_bfd)) || !bfd_set_start_address(new_bfd, bfd_get_start_address(org_bfd)) || !bfd_set_file_flags(new_bfd, (bfd_get_file_flags(org_bfd) & bfd_applicable_file_flags(new_bfd))) || !bfd_set_arch_mach (new_bfd, bfd_get_arch (org_bfd), bfd_get_mach (org_bfd))) { bfd_perror(tempname); goto err; } bfd_map_over_sections(org_bfd, setup_section, (void *)new_bfd); storage_needed = bfd_get_symtab_upper_bound(org_bfd); if (storage_needed < 0) goto err; if (storage_needed == 0) goto out; org_symtable = (asymbol **)malloc(storage_needed); number_of_symbols = bfd_canonicalize_symtab(org_bfd, org_symtable); if (number_of_symbols < 0) goto err; new_symtable = (asymbol **)malloc(storage_needed); if (new_symtable == NULL) goto err; name = (char **)calloc(sizeof (char *), number_of_symbols); if (name == NULL) goto err; n = 0; for (i = 0; i < number_of_symbols; i++) { const char *symname; new_symtable[i] = org_symtable[i]; if (!(new_symtable[i]->flags & BSF_GLOBAL)) continue; symname = bfd_asymbol_name(new_symtable[i]); if (in_keep_list(symname)) continue; n++; /* * make sure there's size for the next entry, even if it's * as large as it can be. * * "_$$hide$$ " -> * 9 + 3 + sizes of fn and sym name */ name[i] = (char *)malloc(12 + fn_size + strlen(symname)); sprintf(name[i], "_$$hide$$ %s %s", filename, symname); new_symtable[i]->name = name[i]; } if (n > 0) { struct bfd_cookie cookie; new_symtable[number_of_symbols] = NULL; bfd_set_symtab(new_bfd, new_symtable, number_of_symbols); cookie.bfd = new_bfd; cookie.symtable = org_symtable; bfd_map_over_sections(org_bfd, copy_section, (void *)&cookie); if (!bfd_copy_private_bfd_data(org_bfd, new_bfd)) { bfd_perror("bfd_copy_private_bfd_data"); goto err; } } bfd_close(new_bfd); bfd_close(org_bfd); if (rename(tempname, filename) < 0) perror("rename"); unlink(tempname); for (i = 0; i < number_of_symbols; i++) if (name[i]) free(name[i]); free(new_symtable); free(name); out: return (rv); err: rv = 1; if (org_bfd) bfd_close(org_bfd); if (new_bfd) { unlink(tempname); bfd_close(new_bfd); } goto out; } static void setup_section(ibfd, isection, arg) bfd *ibfd; sec_ptr isection; PTR arg; { sec_ptr osection; bfd *obfd = (bfd *)arg; if ((osection = bfd_make_section_anyway(obfd, bfd_section_name(ibfd, isection))) == NULL || !bfd_set_section_size(obfd, osection, bfd_section_size(ibfd, isection)) || !bfd_set_section_vma(obfd, osection, bfd_section_vma(ibfd, isection)) || !bfd_set_section_alignment(obfd, osection, bfd_section_alignment(ibfd, isection)) || !bfd_set_section_flags(obfd, osection, bfd_get_section_flags(ibfd, isection))) { bfd_perror("setup_section"); return; } isection->output_section = osection; isection->output_offset = 0; if (!bfd_copy_private_section_data(ibfd, isection, obfd, osection)) { bfd_perror("setup_section"); return; } } static void copy_section(ibfd, isection, arg) bfd *ibfd; sec_ptr isection; PTR arg; { struct bfd_cookie *bc = (struct bfd_cookie *)arg; bfd *obfd = bc->bfd; asymbol **isym = bc->symtable; arelent **relpp; sec_ptr osection; PTR memhunk; bfd_size_type size; long relcount, relsize; osection = isection->output_section; size = isection->rawsize; if (size == 0 || osection == 0) return; relsize = bfd_get_reloc_upper_bound(ibfd, isection); if (relsize < 0) { bfd_perror(bfd_get_filename(ibfd)); } if (relsize == 0) { bfd_set_reloc(obfd, osection, NULL, 0); } else { relpp = (arelent **)malloc(relsize); relcount = bfd_canonicalize_reloc(ibfd, isection, relpp, isym); if (relcount < 0) bfd_perror(bfd_get_filename(ibfd)); bfd_set_reloc(obfd, osection, relpp, relcount); } isection->size = isection->rawsize; isection->reloc_done = 1; if (bfd_get_section_flags(ibfd, isection) & SEC_HAS_CONTENTS) { memhunk = (PTR)malloc((unsigned int)size); if (!bfd_get_section_contents(ibfd, isection, memhunk, 0, size)) bfd_perror(bfd_get_filename(ibfd)); if (!bfd_set_section_contents(obfd, osection, memhunk, 0, size)) bfd_perror(bfd_get_filename(obfd)); free(memhunk); } else if (bfd_get_section_flags(ibfd, isection) & SEC_LOAD) { memhunk = (PTR)malloc((unsigned int)size); memset(memhunk, 0, size); if (!bfd_set_section_contents(obfd, osection, memhunk, 0, size)) bfd_perror(bfd_get_filename(obfd)); free(memhunk); } }