1 /* $NetBSD: alpha_reloc.c,v 1.47 2025/04/18 19:11:44 riastradh Exp $ */ 2 3 /* 4 * Copyright (c) 2001 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 /* 39 * Copyright 1996, 1997, 1998, 1999 John D. Polstra. 40 * All rights reserved. 41 * 42 * Redistribution and use in source and binary forms, with or without 43 * modification, are permitted provided that the following conditions 44 * are met: 45 * 1. Redistributions of source code must retain the above copyright 46 * notice, this list of conditions and the following disclaimer. 47 * 2. Redistributions in binary form must reproduce the above copyright 48 * notice, this list of conditions and the following disclaimer in the 49 * documentation and/or other materials provided with the distribution. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 52 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 53 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 54 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 55 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 56 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 60 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 */ 62 63 #include <sys/cdefs.h> 64 #ifndef lint 65 __RCSID("$NetBSD: alpha_reloc.c,v 1.47 2025/04/18 19:11:44 riastradh Exp $"); 66 #endif /* not lint */ 67 68 #include <sys/types.h> 69 #include <sys/tls.h> 70 #include <string.h> 71 72 #include "rtld.h" 73 #include "debug.h" 74 75 #ifdef RTLD_DEBUG_ALPHA 76 #define adbg(x) xprintf x 77 #else 78 #define adbg(x) /* nothing */ 79 #endif 80 81 void _rtld_bind_start_secureplt(void); 82 void _rtld_bind_start(void); 83 void _rtld_bind_start_old(void); 84 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 85 caddr_t _rtld_bind(const Obj_Entry *, Elf_Addr); 86 static inline int _rtld_relocate_plt_object(const Obj_Entry *, 87 const Elf_Rela *, Elf_Addr *); 88 89 void 90 _rtld_setup_pltgot(const Obj_Entry *obj) 91 { 92 uint32_t word0; 93 94 /* 95 * If we're using Alpha secureplt, the PLTGOT points to the 96 * .got.plt section. Just fill in the rtld binding stub and 97 * we're done -- we're not writing to instruction memory, so no 98 * imb needed. 99 */ 100 if (obj->secureplt) { 101 obj->pltgot[0] = (Elf_Addr) _rtld_bind_start_secureplt; 102 obj->pltgot[1] = (Elf_Addr) obj; 103 return; 104 } 105 106 /* 107 * The non-secureplt PLTGOT on the Alpha looks like this: 108 * 109 * PLT HEADER 110 * . 111 * . 32 bytes 112 * . 113 * PLT ENTRY #0 114 * . 115 * . 12 bytes 116 * . 117 * PLT ENTRY #1 118 * . 119 * . 12 bytes 120 * . 121 * etc. 122 * 123 * The old-format entries look like (displacements filled in 124 * by the linker): 125 * 126 * ldah $28, 0($31) # 0x279f0000 127 * lda $28, 0($28) # 0x239c0000 128 * br $31, plt0 # 0xc3e00000 129 * 130 * The new-format entries look like: 131 * 132 * br $28, plt0 # 0xc3800000 133 * # 0x00000000 134 * # 0x00000000 135 * 136 * What we do is fetch the first PLT entry and check to 137 * see the first word of it matches the first word of the 138 * old format. If so, we use a binding routine that can 139 * handle the old format, otherwise we use a binding routine 140 * that handles the new format. 141 * 142 * Note that this is done on a per-object basis, we can mix 143 * and match shared objects build with both the old and new 144 * linker. 145 */ 146 word0 = *(uint32_t *)(((char *) obj->pltgot) + 32); 147 if ((word0 & 0xffff0000) == 0x279f0000) { 148 /* Old PLT entry format. */ 149 adbg(("ALPHA: object %p has old PLT format\n", obj)); 150 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start_old; 151 obj->pltgot[3] = (Elf_Addr) obj; 152 } else { 153 /* New PLT entry format. */ 154 adbg(("ALPHA: object %p has new PLT format\n", obj)); 155 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; 156 obj->pltgot[3] = (Elf_Addr) obj; 157 } 158 159 __asm volatile("imb"); 160 } 161 162 /* 163 * It is possible for the compiler to emit relocations for unaligned data. 164 * We handle this situation with these inlines. 165 */ 166 #define RELOC_ALIGNED_P(x) \ 167 (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) 168 169 static inline Elf_Addr 170 load_ptr(void *where) 171 { 172 Elf_Addr res; 173 174 memcpy(&res, where, sizeof(res)); 175 176 return (res); 177 } 178 179 static inline void 180 store_ptr(void *where, Elf_Addr val) 181 { 182 183 memcpy(where, &val, sizeof(val)); 184 } 185 186 void 187 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 188 { 189 const Elf_Rela *rela = 0, *relalim; 190 Elf_Addr relasz = 0; 191 Elf_Addr *where; 192 193 for (; dynp->d_tag != DT_NULL; dynp++) { 194 switch (dynp->d_tag) { 195 case DT_RELA: 196 rela = (const Elf_Rela *)(relocbase + dynp->d_un.d_ptr); 197 break; 198 case DT_RELASZ: 199 relasz = dynp->d_un.d_val; 200 break; 201 } 202 } 203 relalim = (const Elf_Rela *)((const uint8_t *)rela + relasz); 204 for (; rela < relalim; rela++) { 205 where = (Elf_Addr *)(relocbase + rela->r_offset); 206 /* XXX For some reason I see a few GLOB_DAT relocs here. */ 207 *where += (Elf_Addr)relocbase; 208 } 209 } 210 211 int 212 _rtld_relocate_nonplt_objects(Obj_Entry *obj) 213 { 214 const Elf_Rela *rela; 215 Elf_Addr target = -1; 216 const Elf_Sym *def = NULL; 217 const Obj_Entry *defobj = NULL; 218 unsigned long last_symnum = ULONG_MAX; 219 220 for (rela = obj->rela; rela < obj->relalim; rela++) { 221 Elf_Addr *where; 222 Elf_Addr tmp; 223 unsigned long symnum = ELF_R_SYM(rela->r_info); 224 225 where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 226 227 switch (ELF_R_TYPE(rela->r_info)) { 228 case R_TYPE(REFQUAD): 229 case R_TYPE(GLOB_DAT): 230 case R_TYPE(TPREL64): 231 case R_TYPE(DTPMOD64): 232 case R_TYPE(DTPREL64): 233 if (last_symnum != symnum) { 234 last_symnum = symnum; 235 def = _rtld_find_symdef(symnum, obj, &defobj, 236 false); 237 if (def == NULL) 238 return -1; 239 } 240 break; 241 242 default: 243 break; 244 } 245 246 switch (ELF_R_TYPE(rela->r_info)) { 247 case R_TYPE(NONE): 248 break; 249 250 case R_TYPE(REFQUAD): 251 case R_TYPE(GLOB_DAT): 252 target = (Elf_Addr)(defobj->relocbase + 253 def->st_value); 254 255 tmp = target + rela->r_addend; 256 if (__predict_true(RELOC_ALIGNED_P(where))) { 257 if (*where != tmp) 258 *where = tmp; 259 } else { 260 if (load_ptr(where) != tmp) 261 store_ptr(where, tmp); 262 } 263 rdbg(("REFQUAD/GLOB_DAT %s in %s --> %p in %s", 264 obj->strtab + obj->symtab[symnum].st_name, 265 obj->path, (void *)tmp, defobj->path)); 266 break; 267 268 case R_TYPE(RELATIVE): 269 if (__predict_true(RELOC_ALIGNED_P(where))) 270 *where += (Elf_Addr)obj->relocbase; 271 else 272 store_ptr(where, 273 load_ptr(where) + (Elf_Addr)obj->relocbase); 274 rdbg(("RELATIVE in %s --> %p", obj->path, 275 (void *)*where)); 276 break; 277 278 case R_TYPE(COPY): 279 /* 280 * These are deferred until all other relocations have 281 * been done. All we do here is make sure that the 282 * COPY relocation is not in a shared library. They 283 * are allowed only in executable files. 284 */ 285 if (obj->isdynamic) { 286 _rtld_error( 287 "%s: Unexpected R_COPY relocation in shared library", 288 obj->path); 289 return -1; 290 } 291 rdbg(("COPY (avoid in main)")); 292 break; 293 294 case R_TYPE(TPREL64): 295 if (!defobj->tls_static && 296 _rtld_tls_offset_allocate(__UNCONST(defobj))) 297 return -1; 298 299 tmp = (Elf64_Addr)(def->st_value + 300 sizeof(struct tls_tcb) + defobj->tlsoffset + 301 rela->r_addend); 302 303 if (__predict_true(RELOC_ALIGNED_P(where))) 304 *where = tmp; 305 else 306 store_ptr(where, tmp); 307 308 rdbg(("TPREL64 %s in %s --> %p", 309 obj->strtab + obj->symtab[symnum].st_name, 310 obj->path, (void *)*where)); 311 312 break; 313 314 case R_TYPE(DTPMOD64): 315 tmp = (Elf64_Addr)defobj->tlsindex; 316 if (__predict_true(RELOC_ALIGNED_P(where))) 317 *where = tmp; 318 else 319 store_ptr(where, tmp); 320 321 rdbg(("DTPMOD64 %s in %s --> %p", 322 obj->strtab + obj->symtab[symnum].st_name, 323 obj->path, (void *)*where)); 324 325 break; 326 327 case R_TYPE(DTPREL64): 328 tmp = (Elf64_Addr)(def->st_value + rela->r_addend); 329 if (__predict_true(RELOC_ALIGNED_P(where))) 330 *where = tmp; 331 else 332 store_ptr(where, tmp); 333 334 rdbg(("DTPREL64 %s in %s --> %p", 335 obj->strtab + obj->symtab[symnum].st_name, 336 obj->path, (void *)*where)); 337 338 break; 339 340 default: 341 rdbg(("sym = %lu, type = %lu, offset = %p, " 342 "addend = %p, contents = %p, symbol = %s", 343 (u_long)ELF_R_SYM(rela->r_info), 344 (u_long)ELF_R_TYPE(rela->r_info), 345 (void *)rela->r_offset, (void *)rela->r_addend, 346 (void *)load_ptr(where), 347 obj->strtab + obj->symtab[symnum].st_name)); 348 _rtld_error("%s: Unsupported relocation type %ld " 349 "in non-PLT relocations", 350 obj->path, (u_long) ELF_R_TYPE(rela->r_info)); 351 return -1; 352 } 353 } 354 return 0; 355 } 356 357 int 358 _rtld_relocate_plt_lazy(Obj_Entry *obj) 359 { 360 const Elf_Rela *rela; 361 362 if (!obj->relocbase) 363 return 0; 364 365 for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) { 366 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 367 368 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT)); 369 370 /* Just relocate the GOT slots pointing into the PLT */ 371 *where += (Elf_Addr)obj->relocbase; 372 rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where)); 373 } 374 375 return 0; 376 } 377 378 static inline int 379 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela, 380 Elf_Addr *tp) 381 { 382 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 383 Elf_Addr new_value; 384 const Elf_Sym *def; 385 const Obj_Entry *defobj; 386 Elf_Addr stubaddr; 387 unsigned long info = rela->r_info; 388 389 assert(ELF_R_TYPE(info) == R_TYPE(JMP_SLOT)); 390 391 def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, tp != NULL); 392 if (__predict_false(def == NULL)) 393 return -1; 394 if (__predict_false(def == &_rtld_sym_zero)) 395 return 0; 396 397 if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { 398 if (tp == NULL) 399 return 0; 400 new_value = _rtld_resolve_ifunc(defobj, def); 401 } else { 402 new_value = (Elf_Addr)(defobj->relocbase + def->st_value); 403 } 404 rdbg(("bind now/fixup in %s --> old=%p new=%p", 405 defobj->strtab + def->st_name, (void *)*where, (void *)new_value)); 406 407 if ((stubaddr = *where) != new_value) { 408 int64_t delta, idisp; 409 uint32_t insn[3], *stubptr; 410 int insncnt; 411 Elf_Addr pc; 412 413 /* Point this GOT entry at the target. */ 414 *where = new_value; 415 416 /* 417 * Alpha shared objects may have multiple GOTs, each 418 * of which may point to this entry in the PLT. But, 419 * we only have a reference to the first GOT entry which 420 * points to this PLT entry. In order to avoid having to 421 * re-bind this call every time a non-first GOT entry is 422 * used, we will attempt to patch up the PLT entry to 423 * reference the target, rather than the binder. 424 * 425 * When the PLT stub gets control, PV contains the address 426 * of the PLT entry. Each PLT entry has room for 3 insns. 427 * If the displacement of the target from PV fits in a signed 428 * 32-bit integer, we can simply add it to PV. Otherwise, 429 * we must load the GOT entry itself into PV. 430 * 431 * Note if the shared object uses the old PLT format, then 432 * we cannot patch up the PLT safely, and so we skip it 433 * in that case[*]. And if the shared object has a read-only 434 * secureplt, then we also skip it. 435 * 436 * [*] Actually, if we're not doing lazy-binding, then 437 * we *can* (and do) patch up this PLT entry; the PLTGOT 438 * thunk won't yet point to any binder entry point, and 439 * so this test will fail as it would for the new PLT 440 * entry format. 441 */ 442 if (obj->secureplt) { 443 rdbg((" secureplt format")); 444 goto out; 445 } 446 if (obj->pltgot[2] == (Elf_Addr) &_rtld_bind_start_old) { 447 rdbg((" old PLT format")); 448 goto out; 449 } 450 451 delta = new_value - stubaddr; 452 rdbg((" stubaddr=%p, where-stubaddr=%ld, delta=%ld", 453 (void *)stubaddr, (long)where - (long)stubaddr, 454 (long)delta)); 455 insncnt = 0; 456 if ((int32_t)delta == delta) { 457 /* 458 * We can adjust PV with an LDA, LDAH sequence. 459 * 460 * First, build an LDA insn to adjust the low 16 461 * bits. 462 */ 463 insn[insncnt++] = 0x08 << 26 | 27 << 21 | 27 << 16 | 464 (delta & 0xffff); 465 rdbg((" LDA $27,%d($27)", (int16_t)delta)); 466 /* 467 * Adjust the delta to account for the effects of 468 * the LDA, including sign-extension. 469 */ 470 delta -= (int16_t)delta; 471 if (delta != 0) { 472 /* 473 * Build an LDAH instruction to adjust the 474 * high 16 bits. 475 */ 476 insn[insncnt++] = 0x09 << 26 | 27 << 21 | 477 27 << 16 | ((delta >> 16) & 0xffff); 478 rdbg((" LDAH $27,%d($27)", 479 (int16_t)(delta >> 16))); 480 } 481 } else { 482 int64_t dhigh; 483 484 /* We must load the GOT entry. */ 485 delta = (Elf_Addr)where - stubaddr; 486 487 /* 488 * If the GOT entry is too far away from the PLT 489 * entry, then we can't patch up the PLT entry. 490 * This PLT entry will have to be bound for each 491 * GOT entry except for the first one. This program 492 * will still run, albeit very slowly. It is very 493 * unlikely that this case will ever happen in 494 * practice. 495 */ 496 if ((int32_t)delta != delta) { 497 rdbg((" PLT stub too far from GOT to relocate")); 498 goto out; 499 } 500 dhigh = delta - (int16_t)delta; 501 if (dhigh != 0) { 502 /* 503 * Build an LDAH instruction to adjust the 504 * high 16 bits. 505 */ 506 insn[insncnt++] = 0x09 << 26 | 27 << 21 | 507 27 << 16 | ((dhigh >> 16) & 0xffff); 508 rdbg((" LDAH $27,%d($27)", 509 (int16_t)(dhigh >> 16))); 510 } 511 /* Build an LDQ to load the GOT entry. */ 512 insn[insncnt++] = 0x29 << 26 | 27 << 21 | 513 27 << 16 | (delta & 0xffff); 514 rdbg((" LDQ $27,%d($27)", 515 (int16_t)delta)); 516 } 517 518 /* 519 * Now, build a JMP or BR insn to jump to the target. If 520 * the displacement fits in a sign-extended 21-bit field, 521 * we can use the more efficient BR insn. Otherwise, we 522 * have to jump indirect through PV. 523 */ 524 pc = stubaddr + (4 * (insncnt + 1)); 525 idisp = (int64_t)(new_value - pc) >> 2; 526 if (-0x100000 <= idisp && idisp < 0x100000) { 527 insn[insncnt++] = 0x30 << 26 | 31 << 21 | 528 (idisp & 0x1fffff); 529 rdbg((" BR $31,%p", (void *)new_value)); 530 } else { 531 insn[insncnt++] = 0x1a << 26 | 31 << 21 | 532 27 << 16 | (idisp & 0x3fff); 533 rdbg((" JMP $31,($27),%d", 534 (int)(idisp & 0x3fff))); 535 } 536 537 /* 538 * Fill in the tail of the PLT entry first, for reentrancy. 539 * Until we have overwritten the first insn (an unconditional 540 * branch), the remaining insns have no effect. 541 */ 542 stubptr = (uint32_t *)stubaddr; 543 while (insncnt > 1) { 544 insncnt--; 545 stubptr[insncnt] = insn[insncnt]; 546 } 547 /* 548 * Commit the tail of the insn sequence to memory 549 * before overwriting the first insn. 550 */ 551 __asm volatile("wmb" ::: "memory"); 552 stubptr[0] = insn[0]; 553 /* 554 * I-stream will be sync'd when we either return from 555 * the binder (lazy bind case) or when the PLTGOT thunk 556 * is patched up (bind-now case). 557 */ 558 } 559 out: 560 if (tp) 561 *tp = new_value; 562 563 return 0; 564 } 565 566 caddr_t 567 _rtld_bind(const Obj_Entry *obj, Elf_Addr reloff) 568 { 569 const Elf_Rela *rela = 570 (const Elf_Rela *)((const uint8_t *)obj->pltrela + reloff); 571 Elf_Addr result = 0; /* XXX gcc */ 572 int err; 573 574 _rtld_shared_enter(); 575 err = _rtld_relocate_plt_object(obj, rela, &result); 576 if (err) 577 _rtld_die(); 578 _rtld_shared_exit(); 579 580 return (caddr_t)result; 581 } 582 583 int 584 _rtld_relocate_plt_objects(const Obj_Entry *obj) 585 { 586 const Elf_Rela *rela; 587 588 for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) 589 if (_rtld_relocate_plt_object(obj, rela, NULL) < 0) 590 return -1; 591 592 return 0; 593 } 594