summaryrefslogtreecommitdiffstats
path: root/admin/objprelink.c
diff options
context:
space:
mode:
Diffstat (limited to 'admin/objprelink.c')
-rw-r--r--admin/objprelink.c530
1 files changed, 530 insertions, 0 deletions
diff --git a/admin/objprelink.c b/admin/objprelink.c
new file mode 100644
index 00000000..319bd680
--- /dev/null
+++ b/admin/objprelink.c
@@ -0,0 +1,530 @@
+/* objprelink -- prelinks c++ files for producing a more efficient dynlink.
+ Copyright 2001, Leon Bottou
+
+ This program is free software; you can redistribute it and/or modify ait
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ Compile with:
+ gcc -O2 -o objprelink objprelink.c /usr/lib/libbfd.a /usr/lib/libiberty.a
+
+ This is i386 only.
+ There is very experimental PPC support (totally unproven).
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include "bfd.h"
+
+static int verbose = 0;
+static int replace = 1;
+static const char *target = 0;
+static const char *program_name = "objprelink";
+static const char *enomem = "out of memory";
+static const char *eassert = "assertion failed";
+
+static void
+fatal(const char *fname, int lineno, const char *msg)
+{
+ if (program_name)
+ fprintf(stderr,"%s ", program_name);
+ if (fname)
+ fprintf(stderr,"(%s:%d) ", fname, lineno);
+ if (program_name || fname)
+ fprintf(stderr," : ");
+ if (bfd_get_error() != bfd_error_no_error)
+ bfd_perror(msg);
+ else if (errno)
+ perror(msg);
+ else
+ fprintf(stderr,"%s\n", msg);
+ exit(5);
+}
+
+#define FATAL(s) \
+ do{fatal(__FILE__,__LINE__,s);}while(0)
+#define ASSERT(x,s) \
+ do{if(!(x))fatal(__FILE__,__LINE__,s);}while(0)
+#define CLEARERR() \
+ do{errno=0;bfd_set_error(bfd_error_no_error);}while(0)
+
+static const char *
+M(const char *format, ...)
+{
+ static char buffer[256*256];
+ va_list ap;
+ va_start(ap, format);
+ vsprintf(buffer, format, ap);
+ va_end(ap);
+ return buffer;
+}
+
+static void
+usage(FILE *stream, int status)
+{
+ fprintf(stream,
+ "Usage: %s [-v] [-n] <objectfiles>\n"
+ "-- \"prelink\" c++ virtual tables\n"
+ "\n"
+ "This program massages object files in order to accelerate the loading\n"
+ "of shared object files (.so files) created by linking these object files.\n"
+ "This is intel specific at the moment\n"
+ "\n"
+ "The program scans the specified object file for linkonce sections\n"
+ "containing virtual tables with 32 bit absolute relocations (R_386_32)\n"
+ "to undefined procedures. For each such procedure, it creates a linkonce\n"
+ "text section containing a relay jump. All absolute relocations to this\n"
+ "procedure are then redirected to the single relay jump.\n"
+ "\n"
+ "Program objprelink should be called on all object files before linking.\n"
+ "\n"
+ "Option -v selects verbose mode\n"
+ "Option -n does not change the object files but generates a new object\n"
+ "whose name is composed by appending \".new\" to the original filename.\n",
+ program_name);
+ exit(status);
+}
+
+
+/* ---------------- MACHINE DEPENDENT PART ---- */
+
+static char *stub;
+static int stub_size;
+static int stub_alignment;
+static void (*stub_install)(bfd*, asection*, asymbol**);
+
+static reloc_howto_type *howto_32;
+
+
+/* I386 */
+
+static char stub_i386[] = {
+ 0xb8, 0, 0, 0, 0, /* mov $0,%eax */
+ 0xff, 0xe0 /* jmp *(%eax) */
+};
+
+static void
+stub_install_i386(bfd *obfd, asection *p, asymbol **jumpto)
+{
+ int status;
+ int relcount = 1;
+ arelent **reloc = (arelent**)bfd_alloc(obfd, sizeof(arelent*)*relcount);
+ arelent *relent = (arelent*)bfd_alloc(obfd, sizeof(arelent)*relcount);
+ ASSERT(reloc, enomem);
+ ASSERT(relent, enomem);
+ reloc[0] = &relent[0];
+ reloc[0]->sym_ptr_ptr = jumpto;
+ reloc[0]->address = 1;
+ reloc[0]->addend = 0;
+ reloc[0]->howto = howto_32;
+ bfd_set_reloc(obfd, p, reloc, relcount);
+ status = bfd_set_section_contents(obfd, p, stub, (file_ptr) 0, stub_size);
+ ASSERT(status, eassert);
+}
+
+/* PPC */
+
+static char stub_ppc[] = {
+ 0x48, 0, 0, 0
+};
+
+static reloc_howto_type *howto_ppc_pltrel24;
+static reloc_howto_type *howto_ppc_rel24;
+
+static void
+stub_install_ppc(bfd *obfd, asection *p, asymbol **jumpto)
+{
+ int status;
+ int relcount = 1;
+ arelent **reloc = (arelent**)bfd_alloc(obfd, sizeof(arelent*)*relcount);
+ arelent *relent = (arelent*)bfd_alloc(obfd, sizeof(arelent)*relcount);
+ ASSERT(reloc, enomem);
+ ASSERT(relent, enomem);
+ reloc[0] = &relent[0];
+ reloc[0]->sym_ptr_ptr = jumpto;
+ reloc[0]->address = 0;
+ reloc[0]->addend = 0;
+ reloc[0]->howto = howto_ppc_pltrel24; /* Second choice: howto_ppc_rel24 */
+ bfd_set_reloc(obfd, p, reloc, relcount);
+ status = bfd_set_section_contents(obfd, p, stub, (file_ptr) 0, stub_size);
+ ASSERT(status, eassert);
+}
+
+/* ALPHA */
+
+/* ALL */
+
+static void
+prepare_stub_data(bfd *obfd, asymbol **syms, int symcount)
+{
+ switch (bfd_get_arch(obfd))
+ {
+ case bfd_arch_i386:
+ stub = stub_i386;
+ stub_size = sizeof(stub_i386);
+ stub_install = stub_install_i386;
+ stub_alignment = 1;
+ break;
+
+ case bfd_arch_powerpc:
+ fprintf(stderr,"Warning: PPC support is experimental\n");
+ howto_ppc_pltrel24 = bfd_reloc_type_lookup(obfd, BFD_RELOC_24_PLT_PCREL);
+ howto_ppc_rel24 = bfd_reloc_type_lookup(obfd, BFD_RELOC_PPC_B26);
+ ASSERT(howto_ppc_pltrel24, eassert);
+ ASSERT(howto_ppc_rel24, eassert);
+ stub = stub_ppc;
+ stub_size = sizeof(stub_ppc);
+ stub_install = stub_install_ppc;
+ stub_alignment = 2;
+ break;
+
+ default:
+ FATAL("This cpu architecture is not supported (yet)");
+ break;
+ }
+ howto_32 = bfd_reloc_type_lookup(obfd, BFD_RELOC_32);
+ ASSERT(howto_32, eassert);
+}
+
+
+/* ---------- END OF MACHINE DEPENDENT PART ---- */
+
+
+
+/* Symbol hash table */
+
+typedef struct symbol_entry_s {
+ struct bfd_hash_entry root;
+ struct symbol_entry_s *next;
+ asection *relay;
+ asymbol *sym;
+} symbol_entry;
+
+static struct bfd_hash_entry *
+init_symbol_entry(struct bfd_hash_entry *entry,
+ struct bfd_hash_table *table,
+ const char *string)
+{
+ symbol_entry *ret = (symbol_entry*)entry;
+ if (! ret)
+ ret = (symbol_entry*)bfd_hash_allocate(table, sizeof(symbol_entry));
+ ASSERT(ret,eassert);
+ memset(ret, 0, sizeof(symbol_entry));
+ return bfd_hash_newfunc((struct bfd_hash_entry*)ret, table, string);
+}
+
+static symbol_entry *
+lookup_symbol(struct bfd_hash_table *table, const char *id, boolean create)
+{
+ return (symbol_entry*) bfd_hash_lookup(table, id, create, true);
+}
+
+/* Cached relocations stored
+ in section userdata field */
+
+struct section_userdata {
+ int relcount;
+ arelent **reloc;
+};
+
+/* Check if section is a virtual table */
+
+static const char *
+is_vt_section(bfd *abfd, asection *p)
+{
+ const char *name = bfd_section_name(abfd, p);
+ static const char prefix[] = ".gnu.linkonce.d.__vt_";
+ static flagword w = (SEC_ALLOC|SEC_LOAD|SEC_RELOC|SEC_DATA|SEC_LINK_ONCE);
+ flagword f = bfd_get_section_flags (abfd, p);
+ if (f & w == w)
+ if (!strncmp(name, prefix, sizeof(prefix)-1))
+ return name + sizeof(prefix)-6;
+ return false;
+}
+
+/* Process one object file.
+ Returns true if a new file has been successfully created. */
+
+static boolean
+process(char *iname, char *oname)
+{
+
+ bfd *ibfd = 0;
+ bfd *obfd = 0;
+ int isymcount = 0;
+ asymbol **isyms = 0;
+ struct bfd_hash_table vtsymtab;
+ symbol_entry *vtsyms = 0;
+ int vtcount = 0;
+
+ int relcount;
+ arelent **reloc;
+ asection *p;
+ symbol_entry *hsym;
+ int storage_needed;
+ int status;
+ int i;
+
+ if (verbose)
+ printf("processing file %s\n", iname);
+
+ /* --- open bfds */
+ ibfd = bfd_openr (iname, target);
+ ASSERT(ibfd, M("cannot read file \"%s\"", iname));
+ if (! bfd_check_format(ibfd, bfd_object))
+ FATAL(M("file \"%s\" is not a regular object file", iname));
+ CLEARERR();
+
+ /* --- get input symbols */
+ storage_needed = bfd_get_symtab_upper_bound(ibfd);
+ ASSERT(storage_needed>=0, eassert);
+ isyms = (asymbol**) bfd_alloc(ibfd, storage_needed);
+ ASSERT(isyms, enomem);
+ isymcount = bfd_canonicalize_symtab(ibfd, isyms);
+ ASSERT(isymcount>=0, eassert);
+ CLEARERR();
+
+ /* --- prepare variables required for stub generation */
+ prepare_stub_data(ibfd, isyms, isymcount);
+
+ /* --- cache all relocation entries */
+ for (p=ibfd->sections; p; p=p->next)
+ {
+ const char *name = bfd_section_name(ibfd, p);
+ if (! strncmp(name, ".gnu.linkonce.t.stub.", 21))
+ {
+ if (verbose)
+ printf(" file has already been processed\n");
+ bfd_close(ibfd);
+ return false;
+ }
+ p->userdata = 0;
+ storage_needed = bfd_get_reloc_upper_bound(ibfd, p);
+ if (storage_needed > 0)
+ {
+ struct section_userdata *u = (void*)bfd_alloc(ibfd, sizeof(*u));
+ ASSERT(u, enomem);
+ memset(u, 0, sizeof(*u));
+ reloc = (arelent**)bfd_alloc(ibfd, storage_needed);
+ ASSERT(reloc, enomem);
+ relcount = bfd_canonicalize_reloc(ibfd, p, reloc, isyms);
+ ASSERT(relcount>=0, M("Problem with relocations for section %s in file %s",
+ bfd_section_name(ibfd,p), bfd_get_filename(ibfd)));
+ u->relcount = relcount;
+ u->reloc = reloc;
+ p->userdata = u;
+ }
+ }
+
+ /* --- construct hash table of vt undefined symbols */
+ memset(&vtsymtab, 0, sizeof(vtsymtab));
+ status = bfd_hash_table_init(&vtsymtab, init_symbol_entry);
+ ASSERT(status, eassert);
+ for (p=ibfd->sections; p; p=p->next)
+ {
+ const char *vtname = is_vt_section(ibfd, p);
+ if (vtname)
+ {
+ if (verbose)
+ printf(" scanning section \"%s\"\n", vtname);
+ reloc = (p->userdata) ? ((struct section_userdata*)p->userdata)->reloc : 0;
+ relcount = (p->userdata) ? ((struct section_userdata*)p->userdata)->relcount : 0;
+ for (i=0; i<relcount; i++)
+ {
+ arelent *rel = reloc[i];
+ asymbol *sym = (rel->sym_ptr_ptr ? *rel->sym_ptr_ptr : 0);
+ if (sym &&
+ rel->howto->type == howto_32->type &&
+ !lookup_symbol(&vtsymtab, sym->name, false))
+ {
+ hsym = lookup_symbol(&vtsymtab, sym->name, true);
+ ASSERT(hsym, eassert);
+ hsym->next = vtsyms;
+ hsym->sym = sym;
+ vtsyms = hsym;
+ vtcount += 1;
+ }
+ }
+ }
+ }
+ if (verbose)
+ printf(" found %d symbols in need for a stub\n", vtcount);
+ if (! vtcount)
+ {
+ bfd_hash_table_free(&vtsymtab);
+ bfd_close(ibfd);
+ return false;
+ }
+
+ /* --- create output bfd */
+ obfd = bfd_openw (oname, bfd_get_target (ibfd));
+ ASSERT(obfd, M("cannot create file \"%s\"", iname));
+ bfd_set_format (obfd, bfd_get_format (ibfd));
+ bfd_set_start_address (obfd, bfd_get_start_address(ibfd));
+ bfd_set_file_flags (obfd, (bfd_get_file_flags (ibfd) & bfd_applicable_file_flags (obfd)));
+ bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), bfd_get_mach (ibfd));
+
+ /* --- create copied sections */
+ for (p=ibfd->sections; p; p=p->next)
+ {
+ const char *err = M("cannot replicate section %s from file %s",
+ bfd_section_name (ibfd, p), iname);
+ asection *isection = p;
+ asection *osection = bfd_make_section_anyway (obfd, bfd_section_name (ibfd, isection));
+ ASSERT(osection, err);
+ status = bfd_set_section_size (obfd, osection, bfd_section_size (ibfd, isection));
+ ASSERT(status, err);
+ status = bfd_set_section_vma (obfd, osection, bfd_section_vma (ibfd, isection));
+ ASSERT(status, err);
+ osection->lma = isection->lma;
+ status = bfd_set_section_alignment(obfd, osection, bfd_section_alignment (ibfd, isection));
+ ASSERT(status, err);
+ status = bfd_set_section_flags(obfd, osection, bfd_get_section_flags (ibfd, isection));
+ ASSERT(status, err);
+ isection->output_section = osection;
+ isection->output_offset = 0;
+ status = bfd_copy_private_section_data (ibfd, isection, obfd, osection);
+ ASSERT(status, err);
+ }
+
+ /* --- create stub sections */
+ if (verbose)
+ printf(" defining stub sections\n");
+ for (hsym=vtsyms; hsym; hsym=hsym->next)
+ {
+ asection *osection;
+ asymbol *sym;
+ char *name;
+ /* new section */
+ name = (char*)bfd_alloc(obfd, strlen(hsym->sym->name) + 28);
+ ASSERT(name, enomem);
+ strcpy(name,".gnu.linkonce.t.stub.");
+ strcat(name,hsym->sym->name);
+ hsym->relay = osection = bfd_make_section_anyway (obfd, name);
+ ASSERT(osection, enomem);
+ bfd_set_section_size(obfd, osection, stub_size);
+ bfd_set_section_alignment(obfd, osection, stub_alignment);
+ bfd_set_section_flags(obfd, osection,
+ SEC_HAS_CONTENTS|SEC_ALLOC|SEC_LOAD|
+ SEC_RELOC|SEC_READONLY|SEC_CODE|SEC_LINK_ONCE);
+ }
+
+ /* --- copy symbols */
+ bfd_set_symtab (obfd, isyms, isymcount);
+
+ /* --- copy section relocs and data */
+ if (verbose)
+ printf(" copying section data from %s\n", iname);
+ for (p=ibfd->sections; p; p=p->next)
+ {
+ const char *sname;
+ const char *err = M("cannot copy data from section %s in file %s",
+ bfd_section_name (ibfd, p), iname);
+ asection *isection = p;
+ asection *osection = p->output_section;
+ bfd_size_type size = bfd_get_section_size_before_reloc (isection);
+ /* copy relocs */
+ reloc = (p->userdata) ? ((struct section_userdata*)isection->userdata)->reloc : 0;
+ relcount = (p->userdata) ? ((struct section_userdata*)isection->userdata)->relcount : 0;
+ sname = is_vt_section(ibfd, isection);
+ if (sname)
+ {
+ if (verbose)
+ printf(" patching relocs for %s\n", sname);
+ for (i=0; i<relcount; i++)
+ {
+ arelent *rel = reloc[i];
+ asymbol *sym = (rel->sym_ptr_ptr ? *rel->sym_ptr_ptr : 0);
+ ASSERT(sym, eassert);
+ hsym = lookup_symbol(&vtsymtab, sym->name, false);
+ if (hsym)
+ rel->sym_ptr_ptr = hsym->relay->symbol_ptr_ptr;
+ }
+ }
+ bfd_set_reloc(obfd, osection, (relcount) ? reloc : 0, relcount);
+ /* copy data */
+ if (bfd_get_section_flags (ibfd, isection) & SEC_HAS_CONTENTS)
+ {
+ void *memchunk = malloc(size);
+ ASSERT(memchunk, enomem);
+ status = bfd_get_section_contents (ibfd, isection, memchunk, (file_ptr) 0, size);
+ ASSERT(status, eassert);
+ status = bfd_set_section_contents(obfd, osection, memchunk, (file_ptr) 0, size);
+ ASSERT(status, eassert);
+ free(memchunk);
+ }
+ }
+
+ /* --- generate relocs and data for stub sections */
+ for (hsym=vtsyms; hsym; hsym=hsym->next)
+ {
+ asection *osection = hsym->relay;
+ (*stub_install)(obfd, osection, &hsym->sym);
+ }
+
+ /* --- copy private data */
+ status = bfd_copy_private_bfd_data (ibfd, obfd);
+ ASSERT(status, M("error copying private data for %s", iname));
+
+ /* --- close bfds */
+ if (verbose)
+ printf(" finishing %s\n", iname);
+ status = bfd_close(obfd);
+ ASSERT(status, M("error while writing %s", oname));
+ bfd_close(ibfd);
+
+ /* --- cleanup */
+ bfd_hash_table_free(&vtsymtab);
+ return true;
+}
+
+
+
+/* Main */
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ int status = 0;
+ program_name = strrchr(argv[0],'/');
+ program_name = (program_name) ? program_name + 1 : argv[0];
+ if (argc < 2)
+ usage(stderr, 5);
+ bfd_init();
+ bfd_set_error_program_name(program_name);
+ for (i=1; i<argc; i++)
+ {
+ if (!strcmp(argv[i], "-v"))
+ verbose = 1;
+ else if (!strcmp(argv[i], "-n"))
+ replace = 0;
+ else if (!strncmp(argv[i], "--target=", 9))
+ target = argv[i] + 9;
+ else
+ {
+ char *name = argv[i];
+ char *newname = malloc(strlen(name) + 8);
+ ASSERT(newname,enomem);
+ strcpy(newname, name);
+ strcat(newname, ".new");
+ if (process(name, newname) && replace)
+ {
+ unlink(name);
+ link(newname, name);
+ unlink(newname);
+ }
+ free(newname);
+ }
+ }
+ /* Finished */
+ return status;
+}
+
+