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