1 /* $NetBSD: hppa_reloc.c,v 1.53 2024/07/29 13:16:19 skrll 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.53 2024/07/29 13:16:19 skrll 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 /* skip R_PARISC_NONE entries */ 629 if (ELF_R_TYPE(rela->r_info) == R_TYPE(NONE)) 630 continue; 631 632 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT)); 633 634 /* 635 * If this is an IPLT reloc for a static function, 636 * fully resolve the PLT entry now. 637 */ 638 if (ELF_R_SYM(rela->r_info) == 0) { 639 func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend); 640 func_sl = (Elf_Addr)(obj->pltgot); 641 } 642 643 /* 644 * Otherwise set up for lazy binding. 645 */ 646 else { 647 /* 648 * This function pointer points to the PLT 649 * stub added by the linker, and instead of 650 * a shared linkage value, we stash this 651 * relocation's offset. The PLT stub has 652 * already been set up to transfer to 653 * _rtld_bind_start. 654 */ 655 func_pc = ((Elf_Addr)(obj->pltgot)) - 16; 656 func_sl = (Elf_Addr) 657 ((const char *)rela - (const char *)(obj->pltrela)); 658 } 659 rdbg(("lazy bind %s(%p) --> old=(%p,%p) new=(%p,%p)", 660 obj->path, 661 (void *)where, 662 (void *)where[0], (void *)where[1], 663 (void *)func_pc, (void *)func_sl)); 664 665 /* 666 * Fill this PLT entry and return. 667 */ 668 where[0] = func_pc; 669 where[1] = func_sl; 670 } 671 return 0; 672 } 673 674 static inline int 675 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela, 676 Elf_Addr *tp) 677 { 678 Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset); 679 const Elf_Sym *def; 680 const Obj_Entry *defobj; 681 Elf_Addr func_pc, func_sl; 682 unsigned long info = rela->r_info; 683 684 assert(ELF_R_TYPE(info) == R_TYPE(IPLT)); 685 686 if (ELF_R_SYM(info) == 0) { 687 func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend); 688 func_sl = (Elf_Addr)(obj->pltgot); 689 } else { 690 def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, 691 tp != NULL); 692 if (__predict_false(def == NULL)) 693 return -1; 694 if (__predict_false(def == &_rtld_sym_zero)) 695 return 0; 696 697 if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { 698 if (tp == NULL) 699 return 0; 700 Elf_Addr ptr = _rtld_resolve_ifunc(defobj, def); 701 assert(RTLD_IS_PLABEL(ptr)); 702 hppa_plabel *label = RTLD_GET_PLABEL(ptr); 703 func_pc = label->hppa_plabel_pc; 704 func_sl = label->hppa_plabel_sl; 705 } else { 706 func_pc = (Elf_Addr)(defobj->relocbase + def->st_value + 707 rela->r_addend); 708 func_sl = (Elf_Addr)(defobj->pltgot); 709 } 710 711 rdbg(("bind now/fixup in %s --> old=(%p,%p) new=(%p,%p)", 712 defobj->strtab + def->st_name, 713 (void *)where[0], (void *)where[1], 714 (void *)func_pc, (void *)func_sl)); 715 } 716 /* 717 * Fill this PLT entry and return. 718 */ 719 if (where[0] != func_pc) 720 where[0] = func_pc; 721 if (where[1] != func_sl) 722 where[1] = func_sl; 723 724 if (tp) 725 *tp = (Elf_Addr)where; 726 727 return 0; 728 } 729 730 caddr_t 731 _rtld_bind(const Obj_Entry *obj, Elf_Word reloff) 732 { 733 const Elf_Rela *rela; 734 Elf_Addr new_value = 0; /* XXX gcc */ 735 int err; 736 737 rela = (const Elf_Rela *)((const char *)obj->pltrela + reloff); 738 739 assert(ELF_R_SYM(rela->r_info) != 0); 740 741 _rtld_shared_enter(); 742 err = _rtld_relocate_plt_object(obj, rela, &new_value); 743 if (err) 744 _rtld_die(); 745 _rtld_shared_exit(); 746 747 return (caddr_t)new_value; 748 } 749 750 int 751 _rtld_relocate_plt_objects(const Obj_Entry *obj) 752 { 753 const Elf_Rela *rela = obj->pltrela; 754 755 for (; rela < obj->pltrelalim; rela++) { 756 if (_rtld_relocate_plt_object(obj, rela, NULL) < 0) 757 return -1; 758 } 759 return 0; 760 } 761 762 Elf_Addr 763 _rtld_call_function_addr(const Obj_Entry *obj, Elf_Addr ptr) 764 { 765 volatile hppa_plabel plabel; 766 Elf_Addr (*f)(void); 767 768 plabel.hppa_plabel_pc = (Elf_Addr)ptr; 769 plabel.hppa_plabel_sl = (Elf_Addr)(obj->pltgot); 770 f = (Elf_Addr (*)(void))RTLD_MAKE_PLABEL(&plabel); 771 772 return f(); 773 } 774