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