1 /* $NetBSD: ppc_reloc.c,v 1.66 2024/11/30 01:04:05 christos Exp $ */ 2 3 /*- 4 * Copyright (C) 1998 Tsubai Masanari 5 * Portions copyright 2002 Charles M. Hannum <root@ihack.net> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /* 32 * Power ELF relocations. 33 * 34 * Reference: 35 * 36 * Power Architecture(R) 32-bit 37 * Application Binary Interface Supplement 1.0 - Linux(R) 38 * http://web.archive.org/web/20120608163845/https://www.power.org/resources/downloads/Power-Arch-32-bit-ABI-supp-1.0-Linux.pdf 39 * 40 * 64-bit PowerPC ELF Application Binary Interface Supplement 1.9 41 * https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.pdf 42 */ 43 44 #include <sys/cdefs.h> 45 #ifndef lint 46 __RCSID("$NetBSD: ppc_reloc.c,v 1.66 2024/11/30 01:04:05 christos Exp $"); 47 #endif /* not lint */ 48 49 #include <stdarg.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <sys/types.h> 54 #include <machine/cpu.h> 55 56 #include "debug.h" 57 #include "rtld.h" 58 59 #include <machine/lwp_private.h> 60 61 void _rtld_powerpc_pltcall(Elf_Word); 62 void _rtld_powerpc_pltresolve(Elf_Word, Elf_Word); 63 64 #define __u64(x) ((uint64_t)(x)) 65 #define __u32(x) ((uint32_t)(x)) 66 #define __ha48 __u64(0xffffffff8000) 67 #define __ha32 __u64(0xffff8000) 68 #define __ha16 __u32(0x8000) 69 #define __ha(x,n) ((((x) >> (n)) + (((x) & __ha##n) == __ha##n)) & 0xffff) 70 #define __hi(x,n) (((x) >> (n)) & 0xffff) 71 #ifdef __LP64 72 #define highesta(x) __ha(__u64(x), 48) 73 #define highest(x) __hi(__u64(x), 48) 74 #define higher(x) __ha(__u64(x), 32) 75 #define higher(x) __hi(__u64(x), 32) 76 #endif 77 #define ha(x) __ha(__u32(x), 16) 78 #define hi(x) __hi(__u32(x), 16) 79 #define lo(x) (__u32(x) & 0xffff) 80 81 #ifdef _LP64 82 /* function descriptor for _rtld_bind_start */ 83 extern const uint64_t _rtld_bind_start[3]; 84 #else 85 void _rtld_bind_bssplt_start(void); 86 void _rtld_bind_secureplt_start(void); 87 #endif 88 Elf_Addr _rtld_bind(const Obj_Entry *, Elf_Word); 89 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 90 static int _rtld_relocate_plt_object(const Obj_Entry *, 91 const Elf_Rela *, int, Elf_Addr *); 92 93 /* 94 * The PPC32 PLT format consists of three sections: 95 * (1) The "pltcall" and "pltresolve" glue code. This is always 18 words. 96 * (2) The code part of the PLT entries. There are 2 words per entry for 97 * up to 8192 entries, then 4 words per entry for any additional entries. 98 * (3) The data part of the PLT entries, comprising a jump table. 99 * This section is half the size of the second section (ie. 1 or 2 words 100 * per entry). 101 */ 102 103 void 104 _rtld_setup_pltgot(const Obj_Entry *obj) 105 { 106 #ifdef _LP64 107 /* 108 * For powerpc64, just copy the function descriptor to pltgot[0]. 109 */ 110 if (obj->pltgot != NULL) { 111 obj->pltgot[0] = (Elf_Addr) _rtld_bind_start[0]; 112 obj->pltgot[1] = (Elf_Addr) _rtld_bind_start[1]; 113 obj->pltgot[2] = (Elf_Addr) obj; 114 } 115 #else 116 /* 117 * Secure-PLT is much more sane. 118 */ 119 if (obj->gotptr != NULL) { 120 obj->gotptr[1] = (Elf_Addr) _rtld_bind_secureplt_start; 121 obj->gotptr[2] = (Elf_Addr) obj; 122 dbg(("obj %s secure-plt gotptr=%p start=%p obj=%p", 123 obj->path, obj->gotptr, 124 (void *) obj->gotptr[1], (void *) obj->gotptr[2])); 125 } else { 126 /* 127 * Setup the plt glue routines (for bss-plt). 128 */ 129 #define BSSPLTCALL_SIZE 20 130 #define BSSPLTRESOLVE_SIZE 24 131 132 Elf_Word *pltcall, *pltresolve; 133 Elf_Word *jmptab; 134 int N = obj->pltrelalim - obj->pltrela; 135 136 /* Entries beyond 8192 take twice as much space. */ 137 if (N > 8192) 138 N += N-8192; 139 140 dbg(("obj %s bss-plt pltgot=%p jmptab=%u start=%p obj=%p", 141 obj->path, obj->pltgot, 18 + N * 2, 142 _rtld_bind_bssplt_start, obj)); 143 144 pltcall = obj->pltgot; 145 jmptab = pltcall + 18 + N * 2; 146 147 memcpy(pltcall, _rtld_powerpc_pltcall, BSSPLTCALL_SIZE); 148 pltcall[1] |= ha(jmptab); 149 pltcall[2] |= lo(jmptab); 150 151 pltresolve = obj->pltgot + 8; 152 153 memcpy(pltresolve, _rtld_powerpc_pltresolve, BSSPLTRESOLVE_SIZE); 154 pltresolve[0] |= ha(_rtld_bind_bssplt_start); 155 pltresolve[1] |= lo(_rtld_bind_bssplt_start); 156 pltresolve[3] |= ha(obj); 157 pltresolve[4] |= lo(obj); 158 159 /* 160 * Invalidate the icache for only the code part of the PLT 161 * (and not the jump table at the end). 162 */ 163 __syncicache(pltcall, (char *)jmptab - (char *)pltcall); 164 } 165 #endif 166 } 167 168 void 169 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 170 { 171 const Elf_Rela *rela = 0, *relalim; 172 Elf_Addr relasz = 0; 173 Elf_Addr *where; 174 175 for (; dynp->d_tag != DT_NULL; dynp++) { 176 switch (dynp->d_tag) { 177 case DT_RELA: 178 rela = (const Elf_Rela *)(relocbase + dynp->d_un.d_ptr); 179 break; 180 case DT_RELASZ: 181 relasz = dynp->d_un.d_val; 182 break; 183 } 184 } 185 relalim = (const Elf_Rela *)((const uint8_t *)rela + relasz); 186 for (; rela < relalim; rela++) { 187 where = (Elf_Addr *)(relocbase + rela->r_offset); 188 *where = (Elf_Addr)(relocbase + rela->r_addend); 189 } 190 } 191 192 int 193 _rtld_relocate_nonplt_objects(Obj_Entry *obj) 194 { 195 const Elf_Rela *rela; 196 const Elf_Sym *def = NULL; 197 const Obj_Entry *defobj = NULL; 198 unsigned long last_symnum = ULONG_MAX; 199 200 for (rela = obj->rela; rela < obj->relalim; rela++) { 201 Elf_Addr *where; 202 Elf_Addr tmp; 203 unsigned long symnum; 204 205 where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 206 symnum = ELF_R_SYM(rela->r_info); 207 208 switch (ELF_R_TYPE(rela->r_info)) { 209 #ifdef _LP64 210 case R_TYPE(ADDR64): /* <address> S + A */ 211 #else 212 case R_TYPE(ADDR32): /* <address> S + A */ 213 case R_TYPE(UADDR32): /* <address> S + A */ 214 #endif 215 case R_TYPE(GLOB_DAT): /* <address> S + A */ 216 case R_TYPE(ADDR16_LO): 217 case R_TYPE(ADDR16_HI): 218 case R_TYPE(ADDR16_HA): 219 case R_TYPE(DTPMOD): 220 case R_TYPE(DTPREL): 221 case R_TYPE(TPREL): 222 if (last_symnum != symnum) { 223 last_symnum = symnum; 224 def = _rtld_find_symdef(symnum, obj, &defobj, 225 false); 226 if (def == NULL) 227 return -1; 228 } 229 break; 230 default: 231 break; 232 } 233 234 switch (ELF_R_TYPE(rela->r_info)) { 235 #if 1 /* XXX Should not be necessary. */ 236 case R_TYPE(JMP_SLOT): 237 #endif 238 case R_TYPE(NONE): 239 break; 240 241 #ifdef _LP64 242 case R_TYPE(ADDR64): /* <address> S + A */ 243 #else 244 case R_TYPE(ADDR32): /* <address> S + A */ 245 case R_TYPE(UADDR32): /* <address> S + A */ 246 #endif 247 case R_TYPE(GLOB_DAT): /* <address> S + A */ 248 tmp = (Elf_Addr)(defobj->relocbase + def->st_value + 249 rela->r_addend); 250 if (*where != tmp) 251 *where = tmp; 252 rdbg(("32/GLOB_DAT %s in %s --> %p in %s", 253 obj->strtab + obj->symtab[symnum].st_name, 254 obj->path, (void *)*where, defobj->path)); 255 break; 256 257 /* 258 * Recent GNU ld does not resolve ADDR16_{LO,HI,HA} if 259 * the reloc is in a writable section and the symbol 260 * is not already referenced from text. 261 */ 262 case R_TYPE(ADDR16_LO): { 263 tmp = (Elf_Addr)(defobj->relocbase + def->st_value + 264 rela->r_addend); 265 266 uint16_t tmp16 = lo(tmp); 267 268 uint16_t *where16 = (uint16_t *)where; 269 if (*where16 != tmp16) 270 *where16 = tmp16; 271 rdbg(("ADDR16_LO %s in %s --> #lo(%p) = 0x%x in %s", 272 obj->strtab + obj->symtab[symnum].st_name, 273 obj->path, (void *)tmp, tmp16, defobj->path)); 274 break; 275 } 276 277 case R_TYPE(ADDR16_HI): 278 case R_TYPE(ADDR16_HA): { 279 tmp = (Elf_Addr)(defobj->relocbase + def->st_value + 280 rela->r_addend); 281 282 uint16_t tmp16 = hi(tmp); 283 if (ELF_R_TYPE(rela->r_info) == R_TYPE(ADDR16_HA) 284 && (tmp & __ha16)) 285 ++tmp16; /* adjust to ha(tmp) */ 286 287 uint16_t *where16 = (uint16_t *)where; 288 if (*where16 != tmp16) 289 *where16 = tmp16; 290 rdbg(("ADDR16_H%c %s in %s --> #h%c(%p) = 0x%x in %s", 291 (ELF_R_TYPE(rela->r_info) == R_TYPE(ADDR16_HI) 292 ? 'I' : 'A'), 293 obj->strtab + obj->symtab[symnum].st_name, 294 obj->path, 295 (ELF_R_TYPE(rela->r_info) == R_TYPE(ADDR16_HI) 296 ? 'i' : 'a'), 297 (void *)tmp, tmp16, defobj->path)); 298 break; 299 } 300 301 case R_TYPE(RELATIVE): /* <address> B + A */ 302 *where = (Elf_Addr)(obj->relocbase + rela->r_addend); 303 rdbg(("RELATIVE in %s --> %p", obj->path, 304 (void *)*where)); 305 break; 306 307 case R_TYPE(COPY): 308 /* 309 * These are deferred until all other relocations have 310 * been done. All we do here is make sure that the 311 * COPY relocation is not in a shared library. They 312 * are allowed only in executable files. 313 */ 314 if (obj->isdynamic) { 315 _rtld_error( 316 "%s: Unexpected R_COPY relocation in shared library", 317 obj->path); 318 return -1; 319 } 320 rdbg(("COPY (avoid in main)")); 321 break; 322 323 case R_TYPE(DTPMOD): 324 *where = (Elf_Addr)defobj->tlsindex; 325 rdbg(("DTPMOD32 %s in %s --> %p in %s", 326 obj->strtab + obj->symtab[symnum].st_name, 327 obj->path, (void *)*where, defobj->path)); 328 break; 329 330 case R_TYPE(DTPREL): 331 *where = (Elf_Addr)(def->st_value + rela->r_addend 332 - TLS_DTV_OFFSET); 333 rdbg(("DTPREL32 %s in %s --> %p in %s", 334 obj->strtab + obj->symtab[symnum].st_name, 335 obj->path, (void *)*where, defobj->path)); 336 break; 337 338 case R_TYPE(TPREL): 339 if (!defobj->tls_static && 340 _rtld_tls_offset_allocate(__UNCONST(defobj))) 341 return -1; 342 343 *where = (Elf_Addr)(def->st_value + rela->r_addend 344 + defobj->tlsoffset - TLS_TP_OFFSET); 345 rdbg(("TPREL32 %s in %s --> %p in %s", 346 obj->strtab + obj->symtab[symnum].st_name, 347 obj->path, (void *)*where, defobj->path)); 348 break; 349 350 case R_TYPE(IRELATIVE): 351 /* IFUNC relocations are handled in _rtld_call_ifunc */ 352 if (obj->ifunc_remaining_nonplt == 0) { 353 obj->ifunc_remaining_nonplt = 354 obj->relalim - rela; 355 } 356 break; 357 358 default: 359 rdbg(("sym = %lu, type = %lu, offset = %p, " 360 "addend = %p, contents = %p, symbol = %s", 361 (u_long)ELF_R_SYM(rela->r_info), 362 (u_long)ELF_R_TYPE(rela->r_info), 363 (void *)rela->r_offset, (void *)rela->r_addend, 364 (void *)*where, 365 obj->strtab + obj->symtab[symnum].st_name)); 366 _rtld_error("%s: Unsupported relocation type %ld " 367 "in non-PLT relocations", 368 obj->path, (u_long) ELF_R_TYPE(rela->r_info)); 369 return -1; 370 } 371 } 372 return 0; 373 } 374 375 int 376 _rtld_relocate_plt_lazy(Obj_Entry *obj) 377 { 378 #ifdef _LP64 379 /* 380 * For PowerPC64, the plt stubs handle an empty function descriptor 381 * so there's nothing to do. 382 */ 383 /* XXX ifunc support */ 384 #else 385 Elf_Addr * const pltresolve = obj->pltgot + 8; 386 const Elf_Rela *rela; 387 388 for (rela = obj->pltrelalim; rela-- > obj->pltrela;) { 389 size_t reloff = rela - obj->pltrela; 390 Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset); 391 392 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT) || 393 ELF_R_TYPE(rela->r_info) == R_TYPE(IRELATIVE)); 394 395 if (ELF_R_TYPE(rela->r_info) == R_TYPE(IRELATIVE)) { 396 /* No ifunc support for old-style insecure PLT. */ 397 assert(obj->gotptr != NULL); 398 obj->ifunc_remaining = obj->pltrelalim - rela; 399 } 400 401 if (obj->gotptr != NULL) { 402 /* 403 * For now, simply treat then as relative. 404 */ 405 *where += (Elf_Addr)obj->relocbase; 406 } else { 407 int distance; 408 409 if (reloff < 32768) { 410 /* li r11,reloff */ 411 *where++ = 0x39600000 | reloff; 412 } else { 413 /* lis r11,ha(reloff) */ 414 /* addi r11,lo(reloff) */ 415 *where++ = 0x3d600000 | ha(reloff); 416 *where++ = 0x396b0000 | lo(reloff); 417 } 418 /* b pltresolve */ 419 distance = (Elf_Addr)pltresolve - (Elf_Addr)where; 420 *where++ = 0x48000000 | (distance & 0x03fffffc); 421 422 /* 423 * Icache invalidation is not done for each entry here 424 * because we sync the entire code part of the PLT once 425 * in _rtld_setup_pltgot() after all the entries have been 426 * initialized. 427 */ 428 /* __syncicache(where - 3, 12); */ 429 } 430 } 431 #endif /* !_LP64 */ 432 433 return 0; 434 } 435 436 static int 437 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela, int reloff, Elf_Addr *tp) 438 { 439 Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset); 440 Elf_Addr value; 441 const Elf_Sym *def; 442 const Obj_Entry *defobj; 443 unsigned long info = rela->r_info; 444 445 assert(ELF_R_TYPE(info) == R_TYPE(JMP_SLOT)); 446 447 def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, tp != NULL); 448 if (__predict_false(def == NULL)) 449 return -1; 450 if (__predict_false(def == &_rtld_sym_zero)) 451 return 0; 452 453 if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { 454 if (tp == NULL) 455 return 0; 456 value = _rtld_resolve_ifunc(defobj, def); 457 } else { 458 value = (Elf_Addr)(defobj->relocbase + def->st_value); 459 } 460 rdbg(("bind now/fixup in %s --> new=%p", 461 defobj->strtab + def->st_name, (void *)value)); 462 463 #ifdef _LP64 464 /* 465 * For PowerPC64 we simply replace the function descriptor in the 466 * PLTGOT with the one from source object. 467 */ 468 assert(where >= (Elf_Word *)obj->pltgot); 469 assert(where < (Elf_Word *)obj->pltgot + (obj->pltrelalim - obj->pltrela)); 470 const Elf_Addr * const fdesc = (Elf_Addr *) value; 471 where[0] = fdesc[0]; 472 where[1] = fdesc[1]; 473 where[2] = fdesc[2]; 474 #else 475 ptrdiff_t distance = value - (Elf_Addr)where; 476 if (obj->gotptr != NULL) { 477 /* 478 * For Secure-PLT we simply replace the entry in GOT with the 479 * address of the routine. 480 */ 481 assert(where >= (Elf_Word *)obj->pltgot); 482 assert(where < (Elf_Word *)obj->pltgot + (obj->pltrelalim - obj->pltrela)); 483 *where = value; 484 } else if (labs(distance) < 32*1024*1024) { /* inside 32MB? */ 485 /* b value # branch directly */ 486 *where = 0x48000000 | (distance & 0x03fffffc); 487 __syncicache(where, 4); 488 } else { 489 Elf_Addr *pltcall, *jmptab; 490 int N = obj->pltrelalim - obj->pltrela; 491 492 /* Entries beyond 8192 take twice as much space. */ 493 if (N > 8192) 494 N += N-8192; 495 496 pltcall = obj->pltgot; 497 jmptab = pltcall + 18 + N * 2; 498 499 jmptab[reloff] = value; 500 501 if (reloff < 32768) { 502 /* li r11,reloff */ 503 *where++ = 0x39600000 | reloff; 504 } else { 505 #ifdef notyet 506 /* lis r11,ha(value) */ 507 /* addi r11,lo(value) */ 508 /* mtctr r11 */ 509 /* bctr */ 510 *where++ = 0x3d600000 | ha(value); 511 *where++ = 0x396b0000 | lo(value); 512 *where++ = 0x7d6903a6; 513 *where++ = 0x4e800420; 514 #else 515 /* lis r11,ha(reloff) */ 516 /* addi r11,lo(reloff) */ 517 *where++ = 0x3d600000 | ha(reloff); 518 *where++ = 0x396b0000 | lo(reloff); 519 #endif 520 } 521 /* b pltcall */ 522 distance = (Elf_Addr)pltcall - (Elf_Addr)where; 523 *where++ = 0x48000000 | (distance & 0x03fffffc); 524 __syncicache(where - 3, 12); 525 } 526 #endif /* _LP64 */ 527 528 if (tp) 529 *tp = value; 530 return 0; 531 } 532 533 Elf_Addr 534 _rtld_bind(const Obj_Entry *obj, Elf_Word reloff) 535 { 536 const Elf_Rela *rela = obj->pltrela + reloff; 537 Elf_Addr new_value; 538 int err; 539 540 new_value = 0; /* XXX gcc */ 541 542 _rtld_shared_enter(); 543 err = _rtld_relocate_plt_object(obj, rela, reloff, &new_value); 544 if (err) 545 _rtld_die(); 546 _rtld_shared_exit(); 547 548 #ifdef _LP64 549 return obj->glink; 550 #else 551 return new_value; 552 #endif 553 } 554 555 int 556 _rtld_relocate_plt_objects(const Obj_Entry *obj) 557 { 558 const Elf_Rela *rela; 559 int reloff; 560 561 for (rela = obj->pltrela, reloff = 0; rela < obj->pltrelalim; rela++, reloff++) { 562 if (_rtld_relocate_plt_object(obj, rela, reloff, NULL) < 0) 563 return -1; 564 } 565 return 0; 566 } 567