diff options
Diffstat (limited to 'libexec/ld.so/ldconfig')
-rw-r--r-- | libexec/ld.so/ldconfig/Makefile | 4 | ||||
-rw-r--r-- | libexec/ld.so/ldconfig/debug.c | 158 | ||||
-rw-r--r-- | libexec/ld.so/ldconfig/dl_prebind.c | 619 | ||||
-rw-r--r-- | libexec/ld.so/ldconfig/etc.c | 16 | ||||
-rw-r--r-- | libexec/ld.so/ldconfig/ldconfig.c | 34 | ||||
-rw-r--r-- | libexec/ld.so/ldconfig/library.c | 342 | ||||
-rw-r--r-- | libexec/ld.so/ldconfig/prebind.8 | 81 | ||||
-rw-r--r-- | libexec/ld.so/ldconfig/prebind.c | 2164 | ||||
-rw-r--r-- | libexec/ld.so/ldconfig/prebind.h | 64 | ||||
-rw-r--r-- | libexec/ld.so/ldconfig/prebind_delete.c | 27 | ||||
-rw-r--r-- | libexec/ld.so/ldconfig/prebind_struct.h | 89 | ||||
-rw-r--r-- | libexec/ld.so/ldconfig/sod.c | 265 |
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; +} |