1/*	$NetBSD: hppa_reloc.c,v 1.50 2023/06/04 01:24:57 joerg Exp $	*/
2
3/*-
4 * Copyright (c) 2002, 2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Fredette and Nick Hudson.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34__RCSID("$NetBSD: hppa_reloc.c,v 1.50 2023/06/04 01:24:57 joerg Exp $");
35#endif /* not lint */
36
37#include <stdlib.h>
38#include <sys/types.h>
39#include <sys/queue.h>
40
41#include <string.h>
42
43#include "rtld.h"
44#include "debug.h"
45
46#ifdef RTLD_DEBUG_HPPA
47#define	hdbg(x)		xprintf x
48#else
49#define	hdbg(x)		/* nothing */
50#endif
51
52caddr_t _rtld_bind(const Obj_Entry *, const Elf_Addr);
53void _rtld_bind_start(void);
54void __rtld_setup_hppa_pltgot(const Obj_Entry *, Elf_Addr *);
55void _rtld_set_dp(Elf_Addr *);
56
57/*
58 * It is possible for the compiler to emit relocations for unaligned data.
59 * We handle this situation with these inlines.
60 */
61#define	RELOC_ALIGNED_P(x) \
62	(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
63
64static inline Elf_Addr
65load_ptr(void *where)
66{
67	if (__predict_true(RELOC_ALIGNED_P(where)))
68		return *(Elf_Addr *)where;
69	else {
70		Elf_Addr res;
71
72		(void)memcpy(&res, where, sizeof(res));
73		return res;
74	}
75}
76
77static inline void
78store_ptr(void *where, Elf_Addr val)
79{
80	if (__predict_true(RELOC_ALIGNED_P(where)))
81		*(Elf_Addr *)where = val;
82	else
83		(void)memcpy(where, &val, sizeof(val));
84}
85
86static __inline void
87fdc(void *addr)
88{
89	__asm volatile("fdc %%r0(%%sr0, %0)" : : "r" (addr));
90}
91
92static __inline void
93fic(void *addr)
94{
95	__asm volatile("fic %%r0(%%sr0,%0)" : : "r" (addr));
96}
97
98static __inline void
99sync(void)
100{
101	__asm volatile("sync" : : : "memory");
102}
103
104#define PLT_STUB_MAGIC1	0x00c0ffee
105#define PLT_STUB_MAGIC2	0xdeadbeef
106
107#define PLT_STUB_INSN1	0x0e801081	/* ldw	0(%r20), %r1 */
108#define PLT_STUB_INSN2	0xe820c000	/* bv	%r0(%r1) */
109
110/*
111 * In the runtime architecture (ABI), PLABEL function pointers are
112 * distinguished from normal function pointers by having the next-least-
113 * significant bit set.  (This bit is referred to as the L field in HP
114 * documentation).  The $$dyncall millicode is aware of this.
115 */
116#define	RTLD_MAKE_PLABEL(plabel)	(((Elf_Addr)(plabel)) | (1 << 1))
117#define RTLD_IS_PLABEL(addr)		(((Elf_Addr)(addr)) & (1 << 1))
118#define	RTLD_GET_PLABEL(addr)	((hppa_plabel *) (((Elf_Addr)addr) & ~3))
119
120/*
121 * This is the PLABEL structure.  The function PC and
122 * shared linkage members must come first, as they are
123 * the actual PLABEL.
124 */
125typedef struct _hppa_plabel {
126	Elf_Addr	hppa_plabel_pc;
127	Elf_Addr	hppa_plabel_sl;
128	SLIST_ENTRY(_hppa_plabel)	hppa_plabel_next;
129} hppa_plabel;
130
131/*
132 * For now allocated PLABEL structures are tracked on a
133 * singly linked list.  This maybe should be revisited.
134 */
135static SLIST_HEAD(hppa_plabel_head, _hppa_plabel) hppa_plabel_list
136    = SLIST_HEAD_INITIALIZER(hppa_plabel_list);
137
138/*
139 * Because I'm hesitant to use NEW while relocating self,
140 * this is a small pool of preallocated PLABELs.
141 */
142#define	HPPA_PLABEL_PRE	(32)
143static hppa_plabel hppa_plabel_pre[HPPA_PLABEL_PRE];
144static int hppa_plabel_pre_next = 0;
145
146void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
147int _rtld_relocate_plt_objects(const Obj_Entry *);
148static inline int _rtld_relocate_plt_object(const Obj_Entry *,
149    const Elf_Rela *, Elf_Addr *);
150
151/*
152 * This bootstraps the dynamic linker by relocating its GOT.
153 * On the hppa, unlike on other architectures, static strings
154 * are found through the GOT.  Static strings are essential
155 * for RTLD_DEBUG, and I suspect they're used early even when
156 * !defined(RTLD_DEBUG), making relocating the GOT essential.
157 *
158 * It gets worse.  Relocating the GOT doesn't mean just walking
159 * it and adding the relocbase to all of the entries.  You must
160 * find and use the GOT relocations, since those RELA relocations
161 * have the necessary addends - the GOT comes initialized as
162 * zeroes.
163 */
164void
165_rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
166{
167	const Elf_Rela	*relafirst, *rela, *relalim;
168	Elf_Addr        relasz;
169	void		*where;
170	Elf_Addr	*pltgot;
171	const Elf_Rela	*plabel_relocs[HPPA_PLABEL_PRE];
172	int		nplabel_relocs = 0;
173	int		i;
174	const Elf_Sym	*symtab, *sym;
175	unsigned long	symnum;
176	hppa_plabel	*plabel;
177
178	/*
179	 * Process the DYNAMIC section, looking for the non-PLT relocations.
180	 */
181	relafirst = NULL;
182	relasz = 0;
183	symtab = NULL;
184	pltgot = NULL;
185	for (; dynp->d_tag != DT_NULL; ++dynp) {
186		switch (dynp->d_tag) {
187
188		case DT_RELA:
189			relafirst = (const Elf_Rela *)
190			    (relocbase + dynp->d_un.d_ptr);
191			break;
192
193		case DT_RELASZ:
194			relasz = dynp->d_un.d_val;
195			break;
196
197		case DT_SYMTAB:
198			symtab = (const Elf_Sym *)
199			    (relocbase + dynp->d_un.d_ptr);
200			break;
201
202		case DT_PLTGOT:
203			pltgot = (Elf_Addr *)
204			    (relocbase + dynp->d_un.d_ptr);
205			break;
206		}
207	}
208	relalim = (const Elf_Rela *)((const char *)relafirst + relasz);
209
210	for (rela = relafirst; rela < relalim; rela++) {
211		symnum = ELF_R_SYM(rela->r_info);
212		where = (void *)(relocbase + rela->r_offset);
213
214		switch (ELF_R_TYPE(rela->r_info)) {
215		case R_TYPE(DIR32):
216			if (symnum == 0)
217				store_ptr(where,
218				    relocbase + rela->r_addend);
219			else {
220				sym = symtab + symnum;
221				store_ptr(where,
222				    relocbase + rela->r_addend + sym->st_value);
223			}
224			break;
225
226		case R_TYPE(PLABEL32):
227			/*
228			 * PLABEL32 relocation processing is done in two phases
229			 *
230			 *  i) local function relocations (symbol number == 0)
231			 *     can be resolved immediately.
232			 *
233			 * ii) external function relocations are deferred until
234			 *     we finish all other relocations so that global
235			 *     data isn't accessed until all other non-PLT
236			 *     relocations have been done.
237			 */
238			if (symnum == 0)
239				*((Elf_Addr *)where) =
240				    relocbase + rela->r_addend;
241			else
242				plabel_relocs[nplabel_relocs++] = rela;
243			break;
244
245		default:
246			break;
247		}
248	}
249
250	assert(nplabel_relocs < HPPA_PLABEL_PRE);
251	for (i = 0; i < nplabel_relocs; i++) {
252		rela = plabel_relocs[i];
253		where = (void *)(relocbase + rela->r_offset);
254		sym = symtab + ELF_R_SYM(rela->r_info);
255
256		plabel = &hppa_plabel_pre[hppa_plabel_pre_next++];
257
258		plabel->hppa_plabel_pc = (Elf_Addr)
259		    (relocbase + sym->st_value + rela->r_addend);
260		plabel->hppa_plabel_sl = (Elf_Addr)pltgot;
261
262		SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next);
263		*((Elf_Addr *)where) = (Elf_Addr)(RTLD_MAKE_PLABEL(plabel));
264	}
265
266#if defined(RTLD_DEBUG_HPPA)
267	for (rela = relafirst; rela < relalim; rela++) {
268		where = (void *)(relocbase + rela->r_offset);
269
270		switch (ELF_R_TYPE(rela->r_info)) {
271		case R_TYPE(DIR32):
272			hdbg(("DIR32 rela @%p(%p) -> %p(%p)\n",
273			    (void *)rela->r_offset,
274			    (void *)where,
275			    (void *)rela->r_addend,
276			    (void *)*((Elf_Addr *)where) ));
277			break;
278
279		case R_TYPE(PLABEL32):
280			symnum = ELF_R_SYM(rela->r_info);
281			if (symnum == 0) {
282				hdbg(("PLABEL rela @%p(%p) -> %p(%p)\n",
283		    		    (void *)rela->r_offset,
284		    		    (void *)where,
285		    		    (void *)rela->r_addend,
286		    		    (void *)*((Elf_Addr *)where) ));
287			} else {
288				sym = symtab + symnum;
289
290				hdbg(("PLABEL32 rela @%p(%p), symnum=%ld(%p) -> %p(%p)\n",
291			    	    (void *)rela->r_offset,
292				    (void *)where,
293				    symnum,
294				    (void *)sym->st_value,
295			    	    (void *)rela->r_addend,
296				    (void *)*((Elf_Addr *)where) ));
297			}
298			break;
299		default:
300			hdbg(("rela XXX reloc\n"));
301			break;
302		}
303	}
304#endif /* RTLD_DEBUG_HPPA */
305}
306
307/*
308 * This allocates a PLABEL.  If called with a non-NULL def, the
309 * plabel is for the function associated with that definition
310 * in the defining object defobj, plus the given addend.  If
311 * called with a NULL def, the plabel is for the function at
312 * the (unrelocated) address in addend in the object defobj.
313 */
314Elf_Addr
315_rtld_function_descriptor_alloc(const Obj_Entry *defobj, const Elf_Sym *def,
316    Elf_Addr addend)
317{
318	Elf_Addr	func_pc, func_sl;
319	hppa_plabel	*plabel;
320
321	if (def != NULL) {
322
323		/*
324		 * We assume that symbols of type STT_NOTYPE
325		 * are undefined.  Return NULL for these.
326		 */
327		if (ELF_ST_TYPE(def->st_info) == STT_NOTYPE)
328			return (Elf_Addr)NULL;
329
330		/* Otherwise assert that this symbol must be a function. */
331		assert(ELF_ST_TYPE(def->st_info) == STT_FUNC);
332
333		func_pc = (Elf_Addr)(defobj->relocbase + def->st_value +
334		    addend);
335	} else
336		func_pc = (Elf_Addr)(defobj->relocbase + addend);
337
338	/*
339	 * Search the existing PLABELs for one matching
340	 * this function.  If there is one, return it.
341	 */
342	func_sl = (Elf_Addr)(defobj->pltgot);
343	SLIST_FOREACH(plabel, &hppa_plabel_list, hppa_plabel_next)
344		if (plabel->hppa_plabel_pc == func_pc &&
345		    plabel->hppa_plabel_sl == func_sl)
346			return RTLD_MAKE_PLABEL(plabel);
347
348	/*
349	 * Once we've used up the preallocated set, we start
350	 * using NEW to allocate plabels.
351	 */
352	if (hppa_plabel_pre_next < HPPA_PLABEL_PRE)
353		plabel = &hppa_plabel_pre[hppa_plabel_pre_next++];
354	else {
355		plabel = NEW(hppa_plabel);
356		if (plabel == NULL)
357			return (Elf_Addr)-1;
358	}
359
360	/* Fill the new entry and insert it on the list. */
361	plabel->hppa_plabel_pc = func_pc;
362	plabel->hppa_plabel_sl = func_sl;
363	SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next);
364
365	return RTLD_MAKE_PLABEL(plabel);
366}
367
368/*
369 * If a pointer is a PLABEL, this unwraps it.
370 */
371const void *
372_rtld_function_descriptor_function(const void *addr)
373{
374	return (RTLD_IS_PLABEL(addr) ?
375	    (const void *) RTLD_GET_PLABEL(addr)->hppa_plabel_pc :
376	    addr);
377}
378
379/* This sets up an object's GOT. */
380void
381_rtld_setup_pltgot(const Obj_Entry *obj)
382{
383	Elf_Word *got = obj->pltgot;
384
385	assert(got[-2] == PLT_STUB_MAGIC1);
386	assert(got[-1] == PLT_STUB_MAGIC2);
387
388	__rtld_setup_hppa_pltgot(obj, got);
389
390	fdc(&got[-2]);
391	fdc(&got[-1]);
392	fdc(&got[1]);
393	sync();
394	fic(&got[-2]);
395	fic(&got[-1]);
396	fic(&got[1]);
397	sync();
398
399	/*
400	 * libc makes use of %t1 (%r22) to pass errno values to __cerror. Fixup
401	 * the PLT stub to not use %r22.
402	 */
403	got[-7] = PLT_STUB_INSN1;
404	got[-6] = PLT_STUB_INSN2;
405	fdc(&got[-7]);
406	fdc(&got[-6]);
407	sync();
408	fic(&got[-7]);
409	fic(&got[-6]);
410	sync();
411}
412
413int
414_rtld_relocate_nonplt_objects(Obj_Entry *obj)
415{
416	const Elf_Rela *rela;
417	const Elf_Sym *def = NULL;
418	const Obj_Entry *defobj = NULL;
419	unsigned long last_symnum = ULONG_MAX;
420
421	/*
422	 * This will be done by the crt0 code, but make sure it's set
423	 * early so that symbols overridden by the non-pic binary
424	 * get the right DP value.
425	 */
426	if (obj->mainprog) {
427		hdbg(("setting DP to %p", obj->pltgot));
428		_rtld_set_dp(obj->pltgot);
429	}
430
431	for (rela = obj->rela; rela < obj->relalim; rela++) {
432		Elf_Addr        *where;
433		Elf_Addr         tmp;
434
435		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
436
437		unsigned long symnum = ELF_R_SYM(rela->r_info);
438		/* First, handle DIR32 and PLABEL32 without symbol. */
439		if (symnum == 0) {
440			switch (ELF_R_TYPE(rela->r_info)) {
441			default:
442				break;
443			case R_TYPE(DIR32):
444				tmp = (Elf_Addr)(obj->relocbase +
445				    rela->r_addend);
446
447				if (load_ptr(where) != tmp)
448					store_ptr(where, tmp);
449				rdbg(("DIR32 in %s --> %p", obj->path,
450					    (void *)load_ptr(where)));
451				continue;
452			case R_TYPE(PLABEL32):
453				/*
454				 * This is a PLABEL for a static function, and
455				 * the dynamic linker has both allocated a PLT
456				 * entry for this function and told us where it
457				 * is.  We can safely use the PLT entry as the
458				 * PLABEL because there should be no other
459				 * PLABEL reloc referencing this function.
460				 * This object should also have an IPLT
461				 * relocation to initialize the PLT entry.
462				 *
463				 * The dynamic linker should also have ensured
464				 * that the addend has the
465				 * next-least-significant bit set; the
466				 * $$dyncall millicode uses this to distinguish
467				 * a PLABEL pointer from a plain function
468				 * pointer.
469				 */
470				tmp = (Elf_Addr)
471				    (obj->relocbase + rela->r_addend);
472
473				if (*where != tmp)
474					*where = tmp;
475				rdbg(("PLABEL32 in %s --> %p", obj->path,
476				    (void *)*where));
477				continue;
478			}
479		}
480
481		switch (ELF_R_TYPE(rela->r_info)) {
482		case R_TYPE(DIR32):
483		case R_TYPE(PLABEL32):
484		case R_TYPE(COPY):
485		case R_TYPE(TLS_TPREL32):
486		case R_TYPE(TLS_DTPMOD32):
487		case R_TYPE(TLS_DTPOFF32):
488			if (last_symnum != symnum) {
489				last_symnum = symnum;
490				if (ELF_R_TYPE(rela->r_info) == R_TYPE(DIR32)) {
491					/*
492					 * DIR32 relocation against local
493					 * symbols are special...
494					 */
495					def = obj->symtab + symnum;
496					defobj = obj;
497					if (def->st_name == 0)
498						break;
499				}
500				def = _rtld_find_symdef(symnum, obj, &defobj,
501				    false);
502				if (def == NULL)
503					return -1;
504			}
505			break;
506		default:
507			break;
508		}
509
510		switch (ELF_R_TYPE(rela->r_info)) {
511		case R_TYPE(NONE):
512			break;
513
514		case R_TYPE(DIR32):
515			tmp = (Elf_Addr)(defobj->relocbase +
516			    def->st_value + rela->r_addend);
517
518			if (load_ptr(where) != tmp)
519				store_ptr(where, tmp);
520			rdbg(("DIR32 %s in %s --> %p in %s",
521			    obj->strtab + obj->symtab[symnum].st_name,
522			    obj->path, (void *)load_ptr(where),
523			    defobj->path));
524			break;
525
526		case R_TYPE(PLABEL32):
527			tmp = _rtld_function_descriptor_alloc(defobj,
528			    def, rela->r_addend);
529			if (tmp == (Elf_Addr)-1)
530				return -1;
531
532			if (*where != tmp)
533				*where = tmp;
534			rdbg(("PLABEL32 %s in %s --> %p in %s",
535			    obj->strtab + obj->symtab[symnum].st_name,
536			    obj->path, (void *)*where, defobj->path));
537			break;
538
539		case R_TYPE(COPY):
540			/*
541			 * These are deferred until all other relocations have
542			 * been done.  All we do here is make sure that the
543			 * COPY relocation is not in a shared library.  They
544			 * are allowed only in executable files.
545			 */
546			if (obj->isdynamic) {
547				_rtld_error(
548			"%s: Unexpected R_COPY relocation in shared library",
549				    obj->path);
550				return -1;
551			}
552			rdbg(("COPY (avoid in main)"));
553			break;
554
555		case R_TYPE(TLS_TPREL32):
556			if (!defobj->tls_static &&
557			    _rtld_tls_offset_allocate(__UNCONST(defobj)))
558				return -1;
559
560			*where = (Elf_Addr)(defobj->tlsoffset + def->st_value +
561			    rela->r_addend + sizeof(struct tls_tcb));
562
563			rdbg(("TPREL32 %s in %s --> %p in %s",
564			    obj->strtab + obj->symtab[symnum].st_name,
565			    obj->path, (void *)*where, defobj->path));
566			break;
567
568		case R_TYPE(TLS_DTPMOD32):
569			*where = (Elf_Addr)(defobj->tlsindex);
570
571			rdbg(("TLS_DTPMOD32 %s in %s --> %p",
572			    obj->strtab + obj->symtab[symnum].st_name,
573			    obj->path, (void *)*where));
574
575			break;
576
577		case R_TYPE(TLS_DTPOFF32):
578			*where = (Elf_Addr)(def->st_value);
579
580			rdbg(("TLS_DTPOFF32 %s in %s --> %p",
581			    obj->strtab + obj->symtab[symnum].st_name,
582			    obj->path, (void *)*where));
583
584			break;
585
586		default:
587			rdbg(("sym = %lu, type = %lu, offset = %p, "
588			    "addend = %p, contents = %p, symbol = %s",
589			    symnum, (u_long)ELF_R_TYPE(rela->r_info),
590			    (void *)rela->r_offset, (void *)rela->r_addend,
591			    (void *)load_ptr(where),
592			    obj->strtab + obj->symtab[symnum].st_name));
593			_rtld_error("%s: Unsupported relocation type %ld "
594			    "in non-PLT relocations",
595			    obj->path, (u_long) ELF_R_TYPE(rela->r_info));
596			return -1;
597		}
598	}
599	return 0;
600}
601
602int
603_rtld_relocate_plt_lazy(Obj_Entry *obj)
604{
605	const Elf_Rela *rela;
606
607	for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) {
608		Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
609		Elf_Addr func_pc, func_sl;
610
611		assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT));
612
613		/*
614		 * If this is an IPLT reloc for a static function,
615		 * fully resolve the PLT entry now.
616		 */
617		if (ELF_R_SYM(rela->r_info) == 0) {
618			func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend);
619			func_sl = (Elf_Addr)(obj->pltgot);
620		}
621
622		/*
623		 * Otherwise set up for lazy binding.
624		 */
625		else {
626			/*
627			 * This function pointer points to the PLT
628			 * stub added by the linker, and instead of
629			 * a shared linkage value, we stash this
630			 * relocation's offset.  The PLT stub has
631			 * already been set up to transfer to
632			 * _rtld_bind_start.
633			 */
634			func_pc = ((Elf_Addr)(obj->pltgot)) - 16;
635			func_sl = (Elf_Addr)
636			    ((const char *)rela - (const char *)(obj->pltrela));
637		}
638		rdbg(("lazy bind %s(%p) --> old=(%p,%p) new=(%p,%p)",
639		    obj->path,
640		    (void *)where,
641		    (void *)where[0], (void *)where[1],
642		    (void *)func_pc, (void *)func_sl));
643
644		/*
645		 * Fill this PLT entry and return.
646		 */
647		where[0] = func_pc;
648		where[1] = func_sl;
649	}
650	return 0;
651}
652
653static inline int
654_rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela,
655    Elf_Addr *tp)
656{
657	Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset);
658	const Elf_Sym *def;
659	const Obj_Entry *defobj;
660	Elf_Addr	func_pc, func_sl;
661	unsigned long info = rela->r_info;
662
663	assert(ELF_R_TYPE(info) == R_TYPE(IPLT));
664
665	if (ELF_R_SYM(info) == 0) {
666		func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend);
667		func_sl = (Elf_Addr)(obj->pltgot);
668	} else {
669		def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj,
670		    tp != NULL);
671		if (__predict_false(def == NULL))
672			return -1;
673		if (__predict_false(def == &_rtld_sym_zero))
674			return 0;
675
676		if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
677			if (tp == NULL)
678				return 0;
679			Elf_Addr ptr = _rtld_resolve_ifunc(defobj, def);
680			assert(RTLD_IS_PLABEL(ptr));
681			hppa_plabel *label = RTLD_GET_PLABEL(ptr);
682			func_pc = label->hppa_plabel_pc;
683			func_sl = label->hppa_plabel_sl;
684		} else {
685			func_pc = (Elf_Addr)(defobj->relocbase + def->st_value +
686			    rela->r_addend);
687			func_sl = (Elf_Addr)(defobj->pltgot);
688		}
689
690		rdbg(("bind now/fixup in %s --> old=(%p,%p) new=(%p,%p)",
691		    defobj->strtab + def->st_name,
692		    (void *)where[0], (void *)where[1],
693		    (void *)func_pc, (void *)func_sl));
694	}
695	/*
696	 * Fill this PLT entry and return.
697	 */
698	if (where[0] != func_pc)
699		where[0] = func_pc;
700	if (where[1] != func_sl)
701		where[1] = func_sl;
702
703	if (tp)
704		*tp = (Elf_Addr)where;
705
706	return 0;
707}
708
709caddr_t
710_rtld_bind(const Obj_Entry *obj, Elf_Word reloff)
711{
712	const Elf_Rela *rela;
713	Elf_Addr new_value = 0;	/* XXX gcc */
714	int err;
715
716	rela = (const Elf_Rela *)((const char *)obj->pltrela + reloff);
717
718	assert(ELF_R_SYM(rela->r_info) != 0);
719
720	_rtld_shared_enter();
721	err = _rtld_relocate_plt_object(obj, rela, &new_value);
722	if (err)
723		_rtld_die();
724	_rtld_shared_exit();
725
726	return (caddr_t)new_value;
727}
728
729int
730_rtld_relocate_plt_objects(const Obj_Entry *obj)
731{
732	const Elf_Rela *rela = obj->pltrela;
733
734	for (; rela < obj->pltrelalim; rela++) {
735		if (_rtld_relocate_plt_object(obj, rela, NULL) < 0)
736			return -1;
737	}
738	return 0;
739}
740
741Elf_Addr
742_rtld_call_function_addr(const Obj_Entry *obj, Elf_Addr ptr)
743{
744	volatile hppa_plabel plabel;
745	Elf_Addr (*f)(void);
746
747	plabel.hppa_plabel_pc = (Elf_Addr)ptr;
748	plabel.hppa_plabel_sl = (Elf_Addr)(obj->pltgot);
749	f = (Elf_Addr (*)(void))RTLD_MAKE_PLABEL(&plabel);
750
751	return f();
752}
753