diff options
author | Dale Rahn <drahn@cvs.openbsd.org> | 2006-05-03 16:10:53 +0000 |
---|---|---|
committer | Dale Rahn <drahn@cvs.openbsd.org> | 2006-05-03 16:10:53 +0000 |
commit | ec4b8bbf8212a67706e8bf0d84a933147a5bf89c (patch) | |
tree | f06a750d1a5fbec05783cff2a3ba488c9e288a69 | |
parent | b1f9a206d3729c6275222429f2ec0529817fc0b3 (diff) |
prebind - how to prelink a binary without throwing security out the window
Prelink fixes the address of libraries making 'return to libc' attacks trival,
prebind uses a different method to achieve most of the same gains, however
without adding any security conerns.
Still under development, now in-tree.
37 files changed, 4172 insertions, 32 deletions
diff --git a/libexec/ld.so/Makefile b/libexec/ld.so/Makefile index 6e7139e0767..6f8d24692a9 100644 --- a/libexec/ld.so/Makefile +++ b/libexec/ld.so/Makefile @@ -1,10 +1,11 @@ -# $OpenBSD: Makefile,v 1.31 2006/05/03 15:48:16 drahn Exp $ +# $OpenBSD: Makefile,v 1.32 2006/05/03 16:10:51 drahn Exp $ -SUBDIR=ldconfig ldd +SUBDIR=ldconfig ldd prebind prebind_strip +#SUBDIR+=prebind prebind_strip VPATH=${.CURDIR}/../../lib/libc/string SRCS= ldasm.S loader.c resolve.c dlfcn.c dl_printf.c rtld_machine.c -SRCS+= util.c sod.c strsep.c strtol.c dir.c library_subr.c +SRCS+= util.c sod.c strsep.c strtol.c dir.c library_subr.c dl_prebind.c .if (${MACHINE_ARCH} == "i386") SRCS+= library_mquery.c .else diff --git a/libexec/ld.so/alpha/ldasm.S b/libexec/ld.so/alpha/ldasm.S index e7f0c1b6df8..7de78da3fd0 100644 --- a/libexec/ld.so/alpha/ldasm.S +++ b/libexec/ld.so/alpha/ldasm.S @@ -1,4 +1,4 @@ -/* $OpenBSD: ldasm.S,v 1.16 2004/05/25 15:56:18 deraadt Exp $ */ +/* $OpenBSD: ldasm.S,v 1.17 2006/05/03 16:10:51 drahn Exp $ */ /* * Copyright (c) 2001 Niklas Hallqvist @@ -306,3 +306,10 @@ LEAF_NOPROFILE(_dl_sysctl, 4) call_pal PAL_OSF1_callsys RET END(_dl_sysctl) + +LEAF_NOPROFILE(_dl_gettimeofday, 2) + ldiq v0, SYS_gettimeofday + call_pal PAL_OSF1_callsys + RET +END(_dl_gettimeofday) + diff --git a/libexec/ld.so/alpha/syscall.h b/libexec/ld.so/alpha/syscall.h index 257cde79630..f1161d6c76d 100644 --- a/libexec/ld.so/alpha/syscall.h +++ b/libexec/ld.so/alpha/syscall.h @@ -1,4 +1,4 @@ -/* $OpenBSD: syscall.h,v 1.14 2004/05/25 15:56:18 deraadt Exp $ */ +/* $OpenBSD: syscall.h,v 1.15 2006/05/03 16:10:51 drahn Exp $ */ /* * Copyright (c) 2001 Niklas Hallqvist @@ -53,6 +53,7 @@ int _dl_getdirentries(int, char*, int, long *); long _dl__syscall(quad_t, ...); int _dl_sigprocmask(int, const sigset_t *, sigset_t *); int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t); +int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp); static inline off_t _dl_lseek(int fildes, off_t offset, int whence) diff --git a/libexec/ld.so/amd64/ldasm.S b/libexec/ld.so/amd64/ldasm.S index 6baa706e195..07fdf37fa91 100644 --- a/libexec/ld.so/amd64/ldasm.S +++ b/libexec/ld.so/amd64/ldasm.S @@ -1,4 +1,4 @@ -/* $OpenBSD: ldasm.S,v 1.5 2005/05/04 03:59:24 drahn Exp $ */ +/* $OpenBSD: ldasm.S,v 1.6 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 2002,2004 Dale Rahn @@ -89,6 +89,7 @@ DL_SYSCALL(issetugid) DL_SYSCALL(getdirentries) DL_SYSCALL(mprotect) DL_SYSCALL(munmap) +DL_SYSCALL(gettimeofday) DL_SYSCALL(exit) DL_SYSCALL2(_syscall,__syscall) DL_SYSCALL2(sysctl,__sysctl) diff --git a/libexec/ld.so/amd64/syscall.h b/libexec/ld.so/amd64/syscall.h index 0656c00f685..0f6a73181b0 100644 --- a/libexec/ld.so/amd64/syscall.h +++ b/libexec/ld.so/amd64/syscall.h @@ -1,4 +1,4 @@ -/* $OpenBSD: syscall.h,v 1.2 2004/02/23 20:52:05 deraadt Exp $ */ +/* $OpenBSD: syscall.h,v 1.3 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 2001 Niklas Hallqvist @@ -53,6 +53,7 @@ int _dl_fcntl(int, int, ...); int _dl_getdirentries(int, char*, int, long *); int _dl_sigprocmask(int, const sigset_t *, sigset_t *); int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t); +int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp); static inline off_t _dl_lseek(int fildes, off_t offset, int whence) diff --git a/libexec/ld.so/arm/ldasm.S b/libexec/ld.so/arm/ldasm.S index 44b2fa24c3b..6ac18075b04 100644 --- a/libexec/ld.so/arm/ldasm.S +++ b/libexec/ld.so/arm/ldasm.S @@ -1,4 +1,4 @@ -/* $OpenBSD: ldasm.S,v 1.5 2005/10/23 06:04:03 drahn Exp $ */ +/* $OpenBSD: ldasm.S,v 1.6 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 2004 Dale Rahn @@ -112,6 +112,7 @@ DL_SYSCALL(write) DL_SYSCALL(stat) DL_SYSCALL(fstat) DL_SYSCALL(fcntl) +DL_SYSCALL(gettimeofday) DL_SYSCALL2(sysctl,__sysctl) DL_SYSCALL(getdirentries) diff --git a/libexec/ld.so/arm/syscall.h b/libexec/ld.so/arm/syscall.h index b654f54dd84..74cd62d828f 100644 --- a/libexec/ld.so/arm/syscall.h +++ b/libexec/ld.so/arm/syscall.h @@ -1,4 +1,4 @@ -/* $OpenBSD: syscall.h,v 1.2 2004/05/25 15:56:18 deraadt Exp $ */ +/* $OpenBSD: syscall.h,v 1.3 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 2001 Niklas Hallqvist @@ -53,6 +53,7 @@ int _dl_fcntl(int, int, ...); int _dl_getdirentries(int, char*, int, long *); int _dl_sigprocmask(int, const sigset_t *, sigset_t *); int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t); +int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp); static inline off_t _dl_lseek(int fildes, off_t offset, int whence) diff --git a/libexec/ld.so/dl_prebind.c b/libexec/ld.so/dl_prebind.c new file mode 100644 index 00000000000..eacd219d1a1 --- /dev/null +++ b/libexec/ld.so/dl_prebind.c @@ -0,0 +1,629 @@ +/* $OpenBSD: dl_prebind.c,v 1.1 2006/05/03 16:10:51 drahn Exp $ */ +/* + * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/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; + u_int32_t offset; + char *c; + struct prebind_footer *footer; + + 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; + void *prebind_data; + struct nameidx *nameidx; + char *nametab; + int idx; + ssize_t len; + + 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) +{ + void *prebind_map; + struct symcachetab *symcachetab; + struct nameidx *nameidx; + u_int32_t *fixupidx; + char *nametab; + struct fixup *fixup; + u_int32_t *fixupcnt; + u_int32_t *libmap; + u_int32_t *idxtolib; + elf_object_t *obj; + struct prebind_footer *footer; + char *c; + int i = 0; + int cur_obj = -1; + int idx; + u_int32_t *poffset; + u_int32_t offset; + u_int32_t symcache_cnt; + + 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 (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 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->targobj_idx, + f->sym_idx, f->flags)); +#endif + tobj = objarray[f->targobj_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->targobj_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->targobj_idx, + f->sym_idx, + SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt)); +#endif + tobj = objarray[f->targobj_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->targobj_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; + } +} + +int validate_errs; + +struct timeval beforetp; +void +_dl_prebind_pre_resolve() +{ + elf_object_t *object; + struct nameidx *nameidx; + char *nametab; + int idx; + char *name; + struct prebind_footer *footer; + + 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; + const char *symn; + const elf_object_t *sobj; + Elf_Addr ret; + const Elf_Sym **this; + + /* Dont 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) +{ + int i; + struct symcachetab *s; + _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) +{ + int i; + struct nameidx *n; + _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) +{ + int i; + struct fixup *f; + _dl_printf("fixup: %d\n", numfixups); + for (i = 0; i < numfixups; i++) { + f = &(fixup[i]); + + _dl_printf("idx %d targobj %d sym idx %d\n", + f->sym, f->targobj_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) +{ + int i; + char *id; + struct symcachetab *symcachetab; + struct nameidx *nameidx; + void *prebind_map; + struct prebind_footer *footer; + u_int32_t *fixupidx; + char *nametab; + struct fixup *fixup; + u_int32_t *fixupcnt; + u_int32_t *libmap; + + 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/dl_prebind.h b/libexec/ld.so/dl_prebind.h new file mode 100644 index 00000000000..ae1b877568a --- /dev/null +++ b/libexec/ld.so/dl_prebind.h @@ -0,0 +1,36 @@ +/* $OpenBSD: dl_prebind.h,v 1.1 2006/05/03 16:10:51 drahn Exp $ */ +/* + * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/exec_elf.h> +#include "resolve.h" +#include "prebind.h" +extern char *_dl_noprebind; +extern char *_dl_prebind_validate; +void _dl_prebind_pre_resolve(void); +void _dl_prebind_post_resolve(void); +void *prebind_load_fd(int fd, const char *name); +void prebind_load_exe(Elf_Phdr *phdp, elf_object_t *exe_obj); + +void +prebind_validate(elf_object_t *req_obj, unsigned int symidx, int flags, + const Elf_Sym *ref_sym); +extern char *_dl_prebind_validate; /* XXX */ + +void prebind_symcache(elf_object_t *object, int pltflag); +void prebind_free(elf_object_t *object); + +extern struct prebind_footer *footer; diff --git a/libexec/ld.so/hppa/ldasm.S b/libexec/ld.so/hppa/ldasm.S index b7f59ab7937..6f5f6e6446c 100644 --- a/libexec/ld.so/hppa/ldasm.S +++ b/libexec/ld.so/hppa/ldasm.S @@ -1,4 +1,4 @@ -/* $OpenBSD: ldasm.S,v 1.3 2005/01/09 17:57:40 mickey Exp $ */ +/* $OpenBSD: ldasm.S,v 1.4 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 2004 Michael Shalayeff @@ -255,6 +255,12 @@ ENTRY(_dl_getdirentries,0) nop EXIT(_dl_getdirentries) +ENTRY(_dl_gettimeofday,0) + SYSCALL(gettimeofday) + bv r0(rp) + nop +EXIT(_dl_gettimeofday) + ENTRY(_dl_sigprocmask,0) stw arg2, HPPA_FRAME_ARG(2)(sp) diff --git a/libexec/ld.so/hppa/rtld_machine.c b/libexec/ld.so/hppa/rtld_machine.c index 5971c2cfb75..3bead5d3e9e 100644 --- a/libexec/ld.so/hppa/rtld_machine.c +++ b/libexec/ld.so/hppa/rtld_machine.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtld_machine.c,v 1.11 2005/09/22 04:07:11 deraadt Exp $ */ +/* $OpenBSD: rtld_machine.c,v 1.12 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 2004 Michael Shalayeff @@ -105,8 +105,10 @@ _dl_md_reloc(elf_object_t *object, int rel, int relasz) numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA); rela = (Elf_RelA *)(object->Dyn.info[rel]); +#ifdef DEBUG DL_DEB(("object %s relasz %x, numrela %x loff %x\n", object->load_name, object->Dyn.info[relasz], numrela, loff)); +#endif if (rela == NULL) return (0); @@ -118,18 +120,22 @@ _dl_md_reloc(elf_object_t *object, int rel, int relasz) if (object->dyn.init && !((Elf_Addr)object->dyn.init & 2)) { Elf_Addr addr = _dl_md_plabel((Elf_Addr)object->dyn.init, object->dyn.pltgot); +#ifdef DEBUG DL_DEB(("PLABEL32: %p:%p(_init) -> 0x%x in %s\n", object->dyn.init, object->dyn.pltgot, addr, object->load_name)); +#endif object->dyn.init = (void *)addr; } if (object->dyn.fini && !((Elf_Addr)object->dyn.fini & 2)) { Elf_Addr addr = _dl_md_plabel((Elf_Addr)object->dyn.fini, object->dyn.pltgot); +#ifdef DEBUG DL_DEB(("PLABEL32: %p:%p(_fini) -> 0x%x in %s\n", object->dyn.fini, object->dyn.pltgot, addr, object->load_name)); +#endif object->dyn.fini = (void *)addr; } @@ -163,7 +169,7 @@ _dl_md_reloc(elf_object_t *object, int rel, int relasz) ooff = _dl_find_symbol_bysym(object, ELF_R_SYM(rela->r_info), &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND| - ((type == RELOC_DIR32) ? SYM_NOTPLT : SYM_PLT), + ((type == RELOC_IPLT) ? SYM_PLT: SYM_NOTPLT), sym, &sobj); if (this == NULL) { if (ELF_ST_BIND(sym->st_info) != STB_WEAK) @@ -172,16 +178,20 @@ _dl_md_reloc(elf_object_t *object, int rel, int relasz) } } +#ifdef DEBUG DL_DEB(("*pt=%x r_addend=%x r_sym=%x\n", *pt, rela->r_addend, ELF_R_SYM(rela->r_info))); +#endif switch (type) { case RELOC_DIR32: if (ELF_R_SYM(rela->r_info) && sym->st_name) { *pt = ooff + this->st_value + rela->r_addend; +#ifdef DEBUG DL_DEB(("[%x]DIR32: %s:%s -> 0x%x in %s\n", i, symn, object->load_name, *pt, sobj->load_name)); +#endif } else { /* * XXX should objects ever get their @@ -194,8 +204,10 @@ _dl_md_reloc(elf_object_t *object, int rel, int relasz) *pt += loff; else *pt += loff + rela->r_addend; +#ifdef DEBUG DL_DEB(("[%x]DIR32: %s @ 0x%x\n", i, object->load_name, *pt)); +#endif } break; @@ -208,13 +220,17 @@ _dl_md_reloc(elf_object_t *object, int rel, int relasz) *pt = _dl_md_plabel(sobj->load_offs + this->st_value + rela->r_addend, sobj->dyn.pltgot); +#ifdef DEBUG DL_DEB(("[%x]PLABEL32: %s:%s -> 0x%x in %s\n", i, symn, object->load_name, *pt, sobj->load_name)); +#endif } else { *pt = loff + rela->r_addend; +#ifdef DEBUG DL_DEB(("[%x]PLABEL32: %s @ 0x%x\n", i, object->load_name, *pt)); +#endif } break; @@ -222,14 +238,18 @@ _dl_md_reloc(elf_object_t *object, int rel, int relasz) if (ELF_R_SYM(rela->r_info)) { pt[0] = ooff + this->st_value + rela->r_addend; pt[1] = (Elf_Addr)sobj->dyn.pltgot; +#ifdef DEBUG DL_DEB(("[%x]IPLT: %s:%s -> 0x%x:0x%x in %s\n", i, symn, object->load_name, pt[0], pt[1], sobj->load_name)); +#endif } else { pt[0] = loff + rela->r_addend; pt[1] = (Elf_Addr)object->dyn.pltgot; +#ifdef DEBUG DL_DEB(("[%x]IPLT: %s @ 0x%x:0x%x\n", i, object->load_name, pt[0], pt[1])); +#endif } break; @@ -243,10 +263,12 @@ _dl_md_reloc(elf_object_t *object, int rel, int relasz) if (cpysrc) { _dl_bcopy((void *)(ooff + cpysrc->st_value), pt, sym->st_size); +#ifdef DEBUG DL_DEB(("[%x]COPY: %s[%x]:%s -> %p[%x] in %s\n", i, symn, ooff + cpysrc->st_value, object->load_name, pt, sym->st_size, sobj->load_name)); +#endif } else DL_DEB(("[%x]COPY: no sym\n", i)); break; diff --git a/libexec/ld.so/hppa/syscall.h b/libexec/ld.so/hppa/syscall.h index af725eeea13..0f6a73181b0 100644 --- a/libexec/ld.so/hppa/syscall.h +++ b/libexec/ld.so/hppa/syscall.h @@ -1,4 +1,4 @@ -/* $OpenBSD: syscall.h,v 1.2 2004/05/25 21:56:49 deraadt Exp $ */ +/* $OpenBSD: syscall.h,v 1.3 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 2001 Niklas Hallqvist @@ -53,6 +53,7 @@ int _dl_fcntl(int, int, ...); int _dl_getdirentries(int, char*, int, long *); int _dl_sigprocmask(int, const sigset_t *, sigset_t *); int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t); +int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp); static inline off_t _dl_lseek(int fildes, off_t offset, int whence) diff --git a/libexec/ld.so/i386/ldasm.S b/libexec/ld.so/i386/ldasm.S index e177457f8be..b5248e33c77 100644 --- a/libexec/ld.so/i386/ldasm.S +++ b/libexec/ld.so/i386/ldasm.S @@ -1,4 +1,4 @@ -/* $OpenBSD: ldasm.S,v 1.9 2006/05/03 15:37:50 drahn Exp $ */ +/* $OpenBSD: ldasm.S,v 1.10 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 2002 Dale Rahn @@ -117,6 +117,7 @@ DL_SYSCALL(write) DL_SYSCALL(stat) DL_SYSCALL(fstat) DL_SYSCALL(fcntl) +DL_SYSCALL(gettimeofday) DL_SYSCALL2(sysctl,__sysctl) DL_SYSCALL(getdirentries) diff --git a/libexec/ld.so/i386/syscall.h b/libexec/ld.so/i386/syscall.h index f047970172c..2d2f36b7b9d 100644 --- a/libexec/ld.so/i386/syscall.h +++ b/libexec/ld.so/i386/syscall.h @@ -1,4 +1,4 @@ -/* $OpenBSD: syscall.h,v 1.6 2004/05/25 15:56:18 deraadt Exp $ */ +/* $OpenBSD: syscall.h,v 1.7 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 2001 Niklas Hallqvist @@ -53,6 +53,7 @@ int _dl_fcntl(int, int, ...); int _dl_getdirentries(int, char*, int, long *); int _dl_sigprocmask(int, const sigset_t *, sigset_t *); int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t); +int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp); static inline off_t _dl_lseek(int fildes, off_t offset, int whence) diff --git a/libexec/ld.so/ld.so.1 b/libexec/ld.so/ld.so.1 index 66daec4ea8b..51b8d4dca08 100644 --- a/libexec/ld.so/ld.so.1 +++ b/libexec/ld.so/ld.so.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ld.so.1,v 1.8 2005/08/16 12:01:43 tom Exp $ +.\" $OpenBSD: ld.so.1,v 1.9 2006/05/03 16:10:51 drahn Exp $ .\" $NetBSD: rtld.1,v 1.2 1995/10/08 23:43:28 pk Exp $ .\" .\" Copyright (c) 1995 Paul Kranenburg @@ -181,6 +181,14 @@ non-standard filesystem layout. When set, be verbose about what .Nm does. +.Pp +.It Ev LD_NOPREBIND= +When set, ignore any prebind data associated with the program or libraries. +.Pp +.It Ev LD_PREBINDVALIDATE= +When set, perform symbol relocation without of the given binary +and the associated libraries, compare the results against the prebind +values, then exit. .El .Sh FILES .Bl -tag -width /var/run/ld.so.hintsXXX -compact diff --git a/libexec/ld.so/library.c b/libexec/ld.so/library.c index 7c948adae24..66774882feb 100644 --- a/libexec/ld.so/library.c +++ b/libexec/ld.so/library.c @@ -1,4 +1,4 @@ -/* $OpenBSD: library.c,v 1.52 2005/11/09 16:41:29 kurt Exp $ */ +/* $OpenBSD: library.c,v 1.53 2006/05/03 16:10:51 drahn Exp $ */ /* * Copyright (c) 2002 Dale Rahn @@ -33,6 +33,7 @@ #include <sys/param.h> #include <fcntl.h> #include <sys/mman.h> +#include "dl_prebind.h" #include "syscall.h" #include "archdep.h" @@ -73,7 +74,6 @@ _dl_unload_shlib(elf_object_t *object) } } - elf_object_t * _dl_tryload_shlib(const char *libname, int type, int flags) { @@ -87,6 +87,7 @@ _dl_tryload_shlib(const char *libname, int type, int flags) Elf_Ehdr *ehdr; Elf_Phdr *phdp; struct stat sb; + void *prebind_data; #define ROUND_PG(x) (((x) + align) & ~(align)) #define TRUNC_PG(x) ((x) & ~(align)) @@ -236,11 +237,15 @@ _dl_tryload_shlib(const char *libname, int type, int flags) } } } + + prebind_data = prebind_load_fd(libfile, libname); + _dl_close(libfile); dynp = (Elf_Dyn *)((unsigned long)dynp + loff); object = _dl_finalize_object(libname, dynp, 0, type, libaddr, loff); if (object) { + object->prebind_data = prebind_data; object->load_size = maxva - minva; /*XXX*/ object->load_list = load_list; /* set inode, dev from stat info */ diff --git a/libexec/ld.so/library_mquery.c b/libexec/ld.so/library_mquery.c index fa38340b8f8..93edfe12ecd 100644 --- a/libexec/ld.so/library_mquery.c +++ b/libexec/ld.so/library_mquery.c @@ -1,4 +1,4 @@ -/* $OpenBSD: library_mquery.c,v 1.32 2005/11/09 16:41:29 kurt Exp $ */ +/* $OpenBSD: library_mquery.c,v 1.33 2006/05/03 16:10:51 drahn Exp $ */ /* * Copyright (c) 2002 Dale Rahn @@ -33,6 +33,7 @@ #include <sys/param.h> #include <fcntl.h> #include <sys/mman.h> +#include "dl_prebind.h" #include "syscall.h" #include "archdep.h" @@ -92,6 +93,7 @@ _dl_tryload_shlib(const char *libname, int type, int flags) int size; Elf_Addr load_end = 0; struct stat sb; + void *prebind_data; #define ROUND_PG(x) (((x) + align) & ~(align)) #define TRUNC_PG(x) ((x) & ~(align)) @@ -273,12 +275,16 @@ retry: _dl_pagesz - (ld->size & align)); load_end = (Elf_Addr)ld->start + ROUND_PG(ld->size); } + + prebind_data = prebind_load_fd(libfile, libname); + _dl_close(libfile); dynp = (Elf_Dyn *)((unsigned long)dynp + LOFF); object = _dl_finalize_object(libname, dynp, 0, type, (Elf_Addr)lowld->start, LOFF); if (object) { + object->prebind_data = prebind_data; object->load_size = (Elf_Addr)load_end - (Elf_Addr)lowld->start; object->load_list = lowld; /* set inode, dev from stat info */ diff --git a/libexec/ld.so/loader.c b/libexec/ld.so/loader.c index 31a28fbd10c..ff23ca1602b 100644 --- a/libexec/ld.so/loader.c +++ b/libexec/ld.so/loader.c @@ -1,4 +1,4 @@ -/* $OpenBSD: loader.c,v 1.100 2005/11/09 16:41:29 kurt Exp $ */ +/* $OpenBSD: loader.c,v 1.101 2006/05/03 16:10:51 drahn Exp $ */ /* * Copyright (c) 1998 Per Fogelstrom, Opsycon AB @@ -43,6 +43,7 @@ #include "resolve.h" #include "sod.h" #include "stdlib.h" +#include "dl_prebind.h" /* * Local decls. @@ -66,6 +67,8 @@ char *_dl_traceld; char *_dl_debug; char *_dl_showmap; char *_dl_norandom; +char *_dl_noprebind; +char *_dl_prebind_validate; struct r_debug *_dl_debug_map; @@ -204,6 +207,8 @@ _dl_setup_env(char **envp) _dl_traceld = _dl_getenv("LD_TRACE_LOADED_OBJECTS", envp); _dl_debug = _dl_getenv("LD_DEBUG", envp); _dl_norandom = _dl_getenv("LD_NORANDOM", envp); + _dl_noprebind = _dl_getenv("LD_NOPREBIND", envp); + _dl_prebind_validate = _dl_getenv("LD_PREBINDVALIDATE", envp); /* * Don't allow someone to change the search paths if he runs @@ -332,6 +337,8 @@ _dl_load_dep_libs(elf_object_t *object, int flags, int booting) return(0); } + + /* * This is the dynamic loader entrypoint. When entering here, depending * on architecture type, the stack and registers are set up according @@ -405,6 +412,10 @@ _dl_boot(const char **argv, char **envp, const long loff, long *dl_data) _dl_add_object(exe_obj); } else if (phdp->p_type == PT_INTERP) { us = _dl_strdup((char *)phdp->p_vaddr); + } else if ((phdp->p_type == PT_LOAD) && + (phdp->p_flags & 0x08000000)) { +// dump_prelink(phdp->p_vaddr, phdp->p_memsz); + prebind_load_exe(phdp, exe_obj); } phdp++; } @@ -441,10 +452,14 @@ _dl_boot(const char **argv, char **envp, const long loff, long *dl_data) * Everything should be in place now for doing the relocation * and binding. Call _dl_rtld to do the job. Fingers crossed. */ + + _dl_prebind_pre_resolve(); failed = 0; if (_dl_traceld == NULL) failed = _dl_rtld(_dl_objects); + _dl_prebind_post_resolve(); + if (_dl_debug || _dl_traceld) _dl_show_objects(); @@ -738,14 +753,14 @@ _dl_rtld(elf_object_t *object) sz = 0; if (object->nchains < DL_SM_SYMBUF_CNT) { _dl_symcache = _dl_sm_symcache_buffer; - DL_DEB(("using static buffer for %d entries\n", - object->nchains)); +// DL_DEB(("using static buffer for %d entries\n", +// object->nchains)); _dl_memset(_dl_symcache, 0, sizeof (sym_cache) * object->nchains); } else { sz = ELF_ROUND(sizeof (sym_cache) * object->nchains, _dl_pagesz); - DL_DEB(("allocating symcache sz %x with mmap\n", sz)); +// DL_DEB(("allocating symcache sz %x with mmap\n", sz)); _dl_symcache = (void *)_dl_mmap(0, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); @@ -754,11 +769,14 @@ _dl_rtld(elf_object_t *object) _dl_symcache = NULL; } } + prebind_symcache(object, SYM_NOTPLT); + /* * Do relocation information first, then GOT. */ fails =_dl_md_reloc(object, DT_REL, DT_RELSZ); fails += _dl_md_reloc(object, DT_RELA, DT_RELASZ); + prebind_symcache(object, SYM_PLT); _dl_md_reloc_got(object, !(_dl_bindnow || object->obj_flags & RTLD_NOW)); @@ -772,7 +790,6 @@ _dl_rtld(elf_object_t *object) return (fails); } - void _dl_call_init(elf_object_t *object) { diff --git a/libexec/ld.so/mips64/syscall.h b/libexec/ld.so/mips64/syscall.h index 13bace0ca9d..9be5ed1c2ef 100644 --- a/libexec/ld.so/mips64/syscall.h +++ b/libexec/ld.so/mips64/syscall.h @@ -1,4 +1,4 @@ -/* $OpenBSD: syscall.h,v 1.3 2005/09/22 04:07:11 deraadt Exp $ */ +/* $OpenBSD: syscall.h,v 1.4 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 1998-2002 Opsycon AB, Sweden. @@ -337,5 +337,24 @@ _dl_sysctl(int *name, u_int namelen, void *oldp, size_t *oldplen, void *newp, "$10","$11","$12","$13","$14","$15","$24","$25"); return status; } +extern inline int +_dl_gettimeofday(struct timeval* tp, struct timezone *tzp) +{ + register int status __asm__ ("$2"); + + __asm__ volatile ( + "move $4,%2\n\t" + "move $5,%3\n\t" + "li $2,%1\n\t" + "syscall\n\t" + "beq $7,$0,1f\n\t" + "li $2,-1\n\t" + "1:" + : "=r" (status) + : "I" (SYS_gettimeofday), "r" (tp), "r" (tzp) + : "$3", "$4", "$5", "$6", "$7", "$8", "$9", + "$10","$11","$12","$13","$14","$15","$24","$25"); + return status; +} #endif /*__DL_SYSCALL_H__*/ diff --git a/libexec/ld.so/powerpc/syscall.h b/libexec/ld.so/powerpc/syscall.h index 8a0ce9168e2..29878f3a988 100644 --- a/libexec/ld.so/powerpc/syscall.h +++ b/libexec/ld.so/powerpc/syscall.h @@ -1,4 +1,4 @@ -/* $OpenBSD: syscall.h,v 1.19 2004/01/12 02:21:21 drahn Exp $ */ +/* $OpenBSD: syscall.h,v 1.20 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 1998 Per Fogelstrom, Opsycon AB @@ -32,6 +32,7 @@ #include <sys/syscall.h> #include <sys/signal.h> +#include <sys/time.h> static off_t _dl_lseek(int, off_t, int); @@ -352,5 +353,26 @@ _dl_sysctl(int *name, u_int namelen, void *oldp, size_t *oldplen, void *newp, : "0", "3", "4", "5", "6", "7", "8"); return status; } +static inline int +_dl_gettimeofday(struct timeval *tp, struct timezone *tzp) +{ + register int status; + + __asm__ volatile ("li 0,%1\n\t" + "mr 3,%2\n\t" + "mr 4,%3\n\t" + "sc\n\t" + "cmpwi 0, 0\n\t" + "beq 1f\n\t" + "li 3,-1\n\t" + "1:" + "mr %0,3\n\t" + : "=r" (status) + : "I" (SYS_gettimeofday), "r" (tp), "r" (tzp) + : "0", "3", "4" ); + return status; +} + #endif /*__DL_SYSCALL_H__*/ + diff --git a/libexec/ld.so/prebind.h b/libexec/ld.so/prebind.h new file mode 100644 index 00000000000..6f9dd6228cf --- /dev/null +++ b/libexec/ld.so/prebind.h @@ -0,0 +1,64 @@ +/* $OpenBSD: prebind.h,v 1.1 2006/05/03 16:10:51 drahn Exp $ */ +/* + * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#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 targobj_idx; + u_int32_t sym_idx; +}; diff --git a/libexec/ld.so/prebind/Makefile b/libexec/ld.so/prebind/Makefile new file mode 100644 index 00000000000..3eadbabc08f --- /dev/null +++ b/libexec/ld.so/prebind/Makefile @@ -0,0 +1,18 @@ +# $OpenBSD: Makefile,v 1.1 2006/05/03 16:10:52 drahn Exp $ + +SRCS= prebind.c sod.c debug.c objarray.c +.if (${MACHINE} == "mips64") +NOPROG= +.else +PROG= prebind +.endif +MAN= prebind.8 + +BINDIR= /usr/sbin + +CFLAGS += -Wall -ggdb +CFLAGS += -I${.CURDIR}/.. + +BINDIR= /usr/sbin + +.include <bsd.prog.mk> diff --git a/libexec/ld.so/prebind/debug.c b/libexec/ld.so/prebind/debug.c new file mode 100644 index 00000000000..413bdb75ebe --- /dev/null +++ b/libexec/ld.so/prebind/debug.c @@ -0,0 +1,154 @@ +/* $OpenBSD: debug.c,v 1.1 2006/05/03 16:10:52 drahn Exp $ */ +/* + * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/syslimits.h> +#include <sys/param.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <nlist.h> +#include <elf_abi.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <dirent.h> +#include "resolve.h" +#include "link.h" +#include "sod.h" +#include "machine/reloc.h" +#include "prebind.h" +#include "prebind_struct.h" + +#ifdef DEBUG1 +void +dump_info(struct elf_object *object) +{ + int i; + const Elf_Sym *symt; + const char *strt; + int numrel; + int numrela; + Elf_Word *needed_list; + + symt = object->dyn.symtab; + strt = object->dyn.strtab; + + for (i = 0; i < object->nchains; i++) { + char * type; + const Elf_Sym *sym = symt + i; + + 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 %d\n", footer->id0); + printf("id1 %d\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/prebind/objarray.c b/libexec/ld.so/prebind/objarray.c new file mode 100644 index 00000000000..1c4f45f751c --- /dev/null +++ b/libexec/ld.so/prebind/objarray.c @@ -0,0 +1,1073 @@ +/* $OpenBSD: objarray.c,v 1.1 2006/05/03 16:10:52 drahn Exp $ */ +/* + * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/syslimits.h> +#include <sys/param.h> +#include <sys/mman.h> +#include <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" +#include "machine/reloc.h" +#include "prebind.h" +#include "prebind_struct.h" + +struct objarray_list { + struct elf_object *obj; + struct symcache_noflag *symcache; + struct symcache_noflag *pltsymcache; + 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 elf_load_existing_prebind(struct elf_object *object, int fd); + +void +elf_add_object_curbin_list(struct elf_object *object) +{ + struct objlist *ol; + ol = malloc(sizeof (struct objlist)); + ol->object = object; + TAILQ_INSERT_TAIL(&(curbin->curbin_list), ol, list); + ol->load_prog = load_object; + +#if 0 + printf("adding object %s %d with prog %s\n", object->load_name, + object->dyn.null, load_object->load_name); +#endif + TAILQ_INSERT_TAIL(&(objarray[object->dyn.null].inst_list), ol, inst_list); +} +void +elf_init_objarray(void) +{ + objarray_sz = 512; + objarray = malloc(sizeof (objarray[0]) * objarray_sz); +} + +void +elf_sum_reloc() +{ + int i, numobjs; + int err = 0; + struct objlist *ol; + struct proglist *pl; + + for (i = 0; i < objarray_cnt; i++) { +#if 0 + printf("%3d: %d obj %s\n", i, objarray[i].obj->dyn.null, + objarray[i].obj->load_name); +#endif + if (TAILQ_EMPTY(&objarray[i].inst_list)) { + printf("skipping %s\n", objarray[i].obj->load_name); + continue; + } + if (objarray[i].oprebind_data != NULL) { + copy_oldsymcache(i); + continue; + } + objarray[i].symcache = calloc(sizeof(struct symcache_noflag), + objarray[i].obj->nchains); + objarray[i].pltsymcache = calloc(sizeof(struct symcache_noflag), + objarray[i].obj->nchains); + + if (objarray[i].symcache == NULL || + objarray[i].pltsymcache == NULL) { + printf("out of memory allocating symcache\n"); + exit (20); + } + + TAILQ_FOREACH(ol, &(objarray[i].inst_list), + inst_list) { + +#if 0 + printf("\tprog %d %s\n", ol->load_prog->dyn.null, + ol->load_prog->load_name); + printf("cache:\n"); +#endif + + elf_copy_syms(objarray[i].symcache, + ol->cache, + ol->object, + ol->load_prog, + ol->object->nchains); + + elf_copy_syms(objarray[i].pltsymcache, + ol->pltcache, + ol->object, + ol->load_prog, + ol->object->nchains); + } + } + TAILQ_FOREACH(ol, &library_list, list) { +#if 0 + printf("processing lib %s\n", ol->object->load_name); +#endif + 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; + pl->libmap = calloc(numobjs, sizeof (u_int32_t *)); + pl->fixup = calloc(2 * numobjs, sizeof (struct fixup *)); + pl->fixupcnt = calloc(2 * numobjs, sizeof (int)); + + numobjs = 0; + TAILQ_FOREACH(ol, &(pl->curbin_list), list) { + elf_calc_fixups(pl, ol, numobjs); + numobjs++; + } + } + TAILQ_FOREACH(pl, &prog_list, list) { + err += elf_prep_bin_prebind(pl); +#if 0 + printf("processing binary %s\n", + TAILQ_FIRST(&pl->curbin_list)->object->load_name); +#endif + } + if (err != 0) + printf("failures %d\n", err); +} + +int +elf_prep_lib_prebind(struct elf_object *object) +{ + int numlibs = 0; + int ret; + 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 = calloc(objarray_cnt, sizeof (int)); + idxtolib = calloc(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 = calloc(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 = calloc(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 = calloc(numlibs, sizeof (struct nameidx)); + nametab = malloc(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); + } +#if 0 + for (i = 0; i < numlibs; i++) { + printf("\tlib %s\n", &nametab[nameidx[i].name]); + } +#endif + + 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 = calloc(objarray_cnt, sizeof (int)); + idxtolib = calloc(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; +#if 0 + printf("obj :%d, idx %d %s\n", numlibs, ref_obj, ol->object->load_name); +#endif + numlibs++; + } + + /* do got */ + symcache_cnt = 0; + for (i = 0; i < object->nchains; i++) { + if (symcache[i].sym != NULL) + symcache_cnt++; + } + + symcachetab = calloc(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 = calloc(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++; + } + + nameidx = calloc(numlibs, sizeof (struct nameidx)); + nametab = malloc(nametablen); + + if (nameidx == NULL || nametab == NULL) + perror("buffers"); + + 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); + } +#if 0 + for (i = 0; i < numlibs; i++) { + printf("\tlib %s\n", &nametab[nameidx[i].name]); + } +#endif + pl->libmapcnt = calloc(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].targobj_idx = + libmap[pl->fixup[2*i][j].targobj_idx]; + } + for (j = 0; j < pl->fixupcnt[2*i+1]; j++) { + pl->fixup[2*i+1][j].targobj_idx = + libmap[pl->fixup[2*i+1][j].targobj_idx]; + } + + pl->libmapcnt[i] = objarray[idxtolib[i]].numlibs; + for (j = 0; j < objarray[idxtolib[i]].numlibs; j++) + pl->libmap[i][j] = libmap[pl->libmap[i][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) +{ + off_t base_offset; + struct prebind_footer footer; + struct stat ifstat; + int fd; + u_int32_t next_start; + u_int32_t *fixuptab = NULL; + u_int32_t *maptab = NULL; + u_int32_t footer_offset; + int 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); + 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 = calloc( 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 = calloc( 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: + 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\n", + object->load_name, + (long)(phdr[loadsection].p_vaddr)); + goto done; + } + + /* verify that extra slot is empty */ + bzero(&phdr[loadsection], sizeof(phdr_empty)); + + ehdr->e_phnum--; + +done: + munmap(buf, 8192); +} +void +elf_calc_fixups(struct proglist *pl, struct objlist *ol, int libidx) +{ + int i; + int numfixups; + int objidx; + int prog; + struct symcache_noflag *symcache; + struct elf_object *prog_obj; + struct elf_object *interp; + + objidx = ol->object->dyn.null, + + prog_obj = TAILQ_FIRST(&(pl->curbin_list))->object; + interp = prog_obj->load_object; + prog = prog_obj->dyn.null; + if (verbose > 3) + printf("fixup GOT %s\n", ol->object->load_name); + symcache = objarray[objidx].symcache; + + numfixups = 0; + for (i = 0; i < ol->object->nchains; i++) { + /* + * this assumes if the same object is found, the same + * symbol will be found as well + */ + if (ol->cache[i].obj != symcache[i].obj && + symcache[i].obj != interp) { + numfixups++; + } + } + pl->fixup[2*libidx] = calloc(numfixups, sizeof (struct fixup)); + + numfixups = 0; + for (i = 0; i < ol->object->nchains; i++) { + /* + * this assumes if the same object is found, the same + * symbol will be found as well + */ + if (ol->cache[i].obj != NULL && + ol->cache[i].obj != symcache[i].obj) { + struct fixup *f = &(pl->fixup[2*libidx][numfixups]); + f->sym = i; + f->targobj_idx = ol->cache[i].obj->dyn.null; + f->sym_idx = ol->cache[i].sym - + ol->cache[i].obj->dyn.symtab; + if (verbose > 3) { + printf("obj %d idx %d targobj %d, sym idx %d\n", + i, + f->sym, f->targobj_idx, f->sym_idx); + } + + numfixups++; + } + } + pl->fixupcnt[2*libidx] = numfixups; +#if 0 + printf("prog %s obj %s had %d got fixups\n", prog_obj->load_name, + ol->object->load_name, numfixups); +#endif + + if (verbose > 3) + printf("fixup PLT %s\n", ol->object->load_name); + /* now PLT */ + + symcache = objarray[objidx].pltsymcache; + + numfixups = 0; + for (i = 0; i < ol->object->nchains; i++) { + /* + * this assumes if the same object is found, the same + * symbol will be found as well + */ + if (ol->pltcache[i].obj != symcache[i].obj) { + numfixups++; + } + } + pl->fixup[2*libidx+1] = calloc(numfixups, sizeof (struct fixup)); + + numfixups = 0; + for (i = 0; i < ol->object->nchains; i++) { + /* + * this assumes if the same object is found, the same + * symbol will be found as well + */ + if (ol->pltcache[i].obj != symcache[i].obj) { + struct fixup *f = &(pl->fixup[2*libidx+1][numfixups]); + f->sym = i; + f->targobj_idx = ol->pltcache[i].obj->dyn.null; + f->sym_idx = ol->pltcache[i].sym - + ol->pltcache[i].obj->dyn.symtab; + if (verbose > 3) { + printf("obj %d idx %d targobj %d, sym idx %d\n", + i, + f->sym, f->targobj_idx, f->sym_idx); + } + + numfixups++; + } + } + pl->fixupcnt[2*libidx+1] = numfixups; + + pl->libmap[libidx] = calloc( objarray[objidx].numlibs, + sizeof(u_int32_t)); + + for (i = 0; i < objarray[objidx].numlibs; i++) { + pl->libmap[libidx][i] = objarray[objidx].idxtolib[i]; + } +#if 0 + printf("prog %s obj %s had %d plt fixups\n", prog_obj->load_name, + ol->object->load_name, numfixups); +#endif + +} + +void +elf_add_object(struct elf_object *object, int lib) +{ + struct objarray_list *newarray; + struct objlist *ol; + ol = malloc(sizeof (struct objlist)); + ol->object = object; + if (lib) + 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(); /* XXX FIX */ + objarray[objarray_cnt].id1 = arc4random(); + 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, j; + 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); + printf("got cache:\n"); + for (j = 0; j < ol->object->nchains; j++) { + if (ol->cache[j].obj != NULL) { + printf("symidx %d: obj %d sym %ld %s\n", + j, (int)ol->cache[j].obj->dyn.null, + ol->cache[j].sym - + ol->cache[j].obj->dyn.symtab, + ol->cache[j].sym->st_name + + ol->cache[j].obj->dyn.strtab + ); + } + } + printf("plt cache:\n"); + for (j = 0; j < ol->object->nchains; j++) { + if (ol->pltcache[j].obj != NULL) { + printf("symidx %d: obj %d sym %ld %s\n", + j, (int)ol->pltcache[j].obj->dyn.null, + ol->pltcache[j].sym - + ol->pltcache[j].obj->dyn.symtab, + ol->pltcache[j].sym->st_name + + ol->pltcache[j].obj->dyn.strtab + ); + } + } + } + } +} + +int +write_txtbusy_file(char *name) +{ + char *prebind_name; + int fd; + int oldfd; + int err; + struct stat sb; + void *buf; + size_t len, wlen; + +printf("txtbusy processing %s\n", name); + 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 = malloc(BUFSZ); + + fd = open(prebind_name, O_RDWR|O_CREAT|O_TRUNC, sb.st_mode); + printf("opened %s %d mode %o\n", prebind_name, fd, sb.st_mode); + oldfd = open(name, O_RDONLY); + printf("opened %s %d\n", name, oldfd); + 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; +} +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; + int found; + char *c; + u_int32_t offset; + u_int32_t *poffset; + struct nameidx *nameidx; + char *nametab; + int *idxtolib; + + + object = objarray[objidx].obj; + + prebind_map = objarray[object->dyn.null].oprebind_data; + + objarray[objidx].symcache = + calloc(sizeof(struct symcache_noflag), object->nchains); + objarray[objidx].pltsymcache = + calloc(sizeof(struct symcache_noflag), object->nchains); + + if (objarray[objidx].symcache == NULL || + objarray[objidx].pltsymcache == NULL) { + printf("out of memory allocating symcache\n"); + exit (20); + } + + 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 = calloc(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/prebind/prebind.8 b/libexec/ld.so/prebind/prebind.8 new file mode 100644 index 00000000000..bcc26c8ef7b --- /dev/null +++ b/libexec/ld.so/prebind/prebind.8 @@ -0,0 +1,60 @@ +.\" $OpenBSD: prebind.8,v 1.1 2006/05/03 16:10:52 drahn 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 +Parse each of the specified files or directories and process each ELF file +(ELF file found in the directory) and the associated DT_NEEDED libraries +and write symbol resolution hint information to each binary and library. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl m +Merge the results of this prebind cache with the prebind information +previously on any libraries referenced. +.It Fl v +Be verbose when running prebind, prints out information about +the file/library that is bein processed. +.Pp +.Sh BUGS and CAVEATS +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. +.Sh SEE AlSO +.Xr ld.so 1 +.Xr prebind_strip 1 +.Sh STANDARDS +None +.Sh HISTORY +A +.Nm +utility first appeared in OpenBSD 4.0. +.Nm +is based loosely on Prelinking, however prelink removes the security +feature of libraries appearing a random order on each invocation, thus +it was incompatible with OpenBSD's Goals. +.Nm +was written as attempt to improve the speed of dynamic linking +without the penalty of loss of security features. diff --git a/libexec/ld.so/prebind/prebind.c b/libexec/ld.so/prebind/prebind.c new file mode 100644 index 00000000000..bb10e124009 --- /dev/null +++ b/libexec/ld.so/prebind/prebind.c @@ -0,0 +1,1437 @@ +/* $OpenBSD: prebind.c,v 1.1 2006/05/03 16:10:52 drahn Exp $ */ +/* + * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/syslimits.h> +#include <sys/param.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <nlist.h> +#include <elf_abi.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <dirent.h> +#include "resolve.h" +#include "link.h" +#include "sod.h" +#ifndef __mips64__ +#include "machine/reloc.h" +#endif +#include "prebind.h" + +/* seems to make sense to limit how big of file can be dealt with */ +#define MAX_FILE_SIZE (512 * 1024 * 1024) + +char *shstrtab; + +#define DEBUG + +/* TODO - library path from ldconfig */ +#define DEFAULT_PATH "/usr/lib:/usr/X11R6/lib:/usr/local/qte/lib" + +/* alpha uses RELOC_JMP_SLOT */ +#ifdef __amd64__ +#define RELOC_JMP_SLOT R_X86_64_JUMP_SLOT +#endif +#ifdef __arm__ +#define RELOC_JMP_SLOT R_ARM_JUMP_SLOT +#endif +#ifdef __hppa__ +#define RELOC_JMP_SLOT RELOC_IPLT +#endif +#ifdef __hppa64__ +#define RELOC_JMP_SLOT RELOC_JMPSLOT +#endif +#ifdef __i386__ +#define RELOC_JMP_SLOT RELOC_JUMP_SLOT +#endif +#ifdef __mips64__ +#define RELOC_JMP_SLOT 0 /* XXX mips64 doesnt have PLT reloc */ +#endif +/* powerpc uses RELOC_JMP_SLOT */ +/* sparc uses RELOC_JMP_SLOT */ +/* sparc64 uses RELOC_JMP_SLOT */ + +#include "prebind_struct.h" +struct proglist *curbin; + +obj_list_ty library_list = + TAILQ_HEAD_INITIALIZER(library_list); + +prog_list_ty prog_list = + TAILQ_HEAD_INITIALIZER(prog_list); + + +struct elf_object * elf_load_object (void *pexe, const char *name); +void elf_free_object(struct elf_object *object); +void map_to_virt(Elf_Phdr *, Elf_Ehdr *, Elf_Addr, u_long *); +#ifdef DEBUG +#endif +int load_obj_needed(struct elf_object *object); +int load_lib(const char *name, struct elf_object *parent); +elf_object_t * elf_load_shlib_hint(struct sod *sod, struct sod *req_sod, + int use_hints, const char *libpath); +char * elf_find_shlib(struct sod *sodp, const char *searchpath, + int nohints); +elf_object_t * elf_tryload_shlib(const char *libname); +int elf_match_file(struct sod *sodp, char *name, int namelen); +void elf_init_objarray(void); +void elf_add_object(struct elf_object *object, int lib); +void elf_print_objarray(void); + +struct elf_object * elf_lookup_object(const char *name); +struct elf_object * elf_lookup_object_devino(dev_t dev, ino_t inode); +void elf_free_curbin_list(struct elf_object *obj); +void elf_resolve_curbin(void); +struct proglist *elf_newbin(void); +void elf_add_prog(struct proglist *object); +void elf_sum_reloc(); +int elf_prep_lib_prebind(struct elf_object *object); +int elf_prep_bin_prebind(struct proglist *pl); + +void elf_dump_footer(struct prebind_footer *footer); + + +void elf_fixup_prog_load(int fd, struct prebind_footer *footer, + struct elf_object *object); +void elf_clear_prog_load(int fd, struct elf_object *object); + +void +elf_find_symbol_rel(const char *s, struct elf_object *object, + Elf_Rel *rel, struct symcache_noflag *symcache, + struct symcache_noflag *pltsymcache); + +void +elf_find_symbol_rela(const char *s, struct elf_object *object, + Elf_RelA *rela, struct symcache_noflag *symcache, + struct symcache_noflag *pltsymcache); + +int elf_find_symbol_obj(elf_object_t *object, const char *name, + unsigned long hash, int flags, const Elf_Sym **this, + const Elf_Sym **weak_sym, elf_object_t **weak_object); + +int verbose; /* how verbose to be when operating */ +int merge_mode; /* merge (do not overwrite) existing prebind library info */ + +struct elf_object *load_object; + +struct elf_object * load_file(const char *filename, int lib); +void load_dir(char *name); +void load_exe(char *name); + +void +load_file_or_dir(char *name) +{ + struct stat sb; + int ret; + + ret = lstat(name, &sb); + printf("ret\n"); + if (ret != 0) + return; + printf("file or dir mode %d\n", sb.st_mode & S_IFMT); + switch(sb.st_mode & S_IFMT) { + case S_IFREG: + load_exe(name); + break; + case S_IFDIR: + printf("loading dir %s\n", name); + load_dir(name); + break; + default: + ; /* links and other files we skip */ + } + +} +void +load_dir(char *name) +{ + DIR *dirp; + struct dirent *dp; + struct stat sb; + char *buf; + + dirp = opendir(name); + if (dirp == NULL) { + /* dir failed to open, skip */ + return; + } + while ((dp = readdir(dirp)) != NULL) { + switch (dp->d_type) { + case DT_UNKNOWN: + /* + * NFS will return unknown, since load_file + * does stat the file, this just + */ + asprintf(&buf, "%s/%s", name, dp->d_name); + lstat(buf, &sb); + if (sb.st_mode == S_IFREG) + load_exe(buf); + free (buf); + case DT_REG: + asprintf(&buf, "%s/%s", name, dp->d_name); + load_exe(buf); + free (buf); + default: + /* other files symlinks, dirs, ... we ignore */ + ; + } + } +} +void +load_exe(char *name) +{ + struct elf_object *object; + struct elf_object *interp; + struct objlist *ol; + int fail = 0; + + curbin = elf_newbin(); + if (verbose > 0) + printf("processing %s\n", name); + object = load_file(name, 0); + if (load_object != NULL) { + TAILQ_FOREACH(ol, &(curbin->curbin_list), list) { + fail = load_obj_needed(ol->object); + if (fail != 0) + break; /* XXX */ + + } + interp = load_file(curbin->interp, 2); + + /* slight abuse of this field */ + object->load_object = interp; + + if (fail == 0) { + elf_resolve_curbin(); + elf_add_prog(curbin); + } else { + printf("failed to load %s\n", name); + elf_free_curbin_list(object); + free (curbin); + } + if (load_object != NULL) { + load_object = NULL; + } + } +} + +struct elf_object * +load_file(const char *filename, int lib) +{ + int fd = -1; + void *buf = NULL; + struct stat ifstat; + Elf_Ehdr *pehdr; + Elf_Shdr *pshdr; + char *pexe; +#ifdef DEBUG1 + int i; +#endif + struct elf_object *obj = NULL; + + + fd = open(filename, O_RDONLY); + if (fd == -1) { + perror (filename); + goto done; + } + + if (fstat(fd, &ifstat) == -1) { + perror (filename); + goto done; + } + + if ((ifstat.st_mode & S_IFMT) != S_IFREG) + goto done; + + if (ifstat.st_size < sizeof (Elf_Ehdr)) { + if (verbose > 0) + printf("%s: short file\n", filename); + goto done; + } + + obj = elf_lookup_object_devino( ifstat.st_dev, ifstat.st_ino); + if (obj != NULL) + goto done; + + buf = mmap(NULL, ifstat.st_size, PROT_READ, MAP_FILE | MAP_SHARED, + fd, 0); + if (buf == MAP_FAILED) { + printf("%s: cannot mmap\n", filename); + goto done; + } + + pehdr = (Elf_Ehdr *) buf; + + if (IS_ELF(*pehdr) == 0) { + goto done; + } + + if( pehdr->e_machine != ELF_TARG_MACH) { + if (verbose > 0) + printf("%s: wrong arch\n", filename); + goto done; + } + + pexe = buf; + if (pehdr->e_shstrndx == 0) { + goto done; + } + pshdr = (Elf_Shdr *) (pexe + pehdr->e_shoff + + (pehdr->e_shstrndx * pehdr->e_shentsize)); + + +#if 0 +printf("e_ehsize %x\n", pehdr->e_ehsize); +printf("e_phoff %x\n", pehdr->e_phoff); +printf("e_shoff %x\n", pehdr->e_shoff); +printf("e_phentsize %x\n", pehdr->e_phentsize); +printf("e_phnum %x\n", pehdr->e_phnum); +printf("e_shentsize %x\n", pehdr->e_shentsize); +printf("e_shstrndx %x\n\n", pehdr->e_shstrndx); +#endif + + shstrtab = (char *) (pexe + pshdr->sh_offset); + + + obj = elf_load_object(pexe, filename); + + + + munmap(buf, ifstat.st_size); + buf = NULL; + + if (obj != NULL) { + obj->dev = ifstat.st_dev; + obj->inode = ifstat.st_ino; + if (load_object == NULL) + load_object = obj; + + elf_add_object(obj, lib); + +#ifdef DEBUG1 + dump_info(obj); +#endif + } + if (lib == 1 && merge_mode == 1) { + /* + * for libraries, check if old prebind info exists + * and load it if we are in merge mode + */ + + elf_load_existing_prebind(obj, fd); + } +done: + if (buf != NULL) + munmap(buf, ifstat.st_size); + if (fd != -1) + close (fd); + return obj; +} + +void __dead +usage() +{ + extern char *__progname; + printf("%s [-v] {programlist}\n", __progname); + exit(1); +} + +extern int64_t prebind_blocks; + +int +main(int argc, char **argv) +{ + int i; + int ch; + extern int optind; + + /* GETOPT */ + while ((ch = getopt(argc, argv, "mv")) != -1) { + switch (ch) { + case 'm': + merge_mode = 1; + break; + case 'v': + verbose++; + break; + default: + usage(); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + + if (argc == 0) { + usage(); + /* NOTREACHED */ + } + + elf_init_objarray(); + + for (i = 0; i < argc; i++) { + load_file_or_dir(argv[i]); + } + if (verbose > 1) { + elf_print_objarray(); + elf_print_prog_list(&prog_list); + } + elf_sum_reloc(); + + printf("total new blocks %lld\n", prebind_blocks); + + return 0; +} + +struct elf_object * +elf_load_object (void *pexe, const char *name) +{ + int i; + struct elf_object *object; + Elf_Dyn *dynp = NULL, *odynp; + Elf_Ehdr *ehdr; + Elf_Phdr *phdr; + const Elf_Sym *symt; + const char *strt; + Elf_Addr loff; + Elf_Word *needed_list; + int needed_cnt = 0; + + object = calloc (1, sizeof (struct elf_object)); + if (object == NULL) { + printf("unable to allocate object for %s\n", name); + exit(10); + } + ehdr = pexe; + loff = (Elf_Addr)pexe; + + object->load_addr = 0; + object->load_name = strdup(name); + + phdr = (Elf_Phdr *)((char *)pexe + ehdr->e_phoff); + for (i = 0; i < ehdr->e_phnum; i++) { + switch (phdr[i].p_type) { + case PT_DYNAMIC: + dynp = (Elf_Dyn *)(phdr[i].p_offset); + break; + case PT_INTERP: + /* XXX can only occur in programs */ + curbin->interp = strdup ((char *)((char *)pexe + + phdr[i].p_offset)); + default: + break; + } + } + + if (dynp == 0) { + return NULL; /* XXX ??? */ + } + + dynp = (Elf_Dyn *)((unsigned long)dynp + loff); + odynp = dynp; + while (dynp->d_tag != DT_NULL) { + if (dynp->d_tag < DT_NUM) + object->Dyn.info[dynp->d_tag] = dynp->d_un.d_val; + else if (dynp->d_tag >= DT_LOPROC && + dynp->d_tag < DT_LOPROC + DT_PROCNUM) + object->Dyn.info[dynp->d_tag + DT_NUM - DT_LOPROC] = + dynp->d_un.d_val; + if (dynp->d_tag == DT_TEXTREL) + object->dyn.textrel = 1; + if (dynp->d_tag == DT_SYMBOLIC) + object->dyn.symbolic = 1; + if (dynp->d_tag == DT_BIND_NOW) + object->obj_flags = RTLD_NOW; + if (dynp->d_tag == DT_NEEDED) + needed_cnt++; + + dynp++; + } + + needed_list = calloc((needed_cnt + 1), (sizeof (Elf_Word))); + if (needed_list == NULL) { + printf("unable to allocate needed_list for %s\n", name); + exit(10); + } + needed_list[needed_cnt] = 0; + for (dynp = odynp, i = 0; dynp->d_tag != DT_NULL; dynp++) { + if (dynp->d_tag == DT_NEEDED) { + needed_list[i] = dynp->d_un.d_val; + i++; + } + } + + if (object->Dyn.info[DT_HASH]) + map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_HASH]); + if (object->Dyn.info[DT_STRTAB]) + map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_STRTAB]); + if (object->Dyn.info[DT_SYMTAB]) + map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_SYMTAB]); + + if (object->Dyn.info[DT_RELA]) + map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_RELA]); + if (object->Dyn.info[DT_RPATH]) + object->Dyn.info[DT_RPATH] += object->Dyn.info[DT_STRTAB]; + if (object->Dyn.info[DT_REL]) + map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_REL]); +#if 0 + if (object->Dyn.info[DT_SONAME]) + map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_SONAME]); +#endif + if (object->Dyn.info[DT_JMPREL]) + map_to_virt(phdr, ehdr, loff, &object->Dyn.info[DT_JMPREL]); + + symt = object->dyn.symtab; + strt = object->dyn.strtab; + + { + Elf_Sym *sym; + char *str; + Elf_Rel *rel; + Elf_RelA *rela; + Elf_Addr *hash; + Elf_Word *hashtab; + void *plt; + size_t hashsz; + + if (object->Dyn.info[DT_HASH] != 0) { + hash = object->dyn.hash; + hashtab = (void *)hash; + object->nbuckets = hashtab[0]; + object->nchains = hashtab[1]; + hashsz = (2 + object->nbuckets + object->nchains) * + sizeof (Elf_Word); + hash = malloc(hashsz); + if (hash == NULL) { + printf("unable to allocate hash for %s\n", + name); + exit(10); + } + bcopy (object->dyn.hash, hash, hashsz); + object->dyn.hash = hash; + object->buckets = ((Elf_Word *)hash + 2); + object->chains = object->buckets + object->nbuckets; + } + + str = malloc(object->dyn.strsz); + bcopy(object->dyn.strtab, str, object->dyn.strsz); + object->dyn.strtab = str; + strt = str; + + sym = malloc(object->nchains * sizeof(Elf_Sym)); + bcopy(object->dyn.symtab, sym, + object->nchains * sizeof(Elf_Sym)); + object->dyn.symtab = sym; + symt = sym; + + if (object->dyn.relsz != 0) { + rel = malloc(object->dyn.relsz); + bcopy(object->dyn.rel, rel, object->dyn.relsz); + object->dyn.rel = rel; + } else { + object->dyn.rel = NULL; + } + if (object->dyn.relasz != 0) { + rela = malloc(object->dyn.relasz); + bcopy(object->dyn.rela, rela, object->dyn.relasz); + object->dyn.rela = rela; + } else { + object->dyn.rela = NULL; + } + if (object->dyn.pltrelsz != 0) { + plt = malloc(object->dyn.pltrelsz); + bcopy((void*)object->dyn.jmprel, plt, + object->dyn.pltrelsz); + object->dyn.jmprel = (long)plt; + } else { + object->dyn.jmprel = NULL; + } + if (object->dyn.rpath != NULL) + object->dyn.rpath = strdup(object->dyn.rpath); + + + object->dyn.needed = (Elf_Addr)needed_list; + + } + +#ifdef DEBUG1 + dump_info(object); +#endif + return object; +} +/* + * Free any extra pieces associated with 'object' + */ +void +elf_free_object(struct elf_object *object) +{ + free (object->load_name); + if (object->dyn.hash != NULL) + free (object->dyn.hash); + free ((void *)object->dyn.strtab); + free ((void *)object->dyn.symtab); + if (object->dyn.rel != NULL) + free (object->dyn.rel); + if (object->dyn.rela != NULL) + free (object->dyn.rela); + if (object->dyn.rpath != NULL) + free ((void *)object->dyn.rpath); + free (object); +} + +/* + * translate an object address into a file offset for the + * file assuming that the file is mapped at base. + */ +void +map_to_virt(Elf_Phdr *phdr, Elf_Ehdr *ehdr, Elf_Addr base, u_long *vaddr) +{ + int i; + + for (i = 0; i < ehdr->e_phnum; i++) { + switch (phdr[i].p_type) { + case PT_LOAD: + if(phdr[i].p_vaddr > *vaddr) + continue; + if(phdr[i].p_vaddr + phdr[i].p_memsz < *vaddr) + continue; +#ifdef DEBUG1 + printf("input address %lx translated to ", *vaddr); +#endif + *vaddr += phdr[i].p_offset - phdr[i].p_vaddr + base; +#ifdef DEBUG1 + printf("%lx, base %lx %lx %llx\n", *vaddr, base, + phdr[i].p_vaddr, phdr[i].p_offset ); +#endif + + default: + break; + } + } +} + +int +load_obj_needed(struct elf_object *object) +{ + int i; + Elf_Word *needed_list; + int err; + + needed_list = (Elf_Word *)object->dyn.needed; + for (i = 0; needed_list[i] != NULL; i++) { + if (verbose > 1) + printf("lib: %s\n", needed_list[i] + + object->dyn.strtab); + err = load_lib(needed_list[i] + object->dyn.strtab, object); + if (err) { + printf("failed to load lib %s\n", + needed_list[i] + object->dyn.strtab); + return 1; + } + } + return 0; +} + +int +load_lib(const char *name, struct elf_object *parent) +{ + struct sod sod, req_sod; + int ignore_hints; + int try_any_minor = 0; + struct elf_object *object = NULL; + +#if 0 + printf("load_lib %s\n", name); +#endif + + ignore_hints = 0; + + if(strchr(name, '/')) { + char *lpath, *lname; + lpath = strdup(name); + lname = strrchr(lpath, '/'); + if (lname == NULL || lname[1] == '\0') { + free(lpath); + return (1); /* failed */ + } + *lname = '\0'; + lname++; + + _dl_build_sod(lname, &sod); + req_sod = sod; + + /* this code does not allow lower minors */ +fullpathagain: + object = elf_load_shlib_hint(&sod, &req_sod, + ignore_hints, lpath); + if (object != NULL) + goto fullpathdone; + + if (try_any_minor == 0) { + try_any_minor = 1; + ignore_hints = 1; + req_sod.sod_minor = -1; + goto fullpathagain; + } + /* ERR */ +fullpathdone: + free(lpath); + free((char *)sod.sod_name); + return (object == NULL); /* failed */ + } + _dl_build_sod(name, &sod); + req_sod = sod; + + /* ignore LD_LIBRARY_PATH */ + +again: + if (parent->dyn.rpath != NULL) { + object = elf_load_shlib_hint(&sod, &req_sod, + ignore_hints, parent->dyn.rpath); + if (object != NULL) + goto done; + } + if (parent != load_object && load_object->dyn.rpath != NULL) { + object = elf_load_shlib_hint(&sod, &req_sod, + ignore_hints, load_object->dyn.rpath); + if (object != NULL) + goto done; + } + object = elf_load_shlib_hint(&sod, &req_sod, + ignore_hints, NULL); + + if (try_any_minor == 0) { + try_any_minor = 1; + ignore_hints = 1; + req_sod.sod_minor = -1; + goto again; + } + if (object == NULL) + printf ("unable to load %s\n", name); + +done: + free((char *)sod.sod_name); + + return (object == NULL); +} + +/* + * attempt to locate and load a library based on libpath, sod info and + * if it needs to respect hints, passing type and flags to perform open + */ +elf_object_t * +elf_load_shlib_hint(struct sod *sod, struct sod *req_sod, + int ignore_hints, const char *libpath) +{ + elf_object_t *object = NULL; + char *hint; + + hint = elf_find_shlib(req_sod, libpath, ignore_hints); + if (hint != NULL) { +#if 1 + if (req_sod->sod_minor < sod->sod_minor) + printf("warning: lib%s.so.%d.%d: " + "minor version >= %d expected, " + "using it anyway\n", + (char *)sod->sod_name, sod->sod_major, + req_sod->sod_minor, sod->sod_minor); +#endif + object = elf_tryload_shlib(hint); + } + return object; +} + +char elf_hint_store[MAXPATHLEN]; + +char * +elf_find_shlib(struct sod *sodp, const char *searchpath, int nohints) +{ + char *hint, lp[PATH_MAX + 10], *path; + struct dirent *dp; + const char *pp; + int match, len; + DIR *dd; + struct sod tsod, bsod; /* transient and best sod */ + + /* if we are to search default directories, and hints + * are not to be used, search the standard path from ldconfig + * (_dl_hint_search_path) or use the default path + */ + if (nohints) + goto nohints; + + if (searchpath == NULL) { + /* search 'standard' locations, find any match in the hints */ + hint = _dl_findhint((char *)sodp->sod_name, sodp->sod_major, + sodp->sod_minor, NULL); + if (hint) + return hint; + } else { + /* search hints requesting matches for only + * the searchpath directories, + */ + pp = searchpath; + while (pp) { + path = lp; + while (path < lp + PATH_MAX && + *pp && *pp != ':' && *pp != ';') + *path++ = *pp++; + *path = 0; + + /* interpret "" as curdir "." */ + if (lp[0] == '\0') { + lp[0] = '.'; + lp[1] = '\0'; + } + + hint = _dl_findhint((char *)sodp->sod_name, + sodp->sod_major, sodp->sod_minor, lp); + if (hint != NULL) + return hint; + + if (*pp) /* Try curdir if ':' at end */ + pp++; + else + pp = 0; + } + } + + /* + * For each directory in the searchpath, read the directory + * entries looking for a match to sod. filename compare is + * done by _dl_match_file() + */ +nohints: + if (searchpath == NULL) { + if (_dl_hint_search_path != NULL) + searchpath = _dl_hint_search_path; + else + searchpath = DEFAULT_PATH; + } + pp = searchpath; + while (pp) { + path = lp; + while (path < lp + PATH_MAX && *pp && *pp != ':' && *pp != ';') + *path++ = *pp++; + *path = 0; + + /* interpret "" as curdir "." */ + if (lp[0] == '\0') { + lp[0] = '.'; + lp[1] = '\0'; + } + + if ((dd = opendir(lp)) != NULL) { + match = 0; + while ((dp = readdir(dd)) != NULL) { + tsod = *sodp; + if (elf_match_file(&tsod, dp->d_name, + dp->d_namlen)) { + /* + * When a match is found, tsod is + * updated with the major+minor found. + * This version is compared with the + * largest so far (kept in bsod), + * and saved if larger. + */ + if (!match || + tsod.sod_major == -1 || + tsod.sod_major > bsod.sod_major || + ((tsod.sod_major == + bsod.sod_major) && + tsod.sod_minor > bsod.sod_minor)) { + bsod = tsod; + match = 1; + len = strlcpy( + elf_hint_store, lp, + MAXPATHLEN); + if (lp[len-1] != '/') { + elf_hint_store[len] = + '/'; + len++; + } + strlcpy( + &elf_hint_store[len], + dp->d_name, + MAXPATHLEN-len); + if (tsod.sod_major == -1) + break; + } + } + } + closedir(dd); + if (match) { + *sodp = bsod; + return (elf_hint_store); + } + } + + if (*pp) /* Try curdir if ':' at end */ + pp++; + else + pp = 0; + } + return NULL; +} + +elf_object_t * +elf_tryload_shlib(const char *libname) +{ + struct elf_object *object; + object = elf_lookup_object(libname); + if (object == NULL) { + object = load_file(libname, 1); + } + if (object == NULL) + printf("tryload_shlib %s\n", libname); + return object; +} + +/* + * elf_match_file() + * + * This fucntion determines if a given name matches what is specified + * in a struct sod. The major must match exactly, and the minor must + * be same or larger. + * + * sodp is updated with the minor if this matches. + */ + +int +elf_match_file(struct sod *sodp, char *name, int namelen) +{ + int match; + struct sod lsod; + char *lname; + + lname = name; + if (sodp->sod_library) { + if (strncmp(name, "lib", 3)) + return 0; + lname += 3; + } + if (strncmp(lname, (char *)sodp->sod_name, + strlen((char *)sodp->sod_name))) + return 0; + + _dl_build_sod(name, &lsod); + + match = 0; + if ((strcmp((char *)lsod.sod_name, (char *)sodp->sod_name) == 0) && + (lsod.sod_library == sodp->sod_library) && + ((sodp->sod_major == -1) || (sodp->sod_major == lsod.sod_major)) && + ((sodp->sod_minor == -1) || + (lsod.sod_minor >= sodp->sod_minor))) { + match = 1; + + /* return version matched */ + sodp->sod_major = lsod.sod_major; + sodp->sod_minor = lsod.sod_minor; + } + free((char *)lsod.sod_name); + return match; +} +void +elf_add_prog(struct proglist *curbin) +{ + TAILQ_INSERT_TAIL(&prog_list, curbin, list); +} + +struct proglist * +elf_newbin(void) +{ + struct proglist *proglist; + proglist = malloc(sizeof (struct proglist)); + if (proglist == NULL) { + printf("unable to allocate proglist\n"); + exit(10); + } + proglist->fixup = NULL; + TAILQ_INIT(&(proglist->curbin_list)); + return proglist; +} + + + +/* + * Copy the contents of a libraries symbol cache instance into + * the 'global' symbol cache for that library + * this will currently resolve conflicts between mismatched + * libraries by flagging any mismatches as invalid + * which will cause all programs to generate a fixup + * It probably would be interesting to modify this to keep the most + * common entry as a library cache, and only have a fixup in programs + * where the symbol is overridden. + * + * Note 1: If 'merge' mode is ever written, this will need to keep + * the 'cached' copies symbols and do fixups for the rest, regardless + * of conflicts + * + * Note 2: This is run once each for the (got)symcache and pltsymcache + */ + +struct elf_object badobj_store; +struct elf_object *badobj = &badobj_store; + +/* + * copy the symbols found in a library symcache to the 'master/common' + * symbol table note that this will skip copying the following references + * 1. non-existing entries + * 2. symobj == prog &&& obj != prog + * 3 symobj == prog's interpter (references to dl_open) + */ +void +elf_copy_syms(struct symcache_noflag *tcache, struct symcache_noflag *scache, + struct elf_object *obj, struct elf_object *prog, int nsyms) +{ + int i; + for (i = 0; i < nsyms; i++) { + if (scache[i].obj != NULL && + (scache[i].obj->dyn.null != prog->dyn.null || + obj->dyn.null == prog->dyn.null) && + scache[i].obj != prog->load_object) { + if (tcache[i].obj != NULL) { + if (scache[i].obj != tcache[i].obj) { + if (verbose > 2) { + printf("sym mismatch %d: " + "obj %d: sym %ld %s " + "nobj %s oobj %s\n", + i, (int)scache[i].obj->dyn.null, + scache[i].sym - + scache[i].obj->dyn.symtab, + scache[i].sym->st_name + + scache[i].obj->dyn.strtab, + scache[i].obj->load_name, + tcache[i].obj->load_name); + } + +#if 0 + if (tcache[i].obj != badobj) + printf("%s: %s conflict\n", + obj->load_name, + scache[i].sym->st_name + + scache[i].obj->dyn.strtab); +#endif + tcache[i].obj = badobj; + tcache[i].sym = NULL; + } + } else { + if (scache[i].obj != prog) { +#if 0 + printf("%s: %s copying\n", + obj->load_name, + scache[i].sym->st_name + + scache[i].obj->dyn.strtab); +#endif + tcache[i].sym = scache[i].sym; + tcache[i].obj = scache[i].obj; + } + } + +#if 0 + printf("symidx %d: obj %d sym %ld %s\n", + i, scache[i].obj->dyn.null, + scache[i].sym - + scache[i].obj->dyn.symtab, + scache[i].sym->st_name + + scache[i].obj->dyn.strtab + ); +#endif + } + } +} + +struct elf_object * +elf_lookup_object(const char *name) +{ + struct objlist *ol; + TAILQ_FOREACH(ol, &(curbin->curbin_list), list) { + if (strcmp (name, ol->object->load_name) == 0) { + return ol->object; + } + + } + TAILQ_FOREACH(ol, &library_list, list) { + if (strcmp (name, ol->object->load_name) == 0) { + elf_add_object_curbin_list(ol->object); + return ol->object; + } + } + return NULL; +} + +struct elf_object * +elf_lookup_object_devino(dev_t dev, ino_t inode) +{ + struct objlist *ol; + TAILQ_FOREACH(ol, &(curbin->curbin_list), list) { + if (ol->object->dev == dev && + ol->object->inode == inode) + return ol->object; + } + TAILQ_FOREACH(ol, &library_list, list) { + if (ol->object->dev == dev && + ol->object->inode == inode) { + elf_add_object_curbin_list(ol->object); + return ol->object; + } + } + return NULL; +} + +void +elf_find_symbol_rel(const char *name, struct elf_object *object, + Elf_Rel *rel, struct symcache_noflag *symcache, + struct symcache_noflag *pltsymcache) +{ + struct objlist *ol; + unsigned long h = 0; + const char *p = name; + const Elf_Sym *sym, *ref_sym = NULL; + const Elf_Sym *weak_sym = NULL; + struct elf_object *weak_obj = NULL; + int flags = 0; + int found = 0; + int type, idx; + struct elf_object *ref_object = NULL; + + sym = object->dyn.symtab + ELF_R_SYM(rel->r_info); + + while (*p) { + unsigned long g; + h = (h << 4) + *p++; + if ((g = h & 0xf0000000)) + h ^= g >> 24; + h &= ~g; + } + + type = ELF_R_TYPE(rel->r_info); + flags = SYM_SEARCH_ALL|SYM_WARNNOTFOUND; + if (type == RELOC_JMP_SLOT) + flags |= SYM_PLT; + + TAILQ_FOREACH(ol, &(curbin->curbin_list), list) { + found = elf_find_symbol_obj(ol->object, name, h, flags, &sym, + &weak_sym, &weak_obj); + if (found) { +#if 0 + printf("found sym %s in obj %s\n", name, ol->object->load_name); +#endif + ref_object = ol->object; + break; + } + + } + if (found) { + ref_object = ol->object; + ref_sym = sym; + } else if (weak_obj != NULL) { + found = 1; + ref_object = weak_obj; + ref_sym = weak_sym; +#if 0 + printf("found sym %s in obj %s %d\n", name, + weak_obj->load_name, flags); +#endif + } + if (found == 1) { +#if 0 + printf("object %s sym %s, ref_object %s flags %d\n", + object->load_name, name, + ref_object->load_name, flags); +#endif + idx = ELF_R_SYM(rel->r_info); + if (flags & SYM_PLT) { + pltsymcache[idx].obj = ref_object; + pltsymcache[idx].sym = ref_sym; + } else { + symcache[idx].obj = ref_object; + symcache[idx].sym = ref_sym; + } + } else { + printf("symbol not found %s\n", name); + } +} + +void +elf_find_symbol_rela(const char *name, struct elf_object *object, + Elf_RelA *rela, struct symcache_noflag *symcache, + struct symcache_noflag *pltsymcache) +{ + struct objlist *ol; + unsigned long h = 0; + const char *p = name; + const Elf_Sym *sym, *ref_sym = NULL; + const Elf_Sym *weak_sym = NULL; + struct elf_object *weak_obj = NULL; + int flags = 0; + int found = 0; + int type, idx; + struct elf_object *ref_object = NULL; + + sym = object->dyn.symtab + ELF_R_SYM(rela->r_info); + + while (*p) { + unsigned long g; + h = (h << 4) + *p++; + if ((g = h & 0xf0000000)) + h ^= g >> 24; + h &= ~g; + } + + type = ELF_R_TYPE(rela->r_info); + flags = SYM_SEARCH_ALL|SYM_WARNNOTFOUND; + if (type == RELOC_JMP_SLOT) + flags |= SYM_PLT; + + TAILQ_FOREACH(ol, &(curbin->curbin_list), list) { + +// printf("searching sym [%s] typ %d in obj %s\n", name, type, ol->object->load_name); + found = elf_find_symbol_obj(ol->object, name, h, flags, &sym, + &weak_sym, &weak_obj); + if (found) { +#if 0 + printf("found sym %s in obj %s\n", name, ol->object->load_name); +#endif + ref_object = ol->object; + break; + } + + } + if (found) { + ref_object = ol->object; + ref_sym = sym; + } else if (weak_obj != NULL) { + found = 1; + ref_object = weak_obj; + ref_sym = weak_sym; +#if 0 + printf("found weak sym %s in obj %s\n", name, weak_obj->load_name); +#endif + } + if (found == 1) { +#if 0 + printf("object %s sym %s, ref_object %s %s\n", + object->load_name, name, + ref_object->load_name, + (flags & SYM_PLT) ? "plt" : "got"); + +#endif + idx = ELF_R_SYM(rela->r_info); + if (flags & SYM_PLT) { + pltsymcache[idx].obj = ref_object; + pltsymcache[idx].sym = ref_sym; + } else { + symcache[idx].obj = ref_object; + symcache[idx].sym = ref_sym; + } + } else { + printf("symbol not found %s\n", name); + } +} + +int +elf_find_symbol_obj(elf_object_t *object, const char *name, unsigned long hash, + int flags, const Elf_Sym **this, const Elf_Sym **weak_sym, + elf_object_t **weak_object) +{ + const Elf_Sym *symt = object->dyn.symtab; + const char *strt = object->dyn.strtab; + long si; + const char *symn; + + for (si = object->buckets[hash % object->nbuckets]; + si != STN_UNDEF; si = object->chains[si]) { + const Elf_Sym *sym = symt + si; + + if (sym->st_value == 0) + continue; + + if (ELF_ST_TYPE(sym->st_info) != STT_NOTYPE && + ELF_ST_TYPE(sym->st_info) != STT_OBJECT && + ELF_ST_TYPE(sym->st_info) != STT_FUNC) + continue; + + symn = strt + sym->st_name; + if (sym != *this && strcmp(symn, name)) + continue; + + /* allow this symbol if we are referring to a function + * which has a value, even if section is UNDEF. + * this allows &func to refer to PLT as per the + * ELF spec. st_value is checked above. + * if flags has SYM_PLT set, we must have actual + * symbol, so this symbol is skipped. + */ + if (sym->st_shndx == SHN_UNDEF) { + if ((flags & SYM_PLT) || sym->st_value == 0 || + ELF_ST_TYPE(sym->st_info) != STT_FUNC) + continue; + } + + if (ELF_ST_BIND(sym->st_info) == STB_GLOBAL) { + *this = sym; + return 1; + } else if (ELF_ST_BIND(sym->st_info) == STB_WEAK) { + if (!*weak_sym) { + *weak_sym = sym; + *weak_object = object; + } + } + } + return 0; +} + +void +elf_reloc(struct elf_object *object, struct symcache_noflag *symcache, + struct symcache_noflag *pltsymcache) +{ + int numrel; + int numrela; + int i; + const Elf_Sym *sym; + Elf_Rel *rel; + Elf_RelA *rela; + numrel = object->dyn.relsz / sizeof(Elf_Rel); +#if 0 + printf("rel relocations: %d\n", numrel); +#endif + rel = object->dyn.rel; + for (i = 0; i < numrel; i++) { + const char *s; + sym = object->dyn.symtab + ELF_R_SYM(rel[i].r_info); + + /* hppa has entries without names, skip them */ + if (sym->st_name == 0) + continue; + + s = (ELF_R_SYM(rel[i].r_info) == 0) ? "<rel>" : + object->dyn.strtab + sym->st_name; +#ifdef DEBUG1 + printf("%d: %x sym %x %s type %d\n", i, rel[i].r_offset, + ELF_R_SYM(rel[i].r_info), s, + ELF_R_TYPE(rel[i].r_info)); +#endif + if (ELF_R_SYM(rel[i].r_info) != 0) { + elf_find_symbol_rel(s, object, &rel[i], + symcache, pltsymcache); + } + } + if (numrel) { + numrel = object->dyn.pltrelsz / sizeof(Elf_Rel); + rel = (Elf_Rel *)(object->Dyn.info[DT_JMPREL]); + for (i = 0; i < numrel; i++) { + const char *s; + sym = object->dyn.symtab + ELF_R_SYM(rel[i].r_info); + + /* hppa has entries without names, skip them */ + if (sym->st_name == 0) + continue; + + s = (ELF_R_SYM(rel[i].r_info) == 0) ? "<rel>" : + object->dyn.strtab + sym->st_name; +#ifdef DEBUG1 + printf("%d: %x sym %d %s type %d\n", i, rel[i].r_offset, + ELF_R_SYM(rel[i].r_info), s, + ELF_R_TYPE(rel[i].r_info)); +#endif + if (ELF_R_SYM(rel[i].r_info) != 0) { + elf_find_symbol_rel(s, object, &rel[i], + symcache, pltsymcache); + } + } + } + + numrela = object->dyn.relasz / sizeof(Elf_RelA); +#if 0 + printf("rela relocations: %d\n", numrela); +#endif + rela = object->dyn.rela; + for (i = 0; i < numrela; i++) { + const char *s; + sym = object->dyn.symtab + ELF_R_SYM(rela[i].r_info); + + /* hppa has entries without names, skip them */ + if (sym->st_name == 0) + continue; + + s = (ELF_R_SYM(rela[i].r_info) == 0) ? "<rel>" : + object->dyn.strtab + sym->st_name; +#ifdef DEBUG1 + printf("%d: %x sym %x %s type %d\n", i, rela[i].r_offset, + ELF_R_SYM(rela[i].r_info), s, + ELF_R_TYPE(rela[i].r_info)); +#endif + if (ELF_R_SYM(rela[i].r_info) != 0) { + elf_find_symbol_rela(s, object, &rela[i], + symcache, pltsymcache); + } + } + if (numrela) { + numrela = object->dyn.pltrelsz / sizeof(Elf_RelA); + rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]); + + for (i = 0; i < numrela; i++) { + const char *s; + sym = object->dyn.symtab + ELF_R_SYM(rela[i].r_info); + + /* hppa has entries without names, skip them */ + if (sym->st_name == 0) + continue; + + s = (ELF_R_SYM(rela[i].r_info) == 0) ? "<rel>" : + object->dyn.strtab + sym->st_name; +#ifdef DEBUG1 + printf("%d: %x sym %x %s type %d\n", i, + rela[i].r_offset, + ELF_R_SYM(rela[i].r_info), s, + ELF_R_TYPE(rela[i].r_info)); +#endif + if (ELF_R_SYM(rela[i].r_info) != 0) { + elf_find_symbol_rela(s, object, &rela[i], + symcache, pltsymcache); + } + } + } +} + +void +elf_resolve_curbin(void) +{ + struct objlist *ol; + +#ifdef DEBUG1 + elf_print_curbin_list(curbin); +#endif + TAILQ_FOREACH(ol, &(curbin->curbin_list), list) { + ol->cache = calloc(sizeof(struct symcache_noflag), + ol->object->nchains); + ol->pltcache = calloc(sizeof(struct symcache_noflag), + ol->object->nchains); + if (ol->cache == NULL || ol->pltcache == NULL) { + printf("unable to allocate memory for cache %s\n", + ol->object->load_name); + exit(20); + } + elf_reloc(ol->object, ol->cache, ol->pltcache); + } +} diff --git a/libexec/ld.so/prebind/prebind_struct.h b/libexec/ld.so/prebind/prebind_struct.h new file mode 100644 index 00000000000..2c69105211b --- /dev/null +++ b/libexec/ld.so/prebind/prebind_struct.h @@ -0,0 +1,64 @@ +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 symcache_noflag *cache; + struct symcache_noflag *pltcache; +}; + +struct proglist { + TAILQ_ENTRY(proglist) list; + TAILQ_HEAD(, objlist) curbin_list; + struct fixup **fixup; + int *fixupcnt; + 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); + + +/* objarray */ +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; +void elf_load_existing_prebind(struct elf_object *object, int fd); diff --git a/libexec/ld.so/prebind/sod.c b/libexec/ld.so/prebind/sod.c new file mode 100644 index 00000000000..f92213cf867 --- /dev/null +++ b/libexec/ld.so/prebind/sod.c @@ -0,0 +1,265 @@ +/* $OpenBSD: sod.c,v 1.1 2006/05/03 16:10:52 drahn 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; +} diff --git a/libexec/ld.so/prebind_strip/Makefile b/libexec/ld.so/prebind_strip/Makefile new file mode 100644 index 00000000000..6b0fa03bbd9 --- /dev/null +++ b/libexec/ld.so/prebind_strip/Makefile @@ -0,0 +1,13 @@ +# $OpenBSD: Makefile,v 1.1 2006/05/03 16:10:52 drahn Exp $ + +SRCS= prebind_strip.c + +PROG= prebind_strip +MAN= prebind_strip.8 + +BINDIR=/usr/sbin + +CFLAGS += -Wall -ggdb +CFLAGS += -I${.CURDIR}/.. + +.include <bsd.prog.mk> diff --git a/libexec/ld.so/prebind_strip/prebind_strip.8 b/libexec/ld.so/prebind_strip/prebind_strip.8 new file mode 100644 index 00000000000..725a00d3eff --- /dev/null +++ b/libexec/ld.so/prebind_strip/prebind_strip.8 @@ -0,0 +1,44 @@ +.\" $OpenBSD: prebind_strip.8,v 1.1 2006/05/03 16:10:52 drahn 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_STRIP 8 +.Os +.Sh NAME +.Nm prebind_strip +.Nd Remove prebind info from binaries and libraries. +.Sh SYNOPSIS +.Nm prebind_strip +.Op Ar file ... +.Sh DESCRIPTION +Remove any prebind information from a binary or shared library. +After this is run the library length and md5 should match the original value. +.Sh SEE AlSO +.Xr ld.so 1 +.Xr prebind 8 +.Sh STANDARDS +None +.Sh HISTORY +A +.Nm +utility first appeared in OpenBSD 4.0. +.Nm +is based loosely on Prelinking, however prelink removes the security +feature of libraries appearing a random order on each invocation, thus +it was incompatible with OpenBSD's Goals. +.Nm +was written as attempt to improve the speed of dynamic linking +without the penalty of loss of security features. diff --git a/libexec/ld.so/prebind_strip/prebind_strip.c b/libexec/ld.so/prebind_strip/prebind_strip.c new file mode 100644 index 00000000000..135626f7439 --- /dev/null +++ b/libexec/ld.so/prebind_strip/prebind_strip.c @@ -0,0 +1,125 @@ +/* $OpenBSD: prebind_strip.c,v 1.1 2006/05/03 16:10:52 drahn Exp $ */ +/* + * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/exec_elf.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include "prebind.h" + +void dump_prebind(char *file); +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, void *a); + +void prebind_remove_load_section(int fd, char *name); + +int +main(int argc, char **argv) +{ + int i; + for (i = 1; i < argc; i++) { + printf("stripping %s\n", argv[i]); + dump_prebind(argv[i]); + } + + return 0; +} + +void +dump_prebind(char *file) +{ + struct prebind_footer footer; + int fd; + ssize_t bytes; + + fd = open(file, O_RDWR); + if (fd == -1) { + perror(file); + return; + } + lseek(fd, -((off_t)sizeof(struct prebind_footer)), SEEK_END); + bytes = read(fd, &footer, sizeof(struct prebind_footer)); + if (bytes != sizeof(struct prebind_footer)) { + perror("short read\n"); + goto done; + } + + 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) { + + } else { + printf("%s: no prebind header\n", file); + goto done; + } + + prebind_remove_load_section(fd, file); + + ftruncate(fd, footer.orig_size); +done: + close(fd); +} + +void +prebind_remove_load_section(int fd, char *name) +{ + void *buf; + Elf_Ehdr *ehdr; + Elf_Phdr *phdr; + int loadsection; + + buf = mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_FILE | MAP_SHARED, + fd, 0); + if (buf == MAP_FAILED) { + perror(name); + printf("%s: cannot mmap for write\n", name); + return; + } + + ehdr = (Elf_Ehdr *) buf; + phdr = (Elf_Phdr *)((char *)buf + ehdr->e_phoff); + + loadsection = ehdr->e_phnum - 1; + + if(ehdr->e_type != ET_EXEC || + (phdr[loadsection].p_flags & 0x08000000) == 0) { + goto done; + } + + 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\n", name, + (long)(phdr[loadsection].p_vaddr)); + goto done; + } + + bzero(&phdr[loadsection], sizeof(Elf_Phdr)); + + ehdr->e_phnum--; +done: + munmap(buf, 8192); +} diff --git a/libexec/ld.so/resolve.c b/libexec/ld.so/resolve.c index fc72d807584..bef723cc59a 100644 --- a/libexec/ld.so/resolve.c +++ b/libexec/ld.so/resolve.c @@ -1,4 +1,4 @@ -/* $OpenBSD: resolve.c,v 1.46 2005/11/09 16:41:29 kurt Exp $ */ +/* $OpenBSD: resolve.c,v 1.47 2006/05/03 16:10:51 drahn Exp $ */ /* * Copyright (c) 1998 Per Fogelstrom, Opsycon AB @@ -35,6 +35,7 @@ #include "syscall.h" #include "archdep.h" #include "resolve.h" +#include "dl_prebind.h" elf_object_t *_dl_objects; elf_object_t *_dl_last_object; @@ -249,6 +250,7 @@ sym_cache *_dl_symcache; int _dl_symcachestat_hits; int _dl_symcachestat_lookups; + Elf_Addr _dl_find_symbol_bysym(elf_object_t *req_obj, unsigned int symidx, const Elf_Sym **this, int flags, const Elf_Sym *ref_sym, const elf_object_t **pobj) @@ -270,6 +272,8 @@ _dl_find_symbol_bysym(elf_object_t *req_obj, unsigned int symidx, *this = _dl_symcache[symidx].sym; if (pobj) *pobj = sobj; + if (_dl_prebind_validate) /* XXX */ + prebind_validate(req_obj, symidx, flags, ref_sym); return sobj->load_offs; } @@ -283,6 +287,15 @@ _dl_find_symbol_bysym(elf_object_t *req_obj, unsigned int symidx, *pobj = sobj; if (_dl_symcache != NULL && symidx < req_obj->nchains) { +#if 0 + DL_DEB(("cache miss %d %p %p, %p %p %s %s %d %d %s\n", + symidx, + _dl_symcache[symidx].sym, *this, + _dl_symcache[symidx].obj, sobj, sobj->load_name, + sobj->dyn.strtab + (*this)->st_name, + _dl_symcache[symidx].flags, flags, req_obj->load_name)); +#endif + _dl_symcache[symidx].sym = *this; _dl_symcache[symidx].obj = sobj; _dl_symcache[symidx].flags = flags; diff --git a/libexec/ld.so/resolve.h b/libexec/ld.so/resolve.h index d3c20b1bdcf..2eb39d3d251 100644 --- a/libexec/ld.so/resolve.h +++ b/libexec/ld.so/resolve.h @@ -1,4 +1,4 @@ -/* $OpenBSD: resolve.h,v 1.50 2005/11/09 16:41:29 kurt Exp $ */ +/* $OpenBSD: resolve.h,v 1.51 2006/05/03 16:10:51 drahn Exp $ */ /* * Copyright (c) 1998 Per Fogelstrom, Opsycon AB @@ -136,6 +136,8 @@ struct elf_object { /* object that caused this module to be loaded, used in symbol lookup */ elf_object_t *load_object; + void *prebind_data; + /* for object confirmation */ dev_t dev; ino_t inode; diff --git a/libexec/ld.so/sparc/ldasm.S b/libexec/ld.so/sparc/ldasm.S index 41304417ab3..1b210378132 100644 --- a/libexec/ld.so/sparc/ldasm.S +++ b/libexec/ld.so/sparc/ldasm.S @@ -1,4 +1,4 @@ -/* $OpenBSD: ldasm.S,v 1.14 2004/05/25 15:56:18 deraadt Exp $ */ +/* $OpenBSD: ldasm.S,v 1.15 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 2001 Jason L. Wright (jason@thought.net) @@ -319,6 +319,18 @@ _dl_sigprocmask: retl clr %o0 + + .section ".text" + .align 4 + .global _dl_gettimeofday + .type _dl_gettimeofday,@function +_dl_gettimeofday: + mov SYS_gettimeofday | SYSCALL_G2RFLAG, %g1 ! calling sys_gettimeofday + add %o7, 8, %g2 ! just return on success + t ST_SYSCALL ! off to wonderland + retl + sub %g0, %o0, %o0 ! error: result = -errno + /* * V8 sparc .{,u}{mul,div,rem} replacements. * We try to mimic them 100%. Full 64 bit sources or outputs, and diff --git a/libexec/ld.so/sparc/syscall.h b/libexec/ld.so/sparc/syscall.h index 92ebc12d3d3..bb07f56cab0 100644 --- a/libexec/ld.so/sparc/syscall.h +++ b/libexec/ld.so/sparc/syscall.h @@ -1,4 +1,4 @@ -/* $OpenBSD: syscall.h,v 1.7 2004/05/25 15:56:18 deraadt Exp $ */ +/* $OpenBSD: syscall.h,v 1.8 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 2001 Niklas Hallqvist @@ -53,6 +53,7 @@ int _dl_fcntl(int, int, ...); int _dl_getdirentries(int, char*, int, long *); int _dl_sigprocmask(int, const sigset_t *, sigset_t *); int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t); +int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp); static inline off_t _dl_lseek(int fildes, off_t offset, int whence) diff --git a/libexec/ld.so/sparc64/ldasm.S b/libexec/ld.so/sparc64/ldasm.S index dcf8186fa96..332bc13ebd8 100644 --- a/libexec/ld.so/sparc64/ldasm.S +++ b/libexec/ld.so/sparc64/ldasm.S @@ -1,4 +1,4 @@ -/* $OpenBSD: ldasm.S,v 1.23 2004/05/25 15:56:19 deraadt Exp $ */ +/* $OpenBSD: ldasm.S,v 1.24 2006/05/03 16:10:52 drahn Exp $ */ /* $NetBSD: rtld_start.S,v 1.5 2001/08/14 22:17:48 eeh Exp $ */ /* @@ -315,3 +315,11 @@ _ENTRY(_dl_sysctl) t ST_SYSCALL ! off to wonderland retl sub %g0, %o0, %o0 ! error: result = -errno + +_ENTRY(_dl_gettimeofday) + mov SYS_gettimeofday | SYSCALL_G2RFLAG, %g1 ! calling sys_gettimeofday + add %o7, 8, %g2 ! just return on success + t ST_SYSCALL ! off to wonderland + retl + sub %g0, %o0, %o0 ! error: result = -errno + diff --git a/libexec/ld.so/sparc64/syscall.h b/libexec/ld.so/sparc64/syscall.h index f6b0638ff04..45c05b0dcbc 100644 --- a/libexec/ld.so/sparc64/syscall.h +++ b/libexec/ld.so/sparc64/syscall.h @@ -1,4 +1,4 @@ -/* $OpenBSD: syscall.h,v 1.14 2003/07/06 20:04:00 deraadt Exp $ */ +/* $OpenBSD: syscall.h,v 1.15 2006/05/03 16:10:52 drahn Exp $ */ /* * Copyright (c) 2001 Niklas Hallqvist @@ -53,6 +53,7 @@ int _dl_fcntl(int, int, ...); int _dl_getdirentries(int, char*, int, long *); int _dl_sigprocmask(int, const sigset_t *, sigset_t *); int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t); +int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp); static inline off_t _dl_lseek(int fildes, off_t offset, int whence) |