xref: /openbsd-src/libexec/ld.so/powerpc64/rtld_machine.c (revision b722ba42570161220f25c5d789b5bec8a0166743)
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