1*b722ba42Sguenther /* $OpenBSD: rtld_machine.c,v 1.7 2022/01/08 06:49:42 guenther Exp $ */
26a2e3db8Sdrahn
36a2e3db8Sdrahn /*
46a2e3db8Sdrahn * Copyright (c) 1999 Dale Rahn
56a2e3db8Sdrahn *
66a2e3db8Sdrahn * Redistribution and use in source and binary forms, with or without
76a2e3db8Sdrahn * modification, are permitted provided that the following conditions
86a2e3db8Sdrahn * are met:
96a2e3db8Sdrahn * 1. Redistributions of source code must retain the above copyright
106a2e3db8Sdrahn * notice, this list of conditions and the following disclaimer.
116a2e3db8Sdrahn * 2. Redistributions in binary form must reproduce the above copyright
126a2e3db8Sdrahn * notice, this list of conditions and the following disclaimer in the
136a2e3db8Sdrahn * documentation and/or other materials provided with the distribution.
146a2e3db8Sdrahn *
156a2e3db8Sdrahn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
166a2e3db8Sdrahn * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
176a2e3db8Sdrahn * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
186a2e3db8Sdrahn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
196a2e3db8Sdrahn * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
206a2e3db8Sdrahn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
216a2e3db8Sdrahn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
226a2e3db8Sdrahn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
236a2e3db8Sdrahn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
246a2e3db8Sdrahn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
256a2e3db8Sdrahn * SUCH DAMAGE.
266a2e3db8Sdrahn *
276a2e3db8Sdrahn */
286a2e3db8Sdrahn
296a2e3db8Sdrahn #define _DYN_LOADER
306a2e3db8Sdrahn
316a2e3db8Sdrahn #include <sys/types.h>
32*b722ba42Sguenther #include <sys/exec_elf.h>
336a2e3db8Sdrahn #include <sys/syscall.h>
346a2e3db8Sdrahn #include <sys/unistd.h>
356a2e3db8Sdrahn
36*b722ba42Sguenther #include <machine/reloc.h>
376a2e3db8Sdrahn
38*b722ba42Sguenther #include "util.h"
396a2e3db8Sdrahn #include "resolve.h"
406a2e3db8Sdrahn
416a2e3db8Sdrahn #define DT_PROC(n) ((n) - DT_LOPROC + DT_NUM)
426a2e3db8Sdrahn
436a2e3db8Sdrahn int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
446a2e3db8Sdrahn
456a2e3db8Sdrahn /* relocation bits */
466a2e3db8Sdrahn #define B24_VALID_RANGE(x) \
476a2e3db8Sdrahn ((((x) & 0xfe000000) == 0x00000000) || (((x) & 0xfe000000) == 0xfe000000))
486a2e3db8Sdrahn
496a2e3db8Sdrahn void _dl_bind_start(void); /* XXX */
506a2e3db8Sdrahn Elf_Addr _dl_bind(elf_object_t *object, int reloff);
516a2e3db8Sdrahn
526a2e3db8Sdrahn int
_dl_md_reloc(elf_object_t * object,int rel,int relasz)536a2e3db8Sdrahn _dl_md_reloc(elf_object_t *object, int rel, int relasz)
546a2e3db8Sdrahn {
556a2e3db8Sdrahn int i;
566a2e3db8Sdrahn int numrela;
576a2e3db8Sdrahn long relrel;
586a2e3db8Sdrahn int fails = 0;
596a2e3db8Sdrahn Elf_Addr loff;
606a2e3db8Sdrahn Elf_RelA *relas;
616a2e3db8Sdrahn /* for jmp table relocations */
626a2e3db8Sdrahn Elf_Addr prev_value = 0, prev_ooff = 0;
636a2e3db8Sdrahn const Elf_Sym *prev_sym = NULL;
646a2e3db8Sdrahn
656a2e3db8Sdrahn loff = object->obj_base;
666a2e3db8Sdrahn numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
676a2e3db8Sdrahn relrel = rel == DT_RELA ? object->relacount : 0;
686a2e3db8Sdrahn relas = (Elf_RelA *)(object->Dyn.info[rel]);
696a2e3db8Sdrahn
706a2e3db8Sdrahn if (relas == NULL)
716a2e3db8Sdrahn return 0;
726a2e3db8Sdrahn
736a2e3db8Sdrahn if (relrel > numrela)
746a2e3db8Sdrahn _dl_die("relcount > numrel: %ld > %d", relrel, numrela);
756a2e3db8Sdrahn
766a2e3db8Sdrahn /* tight loop for leading RELATIVE relocs */
776a2e3db8Sdrahn for (i = 0; i < relrel; i++, relas++) {
786a2e3db8Sdrahn Elf_Addr *r_addr;
796a2e3db8Sdrahn
806a2e3db8Sdrahn r_addr = (Elf_Addr *)(relas->r_offset + loff);
816a2e3db8Sdrahn *r_addr = loff + relas->r_addend;
826a2e3db8Sdrahn }
836a2e3db8Sdrahn for (; i < numrela; i++, relas++) {
846a2e3db8Sdrahn Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff);
856a2e3db8Sdrahn const Elf_Sym *sym;
866a2e3db8Sdrahn const char *symn;
876a2e3db8Sdrahn int type;
886a2e3db8Sdrahn
896a2e3db8Sdrahn if (ELF_R_SYM(relas->r_info) == 0xffffff)
906a2e3db8Sdrahn continue;
916a2e3db8Sdrahn
926a2e3db8Sdrahn type = ELF_R_TYPE(relas->r_info);
936a2e3db8Sdrahn
9492e19e77Skettenis if (type == R_PPC64_JMP_SLOT && rel != DT_JMPREL)
956a2e3db8Sdrahn continue;
966a2e3db8Sdrahn
976a2e3db8Sdrahn sym = object->dyn.symtab;
986a2e3db8Sdrahn sym += ELF_R_SYM(relas->r_info);
996a2e3db8Sdrahn symn = object->dyn.strtab + sym->st_name;
1006a2e3db8Sdrahn
1016a2e3db8Sdrahn if (ELF_R_SYM(relas->r_info) &&
1026a2e3db8Sdrahn !(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
1036a2e3db8Sdrahn ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
1046a2e3db8Sdrahn sym != prev_sym) {
1056a2e3db8Sdrahn struct sym_res sr;
1066a2e3db8Sdrahn
1076a2e3db8Sdrahn sr = _dl_find_symbol(symn,
1086a2e3db8Sdrahn SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
10992e19e77Skettenis ((type == R_PPC64_JMP_SLOT) ?
1106a2e3db8Sdrahn SYM_PLT:SYM_NOTPLT), sym, object);
1116a2e3db8Sdrahn
1126a2e3db8Sdrahn if (sr.sym == NULL) {
1136a2e3db8Sdrahn if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
1146a2e3db8Sdrahn fails++;
1156a2e3db8Sdrahn continue;
1166a2e3db8Sdrahn }
1176a2e3db8Sdrahn prev_sym = sym;
1186a2e3db8Sdrahn prev_value = sr.sym->st_value;
1196a2e3db8Sdrahn prev_ooff = sr.obj->obj_base;
1206a2e3db8Sdrahn }
1216a2e3db8Sdrahn
1226a2e3db8Sdrahn switch (type) {
12392e19e77Skettenis case R_PPC64_ADDR64:
1246a2e3db8Sdrahn if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
1256a2e3db8Sdrahn (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
1266a2e3db8Sdrahn ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
1276a2e3db8Sdrahn *r_addr = prev_ooff + relas->r_addend;
1286a2e3db8Sdrahn } else {
1296a2e3db8Sdrahn *r_addr = prev_ooff + prev_value +
1306a2e3db8Sdrahn relas->r_addend;
1316a2e3db8Sdrahn }
1326a2e3db8Sdrahn break;
13392e19e77Skettenis case R_PPC64_RELATIVE:
1346a2e3db8Sdrahn if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
1356a2e3db8Sdrahn (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
1366a2e3db8Sdrahn ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
1376a2e3db8Sdrahn *r_addr = loff + relas->r_addend;
1386a2e3db8Sdrahn } else {
1396a2e3db8Sdrahn *r_addr = loff + prev_value +
1406a2e3db8Sdrahn relas->r_addend;
1416a2e3db8Sdrahn }
1426a2e3db8Sdrahn break;
1436a2e3db8Sdrahn /*
1446a2e3db8Sdrahn * For Secure-PLT, RELOC_JMP_SLOT simply sets PLT
1456a2e3db8Sdrahn * slots similarly to how RELOC_GLOB_DAT updates GOT
1466a2e3db8Sdrahn * slots.
1476a2e3db8Sdrahn */
14892e19e77Skettenis case R_PPC64_JMP_SLOT:
14992e19e77Skettenis case R_PPC64_GLOB_DAT:
1506a2e3db8Sdrahn *r_addr = prev_ooff + prev_value + relas->r_addend;
1516a2e3db8Sdrahn break;
1526a2e3db8Sdrahn #if 0
1536a2e3db8Sdrahn /* should not be supported ??? */
1546a2e3db8Sdrahn case RELOC_REL24:
1556a2e3db8Sdrahn {
1566a2e3db8Sdrahn Elf_Addr val = prev_ooff + prev_value +
1576a2e3db8Sdrahn relas->r_addend - (Elf_Addr)r_addr;
1586a2e3db8Sdrahn if (!B24_VALID_RANGE(val)) {
1596a2e3db8Sdrahn /* invalid offset */
1606a2e3db8Sdrahn _dl_die("%s: invalid %s offset %llx at %p",
1616a2e3db8Sdrahn object->load_name, "REL24", val,
1626a2e3db8Sdrahn (void *)r_addr);
1636a2e3db8Sdrahn }
1646a2e3db8Sdrahn val &= ~0xfc000003;
1656a2e3db8Sdrahn val |= (*r_addr & 0xfc000003);
1666a2e3db8Sdrahn *r_addr = val;
1676a2e3db8Sdrahn
1686a2e3db8Sdrahn _dl_dcbf(r_addr);
1696a2e3db8Sdrahn }
1706a2e3db8Sdrahn break;
1716a2e3db8Sdrahn #endif
1726a2e3db8Sdrahn #if 0
1736a2e3db8Sdrahn case RELOC_16_LO:
1746a2e3db8Sdrahn {
1756a2e3db8Sdrahn Elf_Addr val;
1766a2e3db8Sdrahn
1776a2e3db8Sdrahn val = loff + relas->r_addend;
1786a2e3db8Sdrahn *(Elf_Half *)r_addr = val;
1796a2e3db8Sdrahn
1806a2e3db8Sdrahn _dl_dcbf(r_addr);
1816a2e3db8Sdrahn }
1826a2e3db8Sdrahn break;
1836a2e3db8Sdrahn #endif
1846a2e3db8Sdrahn #if 0
1856a2e3db8Sdrahn case RELOC_16_HI:
1866a2e3db8Sdrahn {
1876a2e3db8Sdrahn Elf_Addr val;
1886a2e3db8Sdrahn
1896a2e3db8Sdrahn val = loff + relas->r_addend;
1906a2e3db8Sdrahn *(Elf_Half *)r_addr = (val >> 16);
1916a2e3db8Sdrahn
1926a2e3db8Sdrahn _dl_dcbf(r_addr);
1936a2e3db8Sdrahn }
1946a2e3db8Sdrahn break;
1956a2e3db8Sdrahn #endif
1966a2e3db8Sdrahn #if 0
1976a2e3db8Sdrahn case RELOC_16_HA:
1986a2e3db8Sdrahn {
1996a2e3db8Sdrahn Elf_Addr val;
2006a2e3db8Sdrahn
2016a2e3db8Sdrahn val = loff + relas->r_addend;
2026a2e3db8Sdrahn *(Elf_Half *)r_addr = ((val + 0x8000) >> 16);
2036a2e3db8Sdrahn
2046a2e3db8Sdrahn _dl_dcbf(r_addr);
2056a2e3db8Sdrahn }
2066a2e3db8Sdrahn break;
2076a2e3db8Sdrahn #endif
20892e19e77Skettenis #if 0
2096a2e3db8Sdrahn case RELOC_REL14_TAKEN:
2106a2e3db8Sdrahn /* val |= 1 << (31-10) XXX? */
2116a2e3db8Sdrahn case RELOC_REL14:
2126a2e3db8Sdrahn case RELOC_REL14_NTAKEN:
2136a2e3db8Sdrahn {
2146a2e3db8Sdrahn Elf_Addr val = prev_ooff + prev_value +
2156a2e3db8Sdrahn relas->r_addend - (Elf_Addr)r_addr;
2166a2e3db8Sdrahn if (((val & 0xffff8000) != 0) &&
2176a2e3db8Sdrahn ((val & 0xffff8000) != 0xffff8000)) {
2186a2e3db8Sdrahn /* invalid offset */
2196a2e3db8Sdrahn _dl_die("%s: invalid %s offset %llx at %p",
2206a2e3db8Sdrahn object->load_name, "REL14", val,
2216a2e3db8Sdrahn (void *)r_addr);
2226a2e3db8Sdrahn }
2236a2e3db8Sdrahn val &= ~0xffff0003;
2246a2e3db8Sdrahn val |= (*r_addr & 0xffff0003);
2256a2e3db8Sdrahn *r_addr = val;
2266a2e3db8Sdrahn _dl_dcbf(r_addr);
2276a2e3db8Sdrahn }
2286a2e3db8Sdrahn break;
22992e19e77Skettenis #endif
23092e19e77Skettenis case R_PPC64_COPY:
2316a2e3db8Sdrahn {
2326a2e3db8Sdrahn struct sym_res sr;
2336a2e3db8Sdrahn /*
2346a2e3db8Sdrahn * we need to find a symbol, that is not in the current
2356a2e3db8Sdrahn * object, start looking at the beginning of the list,
2366a2e3db8Sdrahn * searching all objects but _not_ the current object,
2376a2e3db8Sdrahn * first one found wins.
2386a2e3db8Sdrahn */
2396a2e3db8Sdrahn sr = _dl_find_symbol(symn,
2406a2e3db8Sdrahn SYM_SEARCH_OTHER|SYM_WARNNOTFOUND| SYM_NOTPLT,
2416a2e3db8Sdrahn sym, object);
2426a2e3db8Sdrahn if (sr.sym != NULL) {
2436a2e3db8Sdrahn _dl_bcopy((void *)(sr.obj->obj_base + sr.sym->st_value),
2446a2e3db8Sdrahn r_addr, sym->st_size);
2456a2e3db8Sdrahn } else
2466a2e3db8Sdrahn fails++;
2476a2e3db8Sdrahn }
2486a2e3db8Sdrahn break;
24992e19e77Skettenis case R_PPC64_NONE:
2506a2e3db8Sdrahn break;
2516a2e3db8Sdrahn
2526a2e3db8Sdrahn default:
2536a2e3db8Sdrahn _dl_die("%s: unsupported relocation '%s' %lld at %p\n",
2546a2e3db8Sdrahn object->load_name, symn,
2556a2e3db8Sdrahn ELF_R_TYPE(relas->r_info), (void *)r_addr );
2566a2e3db8Sdrahn }
2576a2e3db8Sdrahn }
2586a2e3db8Sdrahn
2596a2e3db8Sdrahn return fails;
2606a2e3db8Sdrahn }
2616a2e3db8Sdrahn
2626a2e3db8Sdrahn /*
2636a2e3db8Sdrahn * Relocate the Global Offset Table (GOT).
2646a2e3db8Sdrahn * This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
2656a2e3db8Sdrahn * otherwise the lazy binding plt initialization is performed.
2666a2e3db8Sdrahn */
2676a2e3db8Sdrahn int
_dl_md_reloc_got(elf_object_t * object,int lazy)2686a2e3db8Sdrahn _dl_md_reloc_got(elf_object_t *object, int lazy)
2696a2e3db8Sdrahn {
2706a2e3db8Sdrahn int fails = 0;
2716a2e3db8Sdrahn
2726a2e3db8Sdrahn if (object->Dyn.info[DT_PLTREL] != DT_RELA)
2736a2e3db8Sdrahn return 0;
2746a2e3db8Sdrahn
275af856e3aSkettenis if (!lazy) {
2766a2e3db8Sdrahn fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
277af856e3aSkettenis } else {
278af856e3aSkettenis Elf_Addr *plt;
279122a6b72Skettenis int numplt, n;
280af856e3aSkettenis
281af856e3aSkettenis /* Relocate processor-specific tags. */
282af856e3aSkettenis object->Dyn.info[DT_PROC(DT_PPC64_GLINK)] += object->obj_base;
283af856e3aSkettenis
284af856e3aSkettenis plt = (Elf_Addr *)
285af856e3aSkettenis (Elf_RelA *)(object->Dyn.info[DT_PLTGOT]);
286af856e3aSkettenis numplt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
287af856e3aSkettenis plt[0] = (uint64_t)_dl_bind_start;
288af856e3aSkettenis plt[1] = (uint64_t)object;
289122a6b72Skettenis for (n = 0; n < numplt; n++) {
290122a6b72Skettenis plt[n + 2] = object->Dyn.info[DT_PROC(DT_PPC64_GLINK)] +
291122a6b72Skettenis n * 4 + 32;
292af856e3aSkettenis }
293af856e3aSkettenis }
2946a2e3db8Sdrahn
2956a2e3db8Sdrahn return fails;
2966a2e3db8Sdrahn }
2976a2e3db8Sdrahn
2986a2e3db8Sdrahn Elf_Addr
_dl_bind(elf_object_t * object,int relidx)299af856e3aSkettenis _dl_bind(elf_object_t *object, int relidx)
3006a2e3db8Sdrahn {
3016a2e3db8Sdrahn const Elf_Sym *sym;
3026a2e3db8Sdrahn struct sym_res sr;
3036a2e3db8Sdrahn const char *symn;
3046a2e3db8Sdrahn Elf_RelA *relas;
3056a2e3db8Sdrahn Elf_Addr *plttable;
3066a2e3db8Sdrahn int64_t cookie = pcookie;
3076a2e3db8Sdrahn struct {
3086a2e3db8Sdrahn struct __kbind param;
3096a2e3db8Sdrahn Elf_Addr newval;
3106a2e3db8Sdrahn } buf;
3116a2e3db8Sdrahn
312af856e3aSkettenis relas = ((Elf_RelA *)object->Dyn.info[DT_JMPREL]) + relidx;
3136a2e3db8Sdrahn
3146a2e3db8Sdrahn sym = object->dyn.symtab;
3156a2e3db8Sdrahn sym += ELF_R_SYM(relas->r_info);
3166a2e3db8Sdrahn symn = object->dyn.strtab + sym->st_name;
3176a2e3db8Sdrahn
3186a2e3db8Sdrahn sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
3196a2e3db8Sdrahn sym, object);
3206a2e3db8Sdrahn if (sr.sym == NULL)
3216a2e3db8Sdrahn _dl_die("lazy binding failed!");
3226a2e3db8Sdrahn
3236a2e3db8Sdrahn buf.newval = sr.obj->obj_base + sr.sym->st_value;
3246a2e3db8Sdrahn
3256a2e3db8Sdrahn if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
3266a2e3db8Sdrahn return buf.newval;
3276a2e3db8Sdrahn
3286a2e3db8Sdrahn plttable = (Elf_Addr *)(Elf_RelA *)(object->Dyn.info[DT_PLTGOT]);
329af856e3aSkettenis buf.param.kb_addr = &plttable[relidx + 2];
3306a2e3db8Sdrahn buf.param.kb_size = sizeof(Elf_Addr);
3316a2e3db8Sdrahn
3326a2e3db8Sdrahn {
3336a2e3db8Sdrahn register long syscall_num __asm("r0") = SYS_kbind;
3346a2e3db8Sdrahn register void *arg1 __asm("r3") = &buf.param;
3356a2e3db8Sdrahn register long arg2 __asm("r4") = sizeof(struct __kbind) +
3366a2e3db8Sdrahn sizeof(Elf_Addr);
3376a2e3db8Sdrahn register long arg3 __asm("r5") = cookie;
3386a2e3db8Sdrahn
3396a2e3db8Sdrahn __asm volatile("sc" : "+r" (syscall_num), "+r" (arg1),
3406a2e3db8Sdrahn "+r" (arg2) : "r" (arg3) : "cc", "memory");
3416a2e3db8Sdrahn }
3426a2e3db8Sdrahn
3436a2e3db8Sdrahn return buf.newval;
3446a2e3db8Sdrahn }
345