1 1.53 skrll /* $NetBSD: hppa_reloc.c,v 1.53 2024/07/29 13:16:19 skrll Exp $ */ 2 1.1 fredette 3 1.1 fredette /*- 4 1.21 skrll * Copyright (c) 2002, 2004 The NetBSD Foundation, Inc. 5 1.1 fredette * All rights reserved. 6 1.1 fredette * 7 1.1 fredette * This code is derived from software contributed to The NetBSD Foundation 8 1.21 skrll * by Matt Fredette and Nick Hudson. 9 1.1 fredette * 10 1.1 fredette * Redistribution and use in source and binary forms, with or without 11 1.1 fredette * modification, are permitted provided that the following conditions 12 1.1 fredette * are met: 13 1.1 fredette * 1. Redistributions of source code must retain the above copyright 14 1.1 fredette * notice, this list of conditions and the following disclaimer. 15 1.1 fredette * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 fredette * notice, this list of conditions and the following disclaimer in the 17 1.1 fredette * documentation and/or other materials provided with the distribution. 18 1.1 fredette * 19 1.1 fredette * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 fredette * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 fredette * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 fredette * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 fredette * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 fredette * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 fredette * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 fredette * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 fredette * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 fredette * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 fredette * POSSIBILITY OF SUCH DAMAGE. 30 1.1 fredette */ 31 1.1 fredette 32 1.51 riastrad /* 33 1.51 riastrad * HP PA-RISC ELF relocations. 34 1.51 riastrad * 35 1.51 riastrad * References: 36 1.51 riastrad * 37 1.51 riastrad * [PAELF] Processor-Specific ELF Supplement for PA-RISC, Version 38 1.51 riastrad * 1.5, 1998-08-20. 39 1.51 riastrad * https://parisc.wiki.kernel.org/images-parisc/0/0e/Elf-pa-hp.pdf 40 1.51 riastrad * https://web.archive.org/web/20240712004045/https://parisc.wiki.kernel.org/images-parisc/0/0e/Elf-pa-hp.pdf 41 1.51 riastrad * 42 1.51 riastrad * [PATLS] Randolph Chung, Carlos O'Donell, and John David 43 1.51 riastrad * Anglin, `Implementing Thread Local Storage for HP PA-RISC 44 1.51 riastrad * Linux', 2013-11-11. 45 1.51 riastrad * http://www.parisc-linux.org/documentation/tls/hppa-tls-implementation.pdf 46 1.51 riastrad * https://web.archive.org/web/20240722131647/http://www.parisc-linux.org/documentation/tls/hppa-tls-implementation.pdf 47 1.51 riastrad */ 48 1.51 riastrad 49 1.23 skrll #include <sys/cdefs.h> 50 1.23 skrll #ifndef lint 51 1.53 skrll __RCSID("$NetBSD: hppa_reloc.c,v 1.53 2024/07/29 13:16:19 skrll Exp $"); 52 1.23 skrll #endif /* not lint */ 53 1.23 skrll 54 1.1 fredette #include <stdlib.h> 55 1.1 fredette #include <sys/types.h> 56 1.1 fredette #include <sys/queue.h> 57 1.1 fredette 58 1.24 skrll #include <string.h> 59 1.24 skrll 60 1.1 fredette #include "rtld.h" 61 1.1 fredette #include "debug.h" 62 1.1 fredette 63 1.1 fredette #ifdef RTLD_DEBUG_HPPA 64 1.14 mycroft #define hdbg(x) xprintf x 65 1.1 fredette #else 66 1.1 fredette #define hdbg(x) /* nothing */ 67 1.1 fredette #endif 68 1.12 mycroft 69 1.20 skrll caddr_t _rtld_bind(const Obj_Entry *, const Elf_Addr); 70 1.12 mycroft void _rtld_bind_start(void); 71 1.13 mycroft void __rtld_setup_hppa_pltgot(const Obj_Entry *, Elf_Addr *); 72 1.49 skrll void _rtld_set_dp(Elf_Addr *); 73 1.1 fredette 74 1.1 fredette /* 75 1.24 skrll * It is possible for the compiler to emit relocations for unaligned data. 76 1.24 skrll * We handle this situation with these inlines. 77 1.24 skrll */ 78 1.24 skrll #define RELOC_ALIGNED_P(x) \ 79 1.24 skrll (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) 80 1.24 skrll 81 1.24 skrll static inline Elf_Addr 82 1.24 skrll load_ptr(void *where) 83 1.24 skrll { 84 1.24 skrll if (__predict_true(RELOC_ALIGNED_P(where))) 85 1.24 skrll return *(Elf_Addr *)where; 86 1.24 skrll else { 87 1.24 skrll Elf_Addr res; 88 1.24 skrll 89 1.24 skrll (void)memcpy(&res, where, sizeof(res)); 90 1.24 skrll return res; 91 1.24 skrll } 92 1.24 skrll } 93 1.24 skrll 94 1.24 skrll static inline void 95 1.24 skrll store_ptr(void *where, Elf_Addr val) 96 1.24 skrll { 97 1.24 skrll if (__predict_true(RELOC_ALIGNED_P(where))) 98 1.24 skrll *(Elf_Addr *)where = val; 99 1.24 skrll else 100 1.24 skrll (void)memcpy(where, &val, sizeof(val)); 101 1.24 skrll } 102 1.24 skrll 103 1.42 skrll static __inline void 104 1.42 skrll fdc(void *addr) 105 1.42 skrll { 106 1.42 skrll __asm volatile("fdc %%r0(%%sr0, %0)" : : "r" (addr)); 107 1.42 skrll } 108 1.42 skrll 109 1.42 skrll static __inline void 110 1.46 skrll fic(void *addr) 111 1.46 skrll { 112 1.42 skrll __asm volatile("fic %%r0(%%sr0,%0)" : : "r" (addr)); 113 1.46 skrll } 114 1.42 skrll 115 1.42 skrll static __inline void 116 1.42 skrll sync(void) 117 1.42 skrll { 118 1.42 skrll __asm volatile("sync" : : : "memory"); 119 1.42 skrll } 120 1.42 skrll 121 1.42 skrll #define PLT_STUB_MAGIC1 0x00c0ffee 122 1.42 skrll #define PLT_STUB_MAGIC2 0xdeadbeef 123 1.42 skrll 124 1.42 skrll #define PLT_STUB_INSN1 0x0e801081 /* ldw 0(%r20), %r1 */ 125 1.42 skrll #define PLT_STUB_INSN2 0xe820c000 /* bv %r0(%r1) */ 126 1.42 skrll 127 1.24 skrll /* 128 1.34 skrll * In the runtime architecture (ABI), PLABEL function pointers are 129 1.34 skrll * distinguished from normal function pointers by having the next-least- 130 1.34 skrll * significant bit set. (This bit is referred to as the L field in HP 131 1.34 skrll * documentation). The $$dyncall millicode is aware of this. 132 1.1 fredette */ 133 1.1 fredette #define RTLD_MAKE_PLABEL(plabel) (((Elf_Addr)(plabel)) | (1 << 1)) 134 1.1 fredette #define RTLD_IS_PLABEL(addr) (((Elf_Addr)(addr)) & (1 << 1)) 135 1.1 fredette #define RTLD_GET_PLABEL(addr) ((hppa_plabel *) (((Elf_Addr)addr) & ~3)) 136 1.1 fredette 137 1.1 fredette /* 138 1.1 fredette * This is the PLABEL structure. The function PC and 139 1.1 fredette * shared linkage members must come first, as they are 140 1.1 fredette * the actual PLABEL. 141 1.1 fredette */ 142 1.1 fredette typedef struct _hppa_plabel { 143 1.1 fredette Elf_Addr hppa_plabel_pc; 144 1.1 fredette Elf_Addr hppa_plabel_sl; 145 1.1 fredette SLIST_ENTRY(_hppa_plabel) hppa_plabel_next; 146 1.1 fredette } hppa_plabel; 147 1.1 fredette 148 1.1 fredette /* 149 1.46 skrll * For now allocated PLABEL structures are tracked on a 150 1.1 fredette * singly linked list. This maybe should be revisited. 151 1.1 fredette */ 152 1.1 fredette static SLIST_HEAD(hppa_plabel_head, _hppa_plabel) hppa_plabel_list 153 1.1 fredette = SLIST_HEAD_INITIALIZER(hppa_plabel_list); 154 1.1 fredette 155 1.1 fredette /* 156 1.1 fredette * Because I'm hesitant to use NEW while relocating self, 157 1.1 fredette * this is a small pool of preallocated PLABELs. 158 1.1 fredette */ 159 1.40 chs #define HPPA_PLABEL_PRE (32) 160 1.1 fredette static hppa_plabel hppa_plabel_pre[HPPA_PLABEL_PRE]; 161 1.1 fredette static int hppa_plabel_pre_next = 0; 162 1.1 fredette 163 1.20 skrll void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 164 1.20 skrll int _rtld_relocate_plt_objects(const Obj_Entry *); 165 1.25 skrll static inline int _rtld_relocate_plt_object(const Obj_Entry *, 166 1.25 skrll const Elf_Rela *, Elf_Addr *); 167 1.25 skrll 168 1.1 fredette /* 169 1.1 fredette * This bootstraps the dynamic linker by relocating its GOT. 170 1.1 fredette * On the hppa, unlike on other architectures, static strings 171 1.1 fredette * are found through the GOT. Static strings are essential 172 1.46 skrll * for RTLD_DEBUG, and I suspect they're used early even when 173 1.1 fredette * !defined(RTLD_DEBUG), making relocating the GOT essential. 174 1.1 fredette * 175 1.1 fredette * It gets worse. Relocating the GOT doesn't mean just walking 176 1.1 fredette * it and adding the relocbase to all of the entries. You must 177 1.46 skrll * find and use the GOT relocations, since those RELA relocations 178 1.46 skrll * have the necessary addends - the GOT comes initialized as 179 1.1 fredette * zeroes. 180 1.1 fredette */ 181 1.1 fredette void 182 1.20 skrll _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 183 1.1 fredette { 184 1.1 fredette const Elf_Rela *relafirst, *rela, *relalim; 185 1.22 chs Elf_Addr relasz; 186 1.24 skrll void *where; 187 1.20 skrll Elf_Addr *pltgot; 188 1.20 skrll const Elf_Rela *plabel_relocs[HPPA_PLABEL_PRE]; 189 1.20 skrll int nplabel_relocs = 0; 190 1.20 skrll int i; 191 1.20 skrll const Elf_Sym *symtab, *sym; 192 1.20 skrll unsigned long symnum; 193 1.20 skrll hppa_plabel *plabel; 194 1.1 fredette 195 1.22 chs /* 196 1.22 chs * Process the DYNAMIC section, looking for the non-PLT relocations. 197 1.46 skrll */ 198 1.1 fredette relafirst = NULL; 199 1.22 chs relasz = 0; 200 1.22 chs symtab = NULL; 201 1.22 chs pltgot = NULL; 202 1.1 fredette for (; dynp->d_tag != DT_NULL; ++dynp) { 203 1.1 fredette switch (dynp->d_tag) { 204 1.1 fredette 205 1.1 fredette case DT_RELA: 206 1.1 fredette relafirst = (const Elf_Rela *) 207 1.1 fredette (relocbase + dynp->d_un.d_ptr); 208 1.1 fredette break; 209 1.1 fredette 210 1.1 fredette case DT_RELASZ: 211 1.1 fredette relasz = dynp->d_un.d_val; 212 1.1 fredette break; 213 1.20 skrll 214 1.20 skrll case DT_SYMTAB: 215 1.20 skrll symtab = (const Elf_Sym *) 216 1.20 skrll (relocbase + dynp->d_un.d_ptr); 217 1.20 skrll break; 218 1.20 skrll 219 1.20 skrll case DT_PLTGOT: 220 1.20 skrll pltgot = (Elf_Addr *) 221 1.20 skrll (relocbase + dynp->d_un.d_ptr); 222 1.35 skrll break; 223 1.1 fredette } 224 1.1 fredette } 225 1.28 mjf relalim = (const Elf_Rela *)((const char *)relafirst + relasz); 226 1.1 fredette 227 1.18 skrll for (rela = relafirst; rela < relalim; rela++) { 228 1.20 skrll symnum = ELF_R_SYM(rela->r_info); 229 1.24 skrll where = (void *)(relocbase + rela->r_offset); 230 1.20 skrll 231 1.20 skrll switch (ELF_R_TYPE(rela->r_info)) { 232 1.20 skrll case R_TYPE(DIR32): 233 1.20 skrll if (symnum == 0) 234 1.46 skrll store_ptr(where, 235 1.24 skrll relocbase + rela->r_addend); 236 1.20 skrll else { 237 1.20 skrll sym = symtab + symnum; 238 1.46 skrll store_ptr(where, 239 1.24 skrll relocbase + rela->r_addend + sym->st_value); 240 1.20 skrll } 241 1.20 skrll break; 242 1.20 skrll 243 1.20 skrll case R_TYPE(PLABEL32): 244 1.20 skrll /* 245 1.20 skrll * PLABEL32 relocation processing is done in two phases 246 1.20 skrll * 247 1.20 skrll * i) local function relocations (symbol number == 0) 248 1.20 skrll * can be resolved immediately. 249 1.20 skrll * 250 1.20 skrll * ii) external function relocations are deferred until 251 1.20 skrll * we finish all other relocations so that global 252 1.20 skrll * data isn't accessed until all other non-PLT 253 1.20 skrll * relocations have been done. 254 1.20 skrll */ 255 1.20 skrll if (symnum == 0) 256 1.46 skrll *((Elf_Addr *)where) = 257 1.20 skrll relocbase + rela->r_addend; 258 1.20 skrll else 259 1.20 skrll plabel_relocs[nplabel_relocs++] = rela; 260 1.20 skrll break; 261 1.20 skrll 262 1.20 skrll default: 263 1.20 skrll break; 264 1.20 skrll } 265 1.1 fredette } 266 1.1 fredette 267 1.20 skrll assert(nplabel_relocs < HPPA_PLABEL_PRE); 268 1.20 skrll for (i = 0; i < nplabel_relocs; i++) { 269 1.20 skrll rela = plabel_relocs[i]; 270 1.24 skrll where = (void *)(relocbase + rela->r_offset); 271 1.20 skrll sym = symtab + ELF_R_SYM(rela->r_info); 272 1.46 skrll 273 1.20 skrll plabel = &hppa_plabel_pre[hppa_plabel_pre_next++]; 274 1.20 skrll 275 1.35 skrll plabel->hppa_plabel_pc = (Elf_Addr) 276 1.20 skrll (relocbase + sym->st_value + rela->r_addend); 277 1.35 skrll plabel->hppa_plabel_sl = (Elf_Addr)pltgot; 278 1.20 skrll 279 1.35 skrll SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next); 280 1.20 skrll *((Elf_Addr *)where) = (Elf_Addr)(RTLD_MAKE_PLABEL(plabel)); 281 1.20 skrll } 282 1.46 skrll 283 1.1 fredette #if defined(RTLD_DEBUG_HPPA) 284 1.18 skrll for (rela = relafirst; rela < relalim; rela++) { 285 1.24 skrll where = (void *)(relocbase + rela->r_offset); 286 1.20 skrll 287 1.20 skrll switch (ELF_R_TYPE(rela->r_info)) { 288 1.20 skrll case R_TYPE(DIR32): 289 1.20 skrll hdbg(("DIR32 rela @%p(%p) -> %p(%p)\n", 290 1.1 fredette (void *)rela->r_offset, 291 1.1 fredette (void *)where, 292 1.1 fredette (void *)rela->r_addend, 293 1.20 skrll (void *)*((Elf_Addr *)where) )); 294 1.20 skrll break; 295 1.20 skrll 296 1.20 skrll case R_TYPE(PLABEL32): 297 1.20 skrll symnum = ELF_R_SYM(rela->r_info); 298 1.20 skrll if (symnum == 0) { 299 1.20 skrll hdbg(("PLABEL rela @%p(%p) -> %p(%p)\n", 300 1.20 skrll (void *)rela->r_offset, 301 1.20 skrll (void *)where, 302 1.20 skrll (void *)rela->r_addend, 303 1.20 skrll (void *)*((Elf_Addr *)where) )); 304 1.20 skrll } else { 305 1.20 skrll sym = symtab + symnum; 306 1.20 skrll 307 1.20 skrll hdbg(("PLABEL32 rela @%p(%p), symnum=%ld(%p) -> %p(%p)\n", 308 1.20 skrll (void *)rela->r_offset, 309 1.20 skrll (void *)where, 310 1.20 skrll symnum, 311 1.20 skrll (void *)sym->st_value, 312 1.20 skrll (void *)rela->r_addend, 313 1.20 skrll (void *)*((Elf_Addr *)where) )); 314 1.20 skrll } 315 1.20 skrll break; 316 1.20 skrll default: 317 1.20 skrll hdbg(("rela XXX reloc\n")); 318 1.20 skrll break; 319 1.20 skrll } 320 1.1 fredette } 321 1.1 fredette #endif /* RTLD_DEBUG_HPPA */ 322 1.1 fredette } 323 1.1 fredette 324 1.1 fredette /* 325 1.46 skrll * This allocates a PLABEL. If called with a non-NULL def, the 326 1.1 fredette * plabel is for the function associated with that definition 327 1.46 skrll * in the defining object defobj, plus the given addend. If 328 1.1 fredette * called with a NULL def, the plabel is for the function at 329 1.1 fredette * the (unrelocated) address in addend in the object defobj. 330 1.1 fredette */ 331 1.1 fredette Elf_Addr 332 1.1 fredette _rtld_function_descriptor_alloc(const Obj_Entry *defobj, const Elf_Sym *def, 333 1.1 fredette Elf_Addr addend) 334 1.1 fredette { 335 1.1 fredette Elf_Addr func_pc, func_sl; 336 1.1 fredette hppa_plabel *plabel; 337 1.1 fredette 338 1.1 fredette if (def != NULL) { 339 1.46 skrll 340 1.1 fredette /* 341 1.1 fredette * We assume that symbols of type STT_NOTYPE 342 1.1 fredette * are undefined. Return NULL for these. 343 1.1 fredette */ 344 1.1 fredette if (ELF_ST_TYPE(def->st_info) == STT_NOTYPE) 345 1.1 fredette return (Elf_Addr)NULL; 346 1.1 fredette 347 1.1 fredette /* Otherwise assert that this symbol must be a function. */ 348 1.1 fredette assert(ELF_ST_TYPE(def->st_info) == STT_FUNC); 349 1.1 fredette 350 1.46 skrll func_pc = (Elf_Addr)(defobj->relocbase + def->st_value + 351 1.1 fredette addend); 352 1.1 fredette } else 353 1.1 fredette func_pc = (Elf_Addr)(defobj->relocbase + addend); 354 1.1 fredette 355 1.1 fredette /* 356 1.1 fredette * Search the existing PLABELs for one matching 357 1.1 fredette * this function. If there is one, return it. 358 1.1 fredette */ 359 1.20 skrll func_sl = (Elf_Addr)(defobj->pltgot); 360 1.1 fredette SLIST_FOREACH(plabel, &hppa_plabel_list, hppa_plabel_next) 361 1.1 fredette if (plabel->hppa_plabel_pc == func_pc && 362 1.1 fredette plabel->hppa_plabel_sl == func_sl) 363 1.1 fredette return RTLD_MAKE_PLABEL(plabel); 364 1.1 fredette 365 1.1 fredette /* 366 1.1 fredette * Once we've used up the preallocated set, we start 367 1.1 fredette * using NEW to allocate plabels. 368 1.1 fredette */ 369 1.1 fredette if (hppa_plabel_pre_next < HPPA_PLABEL_PRE) 370 1.1 fredette plabel = &hppa_plabel_pre[hppa_plabel_pre_next++]; 371 1.1 fredette else { 372 1.1 fredette plabel = NEW(hppa_plabel); 373 1.1 fredette if (plabel == NULL) 374 1.1 fredette return (Elf_Addr)-1; 375 1.1 fredette } 376 1.1 fredette 377 1.1 fredette /* Fill the new entry and insert it on the list. */ 378 1.1 fredette plabel->hppa_plabel_pc = func_pc; 379 1.1 fredette plabel->hppa_plabel_sl = func_sl; 380 1.1 fredette SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next); 381 1.1 fredette 382 1.1 fredette return RTLD_MAKE_PLABEL(plabel); 383 1.1 fredette } 384 1.1 fredette 385 1.1 fredette /* 386 1.1 fredette * If a pointer is a PLABEL, this unwraps it. 387 1.1 fredette */ 388 1.1 fredette const void * 389 1.1 fredette _rtld_function_descriptor_function(const void *addr) 390 1.1 fredette { 391 1.46 skrll return (RTLD_IS_PLABEL(addr) ? 392 1.1 fredette (const void *) RTLD_GET_PLABEL(addr)->hppa_plabel_pc : 393 1.1 fredette addr); 394 1.1 fredette } 395 1.1 fredette 396 1.2 mycroft /* This sets up an object's GOT. */ 397 1.2 mycroft void 398 1.2 mycroft _rtld_setup_pltgot(const Obj_Entry *obj) 399 1.2 mycroft { 400 1.42 skrll Elf_Word *got = obj->pltgot; 401 1.42 skrll 402 1.42 skrll assert(got[-2] == PLT_STUB_MAGIC1); 403 1.42 skrll assert(got[-1] == PLT_STUB_MAGIC2); 404 1.46 skrll 405 1.42 skrll __rtld_setup_hppa_pltgot(obj, got); 406 1.42 skrll 407 1.42 skrll fdc(&got[-2]); 408 1.42 skrll fdc(&got[-1]); 409 1.42 skrll fdc(&got[1]); 410 1.42 skrll sync(); 411 1.42 skrll fic(&got[-2]); 412 1.42 skrll fic(&got[-1]); 413 1.42 skrll fic(&got[1]); 414 1.42 skrll sync(); 415 1.42 skrll 416 1.42 skrll /* 417 1.42 skrll * libc makes use of %t1 (%r22) to pass errno values to __cerror. Fixup 418 1.42 skrll * the PLT stub to not use %r22. 419 1.42 skrll */ 420 1.42 skrll got[-7] = PLT_STUB_INSN1; 421 1.42 skrll got[-6] = PLT_STUB_INSN2; 422 1.42 skrll fdc(&got[-7]); 423 1.42 skrll fdc(&got[-6]); 424 1.42 skrll sync(); 425 1.42 skrll fic(&got[-7]); 426 1.42 skrll fic(&got[-6]); 427 1.42 skrll sync(); 428 1.4 mycroft } 429 1.4 mycroft 430 1.4 mycroft int 431 1.33 joerg _rtld_relocate_nonplt_objects(Obj_Entry *obj) 432 1.4 mycroft { 433 1.5 mycroft const Elf_Rela *rela; 434 1.44 joerg const Elf_Sym *def = NULL; 435 1.44 joerg const Obj_Entry *defobj = NULL; 436 1.44 joerg unsigned long last_symnum = ULONG_MAX; 437 1.5 mycroft 438 1.49 skrll /* 439 1.49 skrll * This will be done by the crt0 code, but make sure it's set 440 1.49 skrll * early so that symbols overridden by the non-pic binary 441 1.49 skrll * get the right DP value. 442 1.49 skrll */ 443 1.49 skrll if (obj->mainprog) { 444 1.49 skrll hdbg(("setting DP to %p", obj->pltgot)); 445 1.49 skrll _rtld_set_dp(obj->pltgot); 446 1.49 skrll } 447 1.49 skrll 448 1.5 mycroft for (rela = obj->rela; rela < obj->relalim; rela++) { 449 1.5 mycroft Elf_Addr *where; 450 1.5 mycroft Elf_Addr tmp; 451 1.5 mycroft 452 1.5 mycroft where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 453 1.5 mycroft 454 1.47 skrll unsigned long symnum = ELF_R_SYM(rela->r_info); 455 1.44 joerg /* First, handle DIR32 and PLABEL32 without symbol. */ 456 1.47 skrll if (symnum == 0) { 457 1.44 joerg switch (ELF_R_TYPE(rela->r_info)) { 458 1.44 joerg default: 459 1.44 joerg break; 460 1.44 joerg case R_TYPE(DIR32): 461 1.5 mycroft tmp = (Elf_Addr)(obj->relocbase + 462 1.5 mycroft rela->r_addend); 463 1.5 mycroft 464 1.24 skrll if (load_ptr(where) != tmp) 465 1.24 skrll store_ptr(where, tmp); 466 1.20 skrll rdbg(("DIR32 in %s --> %p", obj->path, 467 1.24 skrll (void *)load_ptr(where))); 468 1.44 joerg continue; 469 1.44 joerg case R_TYPE(PLABEL32): 470 1.5 mycroft /* 471 1.5 mycroft * This is a PLABEL for a static function, and 472 1.5 mycroft * the dynamic linker has both allocated a PLT 473 1.5 mycroft * entry for this function and told us where it 474 1.5 mycroft * is. We can safely use the PLT entry as the 475 1.5 mycroft * PLABEL because there should be no other 476 1.5 mycroft * PLABEL reloc referencing this function. 477 1.5 mycroft * This object should also have an IPLT 478 1.5 mycroft * relocation to initialize the PLT entry. 479 1.5 mycroft * 480 1.5 mycroft * The dynamic linker should also have ensured 481 1.5 mycroft * that the addend has the 482 1.5 mycroft * next-least-significant bit set; the 483 1.5 mycroft * $$dyncall millicode uses this to distinguish 484 1.5 mycroft * a PLABEL pointer from a plain function 485 1.5 mycroft * pointer. 486 1.5 mycroft */ 487 1.19 skrll tmp = (Elf_Addr) 488 1.19 skrll (obj->relocbase + rela->r_addend); 489 1.5 mycroft 490 1.5 mycroft if (*where != tmp) 491 1.5 mycroft *where = tmp; 492 1.14 mycroft rdbg(("PLABEL32 in %s --> %p", obj->path, 493 1.14 mycroft (void *)*where)); 494 1.44 joerg continue; 495 1.44 joerg } 496 1.44 joerg } 497 1.44 joerg 498 1.44 joerg switch (ELF_R_TYPE(rela->r_info)) { 499 1.44 joerg case R_TYPE(DIR32): 500 1.44 joerg case R_TYPE(PLABEL32): 501 1.44 joerg case R_TYPE(COPY): 502 1.44 joerg case R_TYPE(TLS_TPREL32): 503 1.44 joerg case R_TYPE(TLS_DTPMOD32): 504 1.44 joerg case R_TYPE(TLS_DTPOFF32): 505 1.44 joerg if (last_symnum != symnum) { 506 1.44 joerg last_symnum = symnum; 507 1.44 joerg if (ELF_R_TYPE(rela->r_info) == R_TYPE(DIR32)) { 508 1.44 joerg /* 509 1.44 joerg * DIR32 relocation against local 510 1.44 joerg * symbols are special... 511 1.44 joerg */ 512 1.44 joerg def = obj->symtab + symnum; 513 1.44 joerg defobj = obj; 514 1.44 joerg if (def->st_name == 0) 515 1.44 joerg break; 516 1.44 joerg } 517 1.44 joerg def = _rtld_find_symdef(symnum, obj, &defobj, 518 1.44 joerg false); 519 1.44 joerg if (def == NULL) 520 1.44 joerg return -1; 521 1.5 mycroft } 522 1.5 mycroft break; 523 1.44 joerg default: 524 1.44 joerg break; 525 1.44 joerg } 526 1.44 joerg 527 1.44 joerg switch (ELF_R_TYPE(rela->r_info)) { 528 1.44 joerg case R_TYPE(NONE): 529 1.44 joerg break; 530 1.44 joerg 531 1.44 joerg case R_TYPE(DIR32): 532 1.44 joerg tmp = (Elf_Addr)(defobj->relocbase + 533 1.44 joerg def->st_value + rela->r_addend); 534 1.44 joerg 535 1.44 joerg if (load_ptr(where) != tmp) 536 1.44 joerg store_ptr(where, tmp); 537 1.44 joerg rdbg(("DIR32 %s in %s --> %p in %s", 538 1.44 joerg obj->strtab + obj->symtab[symnum].st_name, 539 1.44 joerg obj->path, (void *)load_ptr(where), 540 1.44 joerg defobj->path)); 541 1.44 joerg break; 542 1.44 joerg 543 1.44 joerg case R_TYPE(PLABEL32): 544 1.44 joerg tmp = _rtld_function_descriptor_alloc(defobj, 545 1.44 joerg def, rela->r_addend); 546 1.44 joerg if (tmp == (Elf_Addr)-1) 547 1.44 joerg return -1; 548 1.44 joerg 549 1.44 joerg if (*where != tmp) 550 1.44 joerg *where = tmp; 551 1.44 joerg rdbg(("PLABEL32 %s in %s --> %p in %s", 552 1.44 joerg obj->strtab + obj->symtab[symnum].st_name, 553 1.44 joerg obj->path, (void *)*where, defobj->path)); 554 1.44 joerg break; 555 1.4 mycroft 556 1.5 mycroft case R_TYPE(COPY): 557 1.4 mycroft /* 558 1.5 mycroft * These are deferred until all other relocations have 559 1.5 mycroft * been done. All we do here is make sure that the 560 1.5 mycroft * COPY relocation is not in a shared library. They 561 1.5 mycroft * are allowed only in executable files. 562 1.4 mycroft */ 563 1.10 mycroft if (obj->isdynamic) { 564 1.5 mycroft _rtld_error( 565 1.5 mycroft "%s: Unexpected R_COPY relocation in shared library", 566 1.5 mycroft obj->path); 567 1.4 mycroft return -1; 568 1.5 mycroft } 569 1.14 mycroft rdbg(("COPY (avoid in main)")); 570 1.5 mycroft break; 571 1.4 mycroft 572 1.38 skrll case R_TYPE(TLS_TPREL32): 573 1.50 joerg if (!defobj->tls_static && 574 1.50 joerg _rtld_tls_offset_allocate(__UNCONST(defobj))) 575 1.38 skrll return -1; 576 1.38 skrll 577 1.41 skrll *where = (Elf_Addr)(defobj->tlsoffset + def->st_value + 578 1.38 skrll rela->r_addend + sizeof(struct tls_tcb)); 579 1.38 skrll 580 1.38 skrll rdbg(("TPREL32 %s in %s --> %p in %s", 581 1.38 skrll obj->strtab + obj->symtab[symnum].st_name, 582 1.38 skrll obj->path, (void *)*where, defobj->path)); 583 1.38 skrll break; 584 1.38 skrll 585 1.36 skrll case R_TYPE(TLS_DTPMOD32): 586 1.36 skrll *where = (Elf_Addr)(defobj->tlsindex); 587 1.36 skrll 588 1.36 skrll rdbg(("TLS_DTPMOD32 %s in %s --> %p", 589 1.36 skrll obj->strtab + obj->symtab[symnum].st_name, 590 1.36 skrll obj->path, (void *)*where)); 591 1.36 skrll 592 1.36 skrll break; 593 1.36 skrll 594 1.36 skrll case R_TYPE(TLS_DTPOFF32): 595 1.36 skrll *where = (Elf_Addr)(def->st_value); 596 1.36 skrll 597 1.36 skrll rdbg(("TLS_DTPOFF32 %s in %s --> %p", 598 1.36 skrll obj->strtab + obj->symtab[symnum].st_name, 599 1.36 skrll obj->path, (void *)*where)); 600 1.36 skrll 601 1.36 skrll break; 602 1.36 skrll 603 1.5 mycroft default: 604 1.14 mycroft rdbg(("sym = %lu, type = %lu, offset = %p, " 605 1.5 mycroft "addend = %p, contents = %p, symbol = %s", 606 1.6 mycroft symnum, (u_long)ELF_R_TYPE(rela->r_info), 607 1.5 mycroft (void *)rela->r_offset, (void *)rela->r_addend, 608 1.24 skrll (void *)load_ptr(where), 609 1.6 mycroft obj->strtab + obj->symtab[symnum].st_name)); 610 1.5 mycroft _rtld_error("%s: Unsupported relocation type %ld " 611 1.29 jmmv "in non-PLT relocations", 612 1.5 mycroft obj->path, (u_long) ELF_R_TYPE(rela->r_info)); 613 1.4 mycroft return -1; 614 1.4 mycroft } 615 1.8 mycroft } 616 1.8 mycroft return 0; 617 1.8 mycroft } 618 1.8 mycroft 619 1.8 mycroft int 620 1.45 joerg _rtld_relocate_plt_lazy(Obj_Entry *obj) 621 1.8 mycroft { 622 1.8 mycroft const Elf_Rela *rela; 623 1.8 mycroft 624 1.8 mycroft for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) { 625 1.8 mycroft Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 626 1.8 mycroft Elf_Addr func_pc, func_sl; 627 1.8 mycroft 628 1.52 macallan /* skip R_PARISC_NONE entries */ 629 1.53 skrll if (ELF_R_TYPE(rela->r_info) == R_TYPE(NONE)) 630 1.53 skrll continue; 631 1.53 skrll 632 1.8 mycroft assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT)); 633 1.8 mycroft 634 1.8 mycroft /* 635 1.8 mycroft * If this is an IPLT reloc for a static function, 636 1.8 mycroft * fully resolve the PLT entry now. 637 1.8 mycroft */ 638 1.8 mycroft if (ELF_R_SYM(rela->r_info) == 0) { 639 1.8 mycroft func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend); 640 1.20 skrll func_sl = (Elf_Addr)(obj->pltgot); 641 1.8 mycroft } 642 1.8 mycroft 643 1.8 mycroft /* 644 1.8 mycroft * Otherwise set up for lazy binding. 645 1.8 mycroft */ 646 1.8 mycroft else { 647 1.8 mycroft /* 648 1.8 mycroft * This function pointer points to the PLT 649 1.8 mycroft * stub added by the linker, and instead of 650 1.8 mycroft * a shared linkage value, we stash this 651 1.8 mycroft * relocation's offset. The PLT stub has 652 1.8 mycroft * already been set up to transfer to 653 1.8 mycroft * _rtld_bind_start. 654 1.8 mycroft */ 655 1.20 skrll func_pc = ((Elf_Addr)(obj->pltgot)) - 16; 656 1.19 skrll func_sl = (Elf_Addr) 657 1.28 mjf ((const char *)rela - (const char *)(obj->pltrela)); 658 1.8 mycroft } 659 1.20 skrll rdbg(("lazy bind %s(%p) --> old=(%p,%p) new=(%p,%p)", 660 1.20 skrll obj->path, 661 1.20 skrll (void *)where, 662 1.46 skrll (void *)where[0], (void *)where[1], 663 1.20 skrll (void *)func_pc, (void *)func_sl)); 664 1.8 mycroft 665 1.8 mycroft /* 666 1.8 mycroft * Fill this PLT entry and return. 667 1.8 mycroft */ 668 1.8 mycroft where[0] = func_pc; 669 1.8 mycroft where[1] = func_sl; 670 1.4 mycroft } 671 1.4 mycroft return 0; 672 1.1 fredette } 673 1.20 skrll 674 1.25 skrll static inline int 675 1.34 skrll _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela, 676 1.34 skrll Elf_Addr *tp) 677 1.20 skrll { 678 1.20 skrll Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset); 679 1.20 skrll const Elf_Sym *def; 680 1.20 skrll const Obj_Entry *defobj; 681 1.20 skrll Elf_Addr func_pc, func_sl; 682 1.31 christos unsigned long info = rela->r_info; 683 1.20 skrll 684 1.31 christos assert(ELF_R_TYPE(info) == R_TYPE(IPLT)); 685 1.20 skrll 686 1.31 christos if (ELF_R_SYM(info) == 0) { 687 1.25 skrll func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend); 688 1.25 skrll func_sl = (Elf_Addr)(obj->pltgot); 689 1.25 skrll } else { 690 1.31 christos def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, 691 1.31 christos tp != NULL); 692 1.31 christos if (__predict_false(def == NULL)) 693 1.25 skrll return -1; 694 1.31 christos if (__predict_false(def == &_rtld_sym_zero)) 695 1.31 christos return 0; 696 1.20 skrll 697 1.43 joerg if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { 698 1.43 joerg if (tp == NULL) 699 1.43 joerg return 0; 700 1.43 joerg Elf_Addr ptr = _rtld_resolve_ifunc(defobj, def); 701 1.43 joerg assert(RTLD_IS_PLABEL(ptr)); 702 1.43 joerg hppa_plabel *label = RTLD_GET_PLABEL(ptr); 703 1.43 joerg func_pc = label->hppa_plabel_pc; 704 1.43 joerg func_sl = label->hppa_plabel_sl; 705 1.43 joerg } else { 706 1.43 joerg func_pc = (Elf_Addr)(defobj->relocbase + def->st_value + 707 1.43 joerg rela->r_addend); 708 1.43 joerg func_sl = (Elf_Addr)(defobj->pltgot); 709 1.43 joerg } 710 1.20 skrll 711 1.25 skrll rdbg(("bind now/fixup in %s --> old=(%p,%p) new=(%p,%p)", 712 1.25 skrll defobj->strtab + def->st_name, 713 1.46 skrll (void *)where[0], (void *)where[1], 714 1.25 skrll (void *)func_pc, (void *)func_sl)); 715 1.25 skrll } 716 1.20 skrll /* 717 1.20 skrll * Fill this PLT entry and return. 718 1.20 skrll */ 719 1.20 skrll if (where[0] != func_pc) 720 1.20 skrll where[0] = func_pc; 721 1.20 skrll if (where[1] != func_sl) 722 1.20 skrll where[1] = func_sl; 723 1.20 skrll 724 1.25 skrll if (tp) 725 1.25 skrll *tp = (Elf_Addr)where; 726 1.25 skrll 727 1.25 skrll return 0; 728 1.25 skrll } 729 1.25 skrll 730 1.25 skrll caddr_t 731 1.25 skrll _rtld_bind(const Obj_Entry *obj, Elf_Word reloff) 732 1.25 skrll { 733 1.28 mjf const Elf_Rela *rela; 734 1.32 skrll Elf_Addr new_value = 0; /* XXX gcc */ 735 1.25 skrll int err; 736 1.25 skrll 737 1.28 mjf rela = (const Elf_Rela *)((const char *)obj->pltrela + reloff); 738 1.46 skrll 739 1.25 skrll assert(ELF_R_SYM(rela->r_info) != 0); 740 1.25 skrll 741 1.39 joerg _rtld_shared_enter(); 742 1.46 skrll err = _rtld_relocate_plt_object(obj, rela, &new_value); 743 1.31 christos if (err) 744 1.25 skrll _rtld_die(); 745 1.39 joerg _rtld_shared_exit(); 746 1.25 skrll 747 1.25 skrll return (caddr_t)new_value; 748 1.20 skrll } 749 1.20 skrll 750 1.20 skrll int 751 1.20 skrll _rtld_relocate_plt_objects(const Obj_Entry *obj) 752 1.20 skrll { 753 1.25 skrll const Elf_Rela *rela = obj->pltrela; 754 1.46 skrll 755 1.25 skrll for (; rela < obj->pltrelalim; rela++) { 756 1.25 skrll if (_rtld_relocate_plt_object(obj, rela, NULL) < 0) 757 1.25 skrll return -1; 758 1.20 skrll } 759 1.20 skrll return 0; 760 1.20 skrll } 761 1.43 joerg 762 1.43 joerg Elf_Addr 763 1.43 joerg _rtld_call_function_addr(const Obj_Entry *obj, Elf_Addr ptr) 764 1.43 joerg { 765 1.43 joerg volatile hppa_plabel plabel; 766 1.43 joerg Elf_Addr (*f)(void); 767 1.43 joerg 768 1.43 joerg plabel.hppa_plabel_pc = (Elf_Addr)ptr; 769 1.43 joerg plabel.hppa_plabel_sl = (Elf_Addr)(obj->pltgot); 770 1.43 joerg f = (Elf_Addr (*)(void))RTLD_MAKE_PLABEL(&plabel); 771 1.43 joerg 772 1.43 joerg return f(); 773 1.43 joerg } 774