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