diff options
Diffstat (limited to 'libexec/rtld-elf/ia64/reloc.c')
-rw-r--r-- | libexec/rtld-elf/ia64/reloc.c | 658 |
1 files changed, 0 insertions, 658 deletions
diff --git a/libexec/rtld-elf/ia64/reloc.c b/libexec/rtld-elf/ia64/reloc.c deleted file mode 100644 index 1a41cb3..0000000 --- a/libexec/rtld-elf/ia64/reloc.c +++ /dev/null @@ -1,658 +0,0 @@ -/*- - * Copyright 1996, 1997, 1998, 1999 John D. Polstra. - * 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. - * - * 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. - * - * $FreeBSD$ - */ - -/* - * Dynamic linker for ELF. - * - * John Polstra <jdp@polstra.com>. - */ - -#include <sys/param.h> -#include <sys/mman.h> -#include <machine/ia64_cpu.h> - -#include <dlfcn.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "debug.h" -#include "rtld.h" - -extern Elf_Dyn _DYNAMIC; - -/* - * Macros for loading/storing unaligned 64-bit values. These are - * needed because relocations can point to unaligned data. This - * occurs in the DWARF2 exception frame tables generated by the - * compiler, for instance. - * - * We don't use these when relocating jump slots and GOT entries, - * since they are guaranteed to be aligned. - * - * XXX dfr stub for now. - */ -#define load64(p) (*(u_int64_t *) (p)) -#define store64(p, v) (*(u_int64_t *) (p) = (v)) - -/* Allocate an @fptr. */ - -#define FPTR_CHUNK_SIZE 64 - -struct fptr_chunk { - struct fptr fptrs[FPTR_CHUNK_SIZE]; -}; - -static struct fptr_chunk first_chunk; -static struct fptr_chunk *current_chunk = &first_chunk; -static struct fptr *next_fptr = &first_chunk.fptrs[0]; -static struct fptr *last_fptr = &first_chunk.fptrs[FPTR_CHUNK_SIZE]; - -/* - * We use static storage initially so that we don't have to call - * malloc during init_rtld(). - */ -static struct fptr * -alloc_fptr(Elf_Addr target, Elf_Addr gp) -{ - struct fptr* fptr; - - if (next_fptr == last_fptr) { - current_chunk = xmalloc(sizeof(struct fptr_chunk)); - next_fptr = ¤t_chunk->fptrs[0]; - last_fptr = ¤t_chunk->fptrs[FPTR_CHUNK_SIZE]; - } - fptr = next_fptr; - next_fptr++; - fptr->target = target; - fptr->gp = gp; - return fptr; -} - -static struct fptr ** -alloc_fptrs(Obj_Entry *obj, bool mapped) -{ - struct fptr **fptrs; - size_t fbytes; - - fbytes = obj->dynsymcount * sizeof(struct fptr *); - - /* - * Avoid malloc, if requested. Happens when relocating - * rtld itself on startup. - */ - if (mapped) { - fptrs = mmap(NULL, fbytes, PROT_READ|PROT_WRITE, - MAP_ANON, -1, 0); - if (fptrs == MAP_FAILED) - fptrs = NULL; - } else { - fptrs = xcalloc(1, fbytes); - } - - /* - * This assertion is necessary to guarantee function pointer - * uniqueness - */ - assert(fptrs != NULL); - - return (obj->priv = fptrs); -} - -static void -free_fptrs(Obj_Entry *obj, bool mapped) -{ - struct fptr **fptrs; - size_t fbytes; - - fptrs = obj->priv; - if (fptrs == NULL) - return; - - fbytes = obj->dynsymcount * sizeof(struct fptr *); - if (mapped) - munmap(fptrs, fbytes); - else - free(fptrs); - obj->priv = NULL; -} - -/* Relocate a non-PLT object with addend. */ -static int -reloc_non_plt_obj(Obj_Entry *obj_rtld, Obj_Entry *obj, const Elf_Rela *rela, - SymCache *cache, int flags, RtldLockState *lockstate) -{ - struct fptr **fptrs; - Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rela->r_offset); - - switch (ELF_R_TYPE(rela->r_info)) { - case R_IA_64_REL64LSB: - /* - * We handle rtld's relocations in rtld_start.S - */ - if (obj != obj_rtld) - store64(where, - load64(where) + (Elf_Addr) obj->relocbase); - break; - - case R_IA_64_DIR64LSB: { - const Elf_Sym *def; - const Obj_Entry *defobj; - Elf_Addr target; - - def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - flags, cache, lockstate); - if (def == NULL) - return -1; - - target = (def->st_shndx != SHN_UNDEF) - ? (Elf_Addr)(defobj->relocbase + def->st_value) : 0; - store64(where, target + rela->r_addend); - break; - } - - case R_IA_64_FPTR64LSB: { - /* - * We have to make sure that all @fptr references to - * the same function are identical so that code can - * compare function pointers. - */ - const Elf_Sym *def; - const Obj_Entry *defobj; - struct fptr *fptr = 0; - Elf_Addr target, gp; - int sym_index; - - def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - SYMLOOK_IN_PLT | flags, cache, lockstate); - if (def == NULL) { - /* - * XXX r_debug_state is problematic and find_symdef() - * returns NULL for it. This probably has something to - * do with symbol versioning (r_debug_state is in the - * symbol map). If we return -1 in that case we abort - * relocating rtld, which typically is fatal. So, for - * now just skip the symbol when we're relocating - * rtld. We don't care about r_debug_state unless we - * are being debugged. - */ - if (obj != obj_rtld) - return -1; - break; - } - - if (def->st_shndx != SHN_UNDEF) { - target = (Elf_Addr)(defobj->relocbase + def->st_value); - gp = (Elf_Addr)defobj->pltgot; - - /* rtld is allowed to reference itself only */ - assert(!obj->rtld || obj == defobj); - fptrs = defobj->priv; - if (fptrs == NULL) - fptrs = alloc_fptrs((Obj_Entry *) defobj, - obj->rtld); - - sym_index = def - defobj->symtab; - - /* - * Find the @fptr, using fptrs as a helper. - */ - if (fptrs) - fptr = fptrs[sym_index]; - if (!fptr) { - fptr = alloc_fptr(target, gp); - if (fptrs) - fptrs[sym_index] = fptr; - } - } else - fptr = NULL; - - store64(where, (Elf_Addr)fptr); - break; - } - - case R_IA_64_IPLTLSB: { - /* - * Relocation typically used to populate C++ virtual function - * tables. It creates a 128-bit function descriptor at the - * specified memory address. - */ - const Elf_Sym *def; - const Obj_Entry *defobj; - struct fptr *fptr; - Elf_Addr target, gp; - - def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - flags, cache, lockstate); - if (def == NULL) - return -1; - - if (def->st_shndx != SHN_UNDEF) { - target = (Elf_Addr)(defobj->relocbase + def->st_value); - gp = (Elf_Addr)defobj->pltgot; - } else { - target = 0; - gp = 0; - } - - fptr = (void*)where; - store64(&fptr->target, target); - store64(&fptr->gp, gp); - break; - } - - case R_IA_64_DTPMOD64LSB: { - const Elf_Sym *def; - const Obj_Entry *defobj; - - def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - flags, cache, lockstate); - if (def == NULL) - return -1; - - store64(where, defobj->tlsindex); - break; - } - - case R_IA_64_DTPREL64LSB: { - const Elf_Sym *def; - const Obj_Entry *defobj; - - def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - flags, cache, lockstate); - if (def == NULL) - return -1; - - store64(where, def->st_value + rela->r_addend); - break; - } - - case R_IA_64_TPREL64LSB: { - const Elf_Sym *def; - const Obj_Entry *defobj; - - def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - flags, cache, lockstate); - if (def == NULL) - return -1; - - /* - * We lazily allocate offsets for static TLS as we - * see the first relocation that references the - * TLS block. This allows us to support (small - * amounts of) static TLS in dynamically loaded - * modules. If we run out of space, we generate an - * error. - */ - if (!defobj->tls_done) { - if (!allocate_tls_offset((Obj_Entry*) defobj)) { - _rtld_error("%s: No space available for static " - "Thread Local Storage", obj->path); - return -1; - } - } - - store64(where, defobj->tlsoffset + def->st_value + rela->r_addend); - break; - } - - case R_IA_64_NONE: - break; - - default: - _rtld_error("%s: Unsupported relocation type %u" - " in non-PLT relocations\n", obj->path, - (unsigned int)ELF_R_TYPE(rela->r_info)); - return -1; - } - - return(0); -} - -/* Process the non-PLT relocations. */ -int -reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, - RtldLockState *lockstate) -{ - const Elf_Rel *rellim; - const Elf_Rel *rel; - const Elf_Rela *relalim; - const Elf_Rela *rela; - SymCache *cache; - int bytes = obj->dynsymcount * sizeof(SymCache); - int r = -1; - - /* - * The dynamic loader may be called from a thread, we have - * limited amounts of stack available so we cannot use alloca(). - */ - cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON, -1, 0); - if (cache == MAP_FAILED) - cache = NULL; - - /* Perform relocations without addend if there are any: */ - rellim = (const Elf_Rel *) ((caddr_t) obj->rel + obj->relsize); - for (rel = obj->rel; obj->rel != NULL && rel < rellim; rel++) { - Elf_Rela locrela; - - locrela.r_info = rel->r_info; - locrela.r_offset = rel->r_offset; - locrela.r_addend = 0; - if (reloc_non_plt_obj(obj_rtld, obj, &locrela, cache, flags, - lockstate)) - goto done; - } - - /* Perform relocations with addend if there are any: */ - relalim = (const Elf_Rela *) ((caddr_t) obj->rela + obj->relasize); - for (rela = obj->rela; obj->rela != NULL && rela < relalim; rela++) { - if (reloc_non_plt_obj(obj_rtld, obj, rela, cache, flags, - lockstate)) - goto done; - } - - r = 0; -done: - if (cache) - munmap(cache, bytes); - - /* - * Release temporarily mapped fptrs if relocating - * rtld object itself. A new table will be created - * in make_function_pointer using malloc when needed. - */ - if (obj->rtld && obj->priv) - free_fptrs(obj, true); - - return (r); -} - -/* Process the PLT relocations. */ -int -reloc_plt(Obj_Entry *obj) -{ - /* All PLT relocations are the same kind: Elf_Rel or Elf_Rela. */ - if (obj->pltrelsize != 0) { - const Elf_Rel *rellim; - const Elf_Rel *rel; - - rellim = (const Elf_Rel *) - ((char *)obj->pltrel + obj->pltrelsize); - for (rel = obj->pltrel; rel < rellim; rel++) { - Elf_Addr *where; - - assert(ELF_R_TYPE(rel->r_info) == R_IA_64_IPLTLSB); - - /* Relocate the @fptr pointing into the PLT. */ - where = (Elf_Addr *)(obj->relocbase + rel->r_offset); - *where += (Elf_Addr)obj->relocbase; - } - } else { - const Elf_Rela *relalim; - const Elf_Rela *rela; - - relalim = (const Elf_Rela *) - ((char *)obj->pltrela + obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { - Elf_Addr *where; - - assert(ELF_R_TYPE(rela->r_info) == R_IA_64_IPLTLSB); - - /* Relocate the @fptr pointing into the PLT. */ - where = (Elf_Addr *)(obj->relocbase + rela->r_offset); - *where += (Elf_Addr)obj->relocbase; - } - } - return 0; -} - -int -reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) -{ - - /* XXX not implemented */ - return (0); -} - -int -reloc_gnu_ifunc(Obj_Entry *obj, int flags, - struct Struct_RtldLockState *lockstate) -{ - - /* XXX not implemented */ - return (0); -} - -/* Relocate the jump slots in an object. */ -int -reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) -{ - if (obj->jmpslots_done) - return 0; - /* All PLT relocations are the same kind: Elf_Rel or Elf_Rela. */ - if (obj->pltrelsize != 0) { - const Elf_Rel *rellim; - const Elf_Rel *rel; - - rellim = (const Elf_Rel *) - ((char *)obj->pltrel + obj->pltrelsize); - for (rel = obj->pltrel; rel < rellim; rel++) { - Elf_Addr *where; - const Elf_Sym *def; - const Obj_Entry *defobj; - - assert(ELF_R_TYPE(rel->r_info) == R_IA_64_IPLTLSB); - where = (Elf_Addr *)(obj->relocbase + rel->r_offset); - def = find_symdef(ELF_R_SYM(rel->r_info), obj, - &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); - if (def == NULL) - return -1; - reloc_jmpslot(where, - (Elf_Addr)(defobj->relocbase - + def->st_value), - defobj, obj, rel); - } - } else { - const Elf_Rela *relalim; - const Elf_Rela *rela; - - relalim = (const Elf_Rela *) - ((char *)obj->pltrela + obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { - Elf_Addr *where; - const Elf_Sym *def; - const Obj_Entry *defobj; - - where = (Elf_Addr *)(obj->relocbase + rela->r_offset); - def = find_symdef(ELF_R_SYM(rela->r_info), obj, - &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); - if (def == NULL) - return -1; - reloc_jmpslot(where, - (Elf_Addr)(defobj->relocbase - + def->st_value), - defobj, obj, (Elf_Rel *)rela); - } - } - obj->jmpslots_done = true; - return 0; -} - -/* Fixup the jump slot at "where" to transfer control to "target". */ -Elf_Addr -reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *obj, - const Obj_Entry *refobj, const Elf_Rel *rel) -{ - Elf_Addr stubaddr; - - dbg(" reloc_jmpslot: where=%p, target=%p, gp=%p", - (void *)where, (void *)target, (void *)obj->pltgot); - stubaddr = *where; - if (stubaddr != target) { - - /* - * Point this @fptr directly at the target. Update the - * gp value first so that we don't break another cpu - * which is currently executing the PLT entry. - */ - where[1] = (Elf_Addr) obj->pltgot; - ia64_mf(); - where[0] = target; - ia64_mf(); - } - - /* - * The caller needs an @fptr for the adjusted entry. The PLT - * entry serves this purpose nicely. - */ - return (Elf_Addr) where; -} - -/* - * XXX ia64 doesn't seem to have copy relocations. - * - * Returns 0 on success, -1 on failure. - */ -int -do_copy_relocations(Obj_Entry *dstobj) -{ - - return 0; -} - -/* - * Return the @fptr representing a given function symbol. - */ -void * -make_function_pointer(const Elf_Sym *sym, const Obj_Entry *obj) -{ - struct fptr **fptrs = obj->priv; - int index = sym - obj->symtab; - - if (!fptrs) { - /* - * This should only happen for something like - * dlsym("dlopen"). Actually, I'm not sure it can ever - * happen. - */ - fptrs = alloc_fptrs((Obj_Entry *) obj, false); - } - if (!fptrs[index]) { - Elf_Addr target, gp; - target = (Elf_Addr) (obj->relocbase + sym->st_value); - gp = (Elf_Addr) obj->pltgot; - fptrs[index] = alloc_fptr(target, gp); - } - return fptrs[index]; -} - -void -call_initfini_pointer(const Obj_Entry *obj, Elf_Addr target) -{ - struct fptr fptr; - - fptr.gp = (Elf_Addr) obj->pltgot; - fptr.target = target; - dbg(" initfini: target=%p, gp=%p", - (void *) fptr.target, (void *) fptr.gp); - ((InitFunc) &fptr)(); -} - -void -call_init_pointer(const Obj_Entry *obj, Elf_Addr target) -{ - struct fptr fptr; - - fptr.gp = (Elf_Addr) obj->pltgot; - fptr.target = target; - dbg(" initfini: target=%p, gp=%p", - (void *) fptr.target, (void *) fptr.gp); - ((InitArrFunc) &fptr)(main_argc, main_argv, environ); -} - -/* Initialize the special PLT entries. */ -void -init_pltgot(Obj_Entry *obj) -{ - const Elf_Dyn *dynp; - Elf_Addr *pltres = 0; - - /* - * When there are no PLT relocations, the DT_IA_64_PLT_RESERVE entry - * is bogus. Do not setup the BOR pointers in that case. An example - * of where this happens is /usr/lib/libxpg4.so.3. - */ - if (obj->pltrelasize == 0 && obj->pltrelsize == 0) - return; - - /* - * Find the PLT RESERVE section. - */ - for (dynp = obj->dynamic; dynp->d_tag != DT_NULL; dynp++) { - if (dynp->d_tag == DT_IA_64_PLT_RESERVE) - pltres = (u_int64_t *) - (obj->relocbase + dynp->d_un.d_ptr); - } - if (!pltres) - errx(1, "Can't find DT_IA_64_PLT_RESERVE entry"); - - /* - * The PLT RESERVE section is used to get values to pass to - * _rtld_bind when lazy binding. - */ - pltres[0] = (Elf_Addr) obj; - pltres[1] = FPTR_TARGET(_rtld_bind_start); - pltres[2] = FPTR_GP(_rtld_bind_start); -} - -void -allocate_initial_tls(Obj_Entry *list) -{ - void *tpval; - - /* - * Fix the size of the static TLS block by using the maximum - * offset allocated so far and adding a bit for dynamic modules to - * use. - */ - tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA; - - tpval = allocate_tls(list, NULL, TLS_TCB_SIZE, 16); - __asm __volatile("mov r13 = %0" :: "r"(tpval)); -} - -void *__tls_get_addr(unsigned long module, unsigned long offset) -{ - register Elf_Addr** tp __asm__("r13"); - - return tls_get_addr_common(tp, module, offset); -} |