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