1/*	$NetBSD: ppc_reloc.c,v 1.63 2023/06/04 01:24:57 joerg Exp $	*/
2
3/*-
4 * Copyright (C) 1998	Tsubai Masanari
5 * Portions copyright 2002 Charles M. Hannum <root@ihack.net>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32#ifndef lint
33__RCSID("$NetBSD: ppc_reloc.c,v 1.63 2023/06/04 01:24:57 joerg Exp $");
34#endif /* not lint */
35
36#include <stdarg.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/types.h>
41#include <machine/cpu.h>
42
43#include "debug.h"
44#include "rtld.h"
45
46void _rtld_powerpc_pltcall(Elf_Word);
47void _rtld_powerpc_pltresolve(Elf_Word, Elf_Word);
48
49#define __u64(x)	((uint64_t)(x))
50#define __u32(x)	((uint32_t)(x))
51#define __ha48		__u64(0xffffffff8000)
52#define __ha32		__u64(0xffff8000)
53#define __ha16		__u32(0x8000)
54#define __ha(x,n) ((((x) >> (n)) + (((x) & __ha##n) == __ha##n)) & 0xffff)
55#define __hi(x,n) (((x) >> (n)) & 0xffff)
56#ifdef __LP64
57#define highesta(x)	__ha(__u64(x), 48)
58#define highest(x)	__hi(__u64(x), 48)
59#define higher(x)	__ha(__u64(x), 32)
60#define higher(x)	__hi(__u64(x), 32)
61#endif
62#define ha(x)		__ha(__u32(x), 16)
63#define hi(x)		__hi(__u32(x), 16)
64#define lo(x)		(__u32(x) & 0xffff)
65
66#ifdef _LP64
67/* function descriptor for _rtld_bind_start */
68extern const uint64_t _rtld_bind_start[3];
69#else
70void _rtld_bind_bssplt_start(void);
71void _rtld_bind_secureplt_start(void);
72#endif
73Elf_Addr _rtld_bind(const Obj_Entry *, Elf_Word);
74void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
75static int _rtld_relocate_plt_object(const Obj_Entry *,
76    const Elf_Rela *, int, Elf_Addr *);
77
78/*
79 * The PPC32 PLT format consists of three sections:
80 * (1) The "pltcall" and "pltresolve" glue code.  This is always 18 words.
81 * (2) The code part of the PLT entries.  There are 2 words per entry for
82 *     up to 8192 entries, then 4 words per entry for any additional entries.
83 * (3) The data part of the PLT entries, comprising a jump table.
84 *     This section is half the size of the second section (ie. 1 or 2 words
85 *     per entry).
86 */
87
88void
89_rtld_setup_pltgot(const Obj_Entry *obj)
90{
91#ifdef _LP64
92	/*
93	 * For powerpc64, just copy the function descriptor to pltgot[0].
94	 */
95	if (obj->pltgot != NULL) {
96		obj->pltgot[0] = (Elf_Addr) _rtld_bind_start[0];
97		obj->pltgot[1] = (Elf_Addr) _rtld_bind_start[1];
98		obj->pltgot[2] = (Elf_Addr) obj;
99	}
100#else
101	/*
102	 * Secure-PLT is much more sane.
103	 */
104	if (obj->gotptr != NULL) {
105		obj->gotptr[1] = (Elf_Addr) _rtld_bind_secureplt_start;
106		obj->gotptr[2] = (Elf_Addr) obj;
107		dbg(("obj %s secure-plt gotptr=%p start=%p obj=%p",
108		    obj->path, obj->gotptr,
109		    (void *) obj->gotptr[1], (void *) obj->gotptr[2]));
110	} else {
111/*
112 * Setup the plt glue routines (for bss-plt).
113 */
114#define BSSPLTCALL_SIZE		20
115#define BSSPLTRESOLVE_SIZE	24
116
117		Elf_Word *pltcall, *pltresolve;
118		Elf_Word *jmptab;
119		int N = obj->pltrelalim - obj->pltrela;
120
121		/* Entries beyond 8192 take twice as much space. */
122		if (N > 8192)
123			N += N-8192;
124
125		dbg(("obj %s bss-plt pltgot=%p jmptab=%u start=%p obj=%p",
126		    obj->path, obj->pltgot, 18 + N * 2,
127		    _rtld_bind_bssplt_start, obj));
128
129		pltcall = obj->pltgot;
130		jmptab = pltcall + 18 + N * 2;
131
132		memcpy(pltcall, _rtld_powerpc_pltcall, BSSPLTCALL_SIZE);
133		pltcall[1] |= ha(jmptab);
134		pltcall[2] |= lo(jmptab);
135
136		pltresolve = obj->pltgot + 8;
137
138		memcpy(pltresolve, _rtld_powerpc_pltresolve, BSSPLTRESOLVE_SIZE);
139		pltresolve[0] |= ha(_rtld_bind_bssplt_start);
140		pltresolve[1] |= lo(_rtld_bind_bssplt_start);
141		pltresolve[3] |= ha(obj);
142		pltresolve[4] |= lo(obj);
143
144		/*
145		 * Invalidate the icache for only the code part of the PLT
146		 * (and not the jump table at the end).
147		 */
148		__syncicache(pltcall, (char *)jmptab - (char *)pltcall);
149	}
150#endif
151}
152
153void
154_rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
155{
156	const Elf_Rela *rela = 0, *relalim;
157	Elf_Addr relasz = 0;
158	Elf_Addr *where;
159
160	for (; dynp->d_tag != DT_NULL; dynp++) {
161		switch (dynp->d_tag) {
162		case DT_RELA:
163			rela = (const Elf_Rela *)(relocbase + dynp->d_un.d_ptr);
164			break;
165		case DT_RELASZ:
166			relasz = dynp->d_un.d_val;
167			break;
168		}
169	}
170	relalim = (const Elf_Rela *)((const uint8_t *)rela + relasz);
171	for (; rela < relalim; rela++) {
172		where = (Elf_Addr *)(relocbase + rela->r_offset);
173		*where = (Elf_Addr)(relocbase + rela->r_addend);
174	}
175}
176
177int
178_rtld_relocate_nonplt_objects(Obj_Entry *obj)
179{
180	const Elf_Rela *rela;
181	const Elf_Sym *def = NULL;
182	const Obj_Entry *defobj = NULL;
183	unsigned long last_symnum = ULONG_MAX;
184
185	for (rela = obj->rela; rela < obj->relalim; rela++) {
186		Elf_Addr        *where;
187		Elf_Addr         tmp;
188		unsigned long	 symnum;
189
190		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
191		symnum = ELF_R_SYM(rela->r_info);
192
193		switch (ELF_R_TYPE(rela->r_info)) {
194#ifdef _LP64
195		case R_TYPE(ADDR64):	/* <address> S + A */
196#else
197		case R_TYPE(ADDR32):	/* <address> S + A */
198		case R_TYPE(UADDR32):	/* <address> S + A */
199#endif
200		case R_TYPE(GLOB_DAT):	/* <address> S + A */
201		case R_TYPE(ADDR16_LO):
202		case R_TYPE(ADDR16_HI):
203		case R_TYPE(ADDR16_HA):
204		case R_TYPE(DTPMOD):
205		case R_TYPE(DTPREL):
206		case R_TYPE(TPREL):
207			if (last_symnum != symnum) {
208				last_symnum = symnum;
209				def = _rtld_find_symdef(symnum, obj, &defobj,
210				    false);
211				if (def == NULL)
212					return -1;
213			}
214			break;
215		default:
216			break;
217		}
218
219		switch (ELF_R_TYPE(rela->r_info)) {
220#if 1 /* XXX Should not be necessary. */
221		case R_TYPE(JMP_SLOT):
222#endif
223		case R_TYPE(NONE):
224			break;
225
226#ifdef _LP64
227		case R_TYPE(ADDR64):	/* <address> S + A */
228#else
229		case R_TYPE(ADDR32):	/* <address> S + A */
230		case R_TYPE(UADDR32):	/* <address> S + A */
231#endif
232		case R_TYPE(GLOB_DAT):	/* <address> S + A */
233			tmp = (Elf_Addr)(defobj->relocbase + def->st_value +
234			    rela->r_addend);
235			if (*where != tmp)
236				*where = tmp;
237			rdbg(("32/GLOB_DAT %s in %s --> %p in %s",
238			    obj->strtab + obj->symtab[symnum].st_name,
239			    obj->path, (void *)*where, defobj->path));
240			break;
241
242		/*
243		 * Recent GNU ld does not resolve ADDR16_{LO,HI,HA} if
244		 * the reloc is in a writable section and the symbol
245		 * is not already referenced from text.
246		 */
247		case R_TYPE(ADDR16_LO): {
248			tmp = (Elf_Addr)(defobj->relocbase + def->st_value +
249			    rela->r_addend);
250
251			uint16_t tmp16 = lo(tmp);
252
253			uint16_t *where16 = (uint16_t *)where;
254			if (*where16 != tmp16)
255				*where16 = tmp16;
256			rdbg(("ADDR16_LO %s in %s --> #lo(%p) = 0x%x in %s",
257			    obj->strtab + obj->symtab[symnum].st_name,
258			      obj->path, (void *)tmp, tmp16, defobj->path));
259			break;
260		}
261
262		case R_TYPE(ADDR16_HI):
263		case R_TYPE(ADDR16_HA): {
264			tmp = (Elf_Addr)(defobj->relocbase + def->st_value +
265			    rela->r_addend);
266
267			uint16_t tmp16 = hi(tmp);
268			if (ELF_R_TYPE(rela->r_info) == R_TYPE(ADDR16_HA)
269			    && (tmp & __ha16))
270				++tmp16; /* adjust to ha(tmp) */
271
272			uint16_t *where16 = (uint16_t *)where;
273			if (*where16 != tmp16)
274				*where16 = tmp16;
275			rdbg(("ADDR16_H%c %s in %s --> #h%c(%p) = 0x%x in %s",
276			      (ELF_R_TYPE(rela->r_info) == R_TYPE(ADDR16_HI)
277			           ? 'I' : 'A'),
278			      obj->strtab + obj->symtab[symnum].st_name,
279			      obj->path,
280			      (ELF_R_TYPE(rela->r_info) == R_TYPE(ADDR16_HI)
281			           ? 'i' : 'a'),
282			      (void *)tmp, tmp16, defobj->path));
283			break;
284		}
285
286		case R_TYPE(RELATIVE):	/* <address> B + A */
287			*where = (Elf_Addr)(obj->relocbase + rela->r_addend);
288			rdbg(("RELATIVE in %s --> %p", obj->path,
289			    (void *)*where));
290			break;
291
292		case R_TYPE(COPY):
293			/*
294			 * These are deferred until all other relocations have
295			 * been done.  All we do here is make sure that the
296			 * COPY relocation is not in a shared library.  They
297			 * are allowed only in executable files.
298			 */
299			if (obj->isdynamic) {
300				_rtld_error(
301			"%s: Unexpected R_COPY relocation in shared library",
302				    obj->path);
303				return -1;
304			}
305			rdbg(("COPY (avoid in main)"));
306			break;
307
308		case R_TYPE(DTPMOD):
309			*where = (Elf_Addr)defobj->tlsindex;
310			rdbg(("DTPMOD32 %s in %s --> %p in %s",
311			    obj->strtab + obj->symtab[symnum].st_name,
312			    obj->path, (void *)*where, defobj->path));
313			break;
314
315		case R_TYPE(DTPREL):
316			*where = (Elf_Addr)(def->st_value + rela->r_addend
317			    - TLS_DTV_OFFSET);
318			rdbg(("DTPREL32 %s in %s --> %p in %s",
319			    obj->strtab + obj->symtab[symnum].st_name,
320			    obj->path, (void *)*where, defobj->path));
321			break;
322
323		case R_TYPE(TPREL):
324			if (!defobj->tls_static &&
325			    _rtld_tls_offset_allocate(__UNCONST(defobj)))
326				return -1;
327
328			*where = (Elf_Addr)(def->st_value + rela->r_addend
329			    + defobj->tlsoffset - TLS_TP_OFFSET);
330			rdbg(("TPREL32 %s in %s --> %p in %s",
331			    obj->strtab + obj->symtab[symnum].st_name,
332			    obj->path, (void *)*where, defobj->path));
333			break;
334
335		case R_TYPE(IRELATIVE):
336			/* IFUNC relocations are handled in _rtld_call_ifunc */
337			if (obj->ifunc_remaining_nonplt == 0) {
338				obj->ifunc_remaining_nonplt =
339				    obj->relalim - rela;
340			}
341			break;
342
343		default:
344			rdbg(("sym = %lu, type = %lu, offset = %p, "
345			    "addend = %p, contents = %p, symbol = %s",
346			    (u_long)ELF_R_SYM(rela->r_info),
347			    (u_long)ELF_R_TYPE(rela->r_info),
348			    (void *)rela->r_offset, (void *)rela->r_addend,
349			    (void *)*where,
350			    obj->strtab + obj->symtab[symnum].st_name));
351			_rtld_error("%s: Unsupported relocation type %ld "
352			    "in non-PLT relocations",
353			    obj->path, (u_long) ELF_R_TYPE(rela->r_info));
354			return -1;
355		}
356	}
357	return 0;
358}
359
360int
361_rtld_relocate_plt_lazy(Obj_Entry *obj)
362{
363#ifdef _LP64
364	/*
365	 * For PowerPC64, the plt stubs handle an empty function descriptor
366	 * so there's nothing to do.
367	 */
368	/* XXX ifunc support */
369#else
370	Elf_Addr * const pltresolve = obj->pltgot + 8;
371	const Elf_Rela *rela;
372
373	for (rela = obj->pltrelalim; rela-- > obj->pltrela;) {
374		size_t reloff = rela - obj->pltrela;
375		Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset);
376
377		assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT) ||
378		       ELF_R_TYPE(rela->r_info) == R_TYPE(IRELATIVE));
379
380		if (ELF_R_TYPE(rela->r_info) == R_TYPE(IRELATIVE)) {
381			/* No ifunc support for old-style insecure PLT. */
382			assert(obj->gotptr != NULL);
383			obj->ifunc_remaining = obj->pltrelalim - rela;
384		}
385
386		if (obj->gotptr != NULL) {
387			/*
388			 * For now, simply treat then as relative.
389			 */
390			*where += (Elf_Addr)obj->relocbase;
391		} else {
392			int distance;
393
394			if (reloff < 32768) {
395				/* li	r11,reloff */
396				*where++ = 0x39600000 | reloff;
397			} else {
398				/* lis  r11,ha(reloff) */
399				/* addi	r11,lo(reloff) */
400				*where++ = 0x3d600000 | ha(reloff);
401				*where++ = 0x396b0000 | lo(reloff);
402			}
403			/* b	pltresolve */
404			distance = (Elf_Addr)pltresolve - (Elf_Addr)where;
405			*where++ = 0x48000000 | (distance & 0x03fffffc);
406
407			/*
408			 * Icache invalidation is not done for each entry here
409			 * because we sync the entire code part of the PLT once
410			 * in _rtld_setup_pltgot() after all the entries have been
411			 * initialized.
412			 */
413			/* __syncicache(where - 3, 12); */
414		}
415	}
416#endif /* !_LP64 */
417
418	return 0;
419}
420
421static int
422_rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela, int reloff, Elf_Addr *tp)
423{
424	Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset);
425	Elf_Addr value;
426	const Elf_Sym *def;
427	const Obj_Entry *defobj;
428	unsigned long info = rela->r_info;
429
430	assert(ELF_R_TYPE(info) == R_TYPE(JMP_SLOT));
431
432	def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, tp != NULL);
433	if (__predict_false(def == NULL))
434		return -1;
435	if (__predict_false(def == &_rtld_sym_zero))
436		return 0;
437
438	if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
439		if (tp == NULL)
440			return 0;
441		value = _rtld_resolve_ifunc(defobj, def);
442	} else {
443		value = (Elf_Addr)(defobj->relocbase + def->st_value);
444	}
445	rdbg(("bind now/fixup in %s --> new=%p",
446	    defobj->strtab + def->st_name, (void *)value));
447
448#ifdef _LP64
449	/*
450	 * For PowerPC64 we simply replace the function descriptor in the
451	 * PLTGOT with the one from source object.
452	 */
453	assert(where >= (Elf_Word *)obj->pltgot);
454	assert(where < (Elf_Word *)obj->pltgot + (obj->pltrelalim - obj->pltrela));
455	const Elf_Addr * const fdesc = (Elf_Addr *) value;
456	where[0] = fdesc[0];
457	where[1] = fdesc[1];
458	where[2] = fdesc[2];
459#else
460	ptrdiff_t distance = value - (Elf_Addr)where;
461	if (obj->gotptr != NULL) {
462		/*
463		 * For Secure-PLT we simply replace the entry in GOT with the
464		 * address of the routine.
465		 */
466		assert(where >= (Elf_Word *)obj->pltgot);
467		assert(where < (Elf_Word *)obj->pltgot + (obj->pltrelalim - obj->pltrela));
468		*where = value;
469	} else if (labs(distance) < 32*1024*1024) {	/* inside 32MB? */
470		/* b	value	# branch directly */
471		*where = 0x48000000 | (distance & 0x03fffffc);
472		__syncicache(where, 4);
473	} else {
474		Elf_Addr *pltcall, *jmptab;
475		int N = obj->pltrelalim - obj->pltrela;
476
477		/* Entries beyond 8192 take twice as much space. */
478		if (N > 8192)
479			N += N-8192;
480
481		pltcall = obj->pltgot;
482		jmptab = pltcall + 18 + N * 2;
483
484		jmptab[reloff] = value;
485
486		if (reloff < 32768) {
487			/* li	r11,reloff */
488			*where++ = 0x39600000 | reloff;
489		} else {
490#ifdef notyet
491			/* lis  r11,ha(value) */
492			/* addi	r11,lo(value) */
493			/* mtctr r11 */
494			/* bctr */
495			*where++ = 0x3d600000 | ha(value);
496			*where++ = 0x396b0000 | lo(value);
497			*where++ = 0x7d6903a6;
498			*where++ = 0x4e800420;
499#else
500			/* lis  r11,ha(reloff) */
501			/* addi	r11,lo(reloff) */
502			*where++ = 0x3d600000 | ha(reloff);
503			*where++ = 0x396b0000 | lo(reloff);
504#endif
505		}
506		/* b	pltcall	*/
507		distance = (Elf_Addr)pltcall - (Elf_Addr)where;
508		*where++ = 0x48000000 | (distance & 0x03fffffc);
509		__syncicache(where - 3, 12);
510	}
511#endif /* _LP64 */
512
513	if (tp)
514		*tp = value;
515	return 0;
516}
517
518Elf_Addr
519_rtld_bind(const Obj_Entry *obj, Elf_Word reloff)
520{
521	const Elf_Rela *rela = obj->pltrela + reloff;
522	Elf_Addr new_value;
523	int err;
524
525	new_value = 0;	/* XXX gcc */
526
527	_rtld_shared_enter();
528	err = _rtld_relocate_plt_object(obj, rela, reloff, &new_value);
529	if (err)
530		_rtld_die();
531	_rtld_shared_exit();
532
533#ifdef _LP64
534	return obj->glink;
535#else
536	return new_value;
537#endif
538}
539
540int
541_rtld_relocate_plt_objects(const Obj_Entry *obj)
542{
543	const Elf_Rela *rela;
544	int reloff;
545
546	for (rela = obj->pltrela, reloff = 0; rela < obj->pltrelalim; rela++, reloff++) {
547		if (_rtld_relocate_plt_object(obj, rela, reloff, NULL) < 0)
548			return -1;
549	}
550	return 0;
551}
552