Home | History | Annotate | Line # | Download | only in hppa
hppa_reloc.c revision 1.41
      1 /*	$NetBSD: hppa_reloc.c,v 1.41 2011/12/04 16:53:08 skrll Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2002, 2004 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 and Nick Hudson.
      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  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __RCSID("$NetBSD: hppa_reloc.c,v 1.41 2011/12/04 16:53:08 skrll Exp $");
     35 #endif /* not lint */
     36 
     37 #include <stdlib.h>
     38 #include <sys/types.h>
     39 #include <sys/queue.h>
     40 
     41 #include <string.h>
     42 
     43 #include "rtld.h"
     44 #include "debug.h"
     45 
     46 #ifdef RTLD_DEBUG_HPPA
     47 #define	hdbg(x)		xprintf x
     48 #else
     49 #define	hdbg(x)		/* nothing */
     50 #endif
     51 
     52 caddr_t _rtld_bind(const Obj_Entry *, const Elf_Addr);
     53 void _rtld_bind_start(void);
     54 void __rtld_setup_hppa_pltgot(const Obj_Entry *, Elf_Addr *);
     55 
     56 /*
     57  * It is possible for the compiler to emit relocations for unaligned data.
     58  * We handle this situation with these inlines.
     59  */
     60 #define	RELOC_ALIGNED_P(x) \
     61 	(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
     62 
     63 static inline Elf_Addr
     64 load_ptr(void *where)
     65 {
     66 	if (__predict_true(RELOC_ALIGNED_P(where)))
     67 		return *(Elf_Addr *)where;
     68 	else {
     69 		Elf_Addr res;
     70 
     71 		(void)memcpy(&res, where, sizeof(res));
     72 		return res;
     73 	}
     74 }
     75 
     76 static inline void
     77 store_ptr(void *where, Elf_Addr val)
     78 {
     79 	if (__predict_true(RELOC_ALIGNED_P(where)))
     80 		*(Elf_Addr *)where = val;
     81 	else
     82 		(void)memcpy(where, &val, sizeof(val));
     83 }
     84 
     85 /*
     86  * In the runtime architecture (ABI), PLABEL function pointers are
     87  * distinguished from normal function pointers by having the next-least-
     88  * significant bit set.  (This bit is referred to as the L field in HP
     89  * documentation).  The $$dyncall millicode is aware of this.
     90  */
     91 #define	RTLD_MAKE_PLABEL(plabel)	(((Elf_Addr)(plabel)) | (1 << 1))
     92 #define RTLD_IS_PLABEL(addr)		(((Elf_Addr)(addr)) & (1 << 1))
     93 #define	RTLD_GET_PLABEL(addr)	((hppa_plabel *) (((Elf_Addr)addr) & ~3))
     94 
     95 /*
     96  * This is the PLABEL structure.  The function PC and
     97  * shared linkage members must come first, as they are
     98  * the actual PLABEL.
     99  */
    100 typedef struct _hppa_plabel {
    101 	Elf_Addr	hppa_plabel_pc;
    102 	Elf_Addr	hppa_plabel_sl;
    103 	SLIST_ENTRY(_hppa_plabel)	hppa_plabel_next;
    104 } hppa_plabel;
    105 
    106 /*
    107  * For now allocated PLABEL structures are tracked on a
    108  * singly linked list.  This maybe should be revisited.
    109  */
    110 static SLIST_HEAD(hppa_plabel_head, _hppa_plabel) hppa_plabel_list
    111     = SLIST_HEAD_INITIALIZER(hppa_plabel_list);
    112 
    113 /*
    114  * Because I'm hesitant to use NEW while relocating self,
    115  * this is a small pool of preallocated PLABELs.
    116  */
    117 #define	HPPA_PLABEL_PRE	(32)
    118 static hppa_plabel hppa_plabel_pre[HPPA_PLABEL_PRE];
    119 static int hppa_plabel_pre_next = 0;
    120 
    121 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
    122 int _rtld_relocate_plt_objects(const Obj_Entry *);
    123 static inline int _rtld_relocate_plt_object(const Obj_Entry *,
    124     const Elf_Rela *, Elf_Addr *);
    125 
    126 /*
    127  * This bootstraps the dynamic linker by relocating its GOT.
    128  * On the hppa, unlike on other architectures, static strings
    129  * are found through the GOT.  Static strings are essential
    130  * for RTLD_DEBUG, and I suspect they're used early even when
    131  * !defined(RTLD_DEBUG), making relocating the GOT essential.
    132  *
    133  * It gets worse.  Relocating the GOT doesn't mean just walking
    134  * it and adding the relocbase to all of the entries.  You must
    135  * find and use the GOT relocations, since those RELA relocations
    136  * have the necessary addends - the GOT comes initialized as
    137  * zeroes.
    138  */
    139 void
    140 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
    141 {
    142 	const Elf_Rela	*relafirst, *rela, *relalim;
    143 	Elf_Addr        relasz;
    144 	void		*where;
    145 	Elf_Addr	*pltgot;
    146 	const Elf_Rela	*plabel_relocs[HPPA_PLABEL_PRE];
    147 	int		nplabel_relocs = 0;
    148 	int		i;
    149 	const Elf_Sym	*symtab, *sym;
    150 	unsigned long	symnum;
    151 	hppa_plabel	*plabel;
    152 
    153 	/*
    154 	 * Process the DYNAMIC section, looking for the non-PLT relocations.
    155 	 */
    156 	relafirst = NULL;
    157 	relasz = 0;
    158 	symtab = NULL;
    159 	pltgot = NULL;
    160 	for (; dynp->d_tag != DT_NULL; ++dynp) {
    161 		switch (dynp->d_tag) {
    162 
    163 		case DT_RELA:
    164 			relafirst = (const Elf_Rela *)
    165 			    (relocbase + dynp->d_un.d_ptr);
    166 			break;
    167 
    168 		case DT_RELASZ:
    169 			relasz = dynp->d_un.d_val;
    170 			break;
    171 
    172 		case DT_SYMTAB:
    173 			symtab = (const Elf_Sym *)
    174 			    (relocbase + dynp->d_un.d_ptr);
    175 			break;
    176 
    177 		case DT_PLTGOT:
    178 			pltgot = (Elf_Addr *)
    179 			    (relocbase + dynp->d_un.d_ptr);
    180 			break;
    181 		}
    182 	}
    183 	relalim = (const Elf_Rela *)((const char *)relafirst + relasz);
    184 
    185 	for (rela = relafirst; rela < relalim; rela++) {
    186 		symnum = ELF_R_SYM(rela->r_info);
    187 		where = (void *)(relocbase + rela->r_offset);
    188 
    189 		switch (ELF_R_TYPE(rela->r_info)) {
    190 		case R_TYPE(DIR32):
    191 			if (symnum == 0)
    192 				store_ptr(where,
    193 				    relocbase + rela->r_addend);
    194 			else {
    195 				sym = symtab + symnum;
    196 				store_ptr(where,
    197 				    relocbase + rela->r_addend + sym->st_value);
    198 			}
    199 			break;
    200 
    201 		case R_TYPE(PLABEL32):
    202 			/*
    203 			 * PLABEL32 relocation processing is done in two phases
    204 			 *
    205 			 *  i) local function relocations (symbol number == 0)
    206 			 *     can be resolved immediately.
    207 			 *
    208 			 * ii) external function relocations are deferred until
    209 			 *     we finish all other relocations so that global
    210 			 *     data isn't accessed until all other non-PLT
    211 			 *     relocations have been done.
    212 			 */
    213 			if (symnum == 0)
    214 				*((Elf_Addr *)where) =
    215 				    relocbase + rela->r_addend;
    216 			else
    217 				plabel_relocs[nplabel_relocs++] = rela;
    218 			break;
    219 
    220 		default:
    221 			break;
    222 		}
    223 	}
    224 
    225 	assert(nplabel_relocs < HPPA_PLABEL_PRE);
    226 	for (i = 0; i < nplabel_relocs; i++) {
    227 		rela = plabel_relocs[i];
    228 		where = (void *)(relocbase + rela->r_offset);
    229 		sym = symtab + ELF_R_SYM(rela->r_info);
    230 
    231 		plabel = &hppa_plabel_pre[hppa_plabel_pre_next++];
    232 
    233 		plabel->hppa_plabel_pc = (Elf_Addr)
    234 		    (relocbase + sym->st_value + rela->r_addend);
    235 		plabel->hppa_plabel_sl = (Elf_Addr)pltgot;
    236 
    237 		SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next);
    238 		*((Elf_Addr *)where) = (Elf_Addr)(RTLD_MAKE_PLABEL(plabel));
    239 	}
    240 
    241 #if defined(RTLD_DEBUG_HPPA)
    242 	for (rela = relafirst; rela < relalim; rela++) {
    243 		where = (void *)(relocbase + rela->r_offset);
    244 
    245 		switch (ELF_R_TYPE(rela->r_info)) {
    246 		case R_TYPE(DIR32):
    247 			hdbg(("DIR32 rela @%p(%p) -> %p(%p)\n",
    248 			    (void *)rela->r_offset,
    249 			    (void *)where,
    250 			    (void *)rela->r_addend,
    251 			    (void *)*((Elf_Addr *)where) ));
    252 			break;
    253 
    254 		case R_TYPE(PLABEL32):
    255 			symnum = ELF_R_SYM(rela->r_info);
    256 			if (symnum == 0) {
    257 				hdbg(("PLABEL rela @%p(%p) -> %p(%p)\n",
    258 		    		    (void *)rela->r_offset,
    259 		    		    (void *)where,
    260 		    		    (void *)rela->r_addend,
    261 		    		    (void *)*((Elf_Addr *)where) ));
    262 			} else {
    263 				sym = symtab + symnum;
    264 
    265 				hdbg(("PLABEL32 rela @%p(%p), symnum=%ld(%p) -> %p(%p)\n",
    266 			    	    (void *)rela->r_offset,
    267 				    (void *)where,
    268 				    symnum,
    269 				    (void *)sym->st_value,
    270 			    	    (void *)rela->r_addend,
    271 				    (void *)*((Elf_Addr *)where) ));
    272 			}
    273 			break;
    274 		default:
    275 			hdbg(("rela XXX reloc\n"));
    276 			break;
    277 		}
    278 	}
    279 #endif /* RTLD_DEBUG_HPPA */
    280 }
    281 
    282 /*
    283  * This allocates a PLABEL.  If called with a non-NULL def, the
    284  * plabel is for the function associated with that definition
    285  * in the defining object defobj, plus the given addend.  If
    286  * called with a NULL def, the plabel is for the function at
    287  * the (unrelocated) address in addend in the object defobj.
    288  */
    289 Elf_Addr
    290 _rtld_function_descriptor_alloc(const Obj_Entry *defobj, const Elf_Sym *def,
    291     Elf_Addr addend)
    292 {
    293 	Elf_Addr	func_pc, func_sl;
    294 	hppa_plabel	*plabel;
    295 
    296 	if (def != NULL) {
    297 
    298 		/*
    299 		 * We assume that symbols of type STT_NOTYPE
    300 		 * are undefined.  Return NULL for these.
    301 		 */
    302 		if (ELF_ST_TYPE(def->st_info) == STT_NOTYPE)
    303 			return (Elf_Addr)NULL;
    304 
    305 		/* Otherwise assert that this symbol must be a function. */
    306 		assert(ELF_ST_TYPE(def->st_info) == STT_FUNC);
    307 
    308 		func_pc = (Elf_Addr)(defobj->relocbase + def->st_value +
    309 		    addend);
    310 	} else
    311 		func_pc = (Elf_Addr)(defobj->relocbase + addend);
    312 
    313 	/*
    314 	 * Search the existing PLABELs for one matching
    315 	 * this function.  If there is one, return it.
    316 	 */
    317 	func_sl = (Elf_Addr)(defobj->pltgot);
    318 	SLIST_FOREACH(plabel, &hppa_plabel_list, hppa_plabel_next)
    319 		if (plabel->hppa_plabel_pc == func_pc &&
    320 		    plabel->hppa_plabel_sl == func_sl)
    321 			return RTLD_MAKE_PLABEL(plabel);
    322 
    323 	/*
    324 	 * Once we've used up the preallocated set, we start
    325 	 * using NEW to allocate plabels.
    326 	 */
    327 	if (hppa_plabel_pre_next < HPPA_PLABEL_PRE)
    328 		plabel = &hppa_plabel_pre[hppa_plabel_pre_next++];
    329 	else {
    330 		plabel = NEW(hppa_plabel);
    331 		if (plabel == NULL)
    332 			return (Elf_Addr)-1;
    333 	}
    334 
    335 	/* Fill the new entry and insert it on the list. */
    336 	plabel->hppa_plabel_pc = func_pc;
    337 	plabel->hppa_plabel_sl = func_sl;
    338 	SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next);
    339 
    340 	return RTLD_MAKE_PLABEL(plabel);
    341 }
    342 
    343 /*
    344  * If a pointer is a PLABEL, this unwraps it.
    345  */
    346 const void *
    347 _rtld_function_descriptor_function(const void *addr)
    348 {
    349 	return (RTLD_IS_PLABEL(addr) ?
    350 	    (const void *) RTLD_GET_PLABEL(addr)->hppa_plabel_pc :
    351 	    addr);
    352 }
    353 
    354 /* This sets up an object's GOT. */
    355 void
    356 _rtld_setup_pltgot(const Obj_Entry *obj)
    357 {
    358 	__rtld_setup_hppa_pltgot(obj, obj->pltgot);
    359 }
    360 
    361 int
    362 _rtld_relocate_nonplt_objects(Obj_Entry *obj)
    363 {
    364 	const Elf_Rela *rela;
    365 
    366 	for (rela = obj->rela; rela < obj->relalim; rela++) {
    367 		Elf_Addr        *where;
    368 		const Elf_Sym   *def;
    369 		const Obj_Entry *defobj;
    370 		Elf_Addr         tmp;
    371 		unsigned long	 symnum;
    372 
    373 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
    374 		symnum = ELF_R_SYM(rela->r_info);
    375 
    376 		switch (ELF_R_TYPE(rela->r_info)) {
    377 		case R_TYPE(NONE):
    378 			break;
    379 
    380 		case R_TYPE(DIR32):
    381 			if (symnum) {
    382 				/*
    383 				 * This is either a DIR32 against a symbol
    384 				 * (def->st_name != 0), or against a local
    385 				 * section (def->st_name == 0).
    386 				 */
    387 				def = obj->symtab + symnum;
    388 				defobj = obj;
    389 				if (def->st_name != 0)
    390 					def = _rtld_find_symdef(symnum, obj,
    391 					    &defobj, false);
    392 				if (def == NULL)
    393 					return -1;
    394 
    395 				tmp = (Elf_Addr)(defobj->relocbase +
    396 				    def->st_value + rela->r_addend);
    397 
    398 				if (load_ptr(where) != tmp)
    399 					store_ptr(where, tmp);
    400 				rdbg(("DIR32 %s in %s --> %p in %s",
    401 				    obj->strtab + obj->symtab[symnum].st_name,
    402 				    obj->path, (void *)load_ptr(where),
    403 				    defobj->path));
    404 			} else {
    405 				tmp = (Elf_Addr)(obj->relocbase +
    406 				    rela->r_addend);
    407 
    408 				if (load_ptr(where) != tmp)
    409 					store_ptr(where, tmp);
    410 				rdbg(("DIR32 in %s --> %p", obj->path,
    411 					    (void *)load_ptr(where)));
    412 			}
    413 			break;
    414 
    415 		case R_TYPE(PLABEL32):
    416 			if (symnum) {
    417 				def = _rtld_find_symdef(symnum, obj, &defobj,
    418 				    false);
    419 				if (def == NULL)
    420 					return -1;
    421 
    422 				tmp = _rtld_function_descriptor_alloc(defobj,
    423 				    def, rela->r_addend);
    424 				if (tmp == (Elf_Addr)-1)
    425 					return -1;
    426 
    427 				if (*where != tmp)
    428 					*where = tmp;
    429 				rdbg(("PLABEL32 %s in %s --> %p in %s",
    430 				    obj->strtab + obj->symtab[symnum].st_name,
    431 				    obj->path, (void *)*where, defobj->path));
    432 			} else {
    433 				/*
    434 				 * This is a PLABEL for a static function, and
    435 				 * the dynamic linker has both allocated a PLT
    436 				 * entry for this function and told us where it
    437 				 * is.  We can safely use the PLT entry as the
    438 				 * PLABEL because there should be no other
    439 				 * PLABEL reloc referencing this function.
    440 				 * This object should also have an IPLT
    441 				 * relocation to initialize the PLT entry.
    442 				 *
    443 				 * The dynamic linker should also have ensured
    444 				 * that the addend has the
    445 				 * next-least-significant bit set; the
    446 				 * $$dyncall millicode uses this to distinguish
    447 				 * a PLABEL pointer from a plain function
    448 				 * pointer.
    449 				 */
    450 				tmp = (Elf_Addr)
    451 				    (obj->relocbase + rela->r_addend);
    452 
    453 				if (*where != tmp)
    454 					*where = tmp;
    455 				rdbg(("PLABEL32 in %s --> %p", obj->path,
    456 				    (void *)*where));
    457 			}
    458 			break;
    459 
    460 		case R_TYPE(COPY):
    461 			/*
    462 			 * These are deferred until all other relocations have
    463 			 * been done.  All we do here is make sure that the
    464 			 * COPY relocation is not in a shared library.  They
    465 			 * are allowed only in executable files.
    466 			 */
    467 			if (obj->isdynamic) {
    468 				_rtld_error(
    469 			"%s: Unexpected R_COPY relocation in shared library",
    470 				    obj->path);
    471 				return -1;
    472 			}
    473 			rdbg(("COPY (avoid in main)"));
    474 			break;
    475 
    476 		case R_TYPE(TLS_TPREL32):
    477 			def = _rtld_find_symdef(symnum, obj, &defobj, false);
    478 			if (def == NULL)
    479 				return -1;
    480 
    481 			if (!defobj->tls_done && _rtld_tls_offset_allocate(obj))
    482 				return -1;
    483 
    484 			*where = (Elf_Addr)(defobj->tlsoffset + def->st_value +
    485 			    rela->r_addend + sizeof(struct tls_tcb));
    486 
    487 			rdbg(("TPREL32 %s in %s --> %p in %s",
    488 			    obj->strtab + obj->symtab[symnum].st_name,
    489 			    obj->path, (void *)*where, defobj->path));
    490 			break;
    491 
    492 		case R_TYPE(TLS_DTPMOD32):
    493 			def = _rtld_find_symdef(symnum, obj, &defobj, false);
    494 			if (def == NULL)
    495 				return -1;
    496 
    497 			*where = (Elf_Addr)(defobj->tlsindex);
    498 
    499 			rdbg(("TLS_DTPMOD32 %s in %s --> %p",
    500 			    obj->strtab + obj->symtab[symnum].st_name,
    501 			    obj->path, (void *)*where));
    502 
    503 			break;
    504 
    505 		case R_TYPE(TLS_DTPOFF32):
    506 			def = _rtld_find_symdef(symnum, obj, &defobj, false);
    507 			if (def == NULL)
    508 				return -1;
    509 
    510 			*where = (Elf_Addr)(def->st_value);
    511 
    512 			rdbg(("TLS_DTPOFF32 %s in %s --> %p",
    513 			    obj->strtab + obj->symtab[symnum].st_name,
    514 			    obj->path, (void *)*where));
    515 
    516 			break;
    517 
    518 		default:
    519 			rdbg(("sym = %lu, type = %lu, offset = %p, "
    520 			    "addend = %p, contents = %p, symbol = %s",
    521 			    symnum, (u_long)ELF_R_TYPE(rela->r_info),
    522 			    (void *)rela->r_offset, (void *)rela->r_addend,
    523 			    (void *)load_ptr(where),
    524 			    obj->strtab + obj->symtab[symnum].st_name));
    525 			_rtld_error("%s: Unsupported relocation type %ld "
    526 			    "in non-PLT relocations",
    527 			    obj->path, (u_long) ELF_R_TYPE(rela->r_info));
    528 			return -1;
    529 		}
    530 	}
    531 	return 0;
    532 }
    533 
    534 int
    535 _rtld_relocate_plt_lazy(const Obj_Entry *obj)
    536 {
    537 	const Elf_Rela *rela;
    538 
    539 	for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) {
    540 		Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
    541 		Elf_Addr func_pc, func_sl;
    542 
    543 		assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT));
    544 
    545 		/*
    546 		 * If this is an IPLT reloc for a static function,
    547 		 * fully resolve the PLT entry now.
    548 		 */
    549 		if (ELF_R_SYM(rela->r_info) == 0) {
    550 			func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend);
    551 			func_sl = (Elf_Addr)(obj->pltgot);
    552 		}
    553 
    554 		/*
    555 		 * Otherwise set up for lazy binding.
    556 		 */
    557 		else {
    558 			/*
    559 			 * This function pointer points to the PLT
    560 			 * stub added by the linker, and instead of
    561 			 * a shared linkage value, we stash this
    562 			 * relocation's offset.  The PLT stub has
    563 			 * already been set up to transfer to
    564 			 * _rtld_bind_start.
    565 			 */
    566 			func_pc = ((Elf_Addr)(obj->pltgot)) - 16;
    567 			func_sl = (Elf_Addr)
    568 			    ((const char *)rela - (const char *)(obj->pltrela));
    569 		}
    570 		rdbg(("lazy bind %s(%p) --> old=(%p,%p) new=(%p,%p)",
    571 		    obj->path,
    572 		    (void *)where,
    573 		    (void *)where[0], (void *)where[1],
    574 		    (void *)func_pc, (void *)func_sl));
    575 
    576 		/*
    577 		 * Fill this PLT entry and return.
    578 		 */
    579 		where[0] = func_pc;
    580 		where[1] = func_sl;
    581 	}
    582 	return 0;
    583 }
    584 
    585 static inline int
    586 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela,
    587     Elf_Addr *tp)
    588 {
    589 	Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset);
    590 	const Elf_Sym *def;
    591 	const Obj_Entry *defobj;
    592 	Elf_Addr	func_pc, func_sl;
    593 	unsigned long info = rela->r_info;
    594 
    595 	assert(ELF_R_TYPE(info) == R_TYPE(IPLT));
    596 
    597 	if (ELF_R_SYM(info) == 0) {
    598 		func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend);
    599 		func_sl = (Elf_Addr)(obj->pltgot);
    600 	} else {
    601 		def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj,
    602 		    tp != NULL);
    603 		if (__predict_false(def == NULL))
    604 			return -1;
    605 		if (__predict_false(def == &_rtld_sym_zero))
    606 			return 0;
    607 
    608 		func_pc = (Elf_Addr)(defobj->relocbase + def->st_value +
    609 		    rela->r_addend);
    610 		func_sl = (Elf_Addr)(defobj->pltgot);
    611 
    612 		rdbg(("bind now/fixup in %s --> old=(%p,%p) new=(%p,%p)",
    613 		    defobj->strtab + def->st_name,
    614 		    (void *)where[0], (void *)where[1],
    615 		    (void *)func_pc, (void *)func_sl));
    616 	}
    617 	/*
    618 	 * Fill this PLT entry and return.
    619 	 */
    620 	if (where[0] != func_pc)
    621 		where[0] = func_pc;
    622 	if (where[1] != func_sl)
    623 		where[1] = func_sl;
    624 
    625 	if (tp)
    626 		*tp = (Elf_Addr)where;
    627 
    628 	return 0;
    629 }
    630 
    631 caddr_t
    632 _rtld_bind(const Obj_Entry *obj, Elf_Word reloff)
    633 {
    634 	const Elf_Rela *rela;
    635 	Elf_Addr new_value = 0;	/* XXX gcc */
    636 	int err;
    637 
    638 	rela = (const Elf_Rela *)((const char *)obj->pltrela + reloff);
    639 
    640 	assert(ELF_R_SYM(rela->r_info) != 0);
    641 
    642 	_rtld_shared_enter();
    643 	err = _rtld_relocate_plt_object(obj, rela, &new_value);
    644 	if (err)
    645 		_rtld_die();
    646 	_rtld_shared_exit();
    647 
    648 	return (caddr_t)new_value;
    649 }
    650 
    651 int
    652 _rtld_relocate_plt_objects(const Obj_Entry *obj)
    653 {
    654 	const Elf_Rela *rela = obj->pltrela;
    655 
    656 	for (; rela < obj->pltrelalim; rela++) {
    657 		if (_rtld_relocate_plt_object(obj, rela, NULL) < 0)
    658 			return -1;
    659 	}
    660 	return 0;
    661 }
    662