/* $OpenBSD: rtld_machine.c,v 1.7 2022/01/08 06:49:42 guenther Exp $ */ /* * Copyright (c) 1999 Dale Rahn * * 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 "util.h" #include "resolve.h" #define DT_PROC(n) ((n) - DT_LOPROC + DT_NUM) int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden; /* relocation bits */ #define B24_VALID_RANGE(x) \ ((((x) & 0xfe000000) == 0x00000000) || (((x) & 0xfe000000) == 0xfe000000)) void _dl_bind_start(void); /* XXX */ Elf_Addr _dl_bind(elf_object_t *object, int reloff); int _dl_md_reloc(elf_object_t *object, int rel, int relasz) { int i; int numrela; long relrel; int fails = 0; Elf_Addr loff; Elf_RelA *relas; /* for jmp table relocations */ Elf_Addr prev_value = 0, prev_ooff = 0; const Elf_Sym *prev_sym = NULL; loff = object->obj_base; numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA); relrel = rel == DT_RELA ? object->relacount : 0; relas = (Elf_RelA *)(object->Dyn.info[rel]); if (relas == NULL) return 0; if (relrel > numrela) _dl_die("relcount > numrel: %ld > %d", relrel, numrela); /* tight loop for leading RELATIVE relocs */ for (i = 0; i < relrel; i++, relas++) { Elf_Addr *r_addr; r_addr = (Elf_Addr *)(relas->r_offset + loff); *r_addr = loff + relas->r_addend; } for (; i < numrela; i++, relas++) { Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff); const Elf_Sym *sym; const char *symn; int type; if (ELF_R_SYM(relas->r_info) == 0xffffff) continue; type = ELF_R_TYPE(relas->r_info); if (type == R_PPC64_JMP_SLOT && rel != DT_JMPREL) continue; sym = object->dyn.symtab; sym += ELF_R_SYM(relas->r_info); symn = object->dyn.strtab + sym->st_name; if (ELF_R_SYM(relas->r_info) && !(ELF_ST_BIND(sym->st_info) == STB_LOCAL && ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) && sym != prev_sym) { struct sym_res sr; sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND| ((type == R_PPC64_JMP_SLOT) ? SYM_PLT:SYM_NOTPLT), sym, object); if (sr.sym == NULL) { if (ELF_ST_BIND(sym->st_info) != STB_WEAK) fails++; continue; } prev_sym = sym; prev_value = sr.sym->st_value; prev_ooff = sr.obj->obj_base; } switch (type) { case R_PPC64_ADDR64: if (ELF_ST_BIND(sym->st_info) == STB_LOCAL && (ELF_ST_TYPE(sym->st_info) == STT_SECTION || ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) { *r_addr = prev_ooff + relas->r_addend; } else { *r_addr = prev_ooff + prev_value + relas->r_addend; } break; case R_PPC64_RELATIVE: if (ELF_ST_BIND(sym->st_info) == STB_LOCAL && (ELF_ST_TYPE(sym->st_info) == STT_SECTION || ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) { *r_addr = loff + relas->r_addend; } else { *r_addr = loff + prev_value + relas->r_addend; } break; /* * For Secure-PLT, RELOC_JMP_SLOT simply sets PLT * slots similarly to how RELOC_GLOB_DAT updates GOT * slots. */ case R_PPC64_JMP_SLOT: case R_PPC64_GLOB_DAT: *r_addr = prev_ooff + prev_value + relas->r_addend; break; #if 0 /* should not be supported ??? */ case RELOC_REL24: { Elf_Addr val = prev_ooff + prev_value + relas->r_addend - (Elf_Addr)r_addr; if (!B24_VALID_RANGE(val)) { /* invalid offset */ _dl_die("%s: invalid %s offset %llx at %p", object->load_name, "REL24", val, (void *)r_addr); } val &= ~0xfc000003; val |= (*r_addr & 0xfc000003); *r_addr = val; _dl_dcbf(r_addr); } break; #endif #if 0 case RELOC_16_LO: { Elf_Addr val; val = loff + relas->r_addend; *(Elf_Half *)r_addr = val; _dl_dcbf(r_addr); } break; #endif #if 0 case RELOC_16_HI: { Elf_Addr val; val = loff + relas->r_addend; *(Elf_Half *)r_addr = (val >> 16); _dl_dcbf(r_addr); } break; #endif #if 0 case RELOC_16_HA: { Elf_Addr val; val = loff + relas->r_addend; *(Elf_Half *)r_addr = ((val + 0x8000) >> 16); _dl_dcbf(r_addr); } break; #endif #if 0 case RELOC_REL14_TAKEN: /* val |= 1 << (31-10) XXX? */ case RELOC_REL14: case RELOC_REL14_NTAKEN: { Elf_Addr val = prev_ooff + prev_value + relas->r_addend - (Elf_Addr)r_addr; if (((val & 0xffff8000) != 0) && ((val & 0xffff8000) != 0xffff8000)) { /* invalid offset */ _dl_die("%s: invalid %s offset %llx at %p", object->load_name, "REL14", val, (void *)r_addr); } val &= ~0xffff0003; val |= (*r_addr & 0xffff0003); *r_addr = val; _dl_dcbf(r_addr); } break; #endif case R_PPC64_COPY: { struct sym_res sr; /* * we need to find a symbol, that is not in the current * object, start looking at the beginning of the list, * searching all objects but _not_ the current object, * first one found wins. */ sr = _dl_find_symbol(symn, SYM_SEARCH_OTHER|SYM_WARNNOTFOUND| SYM_NOTPLT, sym, object); if (sr.sym != NULL) { _dl_bcopy((void *)(sr.obj->obj_base + sr.sym->st_value), r_addr, sym->st_size); } else fails++; } break; case R_PPC64_NONE: break; default: _dl_die("%s: unsupported relocation '%s' %lld at %p\n", object->load_name, symn, ELF_R_TYPE(relas->r_info), (void *)r_addr ); } } return fails; } /* * Relocate the Global Offset Table (GOT). * This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW, * otherwise the lazy binding plt initialization is performed. */ int _dl_md_reloc_got(elf_object_t *object, int lazy) { int fails = 0; if (object->Dyn.info[DT_PLTREL] != DT_RELA) return 0; if (!lazy) { fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ); } else { Elf_Addr *plt; int numplt, n; /* Relocate processor-specific tags. */ object->Dyn.info[DT_PROC(DT_PPC64_GLINK)] += object->obj_base; plt = (Elf_Addr *) (Elf_RelA *)(object->Dyn.info[DT_PLTGOT]); numplt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA); plt[0] = (uint64_t)_dl_bind_start; plt[1] = (uint64_t)object; for (n = 0; n < numplt; n++) { plt[n + 2] = object->Dyn.info[DT_PROC(DT_PPC64_GLINK)] + n * 4 + 32; } } return fails; } Elf_Addr _dl_bind(elf_object_t *object, int relidx) { const Elf_Sym *sym; struct sym_res sr; const char *symn; Elf_RelA *relas; Elf_Addr *plttable; int64_t cookie = pcookie; struct { struct __kbind param; Elf_Addr newval; } buf; relas = ((Elf_RelA *)object->Dyn.info[DT_JMPREL]) + relidx; sym = object->dyn.symtab; sym += ELF_R_SYM(relas->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; if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn)) return buf.newval; plttable = (Elf_Addr *)(Elf_RelA *)(object->Dyn.info[DT_PLTGOT]); buf.param.kb_addr = &plttable[relidx + 2]; buf.param.kb_size = sizeof(Elf_Addr); { register long syscall_num __asm("r0") = SYS_kbind; register void *arg1 __asm("r3") = &buf.param; register long arg2 __asm("r4") = sizeof(struct __kbind) + sizeof(Elf_Addr); register long arg3 __asm("r5") = cookie; __asm volatile("sc" : "+r" (syscall_num), "+r" (arg1), "+r" (arg2) : "r" (arg3) : "cc", "memory"); } return buf.newval; }