summaryrefslogtreecommitdiff
path: root/libexec/ld.so/prebind/prebind.c
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/ld.so/prebind/prebind.c')
-rw-r--r--libexec/ld.so/prebind/prebind.c1437
1 files changed, 1437 insertions, 0 deletions
diff --git a/libexec/ld.so/prebind/prebind.c b/libexec/ld.so/prebind/prebind.c
new file mode 100644
index 00000000000..bb10e124009
--- /dev/null
+++ b/libexec/ld.so/prebind/prebind.c
@@ -0,0 +1,1437 @@
+/* $OpenBSD: prebind.c,v 1.1 2006/05/03 16:10:52 drahn Exp $ */
+/*
+ * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, 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.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syslimits.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <elf_abi.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include "resolve.h"
+#include "link.h"
+#include "sod.h"
+#ifndef __mips64__
+#include "machine/reloc.h"
+#endif
+#include "prebind.h"
+
+/* seems to make sense to limit how big of file can be dealt with */
+#define MAX_FILE_SIZE (512 * 1024 * 1024)
+
+char *shstrtab;
+
+#define DEBUG
+
+/* TODO - library path from ldconfig */
+#define DEFAULT_PATH "/usr/lib:/usr/X11R6/lib:/usr/local/qte/lib"
+
+/* alpha uses RELOC_JMP_SLOT */
+#ifdef __amd64__
+#define RELOC_JMP_SLOT R_X86_64_JUMP_SLOT
+#endif
+#ifdef __arm__
+#define RELOC_JMP_SLOT R_ARM_JUMP_SLOT
+#endif
+#ifdef __hppa__
+#define RELOC_JMP_SLOT RELOC_IPLT
+#endif
+#ifdef __hppa64__
+#define RELOC_JMP_SLOT RELOC_JMPSLOT
+#endif
+#ifdef __i386__
+#define RELOC_JMP_SLOT RELOC_JUMP_SLOT
+#endif
+#ifdef __mips64__
+#define RELOC_JMP_SLOT 0 /* XXX mips64 doesnt have PLT reloc */
+#endif
+/* powerpc uses RELOC_JMP_SLOT */
+/* sparc uses RELOC_JMP_SLOT */
+/* sparc64 uses RELOC_JMP_SLOT */
+
+#include "prebind_struct.h"
+struct proglist *curbin;
+
+obj_list_ty library_list =
+ TAILQ_HEAD_INITIALIZER(library_list);
+
+prog_list_ty prog_list =
+ TAILQ_HEAD_INITIALIZER(prog_list);
+
+
+struct elf_object * elf_load_object (void *pexe, const char *name);
+void elf_free_object(struct elf_object *object);
+void map_to_virt(Elf_Phdr *, Elf_Ehdr *, Elf_Addr, u_long *);
+#ifdef DEBUG
+#endif
+int load_obj_needed(struct elf_object *object);
+int load_lib(const char *name, struct elf_object *parent);
+elf_object_t * elf_load_shlib_hint(struct sod *sod, struct sod *req_sod,
+ int use_hints, const char *libpath);
+char * elf_find_shlib(struct sod *sodp, const char *searchpath,
+ int nohints);
+elf_object_t * elf_tryload_shlib(const char *libname);
+int elf_match_file(struct sod *sodp, char *name, int namelen);
+void elf_init_objarray(void);
+void elf_add_object(struct elf_object *object, int lib);
+void elf_print_objarray(void);
+
+struct elf_object * elf_lookup_object(const char *name);
+struct elf_object * elf_lookup_object_devino(dev_t dev, ino_t inode);
+void elf_free_curbin_list(struct elf_object *obj);
+void elf_resolve_curbin(void);
+struct proglist *elf_newbin(void);
+void elf_add_prog(struct proglist *object);
+void elf_sum_reloc();
+int elf_prep_lib_prebind(struct elf_object *object);
+int elf_prep_bin_prebind(struct proglist *pl);
+
+void elf_dump_footer(struct prebind_footer *footer);
+
+
+void elf_fixup_prog_load(int fd, struct prebind_footer *footer,
+ struct elf_object *object);
+void elf_clear_prog_load(int fd, struct elf_object *object);
+
+void
+elf_find_symbol_rel(const char *s, struct elf_object *object,
+ Elf_Rel *rel, struct symcache_noflag *symcache,
+ struct symcache_noflag *pltsymcache);
+
+void
+elf_find_symbol_rela(const char *s, struct elf_object *object,
+ Elf_RelA *rela, struct symcache_noflag *symcache,
+ struct symcache_noflag *pltsymcache);
+
+int elf_find_symbol_obj(elf_object_t *object, const char *name,
+ unsigned long hash, int flags, const Elf_Sym **this,
+ const Elf_Sym **weak_sym, elf_object_t **weak_object);
+
+int verbose; /* how verbose to be when operating */
+int merge_mode; /* merge (do not overwrite) existing prebind library info */
+
+struct elf_object *load_object;
+
+struct elf_object * load_file(const char *filename, int lib);
+void load_dir(char *name);
+void load_exe(char *name);
+
+void
+load_file_or_dir(char *name)
+{
+ struct stat sb;
+ int ret;
+
+ ret = lstat(name, &sb);
+ printf("ret\n");
+ if (ret != 0)
+ return;
+ printf("file or dir mode %d\n", sb.st_mode & S_IFMT);
+ switch(sb.st_mode & S_IFMT) {
+ case S_IFREG:
+ load_exe(name);
+ break;
+ case S_IFDIR:
+ printf("loading dir %s\n", name);
+ load_dir(name);
+ break;
+ default:
+ ; /* links and other files we skip */
+ }
+
+}
+void
+load_dir(char *name)
+{
+ DIR *dirp;
+ struct dirent *dp;
+ struct stat sb;
+ char *buf;
+
+ dirp = opendir(name);
+ if (dirp == NULL) {
+ /* dir failed to open, skip */
+ return;
+ }
+ while ((dp = readdir(dirp)) != NULL) {
+ switch (dp->d_type) {
+ case DT_UNKNOWN:
+ /*
+ * NFS will return unknown, since load_file
+ * does stat the file, this just
+ */
+ asprintf(&buf, "%s/%s", name, dp->d_name);
+ lstat(buf, &sb);
+ if (sb.st_mode == S_IFREG)
+ load_exe(buf);
+ free (buf);
+ case DT_REG:
+ asprintf(&buf, "%s/%s", name, dp->d_name);
+ load_exe(buf);
+ free (buf);
+ default:
+ /* other files symlinks, dirs, ... we ignore */
+ ;
+ }
+ }
+}
+void
+load_exe(char *name)
+{
+ struct elf_object *object;
+ struct elf_object *interp;
+ struct objlist *ol;
+ int fail = 0;
+
+ curbin = elf_newbin();
+ if (verbose > 0)
+ printf("processing %s\n", name);
+ object = load_file(name, 0);
+ if (load_object != NULL) {
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ fail = load_obj_needed(ol->object);
+ if (fail != 0)
+ break; /* XXX */
+
+ }
+ interp = load_file(curbin->interp, 2);
+
+ /* slight abuse of this field */
+ object->load_object = interp;
+
+ if (fail == 0) {
+ elf_resolve_curbin();
+ elf_add_prog(curbin);
+ } else {
+ printf("failed to load %s\n", name);
+ elf_free_curbin_list(object);
+ free (curbin);
+ }
+ if (load_object != NULL) {
+ load_object = NULL;
+ }
+ }
+}
+
+struct elf_object *
+load_file(const char *filename, int lib)
+{
+ int fd = -1;
+ void *buf = NULL;
+ struct stat ifstat;
+ Elf_Ehdr *pehdr;
+ Elf_Shdr *pshdr;
+ char *pexe;
+#ifdef DEBUG1
+ int i;
+#endif
+ struct elf_object *obj = NULL;
+
+
+ fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ perror (filename);
+ goto done;
+ }
+
+ if (fstat(fd, &ifstat) == -1) {
+ perror (filename);
+ goto done;
+ }
+
+ if ((ifstat.st_mode & S_IFMT) != S_IFREG)
+ goto done;
+
+ if (ifstat.st_size < sizeof (Elf_Ehdr)) {
+ if (verbose > 0)
+ printf("%s: short file\n", filename);
+ goto done;
+ }
+
+ obj = elf_lookup_object_devino( ifstat.st_dev, ifstat.st_ino);
+ if (obj != NULL)
+ goto done;
+
+ buf = mmap(NULL, ifstat.st_size, PROT_READ, MAP_FILE | MAP_SHARED,
+ fd, 0);
+ if (buf == MAP_FAILED) {
+ printf("%s: cannot mmap\n", filename);
+ goto done;
+ }
+
+ pehdr = (Elf_Ehdr *) buf;
+
+ if (IS_ELF(*pehdr) == 0) {
+ goto done;
+ }
+
+ if( pehdr->e_machine != ELF_TARG_MACH) {
+ if (verbose > 0)
+ printf("%s: wrong arch\n", filename);
+ goto done;
+ }
+
+ pexe = buf;
+ if (pehdr->e_shstrndx == 0) {
+ goto done;
+ }
+ pshdr = (Elf_Shdr *) (pexe + pehdr->e_shoff +
+ (pehdr->e_shstrndx * pehdr->e_shentsize));
+
+
+#if 0
+printf("e_ehsize %x\n", pehdr->e_ehsize);
+printf("e_phoff %x\n", pehdr->e_phoff);
+printf("e_shoff %x\n", pehdr->e_shoff);
+printf("e_phentsize %x\n", pehdr->e_phentsize);
+printf("e_phnum %x\n", pehdr->e_phnum);
+printf("e_shentsize %x\n", pehdr->e_shentsize);
+printf("e_shstrndx %x\n\n", pehdr->e_shstrndx);
+#endif
+
+ shstrtab = (char *) (pexe + pshdr->sh_offset);
+
+
+ obj = elf_load_object(pexe, filename);
+
+
+
+ munmap(buf, ifstat.st_size);
+ buf = NULL;
+
+ if (obj != NULL) {
+ obj->dev = ifstat.st_dev;
+ obj->inode = ifstat.st_ino;
+ if (load_object == NULL)
+ load_object = obj;
+
+ elf_add_object(obj, lib);
+
+#ifdef DEBUG1
+ dump_info(obj);
+#endif
+ }
+ if (lib == 1 && merge_mode == 1) {
+ /*
+ * for libraries, check if old prebind info exists
+ * and load it if we are in merge mode
+ */
+
+ elf_load_existing_prebind(obj, fd);
+ }
+done:
+ if (buf != NULL)
+ munmap(buf, ifstat.st_size);
+ if (fd != -1)
+ close (fd);
+ return obj;
+}
+
+void __dead
+usage()
+{
+ extern char *__progname;
+ printf("%s [-v] {programlist}\n", __progname);
+ exit(1);
+}
+
+extern int64_t prebind_blocks;
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ int ch;
+ extern int optind;
+
+ /* GETOPT */
+ while ((ch = getopt(argc, argv, "mv")) != -1) {
+ switch (ch) {
+ case 'm':
+ merge_mode = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ usage();
+ /* NOTREACHED */
+ }
+
+ elf_init_objarray();
+
+ for (i = 0; i < argc; i++) {
+ load_file_or_dir(argv[i]);
+ }
+ if (verbose > 1) {
+ elf_print_objarray();
+ elf_print_prog_list(&prog_list);
+ }
+ elf_sum_reloc();
+
+ printf("total new blocks %lld\n", prebind_blocks);
+
+ return 0;
+}
+
+struct elf_object *
+elf_load_object (void *pexe, const char *name)
+{
+ int i;
+ struct elf_object *object;
+ Elf_Dyn *dynp = NULL, *odynp;
+ Elf_Ehdr *ehdr;
+ Elf_Phdr *phdr;
+ const Elf_Sym *symt;
+ const char *strt;
+ Elf_Addr loff;
+ Elf_Word *needed_list;
+ int needed_cnt = 0;
+
+ object = calloc (1, sizeof (struct elf_object));
+ if (object == NULL) {
+ printf("unable to allocate object for %s\n", name);
+ exit(10);
+ }
+ ehdr = pexe;
+ loff = (Elf_Addr)pexe;
+
+ object->load_addr = 0;
+ object->load_name = strdup(name);
+
+ phdr = (Elf_Phdr *)((char *)pexe + ehdr->e_phoff);
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ switch (phdr[i].p_type) {
+ case PT_DYNAMIC:
+ dynp = (Elf_Dyn *)(phdr[i].p_offset);
+ break;
+ case PT_INTERP:
+ /* XXX can only occur in programs */
+ curbin->interp = strdup ((char *)((char *)pexe +
+ phdr[i].p_offset));
+ default:
+ break;
+ }
+ }
+
+ if (dynp == 0) {
+ return NULL; /* XXX ??? */
+ }
+
+ dynp = (Elf_Dyn *)((unsigned long)dynp + loff);
+ odynp = dynp;
+ while (dynp->d_tag != DT_NULL) {
+ if (dynp->d_tag < DT_NUM)
+ object->Dyn.info[dynp->d_tag] = dynp->d_un.d_val;
+ else if (dynp->d_tag >= DT_LOPROC &&
+ dynp->d_tag < DT_LOPROC + DT_PROCNUM)
+ object->Dyn.info[dynp->d_tag + DT_NUM - DT_LOPROC] =
+ dynp->d_un.d_val;
+ if (dynp->d_tag == DT_TEXTREL)
+ object->dyn.textrel = 1;
+ if (dynp->d_tag == DT_SYMBOLIC)
+ object->dyn.symbolic = 1;
+ if (dynp->d_tag == DT_BIND_NOW)
+ object->obj_flags = RTLD_NOW;
+ if (dynp->d_tag == DT_NEEDED)
+ needed_cnt++;
+
+ dynp++;
+ }
+
+ needed_list = calloc((needed_cnt + 1), (sizeof (Elf_Word)));
+ if (needed_list == NULL) {
+ printf("unable to allocate needed_list for %s\n", name);
+ exit(10);
+ }
+ needed_list[needed_cnt] = 0;
+ for (dynp = odynp, i = 0; dynp->d_tag != DT_NULL; dynp++) {
+ if (dynp->d_tag == DT_NEEDED) {
+ needed_list[i] = dynp->d_un.d_val;
+ i++;
+ }
+ }
+
+ if (object->Dyn.info[DT_HASH])
+ map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_HASH]);
+ if (object->Dyn.info[DT_STRTAB])
+ map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_STRTAB]);
+ if (object->Dyn.info[DT_SYMTAB])
+ map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_SYMTAB]);
+
+ if (object->Dyn.info[DT_RELA])
+ map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_RELA]);
+ if (object->Dyn.info[DT_RPATH])
+ object->Dyn.info[DT_RPATH] += object->Dyn.info[DT_STRTAB];
+ if (object->Dyn.info[DT_REL])
+ map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_REL]);
+#if 0
+ if (object->Dyn.info[DT_SONAME])
+ map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_SONAME]);
+#endif
+ if (object->Dyn.info[DT_JMPREL])
+ map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_JMPREL]);
+
+ symt = object->dyn.symtab;
+ strt = object->dyn.strtab;
+
+ {
+ Elf_Sym *sym;
+ char *str;
+ Elf_Rel *rel;
+ Elf_RelA *rela;
+ Elf_Addr *hash;
+ Elf_Word *hashtab;
+ void *plt;
+ size_t hashsz;
+
+ if (object->Dyn.info[DT_HASH] != 0) {
+ hash = object->dyn.hash;
+ hashtab = (void *)hash;
+ object->nbuckets = hashtab[0];
+ object->nchains = hashtab[1];
+ hashsz = (2 + object->nbuckets + object->nchains) *
+ sizeof (Elf_Word);
+ hash = malloc(hashsz);
+ if (hash == NULL) {
+ printf("unable to allocate hash for %s\n",
+ name);
+ exit(10);
+ }
+ bcopy (object->dyn.hash, hash, hashsz);
+ object->dyn.hash = hash;
+ object->buckets = ((Elf_Word *)hash + 2);
+ object->chains = object->buckets + object->nbuckets;
+ }
+
+ str = malloc(object->dyn.strsz);
+ bcopy(object->dyn.strtab, str, object->dyn.strsz);
+ object->dyn.strtab = str;
+ strt = str;
+
+ sym = malloc(object->nchains * sizeof(Elf_Sym));
+ bcopy(object->dyn.symtab, sym,
+ object->nchains * sizeof(Elf_Sym));
+ object->dyn.symtab = sym;
+ symt = sym;
+
+ if (object->dyn.relsz != 0) {
+ rel = malloc(object->dyn.relsz);
+ bcopy(object->dyn.rel, rel, object->dyn.relsz);
+ object->dyn.rel = rel;
+ } else {
+ object->dyn.rel = NULL;
+ }
+ if (object->dyn.relasz != 0) {
+ rela = malloc(object->dyn.relasz);
+ bcopy(object->dyn.rela, rela, object->dyn.relasz);
+ object->dyn.rela = rela;
+ } else {
+ object->dyn.rela = NULL;
+ }
+ if (object->dyn.pltrelsz != 0) {
+ plt = malloc(object->dyn.pltrelsz);
+ bcopy((void*)object->dyn.jmprel, plt,
+ object->dyn.pltrelsz);
+ object->dyn.jmprel = (long)plt;
+ } else {
+ object->dyn.jmprel = NULL;
+ }
+ if (object->dyn.rpath != NULL)
+ object->dyn.rpath = strdup(object->dyn.rpath);
+
+
+ object->dyn.needed = (Elf_Addr)needed_list;
+
+ }
+
+#ifdef DEBUG1
+ dump_info(object);
+#endif
+ return object;
+}
+/*
+ * Free any extra pieces associated with 'object'
+ */
+void
+elf_free_object(struct elf_object *object)
+{
+ free (object->load_name);
+ if (object->dyn.hash != NULL)
+ free (object->dyn.hash);
+ free ((void *)object->dyn.strtab);
+ free ((void *)object->dyn.symtab);
+ if (object->dyn.rel != NULL)
+ free (object->dyn.rel);
+ if (object->dyn.rela != NULL)
+ free (object->dyn.rela);
+ if (object->dyn.rpath != NULL)
+ free ((void *)object->dyn.rpath);
+ free (object);
+}
+
+/*
+ * translate an object address into a file offset for the
+ * file assuming that the file is mapped at base.
+ */
+void
+map_to_virt(Elf_Phdr *phdr, Elf_Ehdr *ehdr, Elf_Addr base, u_long *vaddr)
+{
+ int i;
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ switch (phdr[i].p_type) {
+ case PT_LOAD:
+ if(phdr[i].p_vaddr > *vaddr)
+ continue;
+ if(phdr[i].p_vaddr + phdr[i].p_memsz < *vaddr)
+ continue;
+#ifdef DEBUG1
+ printf("input address %lx translated to ", *vaddr);
+#endif
+ *vaddr += phdr[i].p_offset - phdr[i].p_vaddr + base;
+#ifdef DEBUG1
+ printf("%lx, base %lx %lx %llx\n", *vaddr, base,
+ phdr[i].p_vaddr, phdr[i].p_offset );
+#endif
+
+ default:
+ break;
+ }
+ }
+}
+
+int
+load_obj_needed(struct elf_object *object)
+{
+ int i;
+ Elf_Word *needed_list;
+ int err;
+
+ needed_list = (Elf_Word *)object->dyn.needed;
+ for (i = 0; needed_list[i] != NULL; i++) {
+ if (verbose > 1)
+ printf("lib: %s\n", needed_list[i] +
+ object->dyn.strtab);
+ err = load_lib(needed_list[i] + object->dyn.strtab, object);
+ if (err) {
+ printf("failed to load lib %s\n",
+ needed_list[i] + object->dyn.strtab);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+load_lib(const char *name, struct elf_object *parent)
+{
+ struct sod sod, req_sod;
+ int ignore_hints;
+ int try_any_minor = 0;
+ struct elf_object *object = NULL;
+
+#if 0
+ printf("load_lib %s\n", name);
+#endif
+
+ ignore_hints = 0;
+
+ if(strchr(name, '/')) {
+ char *lpath, *lname;
+ lpath = strdup(name);
+ lname = strrchr(lpath, '/');
+ if (lname == NULL || lname[1] == '\0') {
+ free(lpath);
+ return (1); /* failed */
+ }
+ *lname = '\0';
+ lname++;
+
+ _dl_build_sod(lname, &sod);
+ req_sod = sod;
+
+ /* this code does not allow lower minors */
+fullpathagain:
+ object = elf_load_shlib_hint(&sod, &req_sod,
+ ignore_hints, lpath);
+ if (object != NULL)
+ goto fullpathdone;
+
+ if (try_any_minor == 0) {
+ try_any_minor = 1;
+ ignore_hints = 1;
+ req_sod.sod_minor = -1;
+ goto fullpathagain;
+ }
+ /* ERR */
+fullpathdone:
+ free(lpath);
+ free((char *)sod.sod_name);
+ return (object == NULL); /* failed */
+ }
+ _dl_build_sod(name, &sod);
+ req_sod = sod;
+
+ /* ignore LD_LIBRARY_PATH */
+
+again:
+ if (parent->dyn.rpath != NULL) {
+ object = elf_load_shlib_hint(&sod, &req_sod,
+ ignore_hints, parent->dyn.rpath);
+ if (object != NULL)
+ goto done;
+ }
+ if (parent != load_object && load_object->dyn.rpath != NULL) {
+ object = elf_load_shlib_hint(&sod, &req_sod,
+ ignore_hints, load_object->dyn.rpath);
+ if (object != NULL)
+ goto done;
+ }
+ object = elf_load_shlib_hint(&sod, &req_sod,
+ ignore_hints, NULL);
+
+ if (try_any_minor == 0) {
+ try_any_minor = 1;
+ ignore_hints = 1;
+ req_sod.sod_minor = -1;
+ goto again;
+ }
+ if (object == NULL)
+ printf ("unable to load %s\n", name);
+
+done:
+ free((char *)sod.sod_name);
+
+ return (object == NULL);
+}
+
+/*
+ * attempt to locate and load a library based on libpath, sod info and
+ * if it needs to respect hints, passing type and flags to perform open
+ */
+elf_object_t *
+elf_load_shlib_hint(struct sod *sod, struct sod *req_sod,
+ int ignore_hints, const char *libpath)
+{
+ elf_object_t *object = NULL;
+ char *hint;
+
+ hint = elf_find_shlib(req_sod, libpath, ignore_hints);
+ if (hint != NULL) {
+#if 1
+ if (req_sod->sod_minor < sod->sod_minor)
+ printf("warning: lib%s.so.%d.%d: "
+ "minor version >= %d expected, "
+ "using it anyway\n",
+ (char *)sod->sod_name, sod->sod_major,
+ req_sod->sod_minor, sod->sod_minor);
+#endif
+ object = elf_tryload_shlib(hint);
+ }
+ return object;
+}
+
+char elf_hint_store[MAXPATHLEN];
+
+char *
+elf_find_shlib(struct sod *sodp, const char *searchpath, int nohints)
+{
+ char *hint, lp[PATH_MAX + 10], *path;
+ struct dirent *dp;
+ const char *pp;
+ int match, len;
+ DIR *dd;
+ struct sod tsod, bsod; /* transient and best sod */
+
+ /* if we are to search default directories, and hints
+ * are not to be used, search the standard path from ldconfig
+ * (_dl_hint_search_path) or use the default path
+ */
+ if (nohints)
+ goto nohints;
+
+ if (searchpath == NULL) {
+ /* search 'standard' locations, find any match in the hints */
+ hint = _dl_findhint((char *)sodp->sod_name, sodp->sod_major,
+ sodp->sod_minor, NULL);
+ if (hint)
+ return hint;
+ } else {
+ /* search hints requesting matches for only
+ * the searchpath directories,
+ */
+ pp = searchpath;
+ while (pp) {
+ path = lp;
+ while (path < lp + PATH_MAX &&
+ *pp && *pp != ':' && *pp != ';')
+ *path++ = *pp++;
+ *path = 0;
+
+ /* interpret "" as curdir "." */
+ if (lp[0] == '\0') {
+ lp[0] = '.';
+ lp[1] = '\0';
+ }
+
+ hint = _dl_findhint((char *)sodp->sod_name,
+ sodp->sod_major, sodp->sod_minor, lp);
+ if (hint != NULL)
+ return hint;
+
+ if (*pp) /* Try curdir if ':' at end */
+ pp++;
+ else
+ pp = 0;
+ }
+ }
+
+ /*
+ * For each directory in the searchpath, read the directory
+ * entries looking for a match to sod. filename compare is
+ * done by _dl_match_file()
+ */
+nohints:
+ if (searchpath == NULL) {
+ if (_dl_hint_search_path != NULL)
+ searchpath = _dl_hint_search_path;
+ else
+ searchpath = DEFAULT_PATH;
+ }
+ pp = searchpath;
+ while (pp) {
+ path = lp;
+ while (path < lp + PATH_MAX && *pp && *pp != ':' && *pp != ';')
+ *path++ = *pp++;
+ *path = 0;
+
+ /* interpret "" as curdir "." */
+ if (lp[0] == '\0') {
+ lp[0] = '.';
+ lp[1] = '\0';
+ }
+
+ if ((dd = opendir(lp)) != NULL) {
+ match = 0;
+ while ((dp = readdir(dd)) != NULL) {
+ tsod = *sodp;
+ if (elf_match_file(&tsod, dp->d_name,
+ dp->d_namlen)) {
+ /*
+ * When a match is found, tsod is
+ * updated with the major+minor found.
+ * This version is compared with the
+ * largest so far (kept in bsod),
+ * and saved if larger.
+ */
+ if (!match ||
+ tsod.sod_major == -1 ||
+ tsod.sod_major > bsod.sod_major ||
+ ((tsod.sod_major ==
+ bsod.sod_major) &&
+ tsod.sod_minor > bsod.sod_minor)) {
+ bsod = tsod;
+ match = 1;
+ len = strlcpy(
+ elf_hint_store, lp,
+ MAXPATHLEN);
+ if (lp[len-1] != '/') {
+ elf_hint_store[len] =
+ '/';
+ len++;
+ }
+ strlcpy(
+ &elf_hint_store[len],
+ dp->d_name,
+ MAXPATHLEN-len);
+ if (tsod.sod_major == -1)
+ break;
+ }
+ }
+ }
+ closedir(dd);
+ if (match) {
+ *sodp = bsod;
+ return (elf_hint_store);
+ }
+ }
+
+ if (*pp) /* Try curdir if ':' at end */
+ pp++;
+ else
+ pp = 0;
+ }
+ return NULL;
+}
+
+elf_object_t *
+elf_tryload_shlib(const char *libname)
+{
+ struct elf_object *object;
+ object = elf_lookup_object(libname);
+ if (object == NULL) {
+ object = load_file(libname, 1);
+ }
+ if (object == NULL)
+ printf("tryload_shlib %s\n", libname);
+ return object;
+}
+
+/*
+ * elf_match_file()
+ *
+ * This fucntion determines if a given name matches what is specified
+ * in a struct sod. The major must match exactly, and the minor must
+ * be same or larger.
+ *
+ * sodp is updated with the minor if this matches.
+ */
+
+int
+elf_match_file(struct sod *sodp, char *name, int namelen)
+{
+ int match;
+ struct sod lsod;
+ char *lname;
+
+ lname = name;
+ if (sodp->sod_library) {
+ if (strncmp(name, "lib", 3))
+ return 0;
+ lname += 3;
+ }
+ if (strncmp(lname, (char *)sodp->sod_name,
+ strlen((char *)sodp->sod_name)))
+ return 0;
+
+ _dl_build_sod(name, &lsod);
+
+ match = 0;
+ if ((strcmp((char *)lsod.sod_name, (char *)sodp->sod_name) == 0) &&
+ (lsod.sod_library == sodp->sod_library) &&
+ ((sodp->sod_major == -1) || (sodp->sod_major == lsod.sod_major)) &&
+ ((sodp->sod_minor == -1) ||
+ (lsod.sod_minor >= sodp->sod_minor))) {
+ match = 1;
+
+ /* return version matched */
+ sodp->sod_major = lsod.sod_major;
+ sodp->sod_minor = lsod.sod_minor;
+ }
+ free((char *)lsod.sod_name);
+ return match;
+}
+void
+elf_add_prog(struct proglist *curbin)
+{
+ TAILQ_INSERT_TAIL(&prog_list, curbin, list);
+}
+
+struct proglist *
+elf_newbin(void)
+{
+ struct proglist *proglist;
+ proglist = malloc(sizeof (struct proglist));
+ if (proglist == NULL) {
+ printf("unable to allocate proglist\n");
+ exit(10);
+ }
+ proglist->fixup = NULL;
+ TAILQ_INIT(&(proglist->curbin_list));
+ return proglist;
+}
+
+
+
+/*
+ * Copy the contents of a libraries symbol cache instance into
+ * the 'global' symbol cache for that library
+ * this will currently resolve conflicts between mismatched
+ * libraries by flagging any mismatches as invalid
+ * which will cause all programs to generate a fixup
+ * It probably would be interesting to modify this to keep the most
+ * common entry as a library cache, and only have a fixup in programs
+ * where the symbol is overridden.
+ *
+ * Note 1: If 'merge' mode is ever written, this will need to keep
+ * the 'cached' copies symbols and do fixups for the rest, regardless
+ * of conflicts
+ *
+ * Note 2: This is run once each for the (got)symcache and pltsymcache
+ */
+
+struct elf_object badobj_store;
+struct elf_object *badobj = &badobj_store;
+
+/*
+ * copy the symbols found in a library symcache to the 'master/common'
+ * symbol table note that this will skip copying the following references
+ * 1. non-existing entries
+ * 2. symobj == prog &&& obj != prog
+ * 3 symobj == prog's interpter (references to dl_open)
+ */
+void
+elf_copy_syms(struct symcache_noflag *tcache, struct symcache_noflag *scache,
+ struct elf_object *obj, struct elf_object *prog, int nsyms)
+{
+ int i;
+ for (i = 0; i < nsyms; i++) {
+ if (scache[i].obj != NULL &&
+ (scache[i].obj->dyn.null != prog->dyn.null ||
+ obj->dyn.null == prog->dyn.null) &&
+ scache[i].obj != prog->load_object) {
+ if (tcache[i].obj != NULL) {
+ if (scache[i].obj != tcache[i].obj) {
+ if (verbose > 2) {
+ printf("sym mismatch %d: "
+ "obj %d: sym %ld %s "
+ "nobj %s oobj %s\n",
+ i, (int)scache[i].obj->dyn.null,
+ scache[i].sym -
+ scache[i].obj->dyn.symtab,
+ scache[i].sym->st_name +
+ scache[i].obj->dyn.strtab,
+ scache[i].obj->load_name,
+ tcache[i].obj->load_name);
+ }
+
+#if 0
+ if (tcache[i].obj != badobj)
+ printf("%s: %s conflict\n",
+ obj->load_name,
+ scache[i].sym->st_name +
+ scache[i].obj->dyn.strtab);
+#endif
+ tcache[i].obj = badobj;
+ tcache[i].sym = NULL;
+ }
+ } else {
+ if (scache[i].obj != prog) {
+#if 0
+ printf("%s: %s copying\n",
+ obj->load_name,
+ scache[i].sym->st_name +
+ scache[i].obj->dyn.strtab);
+#endif
+ tcache[i].sym = scache[i].sym;
+ tcache[i].obj = scache[i].obj;
+ }
+ }
+
+#if 0
+ printf("symidx %d: obj %d sym %ld %s\n",
+ i, scache[i].obj->dyn.null,
+ scache[i].sym -
+ scache[i].obj->dyn.symtab,
+ scache[i].sym->st_name +
+ scache[i].obj->dyn.strtab
+ );
+#endif
+ }
+ }
+}
+
+struct elf_object *
+elf_lookup_object(const char *name)
+{
+ struct objlist *ol;
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ if (strcmp (name, ol->object->load_name) == 0) {
+ return ol->object;
+ }
+
+ }
+ TAILQ_FOREACH(ol, &library_list, list) {
+ if (strcmp (name, ol->object->load_name) == 0) {
+ elf_add_object_curbin_list(ol->object);
+ return ol->object;
+ }
+ }
+ return NULL;
+}
+
+struct elf_object *
+elf_lookup_object_devino(dev_t dev, ino_t inode)
+{
+ struct objlist *ol;
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ if (ol->object->dev == dev &&
+ ol->object->inode == inode)
+ return ol->object;
+ }
+ TAILQ_FOREACH(ol, &library_list, list) {
+ if (ol->object->dev == dev &&
+ ol->object->inode == inode) {
+ elf_add_object_curbin_list(ol->object);
+ return ol->object;
+ }
+ }
+ return NULL;
+}
+
+void
+elf_find_symbol_rel(const char *name, struct elf_object *object,
+ Elf_Rel *rel, struct symcache_noflag *symcache,
+ struct symcache_noflag *pltsymcache)
+{
+ struct objlist *ol;
+ unsigned long h = 0;
+ const char *p = name;
+ const Elf_Sym *sym, *ref_sym = NULL;
+ const Elf_Sym *weak_sym = NULL;
+ struct elf_object *weak_obj = NULL;
+ int flags = 0;
+ int found = 0;
+ int type, idx;
+ struct elf_object *ref_object = NULL;
+
+ sym = object->dyn.symtab + ELF_R_SYM(rel->r_info);
+
+ while (*p) {
+ unsigned long g;
+ h = (h << 4) + *p++;
+ if ((g = h & 0xf0000000))
+ h ^= g >> 24;
+ h &= ~g;
+ }
+
+ type = ELF_R_TYPE(rel->r_info);
+ flags = SYM_SEARCH_ALL|SYM_WARNNOTFOUND;
+ if (type == RELOC_JMP_SLOT)
+ flags |= SYM_PLT;
+
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ found = elf_find_symbol_obj(ol->object, name, h, flags, &sym,
+ &weak_sym, &weak_obj);
+ if (found) {
+#if 0
+ printf("found sym %s in obj %s\n", name, ol->object->load_name);
+#endif
+ ref_object = ol->object;
+ break;
+ }
+
+ }
+ if (found) {
+ ref_object = ol->object;
+ ref_sym = sym;
+ } else if (weak_obj != NULL) {
+ found = 1;
+ ref_object = weak_obj;
+ ref_sym = weak_sym;
+#if 0
+ printf("found sym %s in obj %s %d\n", name,
+ weak_obj->load_name, flags);
+#endif
+ }
+ if (found == 1) {
+#if 0
+ printf("object %s sym %s, ref_object %s flags %d\n",
+ object->load_name, name,
+ ref_object->load_name, flags);
+#endif
+ idx = ELF_R_SYM(rel->r_info);
+ if (flags & SYM_PLT) {
+ pltsymcache[idx].obj = ref_object;
+ pltsymcache[idx].sym = ref_sym;
+ } else {
+ symcache[idx].obj = ref_object;
+ symcache[idx].sym = ref_sym;
+ }
+ } else {
+ printf("symbol not found %s\n", name);
+ }
+}
+
+void
+elf_find_symbol_rela(const char *name, struct elf_object *object,
+ Elf_RelA *rela, struct symcache_noflag *symcache,
+ struct symcache_noflag *pltsymcache)
+{
+ struct objlist *ol;
+ unsigned long h = 0;
+ const char *p = name;
+ const Elf_Sym *sym, *ref_sym = NULL;
+ const Elf_Sym *weak_sym = NULL;
+ struct elf_object *weak_obj = NULL;
+ int flags = 0;
+ int found = 0;
+ int type, idx;
+ struct elf_object *ref_object = NULL;
+
+ sym = object->dyn.symtab + ELF_R_SYM(rela->r_info);
+
+ while (*p) {
+ unsigned long g;
+ h = (h << 4) + *p++;
+ if ((g = h & 0xf0000000))
+ h ^= g >> 24;
+ h &= ~g;
+ }
+
+ type = ELF_R_TYPE(rela->r_info);
+ flags = SYM_SEARCH_ALL|SYM_WARNNOTFOUND;
+ if (type == RELOC_JMP_SLOT)
+ flags |= SYM_PLT;
+
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+
+// printf("searching sym [%s] typ %d in obj %s\n", name, type, ol->object->load_name);
+ found = elf_find_symbol_obj(ol->object, name, h, flags, &sym,
+ &weak_sym, &weak_obj);
+ if (found) {
+#if 0
+ printf("found sym %s in obj %s\n", name, ol->object->load_name);
+#endif
+ ref_object = ol->object;
+ break;
+ }
+
+ }
+ if (found) {
+ ref_object = ol->object;
+ ref_sym = sym;
+ } else if (weak_obj != NULL) {
+ found = 1;
+ ref_object = weak_obj;
+ ref_sym = weak_sym;
+#if 0
+ printf("found weak sym %s in obj %s\n", name, weak_obj->load_name);
+#endif
+ }
+ if (found == 1) {
+#if 0
+ printf("object %s sym %s, ref_object %s %s\n",
+ object->load_name, name,
+ ref_object->load_name,
+ (flags & SYM_PLT) ? "plt" : "got");
+
+#endif
+ idx = ELF_R_SYM(rela->r_info);
+ if (flags & SYM_PLT) {
+ pltsymcache[idx].obj = ref_object;
+ pltsymcache[idx].sym = ref_sym;
+ } else {
+ symcache[idx].obj = ref_object;
+ symcache[idx].sym = ref_sym;
+ }
+ } else {
+ printf("symbol not found %s\n", name);
+ }
+}
+
+int
+elf_find_symbol_obj(elf_object_t *object, const char *name, unsigned long hash,
+ int flags, const Elf_Sym **this, const Elf_Sym **weak_sym,
+ elf_object_t **weak_object)
+{
+ const Elf_Sym *symt = object->dyn.symtab;
+ const char *strt = object->dyn.strtab;
+ long si;
+ const char *symn;
+
+ for (si = object->buckets[hash % object->nbuckets];
+ si != STN_UNDEF; si = object->chains[si]) {
+ const Elf_Sym *sym = symt + si;
+
+ if (sym->st_value == 0)
+ continue;
+
+ if (ELF_ST_TYPE(sym->st_info) != STT_NOTYPE &&
+ ELF_ST_TYPE(sym->st_info) != STT_OBJECT &&
+ ELF_ST_TYPE(sym->st_info) != STT_FUNC)
+ continue;
+
+ symn = strt + sym->st_name;
+ if (sym != *this && strcmp(symn, name))
+ continue;
+
+ /* allow this symbol if we are referring to a function
+ * which has a value, even if section is UNDEF.
+ * this allows &func to refer to PLT as per the
+ * ELF spec. st_value is checked above.
+ * if flags has SYM_PLT set, we must have actual
+ * symbol, so this symbol is skipped.
+ */
+ if (sym->st_shndx == SHN_UNDEF) {
+ if ((flags & SYM_PLT) || sym->st_value == 0 ||
+ ELF_ST_TYPE(sym->st_info) != STT_FUNC)
+ continue;
+ }
+
+ if (ELF_ST_BIND(sym->st_info) == STB_GLOBAL) {
+ *this = sym;
+ return 1;
+ } else if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
+ if (!*weak_sym) {
+ *weak_sym = sym;
+ *weak_object = object;
+ }
+ }
+ }
+ return 0;
+}
+
+void
+elf_reloc(struct elf_object *object, struct symcache_noflag *symcache,
+ struct symcache_noflag *pltsymcache)
+{
+ int numrel;
+ int numrela;
+ int i;
+ const Elf_Sym *sym;
+ Elf_Rel *rel;
+ Elf_RelA *rela;
+ numrel = object->dyn.relsz / sizeof(Elf_Rel);
+#if 0
+ printf("rel relocations: %d\n", numrel);
+#endif
+ rel = object->dyn.rel;
+ for (i = 0; i < numrel; i++) {
+ const char *s;
+ sym = object->dyn.symtab + ELF_R_SYM(rel[i].r_info);
+
+ /* hppa has entries without names, skip them */
+ if (sym->st_name == 0)
+ continue;
+
+ s = (ELF_R_SYM(rel[i].r_info) == 0) ? "<rel>" :
+ object->dyn.strtab + sym->st_name;
+#ifdef DEBUG1
+ printf("%d: %x sym %x %s type %d\n", i, rel[i].r_offset,
+ ELF_R_SYM(rel[i].r_info), s,
+ ELF_R_TYPE(rel[i].r_info));
+#endif
+ if (ELF_R_SYM(rel[i].r_info) != 0) {
+ elf_find_symbol_rel(s, object, &rel[i],
+ symcache, pltsymcache);
+ }
+ }
+ if (numrel) {
+ numrel = object->dyn.pltrelsz / sizeof(Elf_Rel);
+ rel = (Elf_Rel *)(object->Dyn.info[DT_JMPREL]);
+ for (i = 0; i < numrel; i++) {
+ const char *s;
+ sym = object->dyn.symtab + ELF_R_SYM(rel[i].r_info);
+
+ /* hppa has entries without names, skip them */
+ if (sym->st_name == 0)
+ continue;
+
+ s = (ELF_R_SYM(rel[i].r_info) == 0) ? "<rel>" :
+ object->dyn.strtab + sym->st_name;
+#ifdef DEBUG1
+ printf("%d: %x sym %d %s type %d\n", i, rel[i].r_offset,
+ ELF_R_SYM(rel[i].r_info), s,
+ ELF_R_TYPE(rel[i].r_info));
+#endif
+ if (ELF_R_SYM(rel[i].r_info) != 0) {
+ elf_find_symbol_rel(s, object, &rel[i],
+ symcache, pltsymcache);
+ }
+ }
+ }
+
+ numrela = object->dyn.relasz / sizeof(Elf_RelA);
+#if 0
+ printf("rela relocations: %d\n", numrela);
+#endif
+ rela = object->dyn.rela;
+ for (i = 0; i < numrela; i++) {
+ const char *s;
+ sym = object->dyn.symtab + ELF_R_SYM(rela[i].r_info);
+
+ /* hppa has entries without names, skip them */
+ if (sym->st_name == 0)
+ continue;
+
+ s = (ELF_R_SYM(rela[i].r_info) == 0) ? "<rel>" :
+ object->dyn.strtab + sym->st_name;
+#ifdef DEBUG1
+ printf("%d: %x sym %x %s type %d\n", i, rela[i].r_offset,
+ ELF_R_SYM(rela[i].r_info), s,
+ ELF_R_TYPE(rela[i].r_info));
+#endif
+ if (ELF_R_SYM(rela[i].r_info) != 0) {
+ elf_find_symbol_rela(s, object, &rela[i],
+ symcache, pltsymcache);
+ }
+ }
+ if (numrela) {
+ numrela = object->dyn.pltrelsz / sizeof(Elf_RelA);
+ rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
+
+ for (i = 0; i < numrela; i++) {
+ const char *s;
+ sym = object->dyn.symtab + ELF_R_SYM(rela[i].r_info);
+
+ /* hppa has entries without names, skip them */
+ if (sym->st_name == 0)
+ continue;
+
+ s = (ELF_R_SYM(rela[i].r_info) == 0) ? "<rel>" :
+ object->dyn.strtab + sym->st_name;
+#ifdef DEBUG1
+ printf("%d: %x sym %x %s type %d\n", i,
+ rela[i].r_offset,
+ ELF_R_SYM(rela[i].r_info), s,
+ ELF_R_TYPE(rela[i].r_info));
+#endif
+ if (ELF_R_SYM(rela[i].r_info) != 0) {
+ elf_find_symbol_rela(s, object, &rela[i],
+ symcache, pltsymcache);
+ }
+ }
+ }
+}
+
+void
+elf_resolve_curbin(void)
+{
+ struct objlist *ol;
+
+#ifdef DEBUG1
+ elf_print_curbin_list(curbin);
+#endif
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ ol->cache = calloc(sizeof(struct symcache_noflag),
+ ol->object->nchains);
+ ol->pltcache = calloc(sizeof(struct symcache_noflag),
+ ol->object->nchains);
+ if (ol->cache == NULL || ol->pltcache == NULL) {
+ printf("unable to allocate memory for cache %s\n",
+ ol->object->load_name);
+ exit(20);
+ }
+ elf_reloc(ol->object, ol->cache, ol->pltcache);
+ }
+}