summaryrefslogtreecommitdiff
path: root/libexec/ld.so/ldconfig
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/ld.so/ldconfig')
-rw-r--r--libexec/ld.so/ldconfig/Makefile4
-rw-r--r--libexec/ld.so/ldconfig/debug.c158
-rw-r--r--libexec/ld.so/ldconfig/dl_prebind.c619
-rw-r--r--libexec/ld.so/ldconfig/etc.c16
-rw-r--r--libexec/ld.so/ldconfig/ldconfig.c34
-rw-r--r--libexec/ld.so/ldconfig/library.c342
-rw-r--r--libexec/ld.so/ldconfig/prebind.881
-rw-r--r--libexec/ld.so/ldconfig/prebind.c2164
-rw-r--r--libexec/ld.so/ldconfig/prebind.h64
-rw-r--r--libexec/ld.so/ldconfig/prebind_delete.c27
-rw-r--r--libexec/ld.so/ldconfig/prebind_struct.h89
-rw-r--r--libexec/ld.so/ldconfig/sod.c265
12 files changed, 3830 insertions, 33 deletions
diff --git a/libexec/ld.so/ldconfig/Makefile b/libexec/ld.so/ldconfig/Makefile
index 1b165066953..c65c6d118f4 100644
--- a/libexec/ld.so/ldconfig/Makefile
+++ b/libexec/ld.so/ldconfig/Makefile
@@ -1,8 +1,8 @@
-# $OpenBSD: Makefile,v 1.6 2006/05/11 22:19:22 deraadt Exp $
+# $OpenBSD: Makefile,v 1.7 2006/05/12 23:20:52 deraadt Exp $
# $NetBSD: Makefile,v 1.10 1995/03/06 04:24:41 cgd Exp $
PROG= ldconfig
-SRCS= ldconfig.c shlib.c etc.c prebind_delete.c
+SRCS= ldconfig.c shlib.c etc.c prebind_delete.c debug.c prebind.c library.c sod.c
LDDIR?= $(.CURDIR)/..
#CFLAGS+=-Werror
CFLAGS+=-I$(.CURDIR) -I$(.CURDIR)/..
diff --git a/libexec/ld.so/ldconfig/debug.c b/libexec/ld.so/ldconfig/debug.c
new file mode 100644
index 00000000000..c768826e4b8
--- /dev/null
+++ b/libexec/ld.so/ldconfig/debug.c
@@ -0,0 +1,158 @@
+/* $OpenBSD: debug.c,v 1.1 2006/05/12 23:20:52 deraadt 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"
+#include "machine/reloc.h"
+#include "prebind.h"
+#include "prebind_struct.h"
+
+#ifdef DEBUG1
+void
+dump_info(struct elf_object *object)
+{
+ int numrel, numrela, i;
+ const Elf_Sym *symt;
+ const char *strt;
+ Elf_Word *needed_list;
+
+ symt = object->dyn.symtab;
+ strt = object->dyn.strtab;
+
+ for (i = 0; i < object->nchains; i++) {
+ const Elf_Sym *sym = symt + i;
+ char *type;
+
+ switch (ELF_ST_TYPE(sym->st_info)) {
+ case STT_FUNC:
+ type = "func";
+ break;
+ case STT_OBJECT:
+ type = "object";
+ break;
+ case STT_NOTYPE:
+ type = "notype";
+ break;
+ default:
+ type = "UNKNOWN";
+ }
+ printf("symbol %d [%s] type %s value %x\n", i,
+ strt + sym->st_name,
+ type, sym->st_value);
+ }
+
+ numrel = object->dyn.relsz / sizeof(Elf_Rel);
+ numrela = object->dyn.relasz / sizeof(Elf_RelA);
+
+ printf("numrel %d numrela %d\n", numrel, numrela);
+
+ printf("rel relocations:\n");
+ for (i = 0; i < numrel ; i++) {
+ Elf_Rel *rel = object->dyn.rel;
+ printf("%d: %x sym %x type %d\n", i, rel[i].r_offset,
+ ELF_R_SYM(rel[i].r_info), ELF_R_TYPE(rel[i].r_info));
+ }
+ printf("rela relocations:\n");
+ for (i = 0; i < numrela ; i++) {
+ Elf_RelA *rela = object->dyn.rela;
+ printf("%d: %x sym %x type %d\n", i, rela[i].r_offset,
+ ELF_R_SYM(rela[i].r_info), ELF_R_TYPE(rela[i].r_info));
+ }
+ needed_list = (Elf_Addr *)object->dyn.needed;
+ for (i = 0; needed_list[i] != NULL; i++) {
+ printf("NEEDED %s\n", needed_list[i] + strt);
+ }
+
+}
+#endif
+
+
+void
+elf_dump_footer(struct prebind_footer *footer)
+{
+ printf("\nbase %llx\n", (long long)footer->prebind_base);
+ printf("nameidx_idx %d\n", footer->nameidx_idx);
+ printf("symcache_idx %d\n", footer->symcache_idx);
+ printf("pltsymcache_idx %d\n", footer->pltsymcache_idx);
+ printf("fixupcnt_idx %d\n", footer->fixupcnt_idx);
+ printf("fixup_cnt %d\n", footer->fixup_cnt);
+ printf("nametab_idx %d\n", footer->nametab_idx);
+ printf("symcache_cnt %d\n", footer->symcache_cnt);
+ printf("pltsymcache_cnt %d\n", footer->pltsymcache_cnt);
+ printf("fixup_cnt %d\n", footer->fixup_cnt);
+ printf("numlibs %d\n", footer->numlibs);
+ printf("id0 %x\n", footer->id0);
+ printf("id1 %x\n", footer->id1);
+ printf("orig_size %lld\n", (long long)footer->orig_size);
+ printf("version %d\n", footer->prebind_version);
+ printf("bind_id %c%c%c%c\n", footer->bind_id[0],
+ footer->bind_id[1], footer->bind_id[2], footer->bind_id[3]);
+}
+
+
+void
+dump_symcachetab(struct symcachetab *symcachetab, int symcache_cnt,
+ struct elf_object *object, int id)
+{
+ int i;
+
+ printf("symcache for %s\n", object->load_name);
+ for (i = 0; i < symcache_cnt; i++) {
+ printf("symidx %d: obj %d sym %d\n",
+ symcachetab[i].idx,
+ symcachetab[i].obj_idx,
+ symcachetab[i].sym_idx);
+ }
+}
+
+void
+elf_print_prog_list (prog_list_ty *prog_list)
+{
+ struct elf_object *object;
+ struct proglist *pl;
+
+ TAILQ_FOREACH(pl, prog_list, list) {
+ object = TAILQ_FIRST(&(pl->curbin_list))->object;
+ printf("bin: %s\n", object->load_name);
+ elf_print_curbin_list(pl);
+ }
+}
+
+void
+elf_print_curbin_list(struct proglist *bin)
+{
+ struct objlist *ol;
+
+ TAILQ_FOREACH(ol, &(bin->curbin_list), list) {
+ printf("\t%s\n", ol->object->load_name);
+ }
+}
+
diff --git a/libexec/ld.so/ldconfig/dl_prebind.c b/libexec/ld.so/ldconfig/dl_prebind.c
new file mode 100644
index 00000000000..e71052bb4ae
--- /dev/null
+++ b/libexec/ld.so/ldconfig/dl_prebind.c
@@ -0,0 +1,619 @@
+/* $OpenBSD: dl_prebind.c,v 1.1 2006/05/12 23:20:52 deraadt 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/mman.h>
+#include <sys/exec.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <nlist.h>
+#include <string.h>
+#include <link.h>
+#include <dlfcn.h>
+#include <unistd.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+#include "sod.h"
+#include "stdlib.h"
+#include "dl_prebind.h"
+
+void elf_dump_footer(struct prebind_footer *footer);
+void dump_prelink(Elf_Addr base, u_long size);
+void prebind_dump_footer(struct prebind_footer *footer, char *file);
+void prebind_dump_symcache(struct symcachetab *symcachetab, u_int32_t cnt);
+void prebind_dump_nameidx(struct nameidx *nameidx, u_int32_t numblibs,
+ char *nametab);
+void prebind_dump_fixup(struct fixup *fixup, u_int32_t numfixups);
+void prebind_dump_libmap(u_int32_t *libmap, u_int32_t numlibs);
+struct prebind_footer *_dl_prebind_data_to_footer(void *data);
+
+void *_dl_prog_prebind_map;
+struct prebind_footer *prog_footer;
+extern char *_dl_noprebind;
+extern char *_dl_prebind_validate;
+
+int _dl_prebind_match_failed; /* = 0 */
+
+char *prebind_bind_now = "prebind";
+
+struct prebind_footer *
+_dl_prebind_data_to_footer(void *prebind_data)
+{
+ u_int32_t *poffset, offset;
+ struct prebind_footer *footer;
+ char *c;
+
+ poffset = prebind_data;
+ c = prebind_data;
+ offset = *poffset;
+ c += offset;
+ footer = (void *)c;
+ return footer;
+}
+
+void
+prebind_load_exe(Elf_Phdr *phdp, elf_object_t *exe_obj)
+{
+ struct prebind_footer *footer;
+
+ exe_obj->prebind_data = (void *)phdp->p_vaddr;
+ _dl_prog_prebind_map = exe_obj->prebind_data;
+
+ footer = _dl_prebind_data_to_footer(_dl_objects->prebind_data);
+
+ if (footer->bind_id[0] == BIND_ID0 &&
+ footer->bind_id[1] == BIND_ID1 &&
+ footer->bind_id[2] == BIND_ID2 &&
+ footer->bind_id[3] == BIND_ID3 &&
+ footer->prebind_version == PREBIND_VERSION) {
+ prog_footer = footer;
+ if (_dl_bindnow == NULL)
+ _dl_bindnow = prebind_bind_now;
+ } else {
+ DL_DEB(("prebind data missing\n"));
+ _dl_prog_prebind_map = NULL;
+ }
+ if (_dl_noprebind != NULL) {
+ /*prog_footer is valid, we should free it */
+ _dl_prog_prebind_map = NULL;
+ prog_footer = NULL;
+ exe_obj->prebind_data = NULL;
+ if (_dl_bindnow == prebind_bind_now)
+ _dl_bindnow = NULL;
+ }
+#if 0
+ else if (_dl_debug)
+ dump_prelink((long)_dl_prog_prebind_map,
+ prog_footer->prebind_size);
+#endif
+}
+
+void *
+prebind_load_fd(int fd, const char *name)
+{
+ struct prebind_footer footer;
+ struct nameidx *nameidx;
+ void *prebind_data;
+ char *nametab;
+ ssize_t len;
+ int idx;
+
+ if (_dl_prog_prebind_map == NULL || _dl_prebind_match_failed)
+ return 0;
+
+ _dl_lseek(fd, -(off_t)sizeof(struct prebind_footer), SEEK_END);
+ len = _dl_read(fd, (void *)&footer, sizeof(struct prebind_footer));
+
+ if (len != sizeof(struct prebind_footer) ||
+ footer.bind_id[0] != BIND_ID0 ||
+ footer.bind_id[1] != BIND_ID1 ||
+ footer.bind_id[2] != BIND_ID2 ||
+ footer.bind_id[3] != BIND_ID3 ||
+ footer.prebind_version != PREBIND_VERSION) {
+ _dl_prebind_match_failed = 1;
+ DL_DEB(("prebind match failed %s\n", name));
+ return (NULL);
+ }
+
+ prebind_data = _dl_mmap(0, footer.prebind_size, PROT_READ,
+ MAP_FILE, fd, footer.prebind_base);
+ DL_DEB(("prebind_load_fd for lib %s\n", name));
+
+ nameidx = _dl_prog_prebind_map + prog_footer->nameidx_idx;
+ nametab = _dl_prog_prebind_map + prog_footer->nametab_idx;
+
+ /* libraries are loaded in random order, so we just have
+ * to look thru the list to find ourselves
+ */
+ for (idx = 0; idx < prog_footer->numlibs; idx++) {
+ if (_dl_strcmp(nametab + nameidx[idx].name, name) == 0)
+ break;
+ }
+
+ if (idx == prog_footer->numlibs) {
+ _dl_prebind_match_failed = 1; /* not found */
+ } else if (footer.id0 != nameidx[idx].id0 ||
+ footer.id1 != nameidx[idx].id1) {
+ _dl_prebind_match_failed = 1;
+ DL_DEB(("prebind match id0 %d pid0 %d id1 %d pid1 %d\n",
+ footer.id0, nameidx[idx].id0,
+ footer.id1, nameidx[idx].id1));
+ }
+
+ if (_dl_prebind_match_failed == 1) {
+ DL_DEB(("prebind match failed for %s\n", name));
+ }
+
+ return prebind_data;
+}
+#define NUM_STATIC_OBJS 10
+elf_object_t *objarray_static[NUM_STATIC_OBJS];
+elf_object_t **objarray;
+
+void
+prebind_symcache(elf_object_t *object, int plt)
+{
+ u_int32_t *fixupidx, *fixupcnt, *libmap, *idxtolib;
+ u_int32_t *poffset, offset, symcache_cnt;
+ struct symcachetab *symcachetab;
+ struct prebind_footer *footer;
+ int i = 0, cur_obj = -1, idx;
+ void *prebind_map;
+ struct nameidx *nameidx;
+ char *nametab, *c;
+ struct fixup *fixup;
+ elf_object_t *obj;
+
+ struct symcachetab *s;
+
+ if (object->prebind_data == NULL)
+ return;
+// DL_DEB(("prebind symcache %s\n", object->load_name));
+
+ obj = _dl_objects;
+ while (obj != NULL) {
+ if (obj == object)
+ cur_obj = i;
+ i++;
+ obj = obj->next;
+ }
+
+ if (cur_obj == -1)
+ return; /* unable to find object ? */
+
+ if (objarray == NULL) {
+ if (i <= NUM_STATIC_OBJS) {
+ objarray = &objarray_static[0];
+ } else {
+ objarray = _dl_malloc(sizeof(elf_object_t *) * i);
+ }
+
+ obj = _dl_objects;
+ i = 0;
+ while (obj != NULL) {
+ objarray[i] = obj;
+ i++;
+ obj = obj->next;
+ }
+ }
+
+ poffset = (u_int32_t *)object->prebind_data;
+ c = object->prebind_data;
+ offset = *poffset;
+ c += offset;
+
+ footer = (void *)c;
+ prebind_map = (void *)object->prebind_data;
+ nameidx = prebind_map + footer->nameidx_idx;;
+ if (plt) {
+ symcachetab = prebind_map + footer->pltsymcache_idx;
+ symcache_cnt = footer->pltsymcache_cnt;
+// DL_DEB(("loading plt %d\n", symcache_cnt));
+ } else {
+ symcachetab = prebind_map + footer->symcache_idx;
+ symcache_cnt = footer->symcache_cnt;
+// DL_DEB(("loading got %d\n", symcache_cnt));
+ }
+ nametab = prebind_map + footer->nametab_idx;
+
+ libmap = _dl_prog_prebind_map + prog_footer->libmap_idx;
+ idxtolib = _dl_prog_prebind_map + libmap[cur_obj];
+
+ for (i = 0; i < symcache_cnt; i++) {
+ struct elf_object *tobj;
+ const Elf_Sym *sym;
+ const char *str;
+
+ s = &(symcachetab[i]);
+ if (cur_obj == 0)
+ idx = s->obj_idx;
+ else
+ idx = idxtolib[s->obj_idx];
+
+ if (idx == -1) /* somehow an invalid object ref happend */
+ continue;
+#if 0
+ DL_DEB(("%s:", object->load_name));
+ DL_DEB(("symidx %d: obj %d %d sym %d flags %x\n",
+ s->idx, s->obj_idx, idx, s->sym_idx,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt));
+#endif
+ tobj = objarray[idx];
+ sym = tobj->dyn.symtab + s->sym_idx;
+ str = tobj->dyn.strtab + sym->st_name;
+#ifdef DEBUG2
+ DL_DEB(("symidx %d: obj %d %s sym %d %s flags %d %x\n",
+ s->idx, s->obj_idx, tobj->load_name,
+ s->sym_idx, str, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt,
+ object->load_addr + sym->st_value));
+#endif
+ _dl_symcache[s->idx].obj = tobj;
+ _dl_symcache[s->idx].sym = sym;
+ _dl_symcache[s->idx].flags =
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt;
+ }
+
+ if (!plt) {
+ fixupidx = _dl_prog_prebind_map + prog_footer->fixup_idx;
+ fixup = _dl_prog_prebind_map + fixupidx[2*cur_obj];
+ fixupcnt = _dl_prog_prebind_map + prog_footer->fixupcnt_idx;
+
+ for (i = 0; i < fixupcnt[2*cur_obj]; i++) {
+ struct fixup *f;
+ struct elf_object *tobj;
+ const Elf_Sym *sym;
+ const char *str;
+
+ f = &(fixup[i]);
+#if 0
+ DL_DEB(("symidx %d: obj %d sym %d flags %x\n",
+ f->sym, f->obj_idx, f->sym_idx, f->flags));
+#endif
+ tobj = objarray[f->obj_idx];
+ sym = tobj->dyn.symtab + f->sym_idx;
+ str = tobj->dyn.strtab + sym->st_name;
+#ifdef DEBUG2
+ DL_DEB(("symidx %d: obj %d %s sym %d %s flags %d %x\n",
+ f->sym, f->obj_idx, tobj->load_name,
+ f->sym_idx, str, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt,
+ object->load_addr + sym->st_value));
+#endif
+ _dl_symcache[f->sym].obj = tobj;
+ _dl_symcache[f->sym].sym = sym;
+ _dl_symcache[f->sym].flags =
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt;
+ }
+ } else {
+
+ fixupidx = _dl_prog_prebind_map + prog_footer->fixup_idx;
+ fixup = _dl_prog_prebind_map + fixupidx[2*cur_obj+1];
+ fixupcnt = _dl_prog_prebind_map + prog_footer->fixupcnt_idx;
+
+#if 0
+ DL_DEB(("prebind loading symbols fixup plt %s\n",
+ object->load_name));
+#endif
+ for (i = 0; i < fixupcnt[2*cur_obj+1]; i++) {
+ struct fixup *f;
+ struct elf_object *tobj;
+ const Elf_Sym *sym;
+ const char *str;
+
+ f = &(fixup[i]);
+#if 0
+ DL_DEB(("symidx %d: obj %d sym %d flags %x\n",
+ f->sym, f->obj_idx, f->sym_idx,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt));
+#endif
+ tobj = objarray[f->obj_idx];
+ sym = tobj->dyn.symtab + f->sym_idx;
+ str = tobj->dyn.strtab + sym->st_name;
+#ifdef DEBUG2
+ DL_DEB(("symidx %d: obj %d %s sym %d %s flags %d %x\n",
+ f->sym, f->obj_idx, tobj->load_name,
+ f->sym_idx, str, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt,
+ object->load_addr + sym->st_value));
+#endif
+ _dl_symcache[f->sym].obj = tobj;
+ _dl_symcache[f->sym].sym = sym;
+ _dl_symcache[f->sym].flags =
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt;
+ }
+ }
+// DL_DEB(("prebind_data loaded\n"));
+}
+
+void
+prebind_free(elf_object_t *object)
+{
+ struct prebind_footer *footer;
+
+ if (object->prebind_data == NULL)
+ return;
+#ifdef DEBUG1
+ DL_DEB(("prebind_free for %s %p\n", object->load_name,
+ object->prebind_data));
+#endif
+ if (object->prebind_data != 0) {
+ footer = _dl_prebind_data_to_footer(object->prebind_data);
+
+#ifdef DEBUG1
+ DL_DEB(("freeing prebind data sz %x\n", footer->prebind_size));
+#endif
+
+ _dl_munmap((void *)ELF_TRUNC((long)object->prebind_data, _dl_pagesz),
+ ELF_ROUND((long)object->prebind_data+footer->prebind_size,
+ _dl_pagesz) - ELF_TRUNC((long)object->prebind_data, _dl_pagesz));
+
+ object->prebind_data = NULL;
+ _dl_prog_prebind_map = NULL;
+
+ if (_dl_bindnow == prebind_bind_now)
+ _dl_bindnow = NULL;
+ }
+}
+
+int validate_errs;
+
+struct timeval beforetp;
+
+void
+_dl_prebind_pre_resolve()
+{
+ struct prebind_footer *footer;
+ elf_object_t *object;
+ struct nameidx *nameidx;
+ char *nametab, *name;
+ int idx;
+
+ if (_dl_prog_prebind_map != NULL) {
+ nameidx = _dl_prog_prebind_map + prog_footer->nameidx_idx;
+ nametab = _dl_prog_prebind_map + prog_footer->nametab_idx;
+ for (idx = 1, object = _dl_objects->next; object != NULL;
+ object = object->next, idx++) {
+ if (object->prebind_data == NULL) {
+ /* ld.so doesn't have prebind data */
+ if (object->next == NULL)
+ continue;
+ DL_DEB(("missing prebind data %s\n",
+ object->load_name));
+ _dl_prebind_match_failed = 1;
+ break;
+ }
+ footer = _dl_prebind_data_to_footer(
+ object->prebind_data);
+ if (footer == NULL ||
+ nameidx[idx].id0 != footer->id0 ||
+ nameidx[idx].id1 != footer->id1) {
+ DL_DEB(("invalid prebind data %s\n",
+ object->load_name));
+ _dl_prebind_match_failed = 1;
+ break;
+ }
+ name = object->load_name;
+ if (_dl_strcmp(nametab + nameidx[idx].name, name)
+ != 0) {
+ DL_DEB(("invalid prebind name %s\n",
+ object->load_name));
+ _dl_prebind_match_failed = 1;
+ break;
+ }
+ }
+ }
+
+ if (_dl_prebind_match_failed) {
+ for (object = _dl_objects; object != NULL;
+ object = object->next)
+ prebind_free(object);
+ if (_dl_bindnow == prebind_bind_now)
+ _dl_bindnow = NULL;
+ }
+
+ if (_dl_debug)
+ _dl_gettimeofday(&beforetp, NULL);
+}
+
+void
+_dl_prebind_post_resolve()
+{
+ char buf[7];
+ int i;
+ struct timeval after_tp;
+ struct timeval diff_tp;
+ elf_object_t *object;
+
+ if (_dl_debug) {
+ _dl_gettimeofday(&after_tp, NULL);
+
+ timersub(&after_tp, &beforetp, &diff_tp);
+
+ for (i = 0; i < 6; i++) {
+ buf[5-i] = (diff_tp.tv_usec % 10) + '0';
+ diff_tp.tv_usec /= 10;
+ }
+ buf[6] = '\0';
+
+ _dl_printf("relocation took %d.%s\n", diff_tp.tv_sec, buf);
+ }
+
+ for (object = _dl_objects; object != NULL; object = object->next)
+ prebind_free(object);
+
+ if (_dl_prebind_validate) {
+ if (validate_errs) {
+ _dl_printf("validate_errs %d\n", validate_errs);
+ _dl_exit(20);
+ } else {
+ _dl_exit(0);
+ }
+ }
+}
+
+void
+prebind_validate(elf_object_t *req_obj, unsigned int symidx, int flags,
+ const Elf_Sym *ref_sym)
+{
+ const Elf_Sym *sym, **this;
+ const elf_object_t *sobj;
+ const char *symn;
+ Elf_Addr ret;
+
+ /* Don't verify non-matching flags*/
+
+ sym = req_obj->dyn.symtab;
+ sym += symidx;
+ symn = req_obj->dyn.strtab + sym->st_name;
+ this = &sym;
+
+ //_dl_printf("checking %s\n", symn);
+ ret = _dl_find_symbol(symn, this, flags, ref_sym, req_obj, &sobj);
+
+ if (_dl_symcache[symidx].sym != *this ||
+ _dl_symcache[symidx].obj != sobj) {
+ _dl_printf("symbol %d mismatch on sym %s req_obj %s,\n"
+ "should be obj %s is obj %s\n",
+ symidx, symn, req_obj->load_name, sobj->load_name,
+ _dl_symcache[symidx].obj->load_name);
+ if (req_obj == sobj)
+ _dl_printf("obj %p %p\n", _dl_symcache[symidx].obj, sobj);
+ sym = _dl_symcache[symidx].obj->dyn.symtab;
+ sym += symidx;
+ symn = _dl_symcache[symidx].obj->dyn.strtab + sym->st_name;
+ _dl_printf("obj %s name %s\n",
+ _dl_symcache[symidx].obj->load_name,
+ symn);
+ }
+}
+
+#ifdef DEBUG1
+void
+prebind_dump_symcache(struct symcachetab *symcachetab, u_int32_t cnt)
+{
+ struct symcachetab *s;
+ int i;
+
+ _dl_printf("cache: cnt %d\n", cnt);
+ for (i = 0; i < cnt; i++) {
+ s = &(symcachetab[i]);
+ _dl_printf("symidx %d: obj %d sym %d\n",
+ s->idx, s->obj_idx, s->sym_idx);
+ }
+}
+
+void
+prebind_dump_nameidx(struct nameidx *nameidx, u_int32_t numlibs, char *nametab)
+{
+ struct nameidx *n;
+ int i;
+
+ _dl_printf("libs:\n");
+ for (i = 0; i < numlibs; i++) {
+ _dl_printf("lib %d offset %d id0 %d, id1 %d\n", i,
+ nameidx[i].name, nameidx[i].id0, nameidx[i].id1);
+ }
+ for (i = 0; i < numlibs; i++) {
+ n = &(nameidx[i]);
+ _dl_printf("nametab %p n %d\n", nametab, n->name);
+ _dl_printf("lib %s %x %x\n", nametab + n->name, n->id0, n->id1);
+ }
+}
+
+void
+prebind_dump_fixup(struct fixup *fixup, u_int32_t numfixups)
+{
+ struct fixup *f;
+ int i;
+
+ _dl_printf("fixup: %d\n", numfixups);
+ for (i = 0; i < numfixups; i++) {
+ f = &(fixup[i]);
+
+ _dl_printf("idx %d obj %d sym idx %d\n",
+ f->sym, f->obj_idx, f->sym_idx);
+
+ }
+}
+
+void
+prebind_dump_libmap(u_int32_t *libmap, u_int32_t numlibs)
+{
+ int i;
+
+ for (i = 0; i < numlibs; i++) {
+ //_dl_printf("lib%d off %d %s\n", i, libmap[i], strtab+libmap[i]);
+ _dl_printf("lib%d off %d\n", i, libmap[i]);
+ }
+}
+
+void
+dl_dump_footer(struct prebind_footer *footer)
+{
+// _dl_printf("base %qd\n", (long long)footer->prebind_base);
+ _dl_printf("nameidx_idx %d\n", footer->nameidx_idx);
+ _dl_printf("symcache_idx %d\n", footer->symcache_idx);
+ _dl_printf("fixupcnt_idx %d\n", footer->fixupcnt_idx);
+ _dl_printf("fixup_cnt %d\n", footer->fixup_cnt);
+ _dl_printf("nametab_idx %d\n", footer->nametab_idx);
+ _dl_printf("symcache_cnt %d\n", footer->symcache_cnt);
+ _dl_printf("fixup_cnt %d\n", footer->fixup_cnt);
+ _dl_printf("numlibs %d\n", footer->numlibs);
+ _dl_printf("id0 %d\n", footer->id0);
+ _dl_printf("id1 %d\n", footer->id1);
+// _dl_printf("orig_size %lld\n", (long long)footer->orig_size);
+ _dl_printf("version %d\n", footer->prebind_version);
+ _dl_printf("bind_id %c%c%c%c\n", footer->bind_id[0],
+ footer->bind_id[1], footer->bind_id[2], footer->bind_id[3]);
+}
+void
+dump_prelink(Elf_Addr base, u_long size)
+{
+ u_int32_t *fixupidx, *fixupcnt, *libmap;
+ struct symcachetab *symcachetab;
+ struct prebind_footer *footer;
+ struct nameidx *nameidx;
+ struct fixup *fixup;
+ char *nametab, *id;
+ void *prebind_map;
+ int i;
+
+ id = (char *) (base+size);
+ id -= 4;
+ DL_DEB(("id %c %c %c %c\n", id[0], id[1], id[2], id[3]));
+ footer = (void *) (base+size - sizeof (struct prebind_footer));
+ dl_dump_footer(footer);
+
+ prebind_map = (void *)base;
+ nameidx = prebind_map + footer->nameidx_idx;;
+ symcachetab = prebind_map + footer->symcache_idx;
+ fixupidx = prebind_map + footer->fixup_idx;
+ nametab = prebind_map + footer->nametab_idx;
+ fixupcnt = prebind_map + footer->fixupcnt_idx;
+ libmap = prebind_map + footer->libmap_idx;
+
+ prebind_dump_symcache(symcachetab, footer->symcache_cnt);
+ prebind_dump_nameidx(nameidx, footer->numlibs, nametab);
+ for (i = 0; i < footer->fixup_cnt; i++) {
+ _dl_printf("fixup %d cnt %d idx %d\n", i, fixupcnt[i], fixupidx[i]);
+ fixup = prebind_map + fixupidx[i];
+ prebind_dump_fixup(fixup, fixupcnt[i]);
+ }
+ prebind_dump_libmap(libmap, footer->numlibs);
+}
+#endif /* DEBUG1 */
diff --git a/libexec/ld.so/ldconfig/etc.c b/libexec/ld.so/ldconfig/etc.c
index a1783c4ebf1..80111fce6f7 100644
--- a/libexec/ld.so/ldconfig/etc.c
+++ b/libexec/ld.so/ldconfig/etc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: etc.c,v 1.5 2003/07/06 20:04:00 deraadt Exp $ */
+/* $OpenBSD: etc.c,v 1.6 2006/05/12 23:20:52 deraadt Exp $ */
/* Public Domain */
@@ -41,6 +41,20 @@ xrealloc(void *ptr, size_t size)
return (nptr);
}
+void *
+xcalloc(size_t nmemb, size_t size)
+{
+ void *ret;
+
+ ret = calloc(nmemb, size);
+ if (ret == NULL) {
+ printf("unable to allocate memory\n");
+ abort();
+ exit (20);
+ }
+ return ret;
+}
+
char *
concat(const char *s1, const char *s2, const char *s3)
{
diff --git a/libexec/ld.so/ldconfig/ldconfig.c b/libexec/ld.so/ldconfig/ldconfig.c
index cab2ee9ba03..fa1fe87151d 100644
--- a/libexec/ld.so/ldconfig/ldconfig.c
+++ b/libexec/ld.so/ldconfig/ldconfig.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ldconfig.c,v 1.19 2006/05/11 22:03:22 deraadt Exp $ */
+/* $OpenBSD: ldconfig.c,v 1.20 2006/05/12 23:20:52 deraadt Exp $ */
/*
* Copyright (c) 1993,1995 Paul Kranenburg
@@ -58,12 +58,12 @@
extern char *__progname;
-static int verbose;
+int verbose;
static int delete;
static int doprebind;
static int nostd;
static int justread;
-static int merge;
+int merge;
static int rescan;
static int unconfig;
@@ -91,7 +91,7 @@ void
usage(void)
{
fprintf(stderr,
- "usage: %s [-DmRrsUv] [path ...]\n", __progname);
+ "usage: %s [-DmPrRsUv] [path ...]\n", __progname);
exit(1);
}
@@ -101,7 +101,7 @@ main(int argc, char *argv[])
int i, c;
int rval = 0;
- while ((c = getopt(argc, argv, "DRmrsUv")) != -1) {
+ while ((c = getopt(argc, argv, "DmPrRsUv")) != -1) {
switch (c) {
case 'R':
rescan = 1;
@@ -124,8 +124,8 @@ main(int argc, char *argv[])
case 'D':
delete = 1;
break;
-// case 'P':
-// doprebind = 1;
+ case 'P':
+ doprebind = 1;
break;
default:
usage();
@@ -139,12 +139,6 @@ main(int argc, char *argv[])
dir_list = xmalloc(1);
*dir_list = '\0';
- if (delete) {
- if (rescan || unconfig || merge || justread || nostd || doprebind)
- errx(1, "cannot mix -U -R -r -s -P options with -D");
- exit (prebind_delete(&argv[optind], verbose));
- }
-
if (justread || merge || rescan) {
if ((rval = readhints()) != 0)
return rval;
@@ -158,11 +152,15 @@ main(int argc, char *argv[])
} else if (!nostd)
std_search_path();
-// if (doprebind) {
-// if (rescan || unconfig || justread || nostd)
-// errx(1, "cannot mix other options with -P");
-// exit (prebind(&argv[optind], verbose, merge));
-// }
+ if (delete) {
+ if (rescan || unconfig || merge || justread || nostd || doprebind)
+ errx(1, "cannot mix -U -R -r -s -P options with -D");
+ exit (prebind_delete(&argv[optind]));
+ } else if (doprebind) {
+ if (rescan || unconfig || justread || nostd)
+ errx(1, "cannot mix other options with -P");
+ exit (prebind(&argv[optind]));
+ }
if (unconfig) {
if (optind < argc)
diff --git a/libexec/ld.so/ldconfig/library.c b/libexec/ld.so/ldconfig/library.c
new file mode 100644
index 00000000000..cc4baa72df9
--- /dev/null
+++ b/libexec/ld.so/ldconfig/library.c
@@ -0,0 +1,342 @@
+/* $OpenBSD: library.c,v 1.1 2006/05/12 23:20:52 deraadt 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 "link.h"
+#include "sod.h"
+#include "resolve.h"
+#include "prebind.h"
+#include "prebind_struct.h"
+
+/* TODO - library path from ldconfig */
+#define DEFAULT_PATH "/usr/lib:/usr/X11R6/lib:/usr/local/qte/lib"
+
+elf_object_t * elf_load_shlib_hint(struct sod *sod, struct sod *req_sod,
+ int ignore_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);
+
+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 (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);
+ 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, OBJTYPE_LIB);
+ }
+ 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;
+}
+
diff --git a/libexec/ld.so/ldconfig/prebind.8 b/libexec/ld.so/ldconfig/prebind.8
new file mode 100644
index 00000000000..2464674a231
--- /dev/null
+++ b/libexec/ld.so/ldconfig/prebind.8
@@ -0,0 +1,81 @@
+.\" $OpenBSD: prebind.8,v 1.1 2006/05/12 23:20:52 deraadt Exp $
+.\"
+.\" Copyright (c) 2006 Dale Rahn <drahn@openbsd.org>
+.\"
+.\" 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.
+.\"
+.Dd May 1, 2006
+.Dt PREBIND 8
+.Os
+.Sh NAME
+.Nm prebind
+.Nd cache symbol lookup information to speed up dynamic linking
+.Sh SYNOPSIS
+.Nm prebind
+.Op Fl mv
+.Op Ar file/dir ...
+.Sh DESCRIPTION
+.Nm
+parses each of the specified files or directories and processes each ELF file
+(ELF file found in the directory) and the associated
+.Dv DT_NEEDED
+libraries,
+and writes symbol resolution hint information to each binary and library.
+.Pp
+.Nm
+will add data to the programs specified and any libraries they reference
+to speed up dynamic linking.
+Since version information is stored in the libraries to validate the
+prebind info, running
+.Nm
+on a subset of programs it was previously
+run on will invalidate the prebind info for those excluded binaries.
+The
+.Fl m
+option will preserve the old prebind info in any library if present.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl m
+Merge into existing prebound libraries.
+This prebinds new binaries without modifying or updating the libraries
+(if prebind data is present), allowing old prebound binaries to continue
+to use the old prebind data.
+.It Fl v
+Be verbose when running
+.Nm :
+prints out information about the file/library that is being processed.
+.El
+.Sh SEE ALSO
+.Xr ld.so 1 ,
+.Xr prebind_strip 8
+.Sh STANDARDS
+None
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Ox 4.0 .
+.Nm
+is based loosely on Prelinking, however prelink removes the security
+feature of libraries appearing in random order on each invocation, thus
+it was incompatible with
+.Ox Ns 's
+goals.
+.Nm
+was written as an attempt to improve the speed of dynamic linking
+without the penalty of loss of security features.
+.Sh BUGS
+Prebind uses a lot of memory depending on how many files/libraries
+are being processed.
+Handling of binaries where a required library has been removed is poor.
diff --git a/libexec/ld.so/ldconfig/prebind.c b/libexec/ld.so/ldconfig/prebind.c
new file mode 100644
index 00000000000..6d7af426cb9
--- /dev/null
+++ b/libexec/ld.so/ldconfig/prebind.c
@@ -0,0 +1,2164 @@
+/* $OpenBSD: prebind.c,v 1.1 2006/05/12 23:20:52 deraadt 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 <errno.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;
+
+/* 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 */
+#if defined(__sparc__) && !defined(__sparc64__)
+/* ARGH, our sparc/include/reloc.h is wrong (for the moment) */
+#undef RELOC_JMP_SLOT
+#define RELOC_JMP_SLOT 21
+#endif
+
+#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 objarray_list {
+ struct elf_object *obj;
+ struct symcache_noflag *symcache;
+ struct symcache_noflag *pltsymcache;
+ struct proglist *proglist;
+ u_int32_t id0;
+ u_int32_t id1;
+ u_int32_t *idxtolib;
+ void *oprebind_data;
+ int numlibs;
+
+ TAILQ_HEAD(, objlist) inst_list;
+} *objarray;
+
+int objarray_cnt;
+int objarray_sz;
+
+int write_txtbusy_file(char *name);
+void copy_oldsymcache(int objidx, void *prebind_data);
+void elf_load_existing_prebind(struct elf_object *object, int fd);
+
+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 *);
+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 objtype);
+void elf_print_objarray(void);
+void elf_reloc(struct elf_object *object);
+
+struct elf_object * elf_lookup_object(const char *name);
+struct elf_object * elf_lookup_object_devino(dev_t dev, ino_t inode,
+ int objtype);
+void elf_free_curbin_list(struct elf_object *obj);
+void elf_resolve_curbin(void);
+struct proglist *elf_newbin(void);
+void elf_sum_reloc();
+int elf_prep_lib_prebind(struct elf_object *object);
+int elf_prep_bin_prebind(struct proglist *pl);
+void add_fixup_prog(struct elf_object *prog, struct elf_object *obj, int idx,
+ const struct elf_object *ref_obj, const Elf_Sym *ref_sym, int flag);
+void add_fixup_oldprog(struct elf_object *prog, struct elf_object *obj, int idx,
+ const struct elf_object *ref_obj, const Elf_Sym *ref_sym, int flag);
+
+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);
+
+struct elf_object *load_object;
+
+struct elf_object *load_file(const char *filename, int lib);
+int elf_check_note(void *buf, Elf_Phdr *phdr);
+void load_file_or_dir(char *name);
+void load_dir(char *name);
+void load_exe(char *name);
+
+int
+prebind(char **argv)
+{
+ int i;
+
+ elf_init_objarray();
+
+ for (i = 0; argv[i]; i++)
+ load_file_or_dir(argv[i]);
+
+ if (verbose > 4) {
+ elf_print_objarray();
+ elf_print_prog_list(&prog_list);
+ }
+ elf_sum_reloc();
+
+ printf("total new blocks %lld\n", prebind_blocks);
+ return (0);
+}
+
+/*
+ * load ELF objects at the specified path it could be
+ * either a either a directory or file, if the object is
+ * a file, attempt to load it as an executable (will ignore shared objects
+ * and any files that are not Elf execuables.
+ * if the object is a directory pass it to a routine to deal with
+ * directory parsing.
+ */
+void
+load_file_or_dir(char *name)
+{
+ struct stat sb;
+ int ret;
+
+ ret = lstat(name, &sb);
+ if (ret != 0)
+ return;
+ switch (sb.st_mode & S_IFMT) {
+ case S_IFREG:
+ load_exe(name);
+ break;
+ case S_IFDIR:
+ if (verbose > 0)
+ printf("loading dir %s\n", name);
+ load_dir(name);
+ break;
+ default:
+ ; /* links and other files we skip */
+ }
+
+}
+
+/*
+ * for all of the objects in the directory, if it is a regular file
+ * load it as a binary, if it is unknown (nfs mount) stat the file
+ * and load the file for S_IFREG
+ * any other type of directory object: symlink, directory, socket, ...
+ * is ignored.
+ */
+void
+load_dir(char *name)
+{
+ struct dirent *dp;
+ struct stat sb;
+ DIR *dirp;
+ char *buf;
+
+ dirp = opendir(name);
+
+ /* if dir failes to open, skip */
+ if (dirp == NULL)
+ 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);
+ break;
+ case DT_REG:
+ asprintf(&buf, "%s/%s", name, dp->d_name);
+ load_exe(buf);
+ free(buf);
+ break;
+ default:
+ /* other files symlinks, dirs, ... we ignore */
+ ;
+ }
+ }
+}
+
+/*
+ * the given pathname is a regular file, however it may or may not
+ * be an ELF file. Attempt to load the given path and calculate prebind
+ * data for it.
+ * if the given file is not a ELF binary this will 'fail' and
+ * should not change any of the prebind state.
+ */
+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, OBJTYPE_EXE);
+ if (object != NULL && load_object != NULL &&
+ object->load_object == NULL) {
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ fail = load_obj_needed(ol->object);
+ if (fail != 0)
+ break; /* XXX */
+
+ }
+ if (fail == 0) {
+ interp = load_file(curbin->interp, OBJTYPE_DLO);
+ object->load_object = interp;
+ if (interp == NULL)
+ fail = 1;
+ }
+
+ /* slight abuse of this field */
+
+ if (fail == 0) {
+ objarray[object->dyn.null].proglist = curbin;
+ elf_resolve_curbin();
+ TAILQ_INSERT_TAIL(&prog_list, curbin, list);
+ } else {
+ printf("failed to load %s\n", name);
+ elf_free_curbin_list(object);
+ free(curbin);
+ }
+ if (load_object != NULL) {
+ load_object = NULL;
+ }
+ } else {
+ free(curbin);
+ }
+}
+
+/*
+ * given a path to a file, attempt to open it and load any data necessary
+ * for prebind. this function is used for executables, libraries and ld.so
+ * file, it will do a lookup on the dev/inode to use a cached version
+ * of the file if it was already loaded, in case a library is referenced
+ * by more than one program or there are hardlinks between executable names.
+ * if the file is not an elf file of the appropriate type, it will return
+ * failure.
+ */
+struct elf_object *
+load_file(const char *filename, int objtype)
+{
+ struct elf_object *obj = NULL;
+ int fd = -1, i, note_found;
+ struct stat ifstat;
+ void *buf = NULL;
+ Elf_Ehdr *ehdr;
+ Elf_Shdr *shdr;
+ Elf_Phdr *phdr;
+ char *pexe;
+
+ 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))
+ goto done;
+
+ obj = elf_lookup_object_devino(ifstat.st_dev, ifstat.st_ino, objtype);
+ 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;
+ }
+
+ ehdr = (Elf_Ehdr *)buf;
+
+ if (IS_ELF(*ehdr) == 0)
+ goto done;
+
+ if (ehdr->e_machine != ELF_TARG_MACH) {
+ if (verbose > 0)
+ printf("%s: wrong arch\n", filename);
+ goto done;
+ }
+
+ if (objtype == OBJTYPE_EXE) {
+ if (ehdr->e_type != ET_EXEC)
+ goto done;
+
+ note_found = 0;
+
+ phdr = (Elf_Phdr *)((char *)buf + ehdr->e_phoff);
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ if (phdr[i].p_type == PT_NOTE) {
+ note_found = elf_check_note(buf,&phdr[i]);
+ break;
+ }
+ }
+ if (note_found == 0)
+ goto done; /* no OpenBSD note found */
+ }
+
+ if ((objtype == OBJTYPE_LIB || objtype == OBJTYPE_DLO) &&
+ (ehdr->e_type != ET_DYN))
+ goto done;
+
+ pexe = buf;
+ if (ehdr->e_shstrndx == 0)
+ goto done;
+
+ shdr = (Elf_Shdr *)(pexe + ehdr->e_shoff +
+ (ehdr->e_shstrndx * ehdr->e_shentsize));
+
+ shstrtab = (char *)(pexe + shdr->sh_offset);
+
+ obj = elf_load_object(pexe, filename);
+
+ munmap(buf, ifstat.st_size);
+ buf = NULL;
+
+ if (obj != NULL) {
+ obj->obj_type = objtype;
+
+ obj->dev = ifstat.st_dev;
+ obj->inode = ifstat.st_ino;
+ if (load_object == NULL)
+ load_object = obj;
+
+ elf_add_object(obj, objtype);
+
+#ifdef DEBUG1
+ dump_info(obj);
+#endif
+ }
+ if ((objtype == OBJTYPE_LIB || objtype == OBJTYPE_DLO) &&
+ merge == 1) {
+ /*
+ * for libraries and dynamic linker, 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;
+}
+
+/*
+ * check if the given executable header on a ELF executable
+ * has the proper OpenBSD note on the file if it is not present
+ * binaries will be skipped.
+ */
+int
+elf_check_note(void *buf, Elf_Phdr *phdr)
+{
+ Elf_Ehdr *ehdr;
+ u_long address;
+ u_int *pint;
+ char *osname;
+
+ ehdr = (Elf_Ehdr *)buf;
+ address = phdr->p_offset;
+ pint = (u_int *)((char *)buf + address);
+ osname = (char *)buf + address + sizeof(*pint) * 3;
+
+ if (pint[0] == 8 /* OpenBSD\0 */ &&
+ pint[1] == 4 /* ??? */ &&
+ pint[2] == 1 /* type_osversion */ &&
+ strcmp("OpenBSD", osname) == 0)
+ return 1;
+
+ return 0;
+}
+
+struct elf_object *
+elf_load_object(void *pexe, const char *name)
+{
+ 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, i;
+
+ 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) {
+ free(object);
+ return NULL; /* not a dynamic binary */
+ }
+
+ 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 (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);
+ if (str == NULL) {
+ printf("unable to allocate strtab for %s\n",
+ name);
+ exit(10);
+ }
+ bcopy(object->dyn.strtab, str, object->dyn.strsz);
+ object->dyn.strtab = str;
+ strt = str;
+
+ sym = malloc(object->nchains * sizeof(Elf_Sym));
+ if (sym == NULL) {
+ printf("unable to allocate symtab for %s\n",
+ name);
+ exit(10);
+ }
+ 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);
+ if (rel == NULL) {
+ printf("unable to allocate rel reloc for %s\n",
+ name);
+ exit(10);
+ }
+ 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);
+ if (rela == NULL) {
+ printf("unable to allocate rela reloc for %s\n",
+ name);
+ exit(10);
+ }
+ 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);
+ if (plt == NULL) {
+ printf("unable to allocate plt reloc for %s\n",
+ name);
+ exit(10);
+ }
+ 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);
+ if (object->dyn.rpath == NULL) {
+ printf("unable to allocate rpath for %s\n",
+ name);
+ exit(10);
+ }
+ }
+ 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;
+ }
+ }
+}
+
+/*
+ * given a dynamic elf object (executable or binary)
+ * load any DT_NEEDED entries which were found when
+ * the object was initially loaded.
+ */
+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;
+}
+
+/*
+ * allocate a proglist entry for a new binary
+ * so that it is available for libraries to reference
+ */
+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.
+ * 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;
+ int lib_prog_ref;
+ for (i = 0; i < nsyms; i++) {
+ if (scache[i].obj == NULL)
+ continue;
+
+ if (tcache[i].obj != NULL) {
+ lib_prog_ref = (obj != prog && scache[i].obj == prog);
+ if (scache[i].obj != tcache[i].obj || lib_prog_ref) {
+ if (verbose > 2) {
+ printf("sym mismatch %d: "
+ "obj %d: sym %ld %s "
+ "nobj %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);
+ }
+
+ /*
+ * if one of the symbol entries
+ * happens to be a self reference
+ * go ahead and keep that reference
+ * prevents some instances of fixups
+ * for every binary, eg one program
+ * overriding malloc() will not make
+ * ever binary have a fixup for libc
+ * references to malloc()
+ */
+ if (scache[i].obj == obj) {
+ tcache[i].obj = scache[i].obj;
+ tcache[i].sym = scache[i].sym;
+ } else if (tcache[i].obj == obj) {
+ /* no change necessary */
+ } else {
+ tcache[i].obj = badobj;
+ tcache[i].sym = NULL;
+ }
+ }
+ } else {
+ if (scache[i].obj != prog) {
+ tcache[i].obj = scache[i].obj;
+ tcache[i].sym = scache[i].sym;
+ }
+ }
+ }
+}
+
+void
+insert_sym_objcache(struct elf_object *obj, int idx,
+ const struct elf_object *ref_obj, const Elf_Sym *ref_sym, int flags)
+{
+ struct symcache_noflag *tcache;
+ struct elf_object *prog;
+
+ prog = TAILQ_FIRST(&(curbin->curbin_list))->object;
+
+ if (flags)
+ tcache = objarray[obj->dyn.null].pltsymcache;
+ else
+ tcache = objarray[obj->dyn.null].symcache;
+
+ if (tcache[idx].obj != NULL) {
+ if (ref_obj != tcache[idx].obj ||
+ (obj != prog && ref_obj == prog)) {
+ if (verbose > 2) {
+ printf("sym mismatch %d: "
+ "obj %d: sym %ld %s "
+ "nobj %s\n",
+ idx, (int)ref_obj->dyn.null,
+ ref_sym -
+ ref_obj->dyn.symtab,
+ ref_sym->st_name +
+ ref_obj->dyn.strtab,
+ ref_obj->load_name);
+ }
+
+ /*
+ * if one of the symbol entries
+ * happens to be a self reference
+ * go ahead and keep that reference
+ * prevents some instances of fixups
+ * for every binary, eg one program
+ * overriding malloc() will not make
+ * ever binary have a fixup for libc
+ * references to malloc()
+ */
+ if (ref_obj == obj) {
+ tcache[idx].obj = ref_obj;
+ tcache[idx].sym = ref_sym;
+ add_fixup_oldprog(prog, obj, idx, ref_obj,
+ ref_sym, flags);
+ } else if (tcache[idx].obj == obj) {
+ /* no change necessary */
+ add_fixup_prog(prog, obj, idx, ref_obj,
+ ref_sym, flags);
+ } else {
+ add_fixup_oldprog(prog, obj, idx,
+ tcache[idx].obj, tcache[idx].sym, flags);
+ tcache[idx].obj = badobj;
+ tcache[idx].sym = NULL;
+ add_fixup_prog(prog, obj, idx, ref_obj,
+ ref_sym, flags);
+ }
+ }
+ } else {
+ if (ref_obj != prog) {
+ tcache[idx].obj = ref_obj;
+ tcache[idx].sym = ref_sym;
+ } else {
+ add_fixup_prog(prog, obj, idx, ref_obj,
+ ref_sym, flags);
+ }
+ }
+}
+
+void
+add_fixup_prog(struct elf_object *prog, struct elf_object *obj, int idx,
+ const struct elf_object *ref_obj, const Elf_Sym *ref_sym, int flag)
+{
+ struct proglist *pl;
+ int i, libidx, cnt;
+
+ pl = objarray[prog->dyn.null].proglist;
+
+ libidx = -1;
+ for (i = 0; i < pl->nobj; i++) {
+ if (pl->libmap[0][i] == obj->dyn.null) {
+ libidx = (i * 2) + ((flag & SYM_PLT) ? 1 : 0);
+ break;
+ }
+ }
+ if (libidx == -1) {
+ printf("unable to find object\n");
+ return;
+ }
+
+ /* have to check for duplicate patches */
+ for (i = 0; i < pl->fixupcnt[libidx]; i++) {
+ if (pl->fixup[libidx][i].sym == idx)
+ return;
+ }
+
+ if (verbose > 1)
+ printf("fixup for obj %s on prog %s sym %s: %d\n",
+ obj->load_name, prog->load_name,
+ ref_obj->dyn.strtab + ref_sym->st_name,
+ pl->fixupcnt[libidx]);
+
+ if (pl->fixupcntalloc[libidx] < pl->fixupcnt[libidx] + 1) {
+ pl->fixupcntalloc[libidx] += 16;
+ pl->fixup[libidx] = realloc(pl->fixup[libidx],
+ sizeof (struct fixup) * pl->fixupcntalloc[libidx]);
+ if (pl->fixup[libidx] == NULL) {
+ printf("realloc fixup, out of memory\n");
+ exit (20);
+ }
+ }
+ cnt = pl->fixupcnt[libidx];
+ pl->fixup[libidx][cnt].sym = idx;
+ pl->fixup[libidx][cnt].obj_idx = ref_obj->dyn.null;
+ pl->fixup[libidx][cnt].sym_idx = ref_sym - ref_obj->dyn.symtab;
+ pl->fixupcnt[libidx]++;
+}
+
+void
+add_fixup_oldprog(struct elf_object *prog, struct elf_object *obj, int idx,
+ const struct elf_object *ref_obj, const Elf_Sym *ref_sym, int flag)
+{
+ struct objlist *ol;
+
+ TAILQ_FOREACH(ol, &(objarray[obj->dyn.null].inst_list), inst_list) {
+ if (ol->load_prog == prog) {
+ continue;
+ }
+ /* process here */
+
+ add_fixup_prog(ol->load_prog, obj, idx, ref_obj, ref_sym, flag);
+ }
+
+}
+
+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, int objtype)
+{
+ struct objlist *ol;
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ if (ol->object->dev == dev &&
+ ol->object->inode == inode) {
+ if (ol->object->obj_type != objtype)
+ return NULL;
+ return ol->object;
+ }
+ }
+ TAILQ_FOREACH(ol, &library_list, list) {
+ if (ol->object->dev == dev &&
+ ol->object->inode == inode) {
+ if (ol->object->obj_type != objtype)
+ return NULL;
+ if (objtype != OBJTYPE_EXE)
+ 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) {
+ 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 (found == 1) {
+ 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) {
+ 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 (found == 1) {
+ 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)
+{
+ const Elf_Sym *sym;
+ Elf_Rel *rel;
+ Elf_RelA *rela;
+ int numrel;
+ int numrela;
+ int i;
+ struct symcache_noflag *symcache;
+ struct symcache_noflag *pltsymcache;
+
+ numrel = object->dyn.relsz / sizeof(Elf_Rel);
+#ifdef DEBUG1
+ printf("rel relocations: %d\n", numrel);
+#endif
+#if 1
+ symcache = calloc(sizeof(struct symcache_noflag),
+ object->nchains);
+ pltsymcache = calloc(sizeof(struct symcache_noflag),
+ object->nchains);
+ if (symcache == NULL || pltsymcache == NULL) {
+ printf("unable to allocate memory for cache %s\n",
+ object->load_name);
+ exit(20);
+ }
+#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]);
+#ifdef DEBUG1
+ printf("rel plt relocations: %d\n", numrel);
+#endif
+ 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);
+#ifdef DEBUG1
+ 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);
+#ifdef DEBUG1
+ printf("rela plt relocations: %d\n", numrela);
+#endif
+ 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);
+ }
+ }
+ }
+
+ for (i = 0; i < object->nchains; i++)
+ if (symcache[i].sym != NULL)
+ insert_sym_objcache(object, i, symcache[i].obj,
+ symcache[i].sym, 0);
+
+ for (i = 0; i < object->nchains; i++)
+ if (pltsymcache[i].sym != NULL)
+ insert_sym_objcache(object, i, pltsymcache[i].obj,
+ pltsymcache[i].sym, SYM_PLT);
+
+ free(symcache);
+ free(pltsymcache);
+}
+
+void
+elf_resolve_curbin(void)
+{
+ struct objlist *ol;
+ int numobj = 0;
+
+#ifdef DEBUG1
+ elf_print_curbin_list(curbin);
+#endif
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ numobj++;
+ }
+ curbin->nobj = numobj;
+ curbin->libmap = xcalloc(numobj, sizeof (u_int32_t *));
+ curbin->libmap[0] = xcalloc(numobj, sizeof (u_int32_t *));
+ curbin->fixup = xcalloc(2 * numobj, sizeof (struct fixup *));
+ curbin->fixupcnt = xcalloc(2 * numobj, sizeof (int));
+ curbin->fixupcntalloc = xcalloc(2 * numobj, sizeof (int));
+
+ numobj = 0;
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ curbin->libmap[0][numobj] = ol->object->dyn.null;
+ numobj++;
+ }
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ elf_reloc(ol->object);
+ }
+}
+
+void
+elf_add_object_curbin_list(struct elf_object *object)
+{
+ struct objlist *ol;
+ ol = xmalloc(sizeof (struct objlist));
+ ol->object = object;
+ TAILQ_INSERT_TAIL(&(curbin->curbin_list), ol, list);
+ if (load_object == NULL)
+ load_object = object;
+ ol->load_prog = load_object;
+
+ TAILQ_INSERT_TAIL(&(objarray[object->dyn.null].inst_list), ol,
+ inst_list);
+}
+void
+elf_init_objarray(void)
+{
+ objarray_sz = 512;
+ objarray = xmalloc(sizeof (objarray[0]) * objarray_sz);
+}
+
+void
+elf_sum_reloc()
+{
+ int numobjs;
+ int err = 0;
+ struct objlist *ol;
+ struct proglist *pl;
+
+ TAILQ_FOREACH(ol, &library_list, list) {
+ err += elf_prep_lib_prebind(ol->object);
+ }
+ TAILQ_FOREACH(pl, &prog_list, list) {
+ numobjs = 0;
+ TAILQ_FOREACH(ol, &(pl->curbin_list), list) {
+ numobjs++;
+ }
+ pl->nobj = numobjs;
+ }
+
+ TAILQ_FOREACH(pl, &prog_list, list)
+ err += elf_prep_bin_prebind(pl);
+
+ if (err != 0)
+ printf("failures %d\n", err);
+}
+
+int
+elf_prep_lib_prebind(struct elf_object *object)
+{
+ int numlibs = 0;
+ int ret = 0;
+ int i;
+ int ref_obj;
+ int *libmap;
+ int *idxtolib;
+ struct nameidx *nameidx;
+ char *nametab;
+ int nametablen;
+ struct symcache_noflag *symcache;
+ struct symcache_noflag *pltsymcache;
+ struct symcachetab *symcachetab;
+ int symcache_cnt = 0;
+ struct symcachetab *pltsymcachetab;
+ int pltsymcache_cnt = 0;
+
+ symcache = objarray[object->dyn.null].symcache;
+ pltsymcache = objarray[object->dyn.null].pltsymcache;
+ libmap = xcalloc(objarray_cnt, sizeof (int));
+ idxtolib = xcalloc(objarray_cnt, sizeof (int));
+ objarray[object->dyn.null].idxtolib = idxtolib;
+
+ for (i = 0; i < objarray_cnt; i++)
+ libmap[i] = -1;
+
+ nametablen = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (symcache[i].sym == NULL)
+ continue;
+ ref_obj = symcache[i].obj->dyn.null;
+ symcache_cnt++;
+ if (libmap[ref_obj] != -1)
+ continue;
+ libmap[ref_obj] = numlibs;
+ idxtolib[numlibs] = ref_obj;
+ nametablen += strlen(symcache[i].obj->load_name) + 1;
+ numlibs++;
+ }
+ symcachetab = xcalloc(symcache_cnt , sizeof(struct symcachetab));
+
+ symcache_cnt = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (symcache[i].sym == NULL)
+ continue;
+ symcachetab[symcache_cnt].idx = i;
+ symcachetab[symcache_cnt].obj_idx =
+ libmap[symcache[i].obj->dyn.null];
+ symcachetab[symcache_cnt].sym_idx =
+ symcache[i].sym - symcache[i].obj->dyn.symtab;
+ symcache_cnt++;
+ }
+ for (i = 0; i < object->nchains; i++) {
+ if (pltsymcache[i].sym == NULL)
+ continue;
+ ref_obj = pltsymcache[i].obj->dyn.null;
+ pltsymcache_cnt++;
+ if (libmap[ref_obj] != -1)
+ continue;
+ libmap[ref_obj] = numlibs;
+ idxtolib[numlibs] = ref_obj;
+ nametablen += strlen(pltsymcache[i].obj->load_name) + 1;
+ numlibs++;
+ }
+ pltsymcachetab = xcalloc(pltsymcache_cnt , sizeof(struct symcachetab));
+
+ pltsymcache_cnt = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (pltsymcache[i].sym == NULL)
+ continue;
+ pltsymcachetab[pltsymcache_cnt].idx = i;
+ pltsymcachetab[pltsymcache_cnt].obj_idx =
+ libmap[pltsymcache[i].obj->dyn.null];
+ pltsymcachetab[pltsymcache_cnt].sym_idx =
+ pltsymcache[i].sym - pltsymcache[i].obj->dyn.symtab;
+ pltsymcache_cnt++;
+ }
+
+ objarray[object->dyn.null].numlibs = numlibs;
+
+ nameidx = xcalloc(numlibs, sizeof (struct nameidx));
+ nametab = xmalloc(nametablen);
+
+ nametablen = 0;
+ for (i = 0; i < numlibs; i++) {
+ nameidx[i].name = nametablen;
+ nameidx[i].id0 = objarray[idxtolib[i]].id0;
+ nameidx[i].id1 = objarray[idxtolib[i]].id1;
+ nametablen += strlen(objarray[idxtolib[i]].obj->load_name) + 1;
+ strlcpy (&nametab[nameidx[i].name],
+ objarray[idxtolib[i]].obj->load_name,
+ nametablen - nameidx[i].name);
+ }
+
+ /* skip writing lib if using old prebind data */
+ if (objarray[object->dyn.null].oprebind_data == NULL)
+ ret = elf_write_lib(object, nameidx, nametab, nametablen,
+ numlibs, 0, NULL, NULL, NULL, NULL, symcachetab,
+ symcache_cnt, pltsymcachetab, pltsymcache_cnt);
+
+ free (nameidx);
+ free (nametab);
+ free (libmap);
+ free(pltsymcachetab);
+ free(symcachetab);
+
+ return ret;
+}
+
+int
+elf_prep_bin_prebind(struct proglist *pl)
+{
+ int ret;
+ int numlibs = 0;
+ int i, j;
+ int ref_obj;
+ int *libmap;
+ int *idxtolib;
+ struct nameidx *nameidx;
+ char *nametab;
+ int nametablen;
+ struct symcache_noflag *symcache;
+ struct symcache_noflag *pltsymcache;
+ struct symcachetab *symcachetab;
+ int symcache_cnt;
+ struct symcachetab *pltsymcachetab;
+ int pltsymcache_cnt;
+ struct elf_object *object;
+ struct objlist *ol;
+
+ object = TAILQ_FIRST(&(pl->curbin_list))->object;
+ symcache = objarray[object->dyn.null].symcache;
+ pltsymcache = objarray[object->dyn.null].pltsymcache;
+ libmap = xcalloc(objarray_cnt, sizeof (int));
+ idxtolib = xcalloc(pl->nobj, sizeof (int));
+
+ for (i = 0; i < objarray_cnt; i++)
+ libmap[i] = -1;
+
+ for (i = 0; i < pl->nobj; i++)
+ idxtolib[i] = -1;
+
+ nametablen = 0;
+ TAILQ_FOREACH(ol, &(pl->curbin_list), list) {
+ ref_obj = ol->object->dyn.null;
+ nametablen += strlen(ol->object->load_name) + 1;
+ libmap[ref_obj] = numlibs;
+ idxtolib[numlibs] = ref_obj;
+ numlibs++;
+ }
+
+ /* do got */
+ symcache_cnt = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (symcache[i].sym != NULL)
+ symcache_cnt++;
+ }
+
+ symcachetab = xcalloc(symcache_cnt , sizeof(struct symcachetab));
+
+ symcache_cnt = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (symcache[i].sym == NULL)
+ continue;
+ symcachetab[symcache_cnt].idx = i;
+ symcachetab[symcache_cnt].obj_idx =
+ libmap[symcache[i].obj->dyn.null];
+ symcachetab[symcache_cnt].sym_idx =
+ symcache[i].sym - symcache[i].obj->dyn.symtab;
+ symcache_cnt++;
+ }
+
+ /* now do plt */
+ pltsymcache_cnt = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (pltsymcache[i].sym != NULL)
+ pltsymcache_cnt++;
+ }
+ pltsymcachetab = xcalloc(pltsymcache_cnt , sizeof(struct symcachetab));
+
+ pltsymcache_cnt = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (pltsymcache[i].sym == NULL)
+ continue;
+ pltsymcachetab[pltsymcache_cnt].idx = i;
+ pltsymcachetab[pltsymcache_cnt].obj_idx =
+ libmap[pltsymcache[i].obj->dyn.null];
+ pltsymcachetab[pltsymcache_cnt].sym_idx =
+ pltsymcache[i].sym - pltsymcache[i].obj->dyn.symtab;
+ pltsymcache_cnt++;
+ }
+
+ objarray[object->dyn.null].numlibs = numlibs;
+
+ nameidx = xcalloc(numlibs, sizeof (struct nameidx));
+ nametab = xmalloc(nametablen);
+
+ nametablen = 0;
+ for (i = 0; i < numlibs; i++) {
+ nameidx[i].name = nametablen;
+ nameidx[i].id0 = objarray[idxtolib[i]].id0;
+ nameidx[i].id1 = objarray[idxtolib[i]].id1;
+ nametablen += strlen(objarray[idxtolib[i]].obj->load_name) + 1;
+
+ strlcpy (&nametab[nameidx[i].name],
+ objarray[idxtolib[i]].obj->load_name,
+ nametablen - nameidx[i].name);
+ }
+ pl->libmapcnt = xcalloc(numlibs, sizeof(u_int32_t));
+
+ /* have to do both got and plt fixups */
+ for (i = 0; i < numlibs; i++) {
+ for (j = 0; j < pl->fixupcnt[2*i]; j++) {
+ pl->fixup[2*i][j].obj_idx =
+ libmap[pl->fixup[2*i][j].obj_idx];
+ }
+ for (j = 0; j < pl->fixupcnt[2*i+1]; j++) {
+ pl->fixup[2*i+1][j].obj_idx =
+ libmap[pl->fixup[2*i+1][j].obj_idx];
+ }
+
+ pl->libmapcnt[i] = objarray[idxtolib[i]].numlibs;
+ pl->libmap[i] = xcalloc(objarray[idxtolib[i]].numlibs,
+ sizeof(u_int32_t));
+ if (i != 0) {
+ for (j = 0; j < objarray[idxtolib[i]].numlibs; j++) {
+ pl->libmap[i][j] =
+ libmap[objarray[idxtolib[i]].idxtolib[j]];
+ }
+ }
+ }
+
+ ret = elf_write_lib(object, nameidx, nametab, nametablen, numlibs,
+ numlibs, pl->fixup, pl->fixupcnt,
+ pl->libmap, pl->libmapcnt,
+ symcachetab, symcache_cnt,
+ pltsymcachetab, pltsymcache_cnt);
+
+ free(symcachetab);
+ free(pltsymcachetab);
+ free(idxtolib);
+ free(nameidx);
+ free(nametab);
+ free(libmap);
+ return ret;
+}
+
+int64_t prebind_blocks;
+
+int
+elf_write_lib(struct elf_object *object, struct nameidx *nameidx,
+ char *nametab, int nametablen, int numlibs,
+ int nfixup, struct fixup **fixup, int *fixupcnt,
+ u_int32_t **libmap, int *libmapcnt,
+ struct symcachetab *symcachetab, int symcache_cnt,
+ struct symcachetab *pltsymcachetab, int pltsymcache_cnt)
+{
+ u_int32_t footer_offset, *maptab = NULL;
+ u_int32_t next_start, *fixuptab = NULL;
+ struct prebind_footer footer;
+ struct stat ifstat;
+ off_t base_offset;
+ size_t len;
+ int fd, i;
+
+ /* open the file */
+ fd = open(object->load_name, O_RDWR);
+ if (fd == -1) {
+ if (errno == ETXTBSY)
+ fd = write_txtbusy_file(object->load_name);
+ if (fd == -1) {
+ perror(object->load_name);
+ return 1;
+ }
+ }
+ lseek(fd, -((off_t)sizeof(struct prebind_footer)), SEEK_END);
+ len = read(fd, &footer, sizeof(struct prebind_footer));
+
+ if (footer.bind_id[0] == BIND_ID0 &&
+ footer.bind_id[1] == BIND_ID1 &&
+ footer.bind_id[2] == BIND_ID2 &&
+ footer.bind_id[3] == BIND_ID3) {
+
+ ftruncate(fd, footer.orig_size);
+ elf_clear_prog_load(fd, object);
+ }
+
+ if (fstat(fd, &ifstat) == -1) {
+ perror(object->load_name);
+ exit(10);
+ }
+ bzero(&footer, sizeof(struct prebind_footer));
+
+ base_offset = ifstat.st_size;
+ prebind_blocks -= ifstat.st_blocks; /* subtract old size */
+
+ /* verify dev/inode - do we care about last modified? */
+
+ /* pieces to store on lib
+ *
+ * offset to footer
+ * nameidx - numlibs * sizeof nameidx
+ * symcache - symcache_cnt * sizeof (symcache_idx)
+ * pltsymcache - pltsymcache_cnt * sizeof (symcache_idx)
+ * fixup(N/A for lib) - nfixup * sizeof (symcache_idx)
+ * nametab - nametablen
+ * footer (not aligned)
+ */
+
+ footer.orig_size = base_offset;
+ base_offset = ELF_ROUND(base_offset, sizeof(u_int64_t));
+ footer.prebind_base = base_offset;
+ footer.nameidx_idx = sizeof(u_int32_t);
+ footer.symcache_idx = footer.nameidx_idx +
+ numlibs * sizeof (struct nameidx);
+ footer.pltsymcache_idx = footer.symcache_idx +
+ symcache_cnt * sizeof (struct nameidx);
+ footer.symcache_cnt = symcache_cnt;
+ footer.pltsymcache_cnt = pltsymcache_cnt;
+ footer.fixup_cnt = 0;
+ footer.numlibs = numlibs;
+ next_start = footer.pltsymcache_idx +
+ (pltsymcache_cnt * sizeof (struct symcachetab));
+ if (nfixup != 0) {
+ footer.fixup_cnt = nfixup;
+ footer.fixup_idx = next_start;
+ next_start += 2*nfixup * sizeof(u_int32_t);
+ footer.fixupcnt_idx = next_start;
+ next_start += 2*nfixup * sizeof(u_int32_t);
+ fixuptab = xcalloc(2*nfixup, sizeof(u_int32_t));
+ for (i = 0; i < 2*nfixup; i++) {
+ fixuptab[i] = next_start;
+ next_start += fixupcnt[i] * sizeof(struct fixup);
+ }
+ footer.libmap_idx = next_start;
+ next_start += 2*nfixup * sizeof(u_int32_t);
+ maptab = xcalloc(2*nfixup, sizeof(u_int32_t));
+ maptab[0] = next_start;
+ for (i = 1; i < nfixup; i++) {
+ maptab[i] = next_start;
+ next_start += libmapcnt[i] * sizeof(u_int32_t);
+ }
+
+ }
+ footer.nametab_idx = next_start;
+ next_start += nametablen;
+ next_start = ELF_ROUND(next_start, sizeof(u_int64_t));
+ footer_offset = next_start;
+ if (verbose > 1) {
+ printf("footer_offset %d\n", footer_offset);
+ }
+ footer.prebind_size = next_start + sizeof(struct prebind_footer);
+
+ footer.prebind_version = PREBIND_VERSION;
+ footer.id0 = objarray[object->dyn.null].id0;
+ footer.id1 = objarray[object->dyn.null].id1;
+ footer.bind_id[0] = BIND_ID0;
+ footer.bind_id[1] = BIND_ID1;
+ footer.bind_id[2] = BIND_ID2;
+ footer.bind_id[3] = BIND_ID3;
+
+ lseek(fd, footer.prebind_base, SEEK_SET);
+ write(fd, &footer_offset, sizeof(u_int32_t));
+
+ lseek(fd, footer.prebind_base+footer.nameidx_idx, SEEK_SET);
+ write(fd, nameidx, numlibs * sizeof (struct nameidx));
+
+ lseek(fd, footer.prebind_base+footer.symcache_idx, SEEK_SET);
+ write(fd, symcachetab, symcache_cnt * sizeof (struct symcachetab));
+
+ lseek(fd, footer.prebind_base+footer.pltsymcache_idx, SEEK_SET);
+ write(fd, pltsymcachetab, pltsymcache_cnt *
+ sizeof (struct symcachetab));
+
+ if (verbose > 3)
+ dump_symcachetab(symcachetab, symcache_cnt, object, 0);
+ if (verbose > 3)
+ dump_symcachetab(pltsymcachetab, pltsymcache_cnt, object, 0);
+
+ if (nfixup != 0) {
+ lseek(fd, footer.prebind_base+footer.fixup_idx, SEEK_SET);
+ write(fd, fixuptab, 2*nfixup * sizeof(u_int32_t));
+ lseek(fd, footer.prebind_base+footer.fixupcnt_idx, SEEK_SET);
+ write(fd, fixupcnt, 2*nfixup * sizeof(u_int32_t));
+ for (i = 0; i < 2*nfixup; i++) {
+ lseek(fd, footer.prebind_base+fixuptab[i],
+ SEEK_SET);
+ write(fd, fixup[i], fixupcnt[i] * sizeof(struct fixup));
+ }
+
+ lseek(fd, footer.prebind_base+footer.libmap_idx, SEEK_SET);
+ write(fd, maptab, nfixup * sizeof(u_int32_t));
+ for (i = 0; i < nfixup; i++) {
+ lseek(fd, footer.prebind_base+maptab[i],
+ SEEK_SET);
+ write(fd, libmap[i], libmapcnt[i] * sizeof(u_int32_t));
+ }
+ }
+ lseek(fd, footer.prebind_base+footer.nametab_idx, SEEK_SET);
+ write(fd, nametab, nametablen);
+ lseek(fd, footer.prebind_base+footer_offset, SEEK_SET);
+ write(fd, &footer, sizeof (struct prebind_footer));
+
+ if (fstat(fd, &ifstat) == -1) {
+ perror(object->load_name);
+ exit(10);
+ }
+ prebind_blocks += ifstat.st_blocks; /* add new size */
+ if (nfixup != 0) {
+ elf_fixup_prog_load(fd, &footer, object);
+
+ free(fixuptab);
+ free(maptab);
+ }
+
+ if (verbose > 0)
+ printf("%s: prebind info %d bytes old size %lld, growth %f\n",
+ object->load_name, footer.prebind_size, footer.orig_size,
+ (double)(footer.prebind_size) / footer.orig_size);
+
+ if (verbose > 1)
+ elf_dump_footer(&footer);
+
+ close (fd);
+ return 0;
+}
+void
+elf_fixup_prog_load(int fd, struct prebind_footer *footer,
+ struct elf_object *object)
+{
+ void *buf;
+ Elf_Ehdr *ehdr;
+ Elf_Phdr *phdr;
+ Elf_Phdr phdr_empty;
+ int loadsection;
+
+ buf = mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_FILE | MAP_SHARED,
+ fd, 0);
+ if (buf == MAP_FAILED) {
+ printf("%s: cannot mmap for write\n", object->load_name);
+ return;
+ }
+
+ ehdr = (Elf_Ehdr *) buf;
+ phdr = (Elf_Phdr *)((char *)buf + ehdr->e_phoff);
+
+ for (loadsection = 0; loadsection < ehdr->e_phnum; loadsection++) {
+ if (phdr[loadsection].p_type == PT_LOAD)
+ break;
+ }
+
+ /* verify that extra slot is empty */
+ bzero(&phdr_empty, sizeof(phdr_empty));
+ if (bcmp(&phdr[ehdr->e_phnum], &phdr_empty, sizeof(phdr_empty)) != 0) {
+ printf("extra slot not empty\n");
+ goto done;
+ }
+ phdr[ehdr->e_phnum].p_type = PT_LOAD;
+ phdr[ehdr->e_phnum].p_flags = PF_R | 0x08000000;
+ phdr[ehdr->e_phnum].p_offset = footer->prebind_base;
+ phdr[ehdr->e_phnum].p_vaddr = footer->prebind_base | 0x80000000;
+ phdr[ehdr->e_phnum].p_paddr = footer->prebind_base | 0x40000000;
+ phdr[ehdr->e_phnum].p_filesz = footer->prebind_size;
+ phdr[ehdr->e_phnum].p_memsz = footer->prebind_size;
+ phdr[ehdr->e_phnum].p_align = phdr[loadsection].p_align;
+ ehdr->e_phnum++;
+
+done:
+ msync(buf, 8192, MS_SYNC);
+ munmap(buf, 8192);
+}
+
+void
+elf_clear_prog_load(int fd, struct elf_object *object)
+{
+ void *buf;
+ Elf_Ehdr *ehdr;
+ Elf_Phdr *phdr;
+ Elf_Phdr phdr_empty;
+ int loadsection;
+
+ buf = mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_FILE | MAP_SHARED,
+ fd, 0);
+ if (buf == MAP_FAILED) {
+ printf("%s: cannot mmap for write\n", object->load_name);
+ return;
+ }
+
+ ehdr = (Elf_Ehdr *) buf;
+ phdr = (Elf_Phdr *)((char *)buf + ehdr->e_phoff);
+
+ if (ehdr->e_type != ET_EXEC) {
+ goto done;
+ }
+
+ loadsection = ehdr->e_phnum - 1;
+ if ((phdr[loadsection].p_type != PT_LOAD) ||
+ ((phdr[loadsection].p_flags & 0x08000000) == 0)) {
+ /* doesn't look like ours */
+ printf("mapped, %s id doesn't match %lx %d %d\n",
+ object->load_name,
+ (long)(phdr[loadsection].p_vaddr),
+ phdr[loadsection].p_flags, loadsection);
+ goto done;
+ }
+
+ /* verify that extra slot is empty */
+ bzero(&phdr[loadsection], sizeof(phdr_empty));
+
+ ehdr->e_phnum--;
+
+done:
+ msync(buf, 8192, MS_SYNC);
+ munmap(buf, 8192);
+}
+
+void
+elf_add_object(struct elf_object *object, int objtype)
+{
+ struct objarray_list *newarray;
+ struct objlist *ol;
+ ol = xmalloc(sizeof (struct objlist));
+ ol->object = object;
+ if (objtype != OBJTYPE_EXE)
+ TAILQ_INSERT_TAIL(&library_list, ol, list);
+ if (objarray_cnt+1 >= objarray_sz) {
+ objarray_sz += 512;
+ newarray = realloc(objarray, sizeof (objarray[0]) *
+ objarray_sz);
+ if (newarray != NULL)
+ objarray = newarray;
+ else {
+ perror("objarray");
+ exit(20);
+ }
+ }
+ object->dyn.null = objarray_cnt; /* Major abuse, I know */
+ TAILQ_INIT(&(objarray[objarray_cnt].inst_list));
+ objarray[objarray_cnt].obj = object;
+ objarray[objarray_cnt].id0 = arc4random();
+ objarray[objarray_cnt].id1 = arc4random();
+
+ objarray[objarray_cnt].symcache = xcalloc(
+ sizeof(struct symcache_noflag), object->nchains);
+ objarray[objarray_cnt].pltsymcache = xcalloc(
+ sizeof(struct symcache_noflag), object->nchains);
+
+ objarray[objarray_cnt].oprebind_data = NULL;
+ objarray[objarray_cnt].proglist = NULL;
+ objarray[objarray_cnt].numlibs = 0;
+ objarray_cnt++;
+
+ elf_add_object_curbin_list(object);
+}
+
+void
+elf_free_curbin_list(elf_object_t *object)
+{
+ struct objlist *ol;
+ int i;
+
+ while (!TAILQ_EMPTY(&(curbin->curbin_list))) {
+ ol = TAILQ_FIRST(&(curbin->curbin_list));
+ TAILQ_REMOVE(&(objarray[ol->object->dyn.null].inst_list), ol, inst_list);
+ TAILQ_REMOVE(&(curbin->curbin_list), ol, list);
+ free(ol);
+ }
+
+ printf("trying to remove %s\n", object->load_name);
+ for (i = objarray_cnt; i != 0;) {
+ i--;
+ printf("obj %s\n", objarray[i].obj->load_name);
+ if (objarray[i].obj == object) {
+ printf("found obj at %d max obj %d\n", i, objarray_cnt);
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ }
+ /* XXX - delete references */
+ objarray_cnt = i;
+ break;
+ }
+ }
+}
+
+void
+elf_print_objarray(void)
+{
+ int i;
+ struct objlist *ol;
+
+ printf("loaded objs # %d\n", objarray_cnt);
+ for (i = 0; i < objarray_cnt; i++) {
+ printf("%3d: %d obj %s\n", i, (int)objarray[i].obj->dyn.null,
+ objarray[i].obj->load_name);
+ TAILQ_FOREACH(ol, &(objarray[i].inst_list),
+ inst_list) {
+ printf("\tprog %s\n", ol->load_prog->load_name);
+ }
+ }
+}
+
+int
+write_txtbusy_file(char *name)
+{
+ char *prebind_name;
+ int fd;
+ int oldfd;
+ int err;
+ struct stat sb;
+ void *buf;
+ size_t len, wlen;
+
+ err = lstat(name, &sb); /* get mode of old file (preserve mode) */
+ if (err != 0)
+ return -1; /* stat shouldn't fail but if it does */
+
+ /* pick a better filename (pulling apart string?) */
+ err = asprintf(&prebind_name, "%s%s", name, ".prebXXXXXXXXXX");
+ if (err == -1) {
+ /* fail */
+ exit (10); /* bail on memory failure */
+ }
+ mkstemp(prebind_name);
+
+ /* allocate a 256k buffer to copy the file */
+#define BUFSZ (256 * 1024)
+ buf = xmalloc(BUFSZ);
+
+ fd = open(prebind_name, O_RDWR|O_CREAT|O_TRUNC, sb.st_mode);
+ oldfd = open(name, O_RDONLY);
+ while ((len = read(oldfd, buf, BUFSZ)) > 0) {
+ wlen = write(fd, buf, len);
+ if (wlen != len) {
+ /* write failed */
+ close(fd);
+ close(oldfd);
+ unlink(prebind_name);
+ free(buf);
+ return -1;
+ }
+ }
+
+ /* this mode is used above, but is modified by umask */
+ chmod (prebind_name, sb.st_mode);
+ close(oldfd);
+ unlink(name);
+ rename (prebind_name, name);
+ free (buf);
+
+ return fd;
+}
+
+void
+elf_load_existing_prebind(struct elf_object *object, int fd)
+{
+ struct prebind_footer footer;
+ void *prebind_data;
+
+ lseek(fd, -((off_t)sizeof(struct prebind_footer)), SEEK_END);
+ read(fd, &footer, sizeof(struct prebind_footer));
+
+ if (footer.bind_id[0] != BIND_ID0 ||
+ footer.bind_id[1] != BIND_ID1 ||
+ footer.bind_id[2] != BIND_ID2 ||
+ footer.bind_id[3] != BIND_ID3) {
+ return;
+ }
+
+ prebind_data = mmap(0, footer.prebind_size, PROT_READ,
+ MAP_FILE, fd, footer.prebind_base);
+ objarray[object->dyn.null].oprebind_data = prebind_data;
+ objarray[object->dyn.null].id0 = footer.id0;
+ objarray[object->dyn.null].id1 = footer.id1;
+
+ copy_oldsymcache(object->dyn.null, prebind_data);
+}
+void
+copy_oldsymcache(int objidx, void *prebind_map)
+{
+ struct prebind_footer *footer;
+ struct elf_object *object;
+ struct elf_object *tobj;
+ struct symcache_noflag *tcache;
+ struct symcachetab *symcache;
+ int i, j, found, *idxtolib;
+ char *c, *nametab;
+ u_int32_t offset;
+ u_int32_t *poffset;
+ struct nameidx *nameidx;
+
+ object = objarray[objidx].obj;
+
+ poffset = (u_int32_t *)prebind_map;
+ c = prebind_map;
+ offset = *poffset;
+ c += offset;
+ footer = (void *)c;
+
+ nameidx = prebind_map + footer->nameidx_idx;
+ nametab = prebind_map + footer->nametab_idx;
+
+ idxtolib = xcalloc(footer->numlibs, sizeof(int));
+ found = 0;
+ for (i = 0; i < footer->numlibs; i++) {
+ found = 0;
+ for (j = 0; j < objarray_cnt; j++) {
+ if (objarray[j].id0 == nameidx[i].id0 &&
+ objarray[j].id1 == nameidx[i].id1) {
+ found = 1;
+ idxtolib[i] = j;
+ if (strcmp(objarray[j].obj->load_name,
+ &nametab[nameidx[i].name]) != 0) {
+ printf("warning filename mismatch"
+ " [%s] [%s]\n",
+ objarray[j].obj->load_name,
+ &nametab[nameidx[i].name]);
+ }
+ }
+ }
+ if (found == 0)
+ break;
+ }
+ if (found == 0)
+ goto done;
+
+ /* build idxtolibs */
+
+ tcache = objarray[objidx].symcache;
+ symcache = prebind_map + footer->symcache_idx;
+
+ for (i = 0; i < footer->symcache_cnt; i++) {
+ tobj = objarray[idxtolib[symcache[i].obj_idx]].obj;
+
+ tcache[symcache[i].idx].obj = tobj;
+ tcache[symcache[i].idx].sym = tobj->dyn.symtab +
+ symcache[i].sym_idx;
+ }
+
+ tcache = objarray[objidx].pltsymcache;
+ symcache = prebind_map + footer->pltsymcache_idx;
+ for (i = 0; i < footer->pltsymcache_cnt; i++) {
+ tobj = objarray[idxtolib[symcache[i].obj_idx]].obj;
+
+ tcache[symcache[i].idx].obj = tobj;
+ tcache[symcache[i].idx].sym = tobj->dyn.symtab +
+ symcache[i].sym_idx;
+ }
+done:
+ free (idxtolib);
+ /* munmap(prebind_map, size);*/
+}
diff --git a/libexec/ld.so/ldconfig/prebind.h b/libexec/ld.so/ldconfig/prebind.h
new file mode 100644
index 00000000000..2cfa5e7d591
--- /dev/null
+++ b/libexec/ld.so/ldconfig/prebind.h
@@ -0,0 +1,64 @@
+/* $OpenBSD: prebind.h,v 1.1 2006/05/12 23:20:52 deraadt 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.
+ */
+
+#define PREBIND_VERSION 2
+struct prebind_footer {
+ off_t prebind_base;
+ u_int32_t nameidx_idx;
+ u_int32_t symcache_idx;
+ u_int32_t pltsymcache_idx;
+ u_int32_t fixup_idx;
+ u_int32_t nametab_idx;
+ u_int32_t fixupcnt_idx;
+ u_int32_t libmap_idx;
+
+ u_int32_t symcache_cnt;
+ u_int32_t pltsymcache_cnt;
+ u_int32_t fixup_cnt;
+ u_int32_t numlibs;
+ u_int32_t prebind_size;
+
+ u_int32_t id0;
+ u_int32_t id1;
+ /* do not modify or add fields below this point in the struct */
+ off_t orig_size;
+ u_int32_t prebind_version;
+ char bind_id[4];
+#define BIND_ID0 'P'
+#define BIND_ID1 'R'
+#define BIND_ID2 'E'
+#define BIND_ID3 'B'
+};
+
+
+struct nameidx {
+ u_int32_t name;
+ u_int32_t id0;
+ u_int32_t id1;
+};
+
+struct symcachetab {
+ u_int32_t idx;
+ u_int32_t obj_idx;
+ u_int32_t sym_idx;
+};
+
+struct fixup {
+ u_int32_t sym;
+ u_int32_t obj_idx;
+ u_int32_t sym_idx;
+};
diff --git a/libexec/ld.so/ldconfig/prebind_delete.c b/libexec/ld.so/ldconfig/prebind_delete.c
index ddcad2c9dc5..b5fa6a3cf03 100644
--- a/libexec/ld.so/ldconfig/prebind_delete.c
+++ b/libexec/ld.so/ldconfig/prebind_delete.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: prebind_delete.c,v 1.2 2006/05/11 22:19:23 deraadt Exp $ */
+/* $OpenBSD: prebind_delete.c,v 1.3 2006/05/12 23:20:52 deraadt Exp $ */
/*
* Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/exec_elf.h>
+#include <elf_abi.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
@@ -30,18 +31,20 @@
#define BUFSZ (256 * 1024)
-int strip_prebind(char *file, int verbose);
-int prebind_remove_load_section(int fd, char *name, int verbose);
+int strip_prebind(char *file);
+int prebind_remove_load_section(int fd, char *name);
int prebind_newfile(int fd, char *name, struct stat *st,
- struct prebind_footer *footer, int verbose);
+ struct prebind_footer *footer);
+
+extern int verbose;
int
-prebind_delete(char **argv, int verbose)
+prebind_delete(char **argv)
{
extern char *__progname;
while (*argv) {
- if (strip_prebind(*argv, verbose) == -1)
+ if (strip_prebind(*argv) == -1)
return (1);
argv++;
}
@@ -49,7 +52,7 @@ prebind_delete(char **argv, int verbose)
}
int
-strip_prebind(char *file, int verbose)
+strip_prebind(char *file)
{
struct prebind_footer footer;
extern char *__progname;
@@ -83,9 +86,9 @@ strip_prebind(char *file, int verbose)
}
if (rdonly) {
- fd = prebind_newfile(fd, file, &st, &footer, verbose);
+ fd = prebind_newfile(fd, file, &st, &footer);
} else {
- prebind_remove_load_section(fd, file, verbose);
+ prebind_remove_load_section(fd, file);
ftruncate(fd, footer.orig_size);
}
@@ -100,7 +103,7 @@ done:
}
int
-prebind_remove_load_section(int fd, char *name, int verbose)
+prebind_remove_load_section(int fd, char *name)
{
Elf_Ehdr *ehdr;
Elf_Phdr *phdr;
@@ -141,7 +144,7 @@ done:
int
prebind_newfile(int infd, char *name, struct stat *st,
- struct prebind_footer *footer, int verbose)
+ struct prebind_footer *footer)
{
struct timeval tv[2];
char *newname, *buf;
@@ -188,7 +191,7 @@ prebind_newfile(int infd, char *name, struct stat *st,
free(buf);
/* now back track, and delete the header */
- if (prebind_remove_load_section(outfd, newname, verbose) == -1)
+ if (prebind_remove_load_section(outfd, newname) == -1)
goto fail;
if (ftruncate(outfd, footer->orig_size) == -1)
goto fail;
diff --git a/libexec/ld.so/ldconfig/prebind_struct.h b/libexec/ld.so/ldconfig/prebind_struct.h
new file mode 100644
index 00000000000..42a64bb868d
--- /dev/null
+++ b/libexec/ld.so/ldconfig/prebind_struct.h
@@ -0,0 +1,89 @@
+/* $OpenBSD: prebind_struct.h,v 1.1 2006/05/12 23:20:53 deraadt 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.
+ */
+
+struct symcache_noflag {
+ const elf_object_t *obj;
+ const Elf_Sym *sym;
+};
+
+struct objlist {
+ TAILQ_ENTRY(objlist) list;
+ TAILQ_ENTRY(objlist) inst_list;
+ struct elf_object *load_prog;
+ struct elf_object *object;
+};
+
+struct proglist {
+ TAILQ_ENTRY(proglist) list;
+ TAILQ_HEAD(, objlist) curbin_list;
+ struct fixup **fixup;
+ int *fixupcnt;
+ int *fixupcntalloc;
+ int nobj;
+ u_int32_t **libmap;
+ u_int32_t *libmapcnt;
+ char *interp;
+};
+extern struct proglist *curbin;
+extern struct elf_object *load_object;
+
+
+typedef TAILQ_HEAD(, proglist) prog_list_ty;
+typedef TAILQ_HEAD(, objlist) obj_list_ty;
+
+extern obj_list_ty library_list;
+extern prog_list_ty prog_list;
+
+/* debug */
+void elf_print_curbin_list(struct proglist *bin);
+void elf_print_prog_list (prog_list_ty *prog_list);
+
+
+void elf_add_object_curbin_list(struct elf_object *object);
+
+void elf_copy_syms(struct symcache_noflag *tcache,
+ struct symcache_noflag *scache, struct elf_object *obj,
+ struct elf_object *prog, int nsyms);
+int elf_prep_lib_prebind(struct elf_object *object);
+int elf_prep_bin_prebind(struct proglist *pl);
+void elf_calc_fixups(struct proglist *pl, struct objlist *ol, int libidx);
+int elf_write_lib(struct elf_object *object, struct nameidx *nameidx,
+ char *nametab, int nametablen, int numlibs,
+ int nfixup, struct fixup **fixup, int *fixupcnt,
+ u_int32_t **libmap, int *libmapcnt,
+ struct symcachetab *symcachetab, int symcache_cnt,
+ struct symcachetab *pltsymcachetab, int pltsymcache_cnt);
+
+void dump_symcachetab(struct symcachetab *symcachetab, int symcache_cnt, struct elf_object *object, int id);
+void dump_info(struct elf_object *object);
+void elf_clear_prog_load(int fd, struct elf_object *object);
+void elf_fixup_prog_load(int fd, struct prebind_footer *footer,
+ struct elf_object *object);
+void elf_dump_footer(struct prebind_footer *footer);
+
+extern int verbose;
+extern int merge;
+
+extern int64_t prebind_blocks;
+extern struct elf_object *load_object;
+struct elf_object *elf_lookup_object(const char *name);
+struct elf_object *load_file(const char *filename, int objtype);
+
+void elf_load_existing_prebind(struct elf_object *object, int fd);
+
+void *xmalloc(size_t);
+void *xcalloc(size_t,size_t);
diff --git a/libexec/ld.so/ldconfig/sod.c b/libexec/ld.so/ldconfig/sod.c
new file mode 100644
index 00000000000..d3383ee7b14
--- /dev/null
+++ b/libexec/ld.so/ldconfig/sod.c
@@ -0,0 +1,265 @@
+/* $OpenBSD: sod.c,v 1.1 2006/05/12 23:20:53 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/syslimits.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <link.h>
+#include <limits.h>
+#include <machine/exec.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#if 0
+#include "syscall.h"
+#include "archdep.h"
+#include "util.h"
+#endif
+#include "sod.h"
+
+int _dl_hinthash(char *cp, int vmajor, int vminor);
+void _dl_maphints(void);
+
+/*
+ * Populate sod struct for dlopen's call to map_object
+ */
+void
+_dl_build_sod(const char *name, struct sod *sodp)
+{
+ unsigned int tuplet;
+ int major, minor;
+ char *realname, *tok, *etok, *cp;
+
+ /* default is an absolute or relative path */
+ sodp->sod_name = (long)strdup(name); /* strtok is destructive */
+ sodp->sod_library = 0;
+ sodp->sod_major = sodp->sod_minor = 0;
+
+ /* does it look like /^lib/ ? */
+ if (strncmp((char *)sodp->sod_name, "lib", 3) != 0)
+ goto backout;
+
+ /* is this a filename? */
+ if (strchr((char *)sodp->sod_name, '/'))
+ goto backout;
+
+ /* skip over 'lib' */
+ cp = (char *)sodp->sod_name + 3;
+
+ realname = cp;
+
+ /* dot guardian */
+ if ((strchr(cp, '.') == NULL) || (*(cp+strlen(cp)-1) == '.'))
+ goto backout;
+
+ cp = strstr(cp, ".so");
+ if (cp == NULL)
+ goto backout;
+
+ /* default */
+ major = minor = -1;
+
+ /* loop through name - parse skipping name */
+ for (tuplet = 0; (tok = strsep(&cp, ".")) != NULL; tuplet++) {
+ switch (tuplet) {
+ case 0:
+ /* empty tok, we already skipped to "\.so.*" */
+ break;
+ case 1:
+ /* 'so' extension */
+ break;
+ case 2:
+ /* major version extension */
+ major = strtol(tok, &etok, 10);
+ if (*tok == '\0' || *etok != '\0')
+ goto backout;
+ break;
+ case 3:
+ /* minor version extension */
+ minor = strtol(tok, &etok, 10);
+ if (*tok == '\0' || *etok != '\0')
+ goto backout;
+ break;
+ /* if we get here, it must be weird */
+ default:
+ goto backout;
+ }
+ }
+ if (realname == NULL)
+ goto backout;
+ cp = (char *)sodp->sod_name;
+ sodp->sod_name = (long)strdup(realname);
+ free(cp);
+ sodp->sod_library = 1;
+ sodp->sod_major = major;
+ sodp->sod_minor = minor;
+ return;
+
+backout:
+ free((char *)sodp->sod_name);
+ sodp->sod_name = (long)strdup(name);
+}
+
+static struct hints_header *hheader = NULL;
+static struct hints_bucket *hbuckets;
+static char *hstrtab;
+char *_dl_hint_search_path = NULL;
+
+#define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1)
+
+void
+_dl_maphints(void)
+{
+ struct stat sb;
+ caddr_t addr = MAP_FAILED;
+ long hsize = 0;
+ int hfd;
+
+ if ((hfd = open(_PATH_LD_HINTS, O_RDONLY)) < 0)
+ goto bad_hints;
+
+ if (fstat(hfd, &sb) != 0 || !S_ISREG(sb.st_mode) ||
+ sb.st_size < sizeof(struct hints_header) || sb.st_size > LONG_MAX)
+ goto bad_hints;
+
+ hsize = (long)sb.st_size;
+ addr = (void *)mmap(0, hsize, PROT_READ, MAP_PRIVATE, hfd, 0);
+ if (addr == MAP_FAILED)
+ goto bad_hints;
+
+ hheader = (struct hints_header *)addr;
+ if (HH_BADMAG(*hheader) || hheader->hh_ehints > hsize)
+ goto bad_hints;
+
+ if (hheader->hh_version != LD_HINTS_VERSION_1 &&
+ hheader->hh_version != LD_HINTS_VERSION_2)
+ goto bad_hints;
+
+ hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab);
+ hstrtab = (char *)(addr + hheader->hh_strtab);
+ if (hheader->hh_version >= LD_HINTS_VERSION_2)
+ _dl_hint_search_path = hstrtab + hheader->hh_dirlist;
+
+ /* close the file descriptor, leaving the hints mapped */
+ close(hfd);
+
+ return;
+
+bad_hints:
+ if (addr != MAP_FAILED)
+ munmap(addr, hsize);
+ if (hfd != -1)
+ close(hfd);
+ hheader = (struct hints_header *)-1;
+}
+
+char *
+_dl_findhint(char *name, int major, int minor, char *preferred_path)
+{
+ struct hints_bucket *bp;
+
+ /*
+ * If not mapped, and we have not tried before, try to map the
+ * hints, if previous attempts failed hheader is -1 and we
+ * do not wish to retry it.
+ */
+ if (hheader == NULL)
+ _dl_maphints();
+
+ /* if it failed to map, return failure */
+ if (!(HINTS_VALID))
+ return NULL;
+
+ bp = hbuckets + (_dl_hinthash(name, major, minor) % hheader->hh_nbucket);
+
+ while (1) {
+ /* Sanity check */
+ if (bp->hi_namex >= hheader->hh_strtab_sz) {
+ printf("Bad name index: %#x\n", bp->hi_namex);
+ exit(7);
+ break;
+ }
+ if (bp->hi_pathx >= hheader->hh_strtab_sz) {
+ printf("Bad path index: %#x\n", bp->hi_pathx);
+ exit(7);
+ break;
+ }
+
+ if (strcmp(name, hstrtab + bp->hi_namex) == 0) {
+ /* It's `name', check version numbers */
+ if (bp->hi_major == major &&
+ (bp->hi_ndewey < 2 || bp->hi_minor >= minor)) {
+ if (preferred_path == NULL) {
+ return hstrtab + bp->hi_pathx;
+ } else {
+ char *path = hstrtab + bp->hi_pathx;
+ char *edir = strrchr(path, '/');
+
+ if ((strncmp(preferred_path, path,
+ (edir - path)) == 0) &&
+ (preferred_path[edir - path] == '\0'))
+ return path;
+ }
+ }
+ }
+
+ if (bp->hi_next == -1)
+ break;
+
+ /* Move on to next in bucket */
+ bp = &hbuckets[bp->hi_next];
+ }
+
+ /* No hints available for name */
+ return NULL;
+}
+
+int
+_dl_hinthash(char *cp, int vmajor, int vminor)
+{
+ int k = 0;
+
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
+ if (hheader->hh_version == LD_HINTS_VERSION_1)
+ k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
+
+ return k;
+}