1 /* $NetBSD: hppa_reloc.c,v 1.16 2003/07/24 10:12:27 skrll 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(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(const Obj_Entry *obj) 325 { 326 const Elf_Rela *rela; 327 328 for (rela = obj->rela; rela < obj->relalim; rela++) { 329 Elf_Addr *where; 330 const Elf_Sym *def; 331 const Obj_Entry *defobj; 332 Elf_Addr tmp; 333 unsigned long symnum; 334 335 where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 336 symnum = ELF_R_SYM(rela->r_info); 337 338 switch (ELF_R_TYPE(rela->r_info)) { 339 case R_TYPE(NONE): 340 break; 341 342 case R_TYPE(DIR32): 343 if (symnum) { 344 /* 345 * This is either a DIR32 against a symbol 346 * (def->st_name != 0), or against a local 347 * section (def->st_name == 0). 348 */ 349 def = obj->symtab + symnum; 350 defobj = obj; 351 if (def->st_name != 0) 352 /* 353 * While we're relocating self, 354 * _rtld_objlist is NULL, so we just 355 * pass in self. 356 */ 357 def = _rtld_find_symdef(symnum, obj, 358 &defobj, false); 359 if (def == NULL) 360 return -1; 361 362 tmp = (Elf_Addr)(defobj->relocbase + 363 def->st_value + rela->r_addend); 364 365 if (*where != tmp) 366 *where = tmp; 367 rdbg(("DIR32 %s in %s --> %p in %s", 368 obj->strtab + obj->symtab[symnum].st_name, 369 obj->path, (void *)*where, defobj->path)); 370 } else { 371 extern Elf_Addr _GLOBAL_OFFSET_TABLE_[]; 372 extern Elf_Addr _GOT_END_[]; 373 374 tmp = (Elf_Addr)(obj->relocbase + 375 rela->r_addend); 376 377 /* This is the ...iffy hueristic. */ 378 if (!self || 379 (caddr_t)where < (caddr_t)_GLOBAL_OFFSET_TABLE_ || 380 (caddr_t)where >= (caddr_t)_GOT_END_) { 381 if (*where != tmp) 382 *where = tmp; 383 rdbg(("DIR32 in %s --> %p", obj->path, 384 (void *)*where)); 385 } else 386 rdbg(("DIR32 in %s stays at %p", 387 obj->path, (void *)*where)); 388 } 389 break; 390 391 case R_TYPE(PLABEL32): 392 if (symnum) { 393 /* 394 * While we're relocating self, _rtld_objlist 395 * is NULL, so we just pass in self. 396 */ 397 def = _rtld_find_symdef(symnum, obj, &defobj, 398 false); 399 if (def == NULL) 400 return -1; 401 402 tmp = _rtld_function_descriptor_alloc(defobj, def, 403 rela->r_addend); 404 if (tmp == (Elf_Addr)-1) 405 return -1; 406 407 if (*where != tmp) 408 *where = tmp; 409 rdbg(("PLABEL32 %s in %s --> %p in %s", 410 obj->strtab + obj->symtab[symnum].st_name, 411 obj->path, (void *)*where, defobj->path)); 412 } else { 413 /* 414 * This is a PLABEL for a static function, and 415 * the dynamic linker has both allocated a PLT 416 * entry for this function and told us where it 417 * is. We can safely use the PLT entry as the 418 * PLABEL because there should be no other 419 * PLABEL reloc referencing this function. 420 * This object should also have an IPLT 421 * relocation to initialize the PLT entry. 422 * 423 * The dynamic linker should also have ensured 424 * that the addend has the 425 * next-least-significant bit set; the 426 * $$dyncall millicode uses this to distinguish 427 * a PLABEL pointer from a plain function 428 * pointer. 429 */ 430 tmp = (Elf_Addr)(obj->relocbase + rela->r_addend); 431 432 if (*where != tmp) 433 *where = tmp; 434 rdbg(("PLABEL32 in %s --> %p", obj->path, 435 (void *)*where)); 436 } 437 break; 438 439 case R_TYPE(COPY): 440 /* 441 * These are deferred until all other relocations have 442 * been done. All we do here is make sure that the 443 * COPY relocation is not in a shared library. They 444 * are allowed only in executable files. 445 */ 446 if (obj->isdynamic) { 447 _rtld_error( 448 "%s: Unexpected R_COPY relocation in shared library", 449 obj->path); 450 return -1; 451 } 452 rdbg(("COPY (avoid in main)")); 453 break; 454 455 default: 456 rdbg(("sym = %lu, type = %lu, offset = %p, " 457 "addend = %p, contents = %p, symbol = %s", 458 symnum, (u_long)ELF_R_TYPE(rela->r_info), 459 (void *)rela->r_offset, (void *)rela->r_addend, 460 (void *)*where, 461 obj->strtab + obj->symtab[symnum].st_name)); 462 _rtld_error("%s: Unsupported relocation type %ld " 463 "in non-PLT relocations\n", 464 obj->path, (u_long) ELF_R_TYPE(rela->r_info)); 465 return -1; 466 } 467 } 468 return 0; 469 } 470 471 int 472 _rtld_relocate_plt_lazy(const Obj_Entry *obj) 473 { 474 const Elf_Rela *rela; 475 476 for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) { 477 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 478 Elf_Addr func_pc, func_sl; 479 480 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT)); 481 482 /* 483 * If this is an IPLT reloc for a static function, 484 * fully resolve the PLT entry now. 485 */ 486 if (ELF_R_SYM(rela->r_info) == 0) { 487 func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend); 488 func_sl = (Elf_Addr)HPPA_OBJ_SL(obj); 489 } 490 491 /* 492 * Otherwise set up for lazy binding. 493 */ 494 else { 495 /* 496 * This function pointer points to the PLT 497 * stub added by the linker, and instead of 498 * a shared linkage value, we stash this 499 * relocation's offset. The PLT stub has 500 * already been set up to transfer to 501 * _rtld_bind_start. 502 */ 503 func_pc = ((Elf_Addr)HPPA_OBJ_GOT(obj)) - 16; 504 func_sl = (Elf_Addr)((caddr_t)rela - (caddr_t)obj->pltrela); 505 } 506 507 /* 508 * Fill this PLT entry and return. 509 */ 510 where[0] = func_pc; 511 where[1] = func_sl; 512 } 513 return 0; 514 } 515