1*1f204c7bSderaadt /* $OpenBSD: rtld_machine.c,v 1.72 2022/01/08 18:30:18 deraadt Exp $ */
289c547eeSrahnds
389c547eeSrahnds /*
489c547eeSrahnds * Copyright (c) 1999 Dale Rahn
589c547eeSrahnds *
689c547eeSrahnds * Redistribution and use in source and binary forms, with or without
789c547eeSrahnds * modification, are permitted provided that the following conditions
889c547eeSrahnds * are met:
989c547eeSrahnds * 1. Redistributions of source code must retain the above copyright
1089c547eeSrahnds * notice, this list of conditions and the following disclaimer.
1189c547eeSrahnds * 2. Redistributions in binary form must reproduce the above copyright
1289c547eeSrahnds * notice, this list of conditions and the following disclaimer in the
1389c547eeSrahnds * documentation and/or other materials provided with the distribution.
1489c547eeSrahnds *
1589c547eeSrahnds * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
1689c547eeSrahnds * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1789c547eeSrahnds * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1889c547eeSrahnds * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
1989c547eeSrahnds * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2089c547eeSrahnds * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2189c547eeSrahnds * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2289c547eeSrahnds * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2389c547eeSrahnds * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2489c547eeSrahnds * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2589c547eeSrahnds * SUCH DAMAGE.
2689c547eeSrahnds *
2789c547eeSrahnds */
2889c547eeSrahnds
2989c547eeSrahnds #define _DYN_LOADER
3089c547eeSrahnds
3189c547eeSrahnds #include <sys/types.h>
32b722ba42Sguenther #include <sys/exec_elf.h>
33325c51e2Sguenther #include <sys/syscall.h>
34325c51e2Sguenther #include <sys/unistd.h>
3589c547eeSrahnds
36b722ba42Sguenther #include <machine/reloc.h>
3789c547eeSrahnds
38b722ba42Sguenther #include "util.h"
3989c547eeSrahnds #include "resolve.h"
40*1f204c7bSderaadt #include "archdep.h"
4189c547eeSrahnds
422bf2b428Skettenis #define DT_PROC(n) ((n) - DT_LOPROC + DT_NUM)
432bf2b428Skettenis
44325c51e2Sguenther int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
45325c51e2Sguenther
46ddabb23eSdrahn /* relocation bits */
47ddabb23eSdrahn #define B24_VALID_RANGE(x) \
48ddabb23eSdrahn ((((x) & 0xfe000000) == 0x00000000) || (((x) & 0xfe000000) == 0xfe000000))
49ddabb23eSdrahn
50ddabb23eSdrahn void _dl_bind_start(void); /* XXX */
5150f3e86fSdrahn Elf_Addr _dl_bind(elf_object_t *object, int reloff);
52ddabb23eSdrahn
5389c547eeSrahnds int
_dl_md_reloc(elf_object_t * object,int rel,int relasz)5489c547eeSrahnds _dl_md_reloc(elf_object_t *object, int rel, int relasz)
5589c547eeSrahnds {
5689c547eeSrahnds int i;
5789c547eeSrahnds int numrela;
5888098a4dSguenther long relrel;
5989c547eeSrahnds int fails = 0;
60e3b0f1d9Sguenther Elf_Addr loff;
61e3b0f1d9Sguenther Elf_RelA *relas;
6289c547eeSrahnds /* for jmp table relocations */
63e3b0f1d9Sguenther Elf_Addr prev_value = 0, prev_ooff = 0;
64e3b0f1d9Sguenther const Elf_Sym *prev_sym = NULL;
6589c547eeSrahnds
66ce11e090Skurt loff = object->obj_base;
67e3b0f1d9Sguenther numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
6888098a4dSguenther relrel = rel == DT_RELA ? object->relacount : 0;
69e3b0f1d9Sguenther relas = (Elf_RelA *)(object->Dyn.info[rel]);
7089c547eeSrahnds
71bb87ac8eSart if (relas == NULL)
72e3b0f1d9Sguenther return 0;
7339b7d201Sderaadt
743b50b772Sguenther if (relrel > numrela)
753b50b772Sguenther _dl_die("relcount > numrel: %ld > %d", relrel, numrela);
7688098a4dSguenther
77989dfc87Sguenther if (object->Dyn.info[DT_PROC(DT_PPC_GOT)] == 0)
78989dfc87Sguenther _dl_die("unsupported insecure BSS PLT object");
7989c547eeSrahnds
8088098a4dSguenther /* tight loop for leading RELATIVE relocs */
8188098a4dSguenther for (i = 0; i < relrel; i++, relas++) {
8288098a4dSguenther Elf_Addr *r_addr;
83c452b65eSdrahn
8488098a4dSguenther r_addr = (Elf_Addr *)(relas->r_offset + loff);
8588098a4dSguenther *r_addr = loff + relas->r_addend;
8688098a4dSguenther }
8788098a4dSguenther for (; i < numrela; i++, relas++) {
88e3b0f1d9Sguenther Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff);
89e3b0f1d9Sguenther const Elf_Sym *sym;
9089c547eeSrahnds const char *symn;
914ffea0b8Sdrahn int type;
9289c547eeSrahnds
93e3b0f1d9Sguenther if (ELF_R_SYM(relas->r_info) == 0xffffff)
9489c547eeSrahnds continue;
9589c547eeSrahnds
96e3b0f1d9Sguenther type = ELF_R_TYPE(relas->r_info);
97ddabb23eSdrahn
98ddabb23eSdrahn if (type == RELOC_JMP_SLOT && rel != DT_JMPREL)
99ddabb23eSdrahn continue;
100ddabb23eSdrahn
10189c547eeSrahnds sym = object->dyn.symtab;
102e3b0f1d9Sguenther sym += ELF_R_SYM(relas->r_info);
10389c547eeSrahnds symn = object->dyn.strtab + sym->st_name;
10489c547eeSrahnds
105e3b0f1d9Sguenther if (ELF_R_SYM(relas->r_info) &&
106e3b0f1d9Sguenther !(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
107e3b0f1d9Sguenther ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
108143e5accSguenther sym != prev_sym) {
109143e5accSguenther struct sym_res sr;
110143e5accSguenther
111143e5accSguenther sr = _dl_find_symbol(symn,
112ce869488Sdrahn SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
11388098a4dSguenther ((type == RELOC_JMP_SLOT) ?
114143e5accSguenther SYM_PLT:SYM_NOTPLT), sym, object);
1154ffea0b8Sdrahn
116143e5accSguenther if (sr.sym == NULL) {
117143e5accSguenther if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
11889c547eeSrahnds fails++;
1196718d15cSdrahn continue;
1206718d15cSdrahn }
12188098a4dSguenther prev_sym = sym;
122143e5accSguenther prev_value = sr.sym->st_value;
123143e5accSguenther prev_ooff = sr.obj->obj_base;
12489c547eeSrahnds }
12589c547eeSrahnds
1264ffea0b8Sdrahn switch (type) {
12789c547eeSrahnds case RELOC_32:
128e3b0f1d9Sguenther if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
129e3b0f1d9Sguenther (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
130e3b0f1d9Sguenther ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
131143e5accSguenther *r_addr = prev_ooff + relas->r_addend;
13289c547eeSrahnds } else {
133143e5accSguenther *r_addr = prev_ooff + prev_value +
13489c547eeSrahnds relas->r_addend;
13589c547eeSrahnds }
13689c547eeSrahnds break;
13789c547eeSrahnds case RELOC_RELATIVE:
138e3b0f1d9Sguenther if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
139e3b0f1d9Sguenther (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
140e3b0f1d9Sguenther ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
14189c547eeSrahnds *r_addr = loff + relas->r_addend;
14289c547eeSrahnds } else {
14388098a4dSguenther *r_addr = loff + prev_value +
14489c547eeSrahnds relas->r_addend;
14589c547eeSrahnds }
14689c547eeSrahnds break;
147989dfc87Sguenther /*
148989dfc87Sguenther * For Secure-PLT, RELOC_JMP_SLOT simply sets PLT
149989dfc87Sguenther * slots similarly to how RELOC_GLOB_DAT updates GOT
150989dfc87Sguenther * slots.
15189c547eeSrahnds */
152989dfc87Sguenther case RELOC_JMP_SLOT:
15389c547eeSrahnds case RELOC_GLOB_DAT:
154143e5accSguenther *r_addr = prev_ooff + prev_value + relas->r_addend;
15589c547eeSrahnds break;
156c452b65eSdrahn #if 1
15789c547eeSrahnds /* should not be supported ??? */
15889c547eeSrahnds case RELOC_REL24:
15989c547eeSrahnds {
160e3b0f1d9Sguenther Elf_Addr val = prev_ooff + prev_value +
161e3b0f1d9Sguenther relas->r_addend - (Elf_Addr)r_addr;
162ddabb23eSdrahn if (!B24_VALID_RANGE(val)) {
16389c547eeSrahnds /* invalid offset */
1643b50b772Sguenther _dl_die("%s: invalid %s offset %x at %p",
1653b50b772Sguenther object->load_name, "REL24", val,
166521c0a0eSguenther (void *)r_addr);
16789c547eeSrahnds }
16889c547eeSrahnds val &= ~0xfc000003;
16989c547eeSrahnds val |= (*r_addr & 0xfc000003);
17089c547eeSrahnds *r_addr = val;
17189c547eeSrahnds
17289c547eeSrahnds _dl_dcbf(r_addr);
17389c547eeSrahnds }
17489c547eeSrahnds break;
175c452b65eSdrahn #endif
176c452b65eSdrahn #if 1
177c452b65eSdrahn case RELOC_16_LO:
178c452b65eSdrahn {
179e3b0f1d9Sguenther Elf_Addr val;
180c452b65eSdrahn
181c452b65eSdrahn val = loff + relas->r_addend;
182e3b0f1d9Sguenther *(Elf_Half *)r_addr = val;
183c452b65eSdrahn
184c452b65eSdrahn _dl_dcbf(r_addr);
185c452b65eSdrahn }
186c452b65eSdrahn break;
187c452b65eSdrahn #endif
188c452b65eSdrahn #if 1
189c452b65eSdrahn case RELOC_16_HI:
190c452b65eSdrahn {
191e3b0f1d9Sguenther Elf_Addr val;
192c452b65eSdrahn
193c452b65eSdrahn val = loff + relas->r_addend;
194e3b0f1d9Sguenther *(Elf_Half *)r_addr = (val >> 16);
195c452b65eSdrahn
196c452b65eSdrahn _dl_dcbf(r_addr);
197c452b65eSdrahn }
198c452b65eSdrahn break;
199c452b65eSdrahn #endif
200c452b65eSdrahn #if 1
201c452b65eSdrahn case RELOC_16_HA:
202c452b65eSdrahn {
203e3b0f1d9Sguenther Elf_Addr val;
204c452b65eSdrahn
205c452b65eSdrahn val = loff + relas->r_addend;
206e3b0f1d9Sguenther *(Elf_Half *)r_addr = ((val + 0x8000) >> 16);
207c452b65eSdrahn
208c452b65eSdrahn _dl_dcbf(r_addr);
209c452b65eSdrahn }
210c452b65eSdrahn break;
211c452b65eSdrahn #endif
21289c547eeSrahnds case RELOC_REL14_TAKEN:
21389c547eeSrahnds /* val |= 1 << (31-10) XXX? */
21489c547eeSrahnds case RELOC_REL14:
21589c547eeSrahnds case RELOC_REL14_NTAKEN:
21689c547eeSrahnds {
217e3b0f1d9Sguenther Elf_Addr val = prev_ooff + prev_value +
218e3b0f1d9Sguenther relas->r_addend - (Elf_Addr)r_addr;
21989c547eeSrahnds if (((val & 0xffff8000) != 0) &&
22039b7d201Sderaadt ((val & 0xffff8000) != 0xffff8000)) {
22189c547eeSrahnds /* invalid offset */
2223b50b772Sguenther _dl_die("%s: invalid %s offset %x at %p",
2233b50b772Sguenther object->load_name, "REL14", val,
224521c0a0eSguenther (void *)r_addr);
22589c547eeSrahnds }
22689c547eeSrahnds val &= ~0xffff0003;
22789c547eeSrahnds val |= (*r_addr & 0xffff0003);
22889c547eeSrahnds *r_addr = val;
22989c547eeSrahnds _dl_dcbf(r_addr);
23089c547eeSrahnds }
23189c547eeSrahnds break;
23289c547eeSrahnds case RELOC_COPY:
2330acae5e5Sdrahn {
234143e5accSguenther struct sym_res sr;
23539b7d201Sderaadt /*
23639b7d201Sderaadt * we need to find a symbol, that is not in the current
23739b7d201Sderaadt * object, start looking at the beginning of the list,
23839b7d201Sderaadt * searching all objects but _not_ the current object,
23939b7d201Sderaadt * first one found wins.
24089c547eeSrahnds */
241143e5accSguenther sr = _dl_find_symbol(symn,
24206462af4Sdrahn SYM_SEARCH_OTHER|SYM_WARNNOTFOUND| SYM_NOTPLT,
243143e5accSguenther sym, object);
244143e5accSguenther if (sr.sym != NULL) {
245143e5accSguenther _dl_bcopy((void *)(sr.obj->obj_base + sr.sym->st_value),
246143e5accSguenther r_addr, sym->st_size);
24706462af4Sdrahn } else
24806462af4Sdrahn fails++;
24989c547eeSrahnds }
25089c547eeSrahnds break;
25189c547eeSrahnds case RELOC_NONE:
25289c547eeSrahnds break;
25389c547eeSrahnds
25489c547eeSrahnds default:
2553b50b772Sguenther _dl_die("%s: unsupported relocation '%s' %d at %p\n",
2563b50b772Sguenther object->load_name, symn,
257e3b0f1d9Sguenther ELF_R_TYPE(relas->r_info), (void *)r_addr );
25889c547eeSrahnds }
25989c547eeSrahnds }
2603e301017Sdrahn
261e3b0f1d9Sguenther return fails;
26289c547eeSrahnds }
26389c547eeSrahnds
264989dfc87Sguenther /*
265989dfc87Sguenther * Relocate the Global Offset Table (GOT).
266989dfc87Sguenther * This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
267989dfc87Sguenther * otherwise the lazy binding plt initialization is performed.
268989dfc87Sguenther */
269989dfc87Sguenther int
_dl_md_reloc_got(elf_object_t * object,int lazy)270989dfc87Sguenther _dl_md_reloc_got(elf_object_t *object, int lazy)
2712bf2b428Skettenis {
272989dfc87Sguenther int fails = 0;
273989dfc87Sguenther
274989dfc87Sguenther if (object->Dyn.info[DT_PLTREL] != DT_RELA)
275e3b0f1d9Sguenther return 0;
276989dfc87Sguenther
277989dfc87Sguenther if (!lazy) {
278989dfc87Sguenther fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
279989dfc87Sguenther } else {
280e3b0f1d9Sguenther Elf_Addr *got;
281e3b0f1d9Sguenther Elf_Addr *plt;
2822bf2b428Skettenis int numplt, i;
2832bf2b428Skettenis
2842bf2b428Skettenis /* Relocate processor-specific tags. */
2852bf2b428Skettenis object->Dyn.info[DT_PROC(DT_PPC_GOT)] += object->obj_base;
2862bf2b428Skettenis
287e3b0f1d9Sguenther got = (Elf_Addr *)
288e3b0f1d9Sguenther (Elf_RelA *)(object->Dyn.info[DT_PROC(DT_PPC_GOT)]);
289e3b0f1d9Sguenther got[1] = (Elf_Addr)_dl_bind_start;
290e3b0f1d9Sguenther got[2] = (Elf_Addr)object;
2912bf2b428Skettenis
292e3b0f1d9Sguenther plt = (Elf_Addr *)
293e3b0f1d9Sguenther (Elf_RelA *)(object->Dyn.info[DT_PLTGOT]);
294e3b0f1d9Sguenther numplt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
2952bf2b428Skettenis for (i = 0; i < numplt; i++)
2962bf2b428Skettenis plt[i] += object->obj_base;
2972bf2b428Skettenis }
2982bf2b428Skettenis
299e3b0f1d9Sguenther return fails;
300ddabb23eSdrahn }
301ddabb23eSdrahn
302ddabb23eSdrahn Elf_Addr
_dl_bind(elf_object_t * object,int reloff)303ddabb23eSdrahn _dl_bind(elf_object_t *object, int reloff)
304ddabb23eSdrahn {
305143e5accSguenther const Elf_Sym *sym;
306143e5accSguenther struct sym_res sr;
307ddabb23eSdrahn const char *symn;
308ddabb23eSdrahn Elf_RelA *relas;
309e3b0f1d9Sguenther Elf_Addr *plttable;
310325c51e2Sguenther int64_t cookie = pcookie;
311325c51e2Sguenther struct {
312989dfc87Sguenther struct __kbind param;
313989dfc87Sguenther Elf_Addr newval;
314325c51e2Sguenther } buf;
315ddabb23eSdrahn
3162bf2b428Skettenis relas = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
317ddabb23eSdrahn
318ddabb23eSdrahn sym = object->dyn.symtab;
319ddabb23eSdrahn sym += ELF_R_SYM(relas->r_info);
320ddabb23eSdrahn symn = object->dyn.strtab + sym->st_name;
321ddabb23eSdrahn
322143e5accSguenther sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
323143e5accSguenther sym, object);
324143e5accSguenther if (sr.sym == NULL)
3253b50b772Sguenther _dl_die("lazy binding failed!");
326ddabb23eSdrahn
327143e5accSguenther buf.newval = sr.obj->obj_base + sr.sym->st_value;
328ae398163Smiod
329143e5accSguenther if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
330989dfc87Sguenther return buf.newval;
331ae398163Smiod
332e3b0f1d9Sguenther plttable = (Elf_Addr *)(Elf_RelA *)(object->Dyn.info[DT_PLTGOT]);
333e3b0f1d9Sguenther buf.param.kb_addr = &plttable[ reloff / sizeof(Elf_RelA) ];
334989dfc87Sguenther buf.param.kb_size = sizeof(Elf_Addr);
335ddabb23eSdrahn
336325c51e2Sguenther {
337325c51e2Sguenther register long syscall_num __asm("r0") = SYS_kbind;
338989dfc87Sguenther register void *arg1 __asm("r3") = &buf.param;
339989dfc87Sguenther register long arg2 __asm("r4") = sizeof(struct __kbind) +
340989dfc87Sguenther sizeof(Elf_Addr);
341325c51e2Sguenther register long arg3 __asm("r5") = 0xffffffff & (cookie >> 32);
342325c51e2Sguenther register long arg4 __asm("r6") = 0xffffffff & cookie;
343325c51e2Sguenther
344325c51e2Sguenther __asm volatile("sc" : "+r" (syscall_num), "+r" (arg1),
345325c51e2Sguenther "+r" (arg2) : "r" (arg3), "r" (arg4) : "cc", "memory");
34691d8decdSdrahn }
347325c51e2Sguenther
348989dfc87Sguenther return buf.newval;
3491a28bf34Srahnds }
350