pkgsrc/cross/bfd-crunchide/files/crunchide.c
2006-10-02 19:11:13 +00:00

442 lines
11 KiB
C

/* $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 <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: crunchide.c,v 1.5 2006/10/02 19:11:13 joerg Exp $");
#endif
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <a.out.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <bfd.h>
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 <symbol-name>] [-f <keep-list-file>] <files> ...\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$$ <filename> <symname><NUL>" ->
* 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);
}
}