diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 2006-05-12 23:20:54 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 2006-05-12 23:20:54 +0000 |
commit | 2b0781cc11cefa28d450d3cc30901ec7ae040340 (patch) | |
tree | 14fe29ed6d31d9dee0f3dea9036cf51de00b6a26 /libexec/ld.so/ldconfig/dl_prebind.c | |
parent | 9588320eaa6af8d8d2023d04ebfea3ea7858acb6 (diff) |
move the prebind code into ldconfig as the -P option.
next we will want to enhance the verbose mode, fix error handling,
and change the prebind delete code to use the same directory handling
code
ok drahn
Diffstat (limited to 'libexec/ld.so/ldconfig/dl_prebind.c')
-rw-r--r-- | libexec/ld.so/ldconfig/dl_prebind.c | 619 |
1 files changed, 619 insertions, 0 deletions
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 */ |