1 /* $NetBSD: hppa_reloc.c,v 1.15 2002/09/26 20:42:11 mycroft Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 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. 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <stdlib.h> 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include <sys/queue.h> 43 44 #include "rtld.h" 45 #include "debug.h" 46 47 #ifdef RTLD_DEBUG_HPPA 48 #define hdbg(x) xprintf x 49 #else 50 #define hdbg(x) /* nothing */ 51 #endif 52 53 void _rtld_bind_start(void); 54 void __rtld_setup_hppa_pltgot(const Obj_Entry *, Elf_Addr *); 55 56 /* 57 * In the runtime architecture (ABI), PLABEL function 58 * pointers are distinguished from normal function 59 * pointers by having the next-least-significant bit 60 * set. (This bit is referred to as the L field in 61 * HP documentation). The $$dyncall millicode is 62 * aware of this. 63 */ 64 #define RTLD_MAKE_PLABEL(plabel) (((Elf_Addr)(plabel)) | (1 << 1)) 65 #define RTLD_IS_PLABEL(addr) (((Elf_Addr)(addr)) & (1 << 1)) 66 #define RTLD_GET_PLABEL(addr) ((hppa_plabel *) (((Elf_Addr)addr) & ~3)) 67 68 /* 69 * This is the PLABEL structure. The function PC and 70 * shared linkage members must come first, as they are 71 * the actual PLABEL. 72 */ 73 typedef struct _hppa_plabel { 74 Elf_Addr hppa_plabel_pc; 75 Elf_Addr hppa_plabel_sl; 76 SLIST_ENTRY(_hppa_plabel) hppa_plabel_next; 77 } hppa_plabel; 78 79 /* 80 * For now allocated PLABEL structures are tracked on a 81 * singly linked list. This maybe should be revisited. 82 */ 83 static SLIST_HEAD(hppa_plabel_head, _hppa_plabel) hppa_plabel_list 84 = SLIST_HEAD_INITIALIZER(hppa_plabel_list); 85 86 /* 87 * Because I'm hesitant to use NEW while relocating self, 88 * this is a small pool of preallocated PLABELs. 89 */ 90 #define HPPA_PLABEL_PRE (10) 91 static hppa_plabel hppa_plabel_pre[HPPA_PLABEL_PRE]; 92 static int hppa_plabel_pre_next = 0; 93 94 /* 95 * The DT_PLTGOT _DYNAMIC entry always gives the linkage table 96 * pointer for an object. This is often, but not always, the 97 * same as the object's value for _GLOBAL_OFFSET_TABLE_. We 98 * cache one object's GOT value, otherwise we look it up. 99 * XXX it would be nice to be able to keep this in the Obj_Entry. 100 */ 101 static const Obj_Entry *hppa_got_cache_obj = NULL; 102 static Elf_Addr *hppa_got_cache_got; 103 #define HPPA_OBJ_SL(obj) ((obj)->pltgot) 104 #define HPPA_OBJ_GOT(obj) ((obj) == hppa_got_cache_obj ? \ 105 hppa_got_cache_got : \ 106 _rtld_fill_hppa_got_cache(obj)) 107 static Elf_Addr *_rtld_fill_hppa_got_cache __P((const Obj_Entry *)); 108 109 /* 110 * This bootstraps the dynamic linker by relocating its GOT. 111 * On the hppa, unlike on other architectures, static strings 112 * are found through the GOT. Static strings are essential 113 * for RTLD_DEBUG, and I suspect they're used early even when 114 * !defined(RTLD_DEBUG), making relocating the GOT essential. 115 * 116 * It gets worse. Relocating the GOT doesn't mean just walking 117 * it and adding the relocbase to all of the entries. You must 118 * find and use the GOT relocations, since those RELA relocations 119 * have the necessary addends - the GOT comes initialized as 120 * zeroes. 121 */ 122 void 123 _rtld_bootstrap_hppa_got(Elf_Dyn *dynp, Elf_Addr relocbase, 124 Elf_Addr got_begin, Elf_Addr got_end) 125 { 126 const Elf_Rela *relafirst, *rela, *relalim; 127 Elf_Addr relasz = 0; 128 Elf_Addr where; 129 130 /* 131 * Process the DYNAMIC section, looking for the non-PLT 132 * relocations. 133 */ 134 relafirst = NULL; 135 for (; dynp->d_tag != DT_NULL; ++dynp) { 136 switch (dynp->d_tag) { 137 138 case DT_RELA: 139 relafirst = (const Elf_Rela *) 140 (relocbase + dynp->d_un.d_ptr); 141 break; 142 143 case DT_RELASZ: 144 relasz = dynp->d_un.d_val; 145 break; 146 } 147 } 148 relalim = (const Elf_Rela *)((caddr_t)relafirst + relasz); 149 150 /* 151 * Process all relocations that look like they're in 152 * the GOT. 153 */ 154 for(rela = relafirst; rela < relalim; rela++) { 155 where = (Elf_Addr)(relocbase + rela->r_offset); 156 if (where >= got_begin && where < got_end) 157 *((Elf_Addr *)where) = relocbase + rela->r_addend; 158 } 159 160 #if defined(RTLD_DEBUG_HPPA) 161 for(rela = relafirst; rela < relalim; rela++) { 162 where = (Elf_Addr)(relocbase + rela->r_offset); 163 if (where >= got_begin && where < got_end) 164 xprintf("GOT rela @%p(%p) -> %p(%p)\n", 165 (void *)rela->r_offset, 166 (void *)where, 167 (void *)rela->r_addend, 168 (void *)*((Elf_Addr *)where)); 169 } 170 #endif /* RTLD_DEBUG_HPPA */ 171 } 172 173 /* 174 * This looks up the object's _GLOBAL_OFFSET_TABLE_ 175 * and caches the result. 176 */ 177 static Elf_Addr * 178 _rtld_fill_hppa_got_cache(const Obj_Entry *obj) 179 { 180 const char *name = "_GLOBAL_OFFSET_TABLE_"; 181 unsigned long hash; 182 const Elf_Sym *def; 183 184 hash = _rtld_elf_hash(name); 185 def = _rtld_symlook_obj(name, hash, obj, true); 186 assert(def != NULL); 187 hppa_got_cache_obj = obj; 188 return hppa_got_cache_got = 189 (Elf_Addr *)(obj->relocbase + def->st_value); 190 } 191 192 /* 193 * This allocates a PLABEL. If called with a non-NULL def, the 194 * plabel is for the function associated with that definition 195 * in the defining object defobj, plus the given addend. If 196 * called with a NULL def, the plabel is for the function at 197 * the (unrelocated) address in addend in the object defobj. 198 */ 199 Elf_Addr 200 _rtld_function_descriptor_alloc(const Obj_Entry *defobj, const Elf_Sym *def, 201 Elf_Addr addend) 202 { 203 Elf_Addr func_pc, func_sl; 204 hppa_plabel *plabel; 205 206 if (def != NULL) { 207 208 /* 209 * We assume that symbols of type STT_NOTYPE 210 * are undefined. Return NULL for these. 211 */ 212 if (ELF_ST_TYPE(def->st_info) == STT_NOTYPE) 213 return (Elf_Addr)NULL; 214 215 /* Otherwise assert that this symbol must be a function. */ 216 assert(ELF_ST_TYPE(def->st_info) == STT_FUNC); 217 218 func_pc = (Elf_Addr)(defobj->relocbase + def->st_value + 219 addend); 220 } else 221 func_pc = (Elf_Addr)(defobj->relocbase + addend); 222 223 /* 224 * Search the existing PLABELs for one matching 225 * this function. If there is one, return it. 226 */ 227 func_sl = (Elf_Addr)HPPA_OBJ_SL(defobj); 228 SLIST_FOREACH(plabel, &hppa_plabel_list, hppa_plabel_next) 229 if (plabel->hppa_plabel_pc == func_pc && 230 plabel->hppa_plabel_sl == func_sl) 231 return RTLD_MAKE_PLABEL(plabel); 232 233 /* 234 * XXX - this assumes that the dynamic linker doesn't 235 * have more than HPPA_PLABEL_PRE PLABEL relocations. 236 * Once we've used up the preallocated set, we start 237 * using NEW to allocate plabels. 238 */ 239 if (hppa_plabel_pre_next < HPPA_PLABEL_PRE) 240 plabel = &hppa_plabel_pre[hppa_plabel_pre_next++]; 241 else { 242 plabel = NEW(hppa_plabel); 243 if (plabel == NULL) 244 return (Elf_Addr)-1; 245 } 246 247 /* Fill the new entry and insert it on the list. */ 248 plabel->hppa_plabel_pc = func_pc; 249 plabel->hppa_plabel_sl = func_sl; 250 SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next); 251 252 return RTLD_MAKE_PLABEL(plabel); 253 } 254 255 /* 256 * If a pointer is a PLABEL, this unwraps it. 257 */ 258 const void * 259 _rtld_function_descriptor_function(const void *addr) 260 { 261 return (RTLD_IS_PLABEL(addr) ? 262 (const void *) RTLD_GET_PLABEL(addr)->hppa_plabel_pc : 263 addr); 264 } 265 266 /* 267 * This handles an IPLT relocation, with or without a symbol. 268 */ 269 int 270 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela, caddr_t *addrp) 271 { 272 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 273 const Elf_Sym *def; 274 const Obj_Entry *defobj; 275 Elf_Addr func_pc, func_sl; 276 277 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT)); 278 279 /* 280 * If this is an IPLT reloc for a static function, 281 * fully resolve the PLT entry now. 282 */ 283 if (ELF_R_SYM(rela->r_info) == 0) { 284 func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend); 285 func_sl = (Elf_Addr)HPPA_OBJ_SL(obj); 286 } 287 288 /* 289 * If we must bind now, fully resolve the PLT entry. 290 */ 291 else { 292 293 /* 294 * Look up the symbol. While we're relocating self, 295 * _rtld_objlist is NULL, so just pass in self. 296 */ 297 def = _rtld_find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, 298 false); 299 if (def == NULL) 300 return -1; 301 func_pc = (Elf_Addr)(defobj->relocbase + def->st_value + 302 rela->r_addend); 303 func_sl = (Elf_Addr)HPPA_OBJ_SL(defobj); 304 } 305 306 /* 307 * Fill this PLT entry and return. 308 */ 309 where[0] = func_pc; 310 where[1] = func_sl; 311 312 *addrp = (caddr_t)where; 313 return 0; 314 } 315 316 /* This sets up an object's GOT. */ 317 void 318 _rtld_setup_pltgot(const Obj_Entry *obj) 319 { 320 __rtld_setup_hppa_pltgot(obj, HPPA_OBJ_GOT(obj)); 321 } 322 323 int 324 _rtld_relocate_nonplt_objects(obj) 325 const Obj_Entry *obj; 326 { 327 const Elf_Rela *rela; 328 329 for (rela = obj->rela; rela < obj->relalim; rela++) { 330 Elf_Addr *where; 331 const Elf_Sym *def; 332 const Obj_Entry *defobj; 333 Elf_Addr tmp; 334 unsigned long symnum; 335 336 where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 337 symnum = ELF_R_SYM(rela->r_info); 338 339 switch (ELF_R_TYPE(rela->r_info)) { 340 case R_TYPE(NONE): 341 break; 342 343 case R_TYPE(DIR32): 344 if (symnum) { 345 /* 346 * This is either a DIR32 against a symbol 347 * (def->st_name != 0), or against a local 348 * section (def->st_name == 0). 349 */ 350 def = obj->symtab + symnum; 351 defobj = obj; 352 if (def->st_name != 0) 353 /* 354 * While we're relocating self, 355 * _rtld_objlist is NULL, so we just 356 * pass in self. 357 */ 358 def = _rtld_find_symdef(symnum, obj, 359 &defobj, false); 360 if (def == NULL) 361 return -1; 362 363 tmp = (Elf_Addr)(defobj->relocbase + 364 def->st_value + rela->r_addend); 365 366 if (*where != tmp) 367 *where = tmp; 368 rdbg(("DIR32 %s in %s --> %p in %s", 369 obj->strtab + obj->symtab[symnum].st_name, 370 obj->path, (void *)*where, defobj->path)); 371 } else { 372 extern Elf_Addr _GLOBAL_OFFSET_TABLE_[]; 373 extern Elf_Addr _GOT_END_[]; 374 375 tmp = (Elf_Addr)(obj->relocbase + 376 rela->r_addend); 377 378 /* This is the ...iffy hueristic. */ 379 if (!self || 380 (caddr_t)where < (caddr_t)_GLOBAL_OFFSET_TABLE_ || 381 (caddr_t)where >= (caddr_t)_GOT_END_) { 382 if (*where != tmp) 383 *where = tmp; 384 rdbg(("DIR32 in %s --> %p", obj->path, 385 (void *)*where)); 386 } else 387 rdbg(("DIR32 in %s stays at %p", 388 obj->path, (void *)*where)); 389 } 390 break; 391 392 case R_TYPE(PLABEL32): 393 if (symnum) { 394 /* 395 * While we're relocating self, _rtld_objlist 396 * is NULL, so we just pass in self. 397 */ 398 def = _rtld_find_symdef(symnum, obj, &defobj, 399 false); 400 if (def == NULL) 401 return -1; 402 403 tmp = _rtld_function_descriptor_alloc(defobj, def, 404 rela->r_addend); 405 if (tmp == (Elf_Addr)-1) 406 return -1; 407 408 if (*where != tmp) 409 *where = tmp; 410 rdbg(("PLABEL32 %s in %s --> %p in %s", 411 obj->strtab + obj->symtab[symnum].st_name, 412 obj->path, (void *)*where, defobj->path)); 413 } else { 414 /* 415 * This is a PLABEL for a static function, and 416 * the dynamic linker has both allocated a PLT 417 * entry for this function and told us where it 418 * is. We can safely use the PLT entry as the 419 * PLABEL because there should be no other 420 * PLABEL reloc referencing this function. 421 * This object should also have an IPLT 422 * relocation to initialize the PLT entry. 423 * 424 * The dynamic linker should also have ensured 425 * that the addend has the 426 * next-least-significant bit set; the 427 * $$dyncall millicode uses this to distinguish 428 * a PLABEL pointer from a plain function 429 * pointer. 430 */ 431 tmp = (Elf_Addr)(obj->relocbase + rela->r_addend); 432 433 if (*where != tmp) 434 *where = tmp; 435 rdbg(("PLABEL32 in %s --> %p", obj->path, 436 (void *)*where)); 437 } 438 break; 439 440 case R_TYPE(COPY): 441 /* 442 * These are deferred until all other relocations have 443 * been done. All we do here is make sure that the 444 * COPY relocation is not in a shared library. They 445 * are allowed only in executable files. 446 */ 447 if (obj->isdynamic) { 448 _rtld_error( 449 "%s: Unexpected R_COPY relocation in shared library", 450 obj->path); 451 return -1; 452 } 453 rdbg(("COPY (avoid in main)")); 454 break; 455 456 default: 457 rdbg(("sym = %lu, type = %lu, offset = %p, " 458 "addend = %p, contents = %p, symbol = %s", 459 symnum, (u_long)ELF_R_TYPE(rela->r_info), 460 (void *)rela->r_offset, (void *)rela->r_addend, 461 (void *)*where, 462 obj->strtab + obj->symtab[symnum].st_name)); 463 _rtld_error("%s: Unsupported relocation type %ld " 464 "in non-PLT relocations\n", 465 obj->path, (u_long) ELF_R_TYPE(rela->r_info)); 466 return -1; 467 } 468 } 469 return 0; 470 } 471 472 int 473 _rtld_relocate_plt_lazy(obj) 474 const Obj_Entry *obj; 475 { 476 const Elf_Rela *rela; 477 478 for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) { 479 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 480 Elf_Addr func_pc, func_sl; 481 482 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT)); 483 484 /* 485 * If this is an IPLT reloc for a static function, 486 * fully resolve the PLT entry now. 487 */ 488 if (ELF_R_SYM(rela->r_info) == 0) { 489 func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend); 490 func_sl = (Elf_Addr)HPPA_OBJ_SL(obj); 491 } 492 493 /* 494 * Otherwise set up for lazy binding. 495 */ 496 else { 497 /* 498 * This function pointer points to the PLT 499 * stub added by the linker, and instead of 500 * a shared linkage value, we stash this 501 * relocation's offset. The PLT stub has 502 * already been set up to transfer to 503 * _rtld_bind_start. 504 */ 505 func_pc = ((Elf_Addr)HPPA_OBJ_GOT(obj)) - 16; 506 func_sl = (Elf_Addr)((caddr_t)rela - (caddr_t)obj->pltrela); 507 } 508 509 /* 510 * Fill this PLT entry and return. 511 */ 512 where[0] = func_pc; 513 where[1] = func_sl; 514 } 515 return 0; 516 } 517