xref: /openbsd-src/libexec/ld.so/powerpc/rtld_machine.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: rtld_machine.c,v 1.52 2014/07/05 17:06:18 miod Exp $ */
2 
3 /*
4  * Copyright (c) 1999 Dale Rahn
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #define _DYN_LOADER
30 
31 #include <sys/types.h>
32 #include <sys/mman.h>
33 
34 #include <nlist.h>
35 #include <link.h>
36 #include <signal.h>
37 
38 #include "syscall.h"
39 #include "archdep.h"
40 #include "resolve.h"
41 
42 void _dl_syncicache(char *from, size_t len);
43 
44 /* relocation bits */
45 #define HA(x) (((Elf_Addr)(x) >> 16) + (((Elf_Addr)(x) & 0x00008000) >> 15))
46 #define L(x) (((Elf_Addr)x) & 0x0000ffff)
47 #define ADDIS_R11_R11	0x3d6b0000
48 #define ADDIS_R11_R0	0x3d600000
49 #define ADDI_R11_R11	0x396b0000
50 #define LWZ_R11_R11	0x816b0000
51 #define LI_R11		0x39600000
52 
53 #define ADDIS_R12_R0	0x3d800000
54 #define ADDI_R12_R12	0x398c0000
55 #define MCTR_R11	0x7d6903a6
56 #define MCTR_R12	0x7d8903a6
57 #define BCTR		0x4e800420
58 #define BR(from, to)	do { \
59 	int lval = (Elf32_Addr)(to) - (Elf32_Addr)(&(from)); \
60 	lval &= ~0xfc000000; \
61 	lval |= 0x48000000; \
62 	(from) = lval; \
63 } while (0)
64 
65 /* these are structures/functions offset from PLT region */
66 #define PLT_CALL_OFFSET		6
67 #define PLT_INFO_OFFSET		10
68 #define PLT_1STRELA_OFFSET	18
69 #define B24_VALID_RANGE(x) \
70     ((((x) & 0xfe000000) == 0x00000000) || (((x) &  0xfe000000) == 0xfe000000))
71 
72 void _dl_bind_start(void); /* XXX */
73 Elf_Addr _dl_bind(elf_object_t *object, int reloff);
74 
75 int
76 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
77 {
78 	int	i;
79 	int	numrela;
80 	long	relrel;
81 	int	fails = 0;
82 	struct load_list *llist;
83 	Elf32_Addr loff;
84 	Elf32_Rela  *relas;
85 	/* for jmp table relocations */
86 	Elf32_Addr *pltresolve;
87 	Elf32_Addr *pltcall;
88 	Elf32_Addr *plttable;
89 	Elf32_Addr *pltinfo;
90 	Elf32_Addr *first_rela;
91 
92 	loff = object->obj_base;
93 	numrela = object->Dyn.info[relasz] / sizeof(Elf32_Rela);
94 	relrel = rel == DT_RELA ? object->relacount : 0;
95 	relas = (Elf32_Rela *)(object->Dyn.info[rel]);
96 
97 #ifdef DL_PRINTF_DEBUG
98 _dl_printf("object relocation size %x, numrela %x\n",
99 	object->Dyn.info[relasz], numrela);
100 #endif
101 
102 	if (relas == NULL)
103 		return(0);
104 
105 	if (relrel > numrela) {
106 		_dl_printf("relcount > numrel: %ld > %ld\n", relrel, numrela);
107 		_dl_exit(20);
108 	}
109 
110 	pltresolve = NULL;
111 	pltcall = NULL;
112 	plttable = NULL;
113 
114 	/* for plt relocation usage */
115 	if (object->Dyn.info[DT_JMPREL] != 0) {
116 		/* resolver stub not set up */
117 		int nplt;
118 
119 		/* Need to construct table to do jumps */
120 		pltresolve = (Elf32_Addr *)(object->Dyn.info[DT_PLTGOT]);
121 		pltcall = (Elf32_Addr *)(pltresolve) + PLT_CALL_OFFSET;
122 		pltinfo = (Elf32_Addr *)(pltresolve) + PLT_INFO_OFFSET;
123 		first_rela =  (Elf32_Addr *)(pltresolve) + PLT_1STRELA_OFFSET;
124 
125 		nplt = object->Dyn.info[DT_PLTRELSZ]/sizeof(Elf32_Rela);
126 
127 		if (nplt >= (2<<12)) {
128 			plttable = (Elf32_Addr *) ((Elf32_Addr)first_rela)
129 			    + (2 * (2<<12)) + (4 * (nplt - (2<<12)));
130 		} else {
131 			plttable = (Elf32_Addr *) ((Elf32_Addr)first_rela)
132 			    + (2 * nplt);
133 		}
134 
135 		pltinfo[0] = (Elf32_Addr)plttable;
136 
137 #ifdef DL_PRINTF_DEBUG
138 		_dl_printf("md_reloc:  plttbl size %x\n",
139 		    (object->Dyn.info[DT_PLTRELSZ]/sizeof(Elf32_Rela)));
140 		_dl_printf("md_reloc: plttable %x\n", plttable);
141 #endif
142 		pltresolve[0] = ADDIS_R12_R0 | HA(_dl_bind_start);
143 		pltresolve[1] = ADDI_R12_R12 | L(_dl_bind_start);
144 		pltresolve[2] = MCTR_R12;
145 		pltresolve[3] = ADDIS_R12_R0 | HA(object);
146 		pltresolve[4] = ADDI_R12_R12 | L(object);
147 		pltresolve[5] = BCTR;
148 		_dl_dcbf(&pltresolve[0]);
149 		_dl_dcbf(&pltresolve[5]);
150 
151 		/* addis r11,r11,.PLTtable@ha*/
152 		pltcall[0] = ADDIS_R11_R11 | HA(plttable);
153 		/* lwz r11,plttable@l(r11) */
154 		pltcall[1] = LWZ_R11_R11 | L(plttable);
155 		pltcall[2] = MCTR_R11;	/* mtctr r11 */
156 		pltcall[3] = BCTR;	/* bctr */
157 		_dl_dcbf(&pltcall[0]);
158 		_dl_dcbf(&pltcall[3]);
159 	} else {
160 		first_rela = NULL;
161 	}
162 
163 	/*
164 	 * Change protection of all write protected segments in the object
165 	 * so we can do relocations such as REL24, REL16 etc. After
166 	 * relocation restore protection.
167 	 */
168 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
169 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
170 			if (!(llist->prot & PROT_WRITE)) {
171 				_dl_mprotect(llist->start, llist->size,
172 				    llist->prot|PROT_WRITE);
173 			}
174 		}
175 	}
176 
177 	/* tight loop for leading RELATIVE relocs */
178 	for (i = 0; i < relrel; i++, relas++) {
179 		Elf_Addr *r_addr;
180 #ifdef DEBUG
181 		const Elf32_Sym *sym;
182 
183 		if (ELF32_R_TYPE(relas->r_info) != RELOC_RELATIVE) {
184 			_dl_printf("RELCOUNT wrong\n");
185 			_dl_exit(20);
186 		}
187 		sym = object->dyn.symtab;
188 		sym += ELF32_R_SYM(relas->r_info);
189 		if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL ||
190 		    (ELF32_ST_TYPE(sym->st_info) != STT_SECTION &&
191 		    ELF32_ST_TYPE(sym->st_info) != STT_NOTYPE)) {
192 			_dl_printf("RELATIVE relocation against symbol\n");
193 			_dl_exit(20);
194 		}
195 #endif
196 		r_addr = (Elf_Addr *)(relas->r_offset + loff);
197 		*r_addr = loff + relas->r_addend;
198 	}
199 	for (; i < numrela; i++, relas++) {
200 		Elf32_Addr *r_addr = (Elf32_Addr *)(relas->r_offset + loff);
201 		Elf32_Addr ooff;
202 		const Elf32_Sym *sym, *this;
203 		const char *symn;
204 		int type;
205 		Elf32_Addr prev_value = 0, prev_ooff = 0;
206 		const Elf32_Sym *prev_sym = NULL;
207 
208 		if (ELF32_R_SYM(relas->r_info) == 0xffffff)
209 			continue;
210 
211 		type = ELF32_R_TYPE(relas->r_info);
212 
213 		if (type == RELOC_JMP_SLOT && rel != DT_JMPREL)
214 			continue;
215 
216 		sym = object->dyn.symtab;
217 		sym += ELF32_R_SYM(relas->r_info);
218 		symn = object->dyn.strtab + sym->st_name;
219 
220 		ooff = 0;
221 		this = NULL;
222 		if (ELF32_R_SYM(relas->r_info) &&
223 		    !(ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
224 		    ELF32_ST_TYPE (sym->st_info) == STT_NOTYPE)) {
225 			if (sym == prev_sym) {
226 				this = sym;	/* XXX any non-NULL */
227 				ooff = prev_ooff;
228 			} else {
229 				ooff = _dl_find_symbol_bysym(object,
230 				    ELF32_R_SYM(relas->r_info), &this,
231 				    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
232 				    ((type == RELOC_JMP_SLOT) ?
233 				    SYM_PLT:SYM_NOTPLT), sym, NULL);
234 
235 				if (this == NULL) {
236 					if (ELF_ST_BIND(sym->st_info) !=
237 					    STB_WEAK)
238 						fails++;
239 					continue;
240 				}
241 				prev_sym = sym;
242 				prev_value = this->st_value;
243 				prev_ooff = ooff;
244 			}
245 		}
246 
247 		switch (type) {
248 #if 1
249 		case RELOC_32:
250 			if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
251 			    (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
252 			    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
253 				*r_addr = ooff + relas->r_addend;
254 			} else {
255 				*r_addr = ooff + prev_value +
256 				    relas->r_addend;
257 			}
258 			break;
259 #endif
260 		case RELOC_RELATIVE:
261 			if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
262 			    (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
263 			    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
264 				*r_addr = loff + relas->r_addend;
265 
266 #ifdef DL_PRINTF_DEBUG
267 _dl_printf("rel1 r_addr %x val %x loff %x ooff %x addend %x\n", r_addr,
268     loff + relas->r_addend, loff, ooff, relas->r_addend);
269 #endif
270 
271 			} else {
272 				*r_addr = loff + prev_value +
273 				    relas->r_addend;
274 			}
275 			break;
276 		case RELOC_JMP_SLOT:
277 		    {
278 			Elf32_Addr target = ooff + prev_value +
279 			    relas->r_addend;
280 			Elf32_Addr val = target - (Elf32_Addr)r_addr;
281 
282 			if (!B24_VALID_RANGE(val)){
283 				int index;
284 #ifdef DL_PRINTF_DEBUG
285 _dl_printf(" ooff %x, sym val %x, addend %x"
286 	" r_addr %x symn [%s] -> %x\n",
287 	ooff, prev_value, relas->r_addend,
288 	r_addr, symn, val);
289 #endif
290 				/* if offset is > RELOC_24 deal with it */
291 				index = (r_addr - first_rela) >> 1;
292 
293 				if (index >= (2 << 12)) {
294 					/* addis r11,r11,.PLTtable@ha*/
295 					r_addr[0] = ADDIS_R11_R0 | HA(index*4);
296 					r_addr[1] = ADDI_R11_R11 | L(index*4);
297 					BR(r_addr[2], pltcall);
298 				} else {
299 					r_addr[0] = LI_R11 | (index * 4);
300 					BR(r_addr[1], pltcall);
301 
302 				}
303 				_dl_dcbf(&r_addr[0]);
304 				_dl_dcbf(&r_addr[2]);
305 				val= ooff + prev_value +
306 				    relas->r_addend;
307 #ifdef DL_PRINTF_DEBUG
308 _dl_printf(" symn [%s] val 0x%x\n", symn, val);
309 #endif
310 				plttable[index] = val;
311 			} else {
312 				/* if the offset is small enough,
313 				 * branch directly to the dest
314 				 */
315 				BR(r_addr[0], target);
316 				_dl_dcbf(&r_addr[0]);
317 			}
318 		    }
319 
320 			break;
321 		case RELOC_GLOB_DAT:
322 			*r_addr = ooff + prev_value + relas->r_addend;
323 			break;
324 #if 1
325 		/* should not be supported ??? */
326 		case RELOC_REL24:
327 		    {
328 			Elf32_Addr val = ooff + prev_value +
329 			    relas->r_addend - (Elf32_Addr)r_addr;
330 			if (!B24_VALID_RANGE(val)){
331 				/* invalid offset */
332 				_dl_exit(20);
333 			}
334 			val &= ~0xfc000003;
335 			val |= (*r_addr & 0xfc000003);
336 			*r_addr = val;
337 
338 			_dl_dcbf(r_addr);
339 		    }
340 		break;
341 #endif
342 #if 1
343 		case RELOC_16_LO:
344 		    {
345 			Elf32_Addr val;
346 
347 			val = loff + relas->r_addend;
348 			*(Elf32_Half *)r_addr = val;
349 
350 			_dl_dcbf(r_addr);
351 		    }
352 		break;
353 #endif
354 #if 1
355 		case RELOC_16_HI:
356 		    {
357 			Elf32_Addr val;
358 
359 			val = loff + relas->r_addend;
360 			*(Elf32_Half *)r_addr = (val >> 16);
361 
362 			_dl_dcbf(r_addr);
363 		    }
364 		break;
365 #endif
366 #if 1
367 		case RELOC_16_HA:
368 		    {
369 			Elf32_Addr val;
370 
371 			val = loff + relas->r_addend;
372 			*(Elf32_Half *)r_addr = ((val + 0x8000) >> 16);
373 
374 			_dl_dcbf(r_addr);
375 		    }
376 		break;
377 #endif
378 		case RELOC_REL14_TAKEN:
379 			/* val |= 1 << (31-10) XXX? */
380 		case RELOC_REL14:
381 		case RELOC_REL14_NTAKEN:
382 		    {
383 			Elf32_Addr val = ooff + prev_value +
384 			    relas->r_addend - (Elf32_Addr)r_addr;
385 			if (((val & 0xffff8000) != 0) &&
386 			    ((val & 0xffff8000) != 0xffff8000)) {
387 				/* invalid offset */
388 				_dl_exit(20);
389 			}
390 			val &= ~0xffff0003;
391 			val |= (*r_addr & 0xffff0003);
392 			*r_addr = val;
393 #ifdef DL_PRINTF_DEBUG
394 			_dl_printf("rel 14 %x val %x\n", r_addr, val);
395 #endif
396 
397 			_dl_dcbf(r_addr);
398 		    }
399 			break;
400 		case RELOC_COPY:
401 		{
402 #ifdef DL_PRINTF_DEBUG
403 			_dl_printf("copy r_addr %x, sym %x [%s] size %d val %x\n",
404 			    r_addr, sym, symn, sym->st_size,
405 			    (ooff + prev_value+
406 			    relas->r_addend));
407 #endif
408 			/*
409 			 * we need to find a symbol, that is not in the current
410 			 * object, start looking at the beginning of the list,
411 			 * searching all objects but _not_ the current object,
412 			 * first one found wins.
413 			 */
414 			const Elf32_Sym *cpysrc = NULL;
415 			Elf32_Addr src_loff;
416 			int size;
417 
418 			src_loff = 0;
419 			src_loff = _dl_find_symbol(symn, &cpysrc,
420 			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND| SYM_NOTPLT,
421 			    sym, object, NULL);
422 			if (cpysrc != NULL) {
423 				size = sym->st_size;
424 				if (sym->st_size != cpysrc->st_size) {
425 					_dl_printf("symbols size differ [%s] \n",
426 					    symn);
427 					size = sym->st_size < cpysrc->st_size ?
428 					    sym->st_size : cpysrc->st_size;
429 				}
430 #ifdef DL_PRINTF_DEBUG
431 _dl_printf(" found other symbol at %x size %d\n",
432     src_loff + cpysrc->st_value,  cpysrc->st_size);
433 #endif
434 				_dl_bcopy((void *)(src_loff + cpysrc->st_value),
435 				    r_addr, size);
436 			} else
437 				fails++;
438 		}
439 			break;
440 		case RELOC_NONE:
441 			break;
442 
443 		default:
444 			_dl_printf("%s:"
445 			    " %s: unsupported relocation '%s' %d at %x\n",
446 			    _dl_progname, object->load_name, symn,
447 			    ELF32_R_TYPE(relas->r_info), r_addr );
448 			_dl_exit(1);
449 		}
450 	}
451 
452 	/* reprotect the unprotected segments */
453 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
454 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
455 			if (!(llist->prot & PROT_WRITE))
456 				_dl_mprotect(llist->start, llist->size,
457 				    llist->prot);
458 		}
459 	}
460 	return(fails);
461 }
462 
463 /*
464  *	Relocate the Global Offset Table (GOT).
465  *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
466  *	otherwise the lazy binding plt initialization is performed.
467  */
468 int
469 _dl_md_reloc_got(elf_object_t *object, int lazy)
470 {
471 	Elf_Addr *pltresolve;
472 	Elf_Addr *first_rela;
473 	Elf_RelA *relas;
474 	Elf_Addr  plt_addr;
475 	int	i;
476 	int	numrela;
477 	int	fails = 0;
478 	int index;
479 	Elf32_Addr *r_addr;
480 	Elf_Addr ooff;
481 	const Elf_Sym *this;
482 
483 	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
484 		return (0);
485 
486 	object->got_addr = 0;
487 	object->got_size = 0;
488 	this = NULL;
489 	ooff = _dl_find_symbol("__got_start", &this,
490 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
491 	    object, NULL);
492 	if (this != NULL)
493 		object->got_addr = ooff + this->st_value;
494 
495 	this = NULL;
496 	ooff = _dl_find_symbol("__got_end", &this,
497 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
498 	    object, NULL);
499 	if (this != NULL)
500 		object->got_size = ooff + this->st_value  - object->got_addr;
501 
502 	plt_addr = 0;
503 	object->plt_size = 0;
504 	this = NULL;
505 	ooff = _dl_find_symbol("__plt_start", &this,
506 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
507 	    object, NULL);
508 	if (this != NULL)
509 		plt_addr = ooff + this->st_value;
510 
511 	this = NULL;
512 	ooff = _dl_find_symbol("__plt_end", &this,
513 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
514 	    object, NULL);
515 	if (this != NULL)
516 		object->plt_size = ooff + this->st_value  - plt_addr;
517 
518 	if (object->got_addr == 0)
519 		object->got_start = 0;
520 	else {
521 		object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
522 		object->got_size += object->got_addr - object->got_start;
523 		object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
524 	}
525 	if (plt_addr == 0)
526 		object->plt_start = 0;
527 	else {
528 		object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz);
529 		object->plt_size += plt_addr - object->plt_start;
530 		object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz);
531 	}
532 
533 	if (object->traced)
534 		lazy = 1;
535 
536 	if (!lazy) {
537 		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
538 	} else {
539 		first_rela = (Elf32_Addr *)
540 		    (((Elf32_Rela *)(object->Dyn.info[DT_JMPREL]))->r_offset +
541 		    object->obj_base);
542 		pltresolve = (Elf32_Addr *)(first_rela) - 18;
543 
544 		relas = (Elf32_Rela *)(object->Dyn.info[DT_JMPREL]);
545 		numrela = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf32_Rela);
546 		r_addr = (Elf32_Addr *)(relas->r_offset + object->obj_base);
547 
548 		for (i = 0, index = 0; i < numrela; i++, r_addr+=2, index++) {
549 			if (index >= (2 << 12)) {
550 				/* addis r11,r0,.PLTtable@ha*/
551 				r_addr[0] = ADDIS_R11_R0 | HA(index*4);
552 				r_addr[1] = ADDI_R11_R11 | L(index*4);
553 				BR(r_addr[2], pltresolve);
554 				/* only every other slot is used after
555 				 * index == 2^14
556 				 */
557 				r_addr += 2;
558 			} else {
559 				r_addr[0] = LI_R11 | (index * 4);
560 				BR(r_addr[1], pltresolve);
561 			}
562 			_dl_dcbf(&r_addr[0]);
563 			_dl_dcbf(&r_addr[2]);
564 		}
565 	}
566 	if (object->got_size != 0) {
567 
568 		_dl_mprotect((void*)object->got_start, object->got_size,
569 		    PROT_READ|PROT_EXEC); /* only PPC is PROT_EXE */
570 		_dl_syncicache((void*)object->got_addr, 4);
571 	}
572 	if (object->plt_size != 0)
573 		_dl_mprotect((void*)object->plt_start, object->plt_size,
574 		    PROT_READ|PROT_EXEC);
575 
576 	return (fails);
577 }
578 
579 Elf_Addr
580 _dl_bind(elf_object_t *object, int reloff)
581 {
582 	const Elf_Sym *sym, *this;
583 	Elf_Addr *r_addr, ooff;
584 	const char *symn;
585 	const elf_object_t *sobj;
586 	Elf_Addr value;
587 	Elf_RelA *relas;
588 	Elf32_Addr val;
589 	Elf32_Addr *pltresolve;
590 	Elf32_Addr *pltcall;
591 	Elf32_Addr *pltinfo;
592 	Elf32_Addr *plttable;
593 	sigset_t savedmask;
594 
595 	relas = ((Elf_RelA *)object->Dyn.info[DT_JMPREL]) + (reloff>>2);
596 
597 	sym = object->dyn.symtab;
598 	sym += ELF_R_SYM(relas->r_info);
599 	symn = object->dyn.strtab + sym->st_name;
600 
601 	r_addr = (Elf_Addr *)(object->obj_base + relas->r_offset);
602 	this = NULL;
603 	ooff = _dl_find_symbol(symn, &this,
604 	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj);
605 	if (this == NULL) {
606 		_dl_printf("lazy binding failed!\n");
607 		*(volatile int *)0 = 0;		/* XXX */
608 	}
609 
610 	value = ooff + this->st_value;
611 
612 	if (sobj->traced && _dl_trace_plt(sobj, symn))
613 		return value;
614 
615 	/* if PLT is protected, allow the write */
616 	if (object->plt_size != 0)  {
617 		_dl_thread_bind_lock(0, &savedmask);
618 		_dl_mprotect((void*)object->plt_start, object->plt_size,
619 		    PROT_READ|PROT_WRITE|PROT_EXEC);
620 	}
621 
622 	val = value - (Elf32_Addr)r_addr;
623 
624 	pltresolve = (Elf32_Addr *)
625 	    (Elf32_Rela *)(object->Dyn.info[DT_PLTGOT]);
626 	pltcall = (Elf32_Addr *)(pltresolve) + PLT_CALL_OFFSET;
627 
628 	if (!B24_VALID_RANGE(val)) {
629 		int index;
630 		/* if offset is > RELOC_24 deal with it */
631 		index = reloff >> 2;
632 
633 		/* update plttable before pltcall branch, to make
634 		 * this a safe race for threads
635 		 */
636 		val = ooff + this->st_value + relas->r_addend;
637 
638 		pltinfo = (Elf32_Addr *)(pltresolve) + PLT_INFO_OFFSET;
639 		plttable = (Elf32_Addr *)pltinfo[0];
640 		plttable[index] = val;
641 
642 		if (index >= (2 << 12)) {
643 			/* r_addr[0,1] is initialized to correct
644 			 * value in reloc_got.
645 			 */
646 			BR(r_addr[2], pltcall);
647 			_dl_dcbf(&r_addr[2]);
648 		} else {
649 			/* r_addr[0] is initialized to correct
650 			 * value in reloc_got.
651 			 */
652 			BR(r_addr[1], pltcall);
653 			_dl_dcbf(&r_addr[1]);
654 		}
655 	} else {
656 		/* if the offset is small enough,
657 		 * branch directly to the dest
658 		 */
659 		BR(r_addr[0], value);
660 		_dl_dcbf(&r_addr[0]);
661 	}
662 
663 	/* if PLT is to be protected, change back to RO/X */
664 	if (object->plt_size != 0) {
665 		_dl_mprotect((void*)object->plt_start, object->plt_size,
666 		    PROT_READ|PROT_EXEC); /* only PPC is PROT_EXE */
667 		_dl_thread_bind_lock(1, &savedmask);
668 	}
669 	return (value);
670 }
671 
672 /* should not be defined here, but it is 32 for all powerpc 603-G4 */
673 #define CACHELINESIZE 32
674 void
675 _dl_syncicache(char *from, size_t len)
676 {
677 	unsigned int off = 0;
678 	int l = len + ((int)from & (CACHELINESIZE-1));
679 
680 	while (off < l) {
681 		asm volatile ("dcbst %1,%0" :: "r"(from), "r"(off));
682 		asm volatile ("sync");
683 		asm volatile ("icbi %1, %0" :: "r"(from), "r"(off));
684 		asm volatile ("sync");
685 		asm volatile ("isync");
686 
687 		off += CACHELINESIZE;
688 	}
689 }
690