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