1 /*	$OpenBSD: rtld_machine.c,v 1.48 2011/04/06 11:36:25 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 	int	fails = 0;
81 	struct load_list *llist;
82 	Elf32_Addr loff;
83 	Elf32_Rela  *relas;
84 	/* for jmp table relocations */
85 	Elf32_Addr *pltresolve;
86 	Elf32_Addr *pltcall;
87 	Elf32_Addr *plttable;
88 	Elf32_Addr *pltinfo;
89 
90 	Elf32_Addr *first_rela;
91 
92 	loff = object->obj_base;
93 	numrela = object->Dyn.info[relasz] / sizeof(Elf32_Rela);
94 	relas = (Elf32_Rela *)(object->Dyn.info[rel]);
95 
96 #ifdef DL_PRINTF_DEBUG
97 _dl_printf("object relocation size %x, numrela %x\n",
98 	object->Dyn.info[relasz], numrela);
99 #endif
100 
101 	if (relas == NULL)
102 		return(0);
103 
104 	pltresolve = NULL;
105 	pltcall = NULL;
106 	plttable = NULL;
107 
108 	/* for plt relocation usage */
109 	if (object->Dyn.info[DT_JMPREL] != 0) {
110 		/* resolver stub not set up */
111 		int nplt;
112 
113 		/* Need to construct table to do jumps */
114 		pltresolve = (Elf32_Addr *)(object->Dyn.info[DT_PLTGOT]);
115 		pltcall = (Elf32_Addr *)(pltresolve) + PLT_CALL_OFFSET;
116 		pltinfo = (Elf32_Addr *)(pltresolve) + PLT_INFO_OFFSET;
117 		first_rela =  (Elf32_Addr *)(pltresolve) + PLT_1STRELA_OFFSET;
118 
119 		nplt = object->Dyn.info[DT_PLTRELSZ]/sizeof(Elf32_Rela);
120 
121 		if (nplt >= (2<<12)) {
122 			plttable = (Elf32_Addr *) ((Elf32_Addr)first_rela)
123 			    + (2 * (2<<12)) + (4 * (nplt - (2<<12)));
124 		} else {
125 			plttable = (Elf32_Addr *) ((Elf32_Addr)first_rela)
126 			    + (2 * nplt);
127 		}
128 
129 		pltinfo[0] = (Elf32_Addr)plttable;
130 
131 #ifdef DL_PRINTF_DEBUG
132 		_dl_printf("md_reloc:  plttbl size %x\n",
133 		    (object->Dyn.info[DT_PLTRELSZ]/sizeof(Elf32_Rela)));
134 		_dl_printf("md_reloc: plttable %x\n", plttable);
135 #endif
136 		pltresolve[0] = ADDIS_R12_R0 | HA(_dl_bind_start);
137 		pltresolve[1] = ADDI_R12_R12 | L(_dl_bind_start);
138 		pltresolve[2] = MCTR_R12;
139 		pltresolve[3] = ADDIS_R12_R0 | HA(object);
140 		pltresolve[4] = ADDI_R12_R12 | L(object);
141 		pltresolve[5] = BCTR;
142 		_dl_dcbf(&pltresolve[0]);
143 		_dl_dcbf(&pltresolve[5]);
144 
145 		/* addis r11,r11,.PLTtable@ha*/
146 		pltcall[0] = ADDIS_R11_R11 | HA(plttable);
147 		/* lwz r11,plttable@l(r11) */
148 		pltcall[1] = LWZ_R11_R11 | L(plttable);
149 		pltcall[2] = MCTR_R11;	/* mtctr r11 */
150 		pltcall[3] = BCTR;	/* bctr */
151 		_dl_dcbf(&pltcall[0]);
152 		_dl_dcbf(&pltcall[3]);
153 	} else {
154 		first_rela = NULL;
155 	}
156 
157 	/*
158 	 * Change protection of all write protected segments in the object
159 	 * so we can do relocations such as REL24, REL16 etc. After
160 	 * relocation restore protection.
161 	 */
162 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
163 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
164 			if (!(llist->prot & PROT_WRITE)) {
165 				_dl_mprotect(llist->start, llist->size,
166 				    llist->prot|PROT_WRITE);
167 			}
168 		}
169 	}
170 
171 
172 	for (i = 0; i < numrela; i++, relas++) {
173 		Elf32_Addr *r_addr = (Elf32_Addr *)(relas->r_offset + loff);
174 		Elf32_Addr ooff;
175 		const Elf32_Sym *sym, *this;
176 		const char *symn;
177 		int type;
178 
179 		if (ELF32_R_SYM(relas->r_info) == 0xffffff)
180 			continue;
181 
182 		type = ELF32_R_TYPE(relas->r_info);
183 
184 		if (type == RELOC_JMP_SLOT && rel != DT_JMPREL)
185 			continue;
186 
187 		sym = object->dyn.symtab;
188 		sym += ELF32_R_SYM(relas->r_info);
189 		symn = object->dyn.strtab + sym->st_name;
190 
191 		ooff = 0;
192 		this = NULL;
193 		if (ELF32_R_SYM(relas->r_info) &&
194 		    !(ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
195 		    ELF32_ST_TYPE (sym->st_info) == STT_NOTYPE)) {
196 			ooff = _dl_find_symbol_bysym(object,
197 			    ELF32_R_SYM(relas->r_info), &this,
198 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
199 			    ((type == RELOC_JMP_SLOT) ? SYM_PLT:SYM_NOTPLT),
200 			    sym, NULL);
201 
202 			if (this == NULL) {
203 				if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
204 					fails++;
205 				continue;
206 			}
207 		}
208 
209 		switch (type) {
210 #if 1
211 		case RELOC_32:
212 			if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
213 			    (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
214 			    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
215 				*r_addr = ooff + relas->r_addend;
216 			} else {
217 				*r_addr = ooff + this->st_value +
218 				    relas->r_addend;
219 			}
220 			break;
221 #endif
222 		case RELOC_RELATIVE:
223 			if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
224 			    (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
225 			    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
226 				*r_addr = loff + relas->r_addend;
227 
228 #ifdef DL_PRINTF_DEBUG
229 _dl_printf("rel1 r_addr %x val %x loff %x ooff %x addend %x\n", r_addr,
230     loff + relas->r_addend, loff, ooff, relas->r_addend);
231 #endif
232 
233 			} else {
234 				*r_addr = loff + this->st_value +
235 				    relas->r_addend;
236 			}
237 			break;
238 		case RELOC_JMP_SLOT:
239 		    {
240 			Elf32_Addr target = ooff + this->st_value +
241 			    relas->r_addend;
242 			Elf32_Addr val = target - (Elf32_Addr)r_addr;
243 
244 			if (!B24_VALID_RANGE(val)){
245 				int index;
246 #ifdef DL_PRINTF_DEBUG
247 _dl_printf(" ooff %x, sym val %x, addend %x"
248 	" r_addr %x symn [%s] -> %x\n",
249 	ooff, this->st_value, relas->r_addend,
250 	r_addr, symn, val);
251 #endif
252 				/* if offset is > RELOC_24 deal with it */
253 				index = (r_addr - first_rela) >> 1;
254 
255 				if (index >= (2 << 12)) {
256 					/* addis r11,r11,.PLTtable@ha*/
257 					r_addr[0] = ADDIS_R11_R0 | HA(index*4);
258 					r_addr[1] = ADDI_R11_R11 | L(index*4);
259 					BR(r_addr[2], pltcall);
260 				} else {
261 					r_addr[0] = LI_R11 | (index * 4);
262 					BR(r_addr[1], pltcall);
263 
264 				}
265 				_dl_dcbf(&r_addr[0]);
266 				_dl_dcbf(&r_addr[2]);
267 				val= ooff + this->st_value +
268 				    relas->r_addend;
269 #ifdef DL_PRINTF_DEBUG
270 _dl_printf(" symn [%s] val 0x%x\n", symn, val);
271 #endif
272 				plttable[index] = val;
273 			} else {
274 				/* if the offset is small enough,
275 				 * branch directly to the dest
276 				 */
277 				BR(r_addr[0], target);
278 				_dl_dcbf(&r_addr[0]);
279 			}
280 		    }
281 
282 			break;
283 		case RELOC_GLOB_DAT:
284 			*r_addr = ooff + this->st_value + relas->r_addend;
285 			break;
286 #if 1
287 		/* should not be supported ??? */
288 		case RELOC_REL24:
289 		    {
290 			Elf32_Addr val = ooff + this->st_value +
291 			    relas->r_addend - (Elf32_Addr)r_addr;
292 			if (!B24_VALID_RANGE(val)){
293 				/* invalid offset */
294 				_dl_exit(20);
295 			}
296 			val &= ~0xfc000003;
297 			val |= (*r_addr & 0xfc000003);
298 			*r_addr = val;
299 
300 			_dl_dcbf(r_addr);
301 		    }
302 		break;
303 #endif
304 #if 1
305 		case RELOC_16_LO:
306 		    {
307 			Elf32_Addr val;
308 
309 			val = loff + relas->r_addend;
310 			*(Elf32_Half *)r_addr = val;
311 
312 			_dl_dcbf(r_addr);
313 		    }
314 		break;
315 #endif
316 #if 1
317 		case RELOC_16_HI:
318 		    {
319 			Elf32_Addr val;
320 
321 			val = loff + relas->r_addend;
322 			*(Elf32_Half *)r_addr = (val >> 16);
323 
324 			_dl_dcbf(r_addr);
325 		    }
326 		break;
327 #endif
328 #if 1
329 		case RELOC_16_HA:
330 		    {
331 			Elf32_Addr val;
332 
333 			val = loff + relas->r_addend;
334 			*(Elf32_Half *)r_addr = ((val + 0x8000) >> 16);
335 
336 			_dl_dcbf(r_addr);
337 		    }
338 		break;
339 #endif
340 		case RELOC_REL14_TAKEN:
341 			/* val |= 1 << (31-10) XXX? */
342 		case RELOC_REL14:
343 		case RELOC_REL14_NTAKEN:
344 		    {
345 			Elf32_Addr val = ooff + this->st_value +
346 			    relas->r_addend - (Elf32_Addr)r_addr;
347 			if (((val & 0xffff8000) != 0) &&
348 			    ((val & 0xffff8000) != 0xffff8000)) {
349 				/* invalid offset */
350 				_dl_exit(20);
351 			}
352 			val &= ~0xffff0003;
353 			val |= (*r_addr & 0xffff0003);
354 			*r_addr = val;
355 #ifdef DL_PRINTF_DEBUG
356 			_dl_printf("rel 14 %x val %x\n", r_addr, val);
357 #endif
358 
359 			_dl_dcbf(r_addr);
360 		    }
361 			break;
362 		case RELOC_COPY:
363 		{
364 #ifdef DL_PRINTF_DEBUG
365 			_dl_printf("copy r_addr %x, sym %x [%s] size %d val %x\n",
366 			    r_addr, sym, symn, sym->st_size,
367 			    (ooff + this->st_value+
368 			    relas->r_addend));
369 #endif
370 			/*
371 			 * we need to find a symbol, that is not in the current
372 			 * object, start looking at the beginning of the list,
373 			 * searching all objects but _not_ the current object,
374 			 * first one found wins.
375 			 */
376 			const Elf32_Sym *cpysrc = NULL;
377 			Elf32_Addr src_loff;
378 			int size;
379 
380 			src_loff = 0;
381 			src_loff = _dl_find_symbol(symn, &cpysrc,
382 			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND| SYM_NOTPLT,
383 			    sym, object, NULL);
384 			if (cpysrc != NULL) {
385 				size = sym->st_size;
386 				if (sym->st_size != cpysrc->st_size) {
387 					_dl_printf("symbols size differ [%s] \n",
388 					    symn);
389 					size = sym->st_size < cpysrc->st_size ?
390 					    sym->st_size : cpysrc->st_size;
391 				}
392 #ifdef DL_PRINTF_DEBUG
393 _dl_printf(" found other symbol at %x size %d\n",
394     src_loff + cpysrc->st_value,  cpysrc->st_size);
395 #endif
396 				_dl_bcopy((void *)(src_loff + cpysrc->st_value),
397 				    r_addr, size);
398 			} else
399 				fails++;
400 		}
401 			break;
402 		case RELOC_NONE:
403 			break;
404 
405 		default:
406 			_dl_printf("%s:"
407 			    " %s: unsupported relocation '%s' %d at %x\n",
408 			    _dl_progname, object->load_name, symn,
409 			    ELF32_R_TYPE(relas->r_info), r_addr );
410 			_dl_exit(1);
411 		}
412 	}
413 
414 	/* reprotect the unprotected segments */
415 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
416 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
417 			if (!(llist->prot & PROT_WRITE))
418 				_dl_mprotect(llist->start, llist->size,
419 				    llist->prot);
420 		}
421 	}
422 	return(fails);
423 }
424 
425 /*
426  *	Relocate the Global Offset Table (GOT).
427  *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
428  *	otherwise the lazy binding plt initialization is performed.
429  */
430 int
431 _dl_md_reloc_got(elf_object_t *object, int lazy)
432 {
433 	Elf_Addr *pltresolve;
434 	Elf_Addr *first_rela;
435 	Elf_RelA *relas;
436 	Elf_Addr  plt_addr;
437 	int	i;
438 	int	numrela;
439 	int	fails = 0;
440 	int index;
441 	Elf32_Addr *r_addr;
442 	Elf_Addr ooff;
443 	const Elf_Sym *this;
444 
445 	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
446 		return (0);
447 
448 	object->got_addr = 0;
449 	object->got_size = 0;
450 	this = NULL;
451 	ooff = _dl_find_symbol("__got_start", &this,
452 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
453 	    object, NULL);
454 	if (this != NULL)
455 		object->got_addr = ooff + this->st_value;
456 
457 	this = NULL;
458 	ooff = _dl_find_symbol("__got_end", &this,
459 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
460 	    object, NULL);
461 	if (this != NULL)
462 		object->got_size = ooff + this->st_value  - object->got_addr;
463 
464 	plt_addr = 0;
465 	object->plt_size = 0;
466 	this = NULL;
467 	ooff = _dl_find_symbol("__plt_start", &this,
468 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
469 	    object, NULL);
470 	if (this != NULL)
471 		plt_addr = ooff + this->st_value;
472 
473 	this = NULL;
474 	ooff = _dl_find_symbol("__plt_end", &this,
475 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
476 	    object, NULL);
477 	if (this != NULL)
478 		object->plt_size = ooff + this->st_value  - plt_addr;
479 
480 	if (object->got_addr == 0)
481 		object->got_start = 0;
482 	else {
483 		object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
484 		object->got_size += object->got_addr - object->got_start;
485 		object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
486 	}
487 	if (plt_addr == 0)
488 		object->plt_start = 0;
489 	else {
490 		object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz);
491 		object->plt_size += plt_addr - object->plt_start;
492 		object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz);
493 	}
494 
495 	if (!lazy) {
496 		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
497 	} else {
498 		first_rela = (Elf32_Addr *)
499 		    (((Elf32_Rela *)(object->Dyn.info[DT_JMPREL]))->r_offset +
500 		    object->obj_base);
501 		pltresolve = (Elf32_Addr *)(first_rela) - 18;
502 
503 		relas = (Elf32_Rela *)(object->Dyn.info[DT_JMPREL]);
504 		numrela = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf32_Rela);
505 		r_addr = (Elf32_Addr *)(relas->r_offset + object->obj_base);
506 
507 		for (i = 0, index = 0; i < numrela; i++, r_addr+=2, index++) {
508 			if (index >= (2 << 12)) {
509 				/* addis r11,r0,.PLTtable@ha*/
510 				r_addr[0] = ADDIS_R11_R0 | HA(index*4);
511 				r_addr[1] = ADDI_R11_R11 | L(index*4);
512 				BR(r_addr[2], pltresolve);
513 				/* only every other slot is used after
514 				 * index == 2^14
515 				 */
516 				r_addr += 2;
517 			} else {
518 				r_addr[0] = LI_R11 | (index * 4);
519 				BR(r_addr[1], pltresolve);
520 			}
521 			_dl_dcbf(&r_addr[0]);
522 			_dl_dcbf(&r_addr[2]);
523 		}
524 	}
525 	if (object->got_size != 0) {
526 
527 		_dl_mprotect((void*)object->got_start, object->got_size,
528 		    PROT_READ|PROT_EXEC); /* only PPC is PROT_EXE */
529 		_dl_syncicache((void*)object->got_addr, 4);
530 	}
531 	if (object->plt_size != 0)
532 		_dl_mprotect((void*)object->plt_start, object->plt_size,
533 		    PROT_READ|PROT_EXEC);
534 
535 	return (fails);
536 }
537 
538 Elf_Addr
539 _dl_bind(elf_object_t *object, int reloff)
540 {
541 	const Elf_Sym *sym, *this;
542 	Elf_Addr *r_addr, ooff;
543 	const char *symn;
544 	Elf_Addr value;
545 	Elf_RelA *relas;
546 	Elf32_Addr val;
547 	Elf32_Addr *pltresolve;
548 	Elf32_Addr *pltcall;
549 	Elf32_Addr *pltinfo;
550 	Elf32_Addr *plttable;
551 	sigset_t savedmask;
552 
553 	relas = ((Elf_RelA *)object->Dyn.info[DT_JMPREL]) + (reloff>>2);
554 
555 	sym = object->dyn.symtab;
556 	sym += ELF_R_SYM(relas->r_info);
557 	symn = object->dyn.strtab + sym->st_name;
558 
559 	r_addr = (Elf_Addr *)(object->obj_base + relas->r_offset);
560 	this = NULL;
561 	ooff = _dl_find_symbol(symn, &this,
562 	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym,
563 	    object, NULL);
564 	if (this == NULL) {
565 		_dl_printf("lazy binding failed!\n");
566 		*((int *)0) = 0;	/* XXX */
567 	}
568 
569 	/* if PLT is protected, allow the write */
570 	if (object->plt_size != 0)  {
571 		_dl_thread_bind_lock(0, &savedmask);
572 		_dl_mprotect((void*)object->plt_start, object->plt_size,
573 		    PROT_READ|PROT_WRITE|PROT_EXEC);
574 	}
575 
576 	value = ooff + this->st_value;
577 
578 	val = value - (Elf32_Addr)r_addr;
579 
580 	pltresolve = (Elf32_Addr *)
581 	    (Elf32_Rela *)(object->Dyn.info[DT_PLTGOT]);
582 	pltcall = (Elf32_Addr *)(pltresolve) + PLT_CALL_OFFSET;
583 
584 	if (!B24_VALID_RANGE(val)) {
585 		int index;
586 		/* if offset is > RELOC_24 deal with it */
587 		index = reloff >> 2;
588 
589 		/* update plttable before pltcall branch, to make
590 		 * this a safe race for threads
591 		 */
592 		val = ooff + this->st_value + relas->r_addend;
593 
594 		pltinfo = (Elf32_Addr *)(pltresolve) + PLT_INFO_OFFSET;
595 		plttable = (Elf32_Addr *)pltinfo[0];
596 		plttable[index] = val;
597 
598 		if (index >= (2 << 12)) {
599 			/* r_addr[0,1] is initialized to correct
600 			 * value in reloc_got.
601 			 */
602 			BR(r_addr[2], pltcall);
603 			_dl_dcbf(&r_addr[2]);
604 		} else {
605 			/* r_addr[0] is initialized to correct
606 			 * value in reloc_got.
607 			 */
608 			BR(r_addr[1], pltcall);
609 			_dl_dcbf(&r_addr[1]);
610 		}
611 	} else {
612 		/* if the offset is small enough,
613 		 * branch directly to the dest
614 		 */
615 		BR(r_addr[0], value);
616 		_dl_dcbf(&r_addr[0]);
617 	}
618 
619 	/* if PLT is to be protected, change back to RO/X */
620 	if (object->plt_size != 0) {
621 		_dl_mprotect((void*)object->plt_start, object->plt_size,
622 		    PROT_READ|PROT_EXEC); /* only PPC is PROT_EXE */
623 		_dl_thread_bind_lock(1, &savedmask);
624 	}
625 	return (value);
626 }
627 
628 /* should not be defined here, but it is 32 for all powerpc 603-G4 */
629 #define CACHELINESIZE 32
630 void
631 _dl_syncicache(char *from, size_t len)
632 {
633 	unsigned int off = 0;
634 	int l = len + ((int)from & (CACHELINESIZE-1));
635 
636 	while (off < l) {
637 		asm volatile ("dcbst %1,%0" :: "r"(from), "r"(off));
638 		asm volatile ("sync");
639 		asm volatile ("icbi %1, %0" :: "r"(from), "r"(off));
640 		asm volatile ("sync");
641 		asm volatile ("isync");
642 
643 		off += CACHELINESIZE;
644 	}
645 }
646 __asm__(".section\t\".text\"\n\t"
647 	".align 2\n\t"
648 	".globl _dl__syscall\n\t"
649 	".type _dl__syscall,@function\n"
650 	"_dl__syscall:\n\t"
651 	"li 0, " XSTRINGIFY(SYS___syscall) "\n\t"
652 	"sc\n\t"
653 	"cmpwi	0, 0\n\t"
654 	"beq	1f\n\t"
655 	"li	3, -1\n\t"
656 	"1:\n\t"
657 	"blr");
658