/* $OpenBSD: rtld_machine.c,v 1.67 2019/08/06 04:01:41 guenther Exp $ */ /* * Copyright (c) 1999 Dale Rahn * Copyright (c) 2001 Niklas Hallqvist * Copyright (c) 2001 Artur Grabowski * * 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. * */ #define _DYN_LOADER #include #include #include #include #include #include #include #include #include "syscall.h" #include "archdep.h" #include "resolve.h" #define DT_PROC(n) ((n) - DT_LOPROC + DT_NUM) int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden; int _dl_md_reloc(elf_object_t *object, int rel, int relasz) { long i; long numrela; long relrel; int fails = 0; Elf64_Addr loff; Elf64_Addr prev_value = 0; const Elf_Sym *prev_sym = NULL; Elf64_Rela *relas; loff = object->obj_base; numrela = object->Dyn.info[relasz] / sizeof(Elf64_Rela); relrel = rel == DT_RELA ? object->relacount : 0; relas = (Elf64_Rela *)(object->Dyn.info[rel]); if (relas == NULL) return(0); if (relrel > numrela) _dl_die("relacount > numrel: %ld > %ld", relrel, numrela); if (! object->Dyn.info[DT_PROC(DT_ALPHA_PLTRO)]) _dl_die("unsupported insecure PLT object"); /* tight loop for leading RELATIVE relocs */ for (i = 0; i < relrel; i++, relas++) { Elf_Addr *r_addr; #ifdef DEBUG if (ELF64_R_TYPE(relas->r_info) != R_TYPE(RELATIVE)) _dl_die("RELACOUNT wrong"); #endif r_addr = (Elf64_Addr *)(relas->r_offset + loff); /* Handle unaligned RELATIVE relocs */ if ((((Elf_Addr)r_addr) & 0x7) != 0) { Elf_Addr tmp; _dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr)); tmp += loff; _dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr)); } else *r_addr += loff; } for (; i < numrela; i++, relas++) { Elf64_Addr *r_addr; struct sym_res sr; const Elf64_Sym *sym; const char *symn; r_addr = (Elf64_Addr *)(relas->r_offset + loff); if (ELF64_R_SYM(relas->r_info) == 0xffffffff) continue; sym = object->dyn.symtab; sym += ELF64_R_SYM(relas->r_info); symn = object->dyn.strtab + sym->st_name; switch (ELF64_R_TYPE(relas->r_info)) { case R_TYPE(REFQUAD): sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT, sym, object); if (sr.sym == NULL) goto resolve_failed; *r_addr += sr.obj->obj_base + sr.sym->st_value + relas->r_addend; break; case R_TYPE(RELATIVE): /* * There is a lot of unaligned RELATIVE * relocs generated by gcc in the exception handlers. */ if ((((Elf_Addr) r_addr) & 0x7) != 0) { Elf_Addr tmp; #if 0 _dl_printf("unaligned RELATIVE: %p type: %d %s 0x%lx -> 0x%lx\n", r_addr, ELF_R_TYPE(relas->r_info), object->load_name, *r_addr, *r_addr+loff); #endif _dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr)); tmp += loff; _dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr)); } else *r_addr += loff; break; case R_TYPE(JMP_SLOT): sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object); if (sr.sym == NULL) goto resolve_failed; *r_addr = sr.obj->obj_base + sr.sym->st_value + relas->r_addend; break; case R_TYPE(GLOB_DAT): if (sym == prev_sym) { *r_addr = prev_value + relas->r_addend; break; } sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT, sym, object); if (sr.sym == NULL) goto resolve_failed; prev_sym = sym; prev_value = sr.obj->obj_base + sr.sym->st_value; *r_addr = prev_value + relas->r_addend; break; case R_TYPE(NONE): break; default: _dl_die("%s: unsupported relocation '%s' %lld at %p", object->load_name, symn, ELF64_R_TYPE(relas->r_info), (void *)r_addr); } continue; resolve_failed: if (ELF_ST_BIND(sym->st_info) != STB_WEAK) fails++; } __asm volatile("imb" : : : "memory"); return (fails); } /* * Resolve a symbol at run-time. */ Elf_Addr _dl_bind(elf_object_t *object, int reloff) { Elf_RelA *rela; struct sym_res sr; const Elf_Sym *sym; const char *symn; uint64_t cookie = pcookie; struct { struct __kbind param; Elf_Addr newval; } buf; rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff); sym = object->dyn.symtab; sym += ELF64_R_SYM(rela->r_info); symn = object->dyn.strtab + sym->st_name; sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object); if (sr.sym == NULL) _dl_die("lazy binding failed!"); buf.newval = sr.obj->obj_base + sr.sym->st_value + rela->r_addend; if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn)) return (buf.newval); buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rela->r_offset); buf.param.kb_size = sizeof(Elf_Addr); /* directly code the syscall, so that it's actually inline here */ { register long syscall_num __asm("$0") /* v0 */ = SYS_kbind; register void *arg1 __asm("$16") /* a0 */ = &buf; register long arg2 __asm("$17") /* a1 */ = sizeof(buf); register long arg3 __asm("$18") /* a2 */ = cookie; __asm volatile( "call_pal %1" : "+r" (syscall_num) : "i" (PAL_OSF1_callsys), "r" (arg1), "r" (arg2), "r" (arg3) : "$19", "$20", "memory"); } return (buf.newval); } void _dl_bind_start(void) __dso_hidden; /* XXX */ /* * Relocate the Global Offset Table (GOT). */ int _dl_md_reloc_got(elf_object_t *object, int lazy) { int fails = 0; Elf_Addr *pltgot; if (object->Dyn.info[DT_PLTREL] != DT_RELA) return (0); pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT]; if (object->traced) lazy = 1; if (!lazy || pltgot == NULL) { fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ); } else { if (object->obj_base != 0) { int i, size; Elf_Addr *addr; Elf_RelA *rela; size = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA); rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]); for (i = 0; i < size; i++) { addr = (Elf_Addr *)(object->obj_base + rela[i].r_offset); *addr += object->obj_base; } } pltgot[0] = (Elf_Addr)_dl_bind_start; pltgot[1] = (Elf_Addr)object; } return (fails); }