1 /*	$OpenBSD: rtld_machine.c,v 1.56 2015/11/02 07:02:53 guenther 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 #include <sys/syscall.h>
34 #include <sys/unistd.h>
35 
36 #include <nlist.h>
37 #include <link.h>
38 
39 #include "syscall.h"
40 #include "archdep.h"
41 #include "resolve.h"
42 
43 #define	DT_PROC(n)	((n) - DT_LOPROC + DT_NUM)
44 
45 void _dl_syncicache(char *from, size_t len);
46 
47 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
48 
49 /* relocation bits */
50 #define HA(x) (((Elf_Addr)(x) >> 16) + (((Elf_Addr)(x) & 0x00008000) >> 15))
51 #define L(x) (((Elf_Addr)x) & 0x0000ffff)
52 #define ADDIS_R11_R11	0x3d6b0000
53 #define ADDIS_R11_R0	0x3d600000
54 #define ADDI_R11_R11	0x396b0000
55 #define LWZ_R11_R11	0x816b0000
56 #define LI_R11		0x39600000
57 
58 #define ADDIS_R12_R0	0x3d800000
59 #define ADDI_R12_R12	0x398c0000
60 #define MCTR_R11	0x7d6903a6
61 #define MCTR_R12	0x7d8903a6
62 #define BCTR		0x4e800420
63 #define BRVAL(from, to)					\
64 	((((Elf32_Addr)(to) - (Elf32_Addr)(&(from)))	\
65 	    & ~0xfc000000) | 0x48000000)
66 #define BR(from, to)	((from) = BRVAL(from, to))
67 
68 
69 #define SLWI_R12_R11_1	0x556c083c
70 #define ADD_R11_R12_R11 0x7d6c5a14
71 
72 /* these are structures/functions offset from PLT region */
73 #define PLT_CALL_OFFSET		8
74 #define PLT_INFO_OFFSET		12
75 #define PLT_1STRELA_OFFSET	18
76 #define B24_VALID_RANGE(x) \
77     ((((x) & 0xfe000000) == 0x00000000) || (((x) &  0xfe000000) == 0xfe000000))
78 
79 void _dl_bind_start(void); /* XXX */
80 Elf_Addr _dl_bind(elf_object_t *object, int reloff);
81 
82 int
83 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
84 {
85 	int	i;
86 	int	numrela;
87 	long	relrel;
88 	int	fails = 0;
89 	struct load_list *llist;
90 	Elf32_Addr loff;
91 	Elf32_Rela  *relas;
92 	/* for jmp table relocations */
93 	Elf32_Addr *pltresolve;
94 	Elf32_Addr *pltcall;
95 	Elf32_Addr *plttable;
96 	Elf32_Addr *pltinfo;
97 	Elf32_Addr *first_rela;
98 	Elf32_Addr prev_value = 0, prev_ooff = 0;
99 	const Elf32_Sym *prev_sym = NULL;
100 
101 	loff = object->obj_base;
102 	numrela = object->Dyn.info[relasz] / sizeof(Elf32_Rela);
103 	relrel = rel == DT_RELA ? object->relacount : 0;
104 	relas = (Elf32_Rela *)(object->Dyn.info[rel]);
105 
106 #ifdef DL_PRINTF_DEBUG
107 _dl_printf("object relocation size %x, numrela %x\n",
108 	object->Dyn.info[relasz], numrela);
109 #endif
110 
111 	if (relas == NULL)
112 		return(0);
113 
114 	if (relrel > numrela) {
115 		_dl_printf("relcount > numrel: %ld > %ld\n", relrel, numrela);
116 		_dl_exit(20);
117 	}
118 
119 	pltresolve = NULL;
120 	pltcall = NULL;
121 	plttable = NULL;
122 
123 	/* for plt relocation usage */
124 	if (object->Dyn.info[DT_JMPREL] != 0 &&
125 	    object->Dyn.info[DT_PROC(DT_PPC_GOT)] == 0) {
126 		/* resolver stub not set up */
127 		int nplt;
128 
129 		/* Need to construct table to do jumps */
130 		pltresolve = (Elf32_Addr *)(object->Dyn.info[DT_PLTGOT]);
131 		pltcall = (Elf32_Addr *)(pltresolve) + PLT_CALL_OFFSET;
132 		pltinfo = (Elf32_Addr *)(pltresolve) + PLT_INFO_OFFSET;
133 		first_rela =  (Elf32_Addr *)(pltresolve) + PLT_1STRELA_OFFSET;
134 
135 		nplt = object->Dyn.info[DT_PLTRELSZ]/sizeof(Elf32_Rela);
136 
137 		if (nplt >= (2<<12)) {
138 			plttable = (Elf32_Addr *) ((Elf32_Addr)first_rela)
139 			    + (2 * (2<<12)) + (4 * (nplt - (2<<12)));
140 		} else {
141 			plttable = (Elf32_Addr *) ((Elf32_Addr)first_rela)
142 			    + (2 * nplt);
143 		}
144 
145 		pltinfo[0] = (Elf32_Addr)plttable;
146 
147 #ifdef DL_PRINTF_DEBUG
148 		_dl_printf("md_reloc:  plttbl size %x\n",
149 		    (object->Dyn.info[DT_PLTRELSZ]/sizeof(Elf32_Rela)));
150 		_dl_printf("md_reloc: plttable %x\n", plttable);
151 #endif
152 		pltresolve[0] = SLWI_R12_R11_1;
153 		pltresolve[1] = ADD_R11_R12_R11;
154 		pltresolve[2] = ADDIS_R12_R0 | HA(_dl_bind_start);
155 		pltresolve[3] = ADDI_R12_R12 | L(_dl_bind_start);
156 		pltresolve[4] = MCTR_R12;
157 		pltresolve[5] = ADDIS_R12_R0 | HA(object);
158 		pltresolve[6] = ADDI_R12_R12 | L(object);
159 		pltresolve[7] = BCTR;
160 		_dl_dcbf(&pltresolve[0]);
161 		_dl_dcbf(&pltresolve[7]);
162 
163 		/* addis r11,r11,.PLTtable@ha*/
164 		pltcall[0] = ADDIS_R11_R11 | HA(plttable);
165 		/* lwz r11,plttable@l(r11) */
166 		pltcall[1] = LWZ_R11_R11 | L(plttable);
167 		pltcall[2] = MCTR_R11;	/* mtctr r11 */
168 		pltcall[3] = BCTR;	/* bctr */
169 		_dl_dcbf(&pltcall[0]);
170 		_dl_dcbf(&pltcall[3]);
171 	} else {
172 		first_rela = NULL;
173 	}
174 
175 	/*
176 	 * Change protection of all write protected segments in the object
177 	 * so we can do relocations such as REL24, REL16 etc. After
178 	 * relocation restore protection.
179 	 */
180 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
181 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
182 			if (!(llist->prot & PROT_WRITE)) {
183 				_dl_mprotect(llist->start, llist->size,
184 				    llist->prot|PROT_WRITE);
185 			}
186 		}
187 	}
188 
189 	/* tight loop for leading RELATIVE relocs */
190 	for (i = 0; i < relrel; i++, relas++) {
191 		Elf_Addr *r_addr;
192 #ifdef DEBUG
193 		const Elf32_Sym *sym;
194 
195 		if (ELF32_R_TYPE(relas->r_info) != RELOC_RELATIVE) {
196 			_dl_printf("RELCOUNT wrong\n");
197 			_dl_exit(20);
198 		}
199 		sym = object->dyn.symtab;
200 		sym += ELF32_R_SYM(relas->r_info);
201 		if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL ||
202 		    (ELF32_ST_TYPE(sym->st_info) != STT_SECTION &&
203 		    ELF32_ST_TYPE(sym->st_info) != STT_NOTYPE)) {
204 			_dl_printf("RELATIVE relocation against symbol\n");
205 			_dl_exit(20);
206 		}
207 #endif
208 		r_addr = (Elf_Addr *)(relas->r_offset + loff);
209 		*r_addr = loff + relas->r_addend;
210 	}
211 	for (; i < numrela; i++, relas++) {
212 		Elf32_Addr *r_addr = (Elf32_Addr *)(relas->r_offset + loff);
213 		Elf32_Addr ooff;
214 		const Elf32_Sym *sym, *this;
215 		const char *symn;
216 		int type;
217 
218 		if (ELF32_R_SYM(relas->r_info) == 0xffffff)
219 			continue;
220 
221 		type = ELF32_R_TYPE(relas->r_info);
222 
223 		if (type == RELOC_JMP_SLOT && rel != DT_JMPREL)
224 			continue;
225 
226 		sym = object->dyn.symtab;
227 		sym += ELF32_R_SYM(relas->r_info);
228 		symn = object->dyn.strtab + sym->st_name;
229 
230 		ooff = 0;
231 		this = NULL;
232 		if (ELF32_R_SYM(relas->r_info) &&
233 		    !(ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
234 		    ELF32_ST_TYPE (sym->st_info) == STT_NOTYPE)) {
235 			if (sym == prev_sym) {
236 				this = sym;	/* XXX any non-NULL */
237 				ooff = prev_ooff;
238 			} else {
239 				ooff = _dl_find_symbol_bysym(object,
240 				    ELF32_R_SYM(relas->r_info), &this,
241 				    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
242 				    ((type == RELOC_JMP_SLOT) ?
243 				    SYM_PLT:SYM_NOTPLT), sym, NULL);
244 
245 				if (this == NULL) {
246 					if (ELF_ST_BIND(sym->st_info) !=
247 					    STB_WEAK)
248 						fails++;
249 					continue;
250 				}
251 				prev_sym = sym;
252 				prev_value = this->st_value;
253 				prev_ooff = ooff;
254 			}
255 		}
256 
257 		/*
258 		 * For Secure-PLT, RELOC_JMP_SLOT simply sets PLT
259 		 * slots similarly to how RELOC_GLOB_DAT updates GOT
260 		 * slots.
261 		 */
262 		if (type == RELOC_JMP_SLOT &&
263 		    object->Dyn.info[DT_PROC(DT_PPC_GOT)])
264 			type = RELOC_GLOB_DAT;
265 
266 		switch (type) {
267 		case RELOC_32:
268 			if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
269 			    (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
270 			    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
271 				*r_addr = ooff + relas->r_addend;
272 			} else {
273 				*r_addr = ooff + prev_value +
274 				    relas->r_addend;
275 			}
276 			break;
277 		case RELOC_RELATIVE:
278 			if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
279 			    (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
280 			    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
281 				*r_addr = loff + relas->r_addend;
282 
283 #ifdef DL_PRINTF_DEBUG
284 _dl_printf("rel1 r_addr %x val %x loff %x ooff %x addend %x\n", r_addr,
285     loff + relas->r_addend, loff, ooff, relas->r_addend);
286 #endif
287 
288 			} else {
289 				*r_addr = loff + prev_value +
290 				    relas->r_addend;
291 			}
292 			break;
293 		case RELOC_JMP_SLOT:
294 		    {
295 			Elf32_Addr target = ooff + prev_value +
296 			    relas->r_addend;
297 			Elf32_Addr val = target - (Elf32_Addr)r_addr;
298 
299 			if (!B24_VALID_RANGE(val)){
300 				int index;
301 #ifdef DL_PRINTF_DEBUG
302 _dl_printf(" ooff %x, sym val %x, addend %x"
303 	" r_addr %x symn [%s] -> %x\n",
304 	ooff, prev_value, relas->r_addend,
305 	r_addr, symn, val);
306 #endif
307 				/* if offset is > RELOC_24 deal with it */
308 				index = (r_addr - first_rela) >> 1;
309 
310 				if (index >= (2 << 12)) {
311 					/* addis r11,r11,.PLTtable@ha*/
312 					r_addr[0] = ADDIS_R11_R0 | HA(index*4);
313 					r_addr[1] = ADDI_R11_R11 | L(index*4);
314 					BR(r_addr[2], pltcall);
315 				} else {
316 					r_addr[0] = LI_R11 | (index * 4);
317 					BR(r_addr[1], pltcall);
318 
319 				}
320 				_dl_dcbf(&r_addr[0]);
321 				_dl_dcbf(&r_addr[2]);
322 				val= ooff + prev_value +
323 				    relas->r_addend;
324 #ifdef DL_PRINTF_DEBUG
325 _dl_printf(" symn [%s] val 0x%x\n", symn, val);
326 #endif
327 				plttable[index] = val;
328 			} else {
329 				/* if the offset is small enough,
330 				 * branch directly to the dest
331 				 */
332 				BR(r_addr[0], target);
333 				_dl_dcbf(&r_addr[0]);
334 			}
335 		    }
336 
337 			break;
338 		case RELOC_GLOB_DAT:
339 			*r_addr = ooff + prev_value + relas->r_addend;
340 			break;
341 #if 1
342 		/* should not be supported ??? */
343 		case RELOC_REL24:
344 		    {
345 			Elf32_Addr val = ooff + prev_value +
346 			    relas->r_addend - (Elf32_Addr)r_addr;
347 			if (!B24_VALID_RANGE(val)){
348 				/* invalid offset */
349 				_dl_exit(20);
350 			}
351 			val &= ~0xfc000003;
352 			val |= (*r_addr & 0xfc000003);
353 			*r_addr = val;
354 
355 			_dl_dcbf(r_addr);
356 		    }
357 		break;
358 #endif
359 #if 1
360 		case RELOC_16_LO:
361 		    {
362 			Elf32_Addr val;
363 
364 			val = loff + relas->r_addend;
365 			*(Elf32_Half *)r_addr = val;
366 
367 			_dl_dcbf(r_addr);
368 		    }
369 		break;
370 #endif
371 #if 1
372 		case RELOC_16_HI:
373 		    {
374 			Elf32_Addr val;
375 
376 			val = loff + relas->r_addend;
377 			*(Elf32_Half *)r_addr = (val >> 16);
378 
379 			_dl_dcbf(r_addr);
380 		    }
381 		break;
382 #endif
383 #if 1
384 		case RELOC_16_HA:
385 		    {
386 			Elf32_Addr val;
387 
388 			val = loff + relas->r_addend;
389 			*(Elf32_Half *)r_addr = ((val + 0x8000) >> 16);
390 
391 			_dl_dcbf(r_addr);
392 		    }
393 		break;
394 #endif
395 		case RELOC_REL14_TAKEN:
396 			/* val |= 1 << (31-10) XXX? */
397 		case RELOC_REL14:
398 		case RELOC_REL14_NTAKEN:
399 		    {
400 			Elf32_Addr val = ooff + prev_value +
401 			    relas->r_addend - (Elf32_Addr)r_addr;
402 			if (((val & 0xffff8000) != 0) &&
403 			    ((val & 0xffff8000) != 0xffff8000)) {
404 				/* invalid offset */
405 				_dl_exit(20);
406 			}
407 			val &= ~0xffff0003;
408 			val |= (*r_addr & 0xffff0003);
409 			*r_addr = val;
410 #ifdef DL_PRINTF_DEBUG
411 			_dl_printf("rel 14 %x val %x\n", r_addr, val);
412 #endif
413 
414 			_dl_dcbf(r_addr);
415 		    }
416 			break;
417 		case RELOC_COPY:
418 		{
419 #ifdef DL_PRINTF_DEBUG
420 			_dl_printf("copy r_addr %x, sym %x [%s] size %d val %x\n",
421 			    r_addr, sym, symn, sym->st_size,
422 			    (ooff + prev_value+
423 			    relas->r_addend));
424 #endif
425 			/*
426 			 * we need to find a symbol, that is not in the current
427 			 * object, start looking at the beginning of the list,
428 			 * searching all objects but _not_ the current object,
429 			 * first one found wins.
430 			 */
431 			const Elf32_Sym *cpysrc = NULL;
432 			Elf32_Addr src_loff;
433 			int size;
434 
435 			src_loff = 0;
436 			src_loff = _dl_find_symbol(symn, &cpysrc,
437 			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND| SYM_NOTPLT,
438 			    sym, object, NULL);
439 			if (cpysrc != NULL) {
440 				size = sym->st_size;
441 				if (sym->st_size != cpysrc->st_size) {
442 					_dl_printf("symbols size differ [%s] \n",
443 					    symn);
444 					size = sym->st_size < cpysrc->st_size ?
445 					    sym->st_size : cpysrc->st_size;
446 				}
447 #ifdef DL_PRINTF_DEBUG
448 _dl_printf(" found other symbol at %x size %d\n",
449     src_loff + cpysrc->st_value,  cpysrc->st_size);
450 #endif
451 				_dl_bcopy((void *)(src_loff + cpysrc->st_value),
452 				    r_addr, size);
453 			} else
454 				fails++;
455 		}
456 			break;
457 		case RELOC_NONE:
458 			break;
459 
460 		default:
461 			_dl_printf("%s:"
462 			    " %s: unsupported relocation '%s' %d at %x\n",
463 			    _dl_progname, object->load_name, symn,
464 			    ELF32_R_TYPE(relas->r_info), r_addr );
465 			_dl_exit(1);
466 		}
467 	}
468 
469 	/* reprotect the unprotected segments */
470 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
471 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
472 			if (!(llist->prot & PROT_WRITE))
473 				_dl_mprotect(llist->start, llist->size,
474 				    llist->prot);
475 		}
476 	}
477 	return(fails);
478 }
479 
480 void
481 _dl_setup_secure_plt(elf_object_t *object)
482 {
483 	Elf32_Addr *got;
484 	Elf32_Addr *plt;
485 	int numplt, i;
486 
487 	/* Relocate processor-specific tags. */
488 	object->Dyn.info[DT_PROC(DT_PPC_GOT)] += object->obj_base;
489 
490 	got = (Elf32_Addr *)
491 	    (Elf32_Rela *)(object->Dyn.info[DT_PROC(DT_PPC_GOT)]);
492 	got[1] = (Elf32_Addr)_dl_bind_start;
493 	got[2] = (Elf32_Addr)object;
494 
495 	plt = (Elf32_Addr *)
496 	   (Elf32_Rela *)(object->Dyn.info[DT_PLTGOT]);
497 	numplt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf32_Rela);
498 	for (i = 0; i < numplt; i++)
499 		plt[i] += object->obj_base;
500 }
501 
502 void
503 _dl_setup_bss_plt(elf_object_t *object)
504 {
505 	Elf_Addr *pltresolve;
506 	Elf_Addr *first_rela;
507 	Elf_RelA *relas;
508 	Elf32_Addr *r_addr;
509 	int numrela, i;
510 	int index;
511 
512 	first_rela = (Elf32_Addr *)
513 	    (((Elf32_Rela *)(object->Dyn.info[DT_JMPREL]))->r_offset +
514 	    object->obj_base);
515 	pltresolve = (Elf32_Addr *)(first_rela) - 18;
516 
517 	relas = (Elf32_Rela *)(object->Dyn.info[DT_JMPREL]);
518 	numrela = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf32_Rela);
519 	r_addr = (Elf32_Addr *)(relas->r_offset + object->obj_base);
520 
521 	for (i = 0, index = 0; i < numrela; i++, r_addr+=2, index++) {
522 		if (index >= (2 << 12)) {
523 			/* addis r11,r0,.PLTtable@ha*/
524 			r_addr[0] = ADDIS_R11_R0 | HA(index*4);
525 			r_addr[1] = ADDI_R11_R11 | L(index*4);
526 			BR(r_addr[2], pltresolve);
527 			/* only every other slot is used after
528 			 * index == 2^14
529 			 */
530 			r_addr += 2;
531 		} else {
532 			r_addr[0] = LI_R11 | (index * 4);
533 			BR(r_addr[1], pltresolve);
534 		}
535 		_dl_dcbf(&r_addr[0]);
536 		_dl_dcbf(&r_addr[2]);
537 	}
538 }
539 
540 /*
541  *	Relocate the Global Offset Table (GOT).
542  *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
543  *	otherwise the lazy binding plt initialization is performed.
544  */
545 int
546 _dl_md_reloc_got(elf_object_t *object, int lazy)
547 {
548 	void *got_addr;
549 	int fails = 0;
550 	int prot_exec = 0;
551 
552 	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
553 		return (0);
554 
555 	/*
556 	 * For BSS-PLT, both the GOT and the PLT need to be
557 	 * executable.  Yuck!
558 	 */
559 	if (object->Dyn.info[DT_PROC(DT_PPC_GOT)] == 0)
560 		prot_exec = PROT_EXEC;
561 
562 	if (object->traced)
563 		lazy = 1;
564 
565 	if (!lazy) {
566 		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
567 	} else {
568 		if (object->Dyn.info[DT_PROC(DT_PPC_GOT)])
569 			_dl_setup_secure_plt(object);
570 		else
571 			_dl_setup_bss_plt(object);
572 	}
573 
574 	/* mprotect the GOT */
575 	got_addr = _dl_protect_segment(object, 0, "__got_start", "__got_end",
576 	    PROT_READ|prot_exec);
577 	if (got_addr != NULL)
578 		_dl_syncicache(got_addr, 4);
579 
580 	/* mprotect the PLT */
581 	_dl_protect_segment(object, 0, "__plt_start", "__plt_end",
582 	    PROT_READ|prot_exec);
583 
584 	return (fails);
585 }
586 
587 Elf_Addr
588 _dl_bind(elf_object_t *object, int reloff)
589 {
590 	const Elf_Sym *sym, *this;
591 	Elf_Addr *r_addr, ooff;
592 	const char *symn;
593 	const elf_object_t *sobj;
594 	Elf_Addr value;
595 	Elf_RelA *relas;
596 	Elf32_Addr val;
597 	Elf32_Addr *pltresolve;
598 	Elf32_Addr *pltcall;
599 	Elf32_Addr *pltinfo;
600 	Elf32_Addr *plttable;
601 	int64_t cookie = pcookie;
602 	struct {
603 		struct __kbind param[2];
604 		Elf_Addr newval[2];
605 	} buf;
606 	struct __kbind *param;
607 	size_t psize;
608 
609 	relas = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
610 
611 	sym = object->dyn.symtab;
612 	sym += ELF_R_SYM(relas->r_info);
613 	symn = object->dyn.strtab + sym->st_name;
614 
615 	this = NULL;
616 	ooff = _dl_find_symbol(symn, &this,
617 	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj);
618 	if (this == NULL) {
619 		_dl_printf("lazy binding failed!\n");
620 		*(volatile int *)0 = 0;		/* XXX */
621 	}
622 
623 	value = ooff + this->st_value;
624 
625 	if (__predict_false(sobj->traced) && _dl_trace_plt(sobj, symn))
626 		return (value);
627 
628 	r_addr = (Elf_Addr *)(object->obj_base + relas->r_offset);
629 	val = value - (Elf32_Addr)r_addr;
630 
631 	if (object->Dyn.info[DT_PROC(DT_PPC_GOT)] == 0) {
632 		if (!B24_VALID_RANGE(val)) {
633 			int index, addr_off;
634 
635 			/* if offset is > RELOC_24 deal with it */
636 			index = reloff / sizeof(Elf32_Rela);
637 
638 			pltresolve = (Elf32_Addr *)
639 			    (Elf32_Rela *)(object->Dyn.info[DT_PLTGOT]);
640 			pltcall = (Elf32_Addr *)(pltresolve) + PLT_CALL_OFFSET;
641 
642 			/*
643 			 * Early plt entries can make short jumps; later ones
644 			 * use a 3 word sequence.  c.f. _dl_md_reloc_got()
645 			 */
646 			addr_off = (index >= (2 << 12)) ? 2 : 1;
647 
648 			/*
649 			 * Update plttable before pltcall branch, to make
650 			 * this a safe race for threads
651 			 */
652 			pltinfo = (Elf32_Addr *)(pltresolve) + PLT_INFO_OFFSET;
653 			plttable = (Elf32_Addr *)pltinfo[0];
654 
655 			buf.param[0].kb_addr = &plttable[index];
656 			buf.param[0].kb_size = sizeof(Elf_Addr);
657 			buf.param[1].kb_addr = &r_addr[addr_off];
658 			buf.param[1].kb_size = sizeof(Elf_Addr);
659 			buf.newval[0] = value + relas->r_addend;
660 			buf.newval[1] = BRVAL(r_addr[addr_off], pltcall);
661 			param = &buf.param[0];
662 			psize = sizeof(buf);
663 		} else {
664 			/*
665 			 * If the offset is small enough, branch directly to
666 			 * the dest.  We use the _second_ kbind params only.
667 			 */
668 			buf.param[1].kb_addr = &r_addr[0];
669 			buf.param[1].kb_size = sizeof(Elf_Addr);
670 			buf.newval[0] = BRVAL(r_addr[0], value);
671 			param = &buf.param[1];
672 			psize = sizeof(struct __kbind) + sizeof(Elf_Addr);
673 		}
674 	} else {
675 		int index = reloff / sizeof(Elf32_Rela);
676 
677 		/*
678 		 * Secure PLT; only needs one update so use the
679 		 * second kbind params.
680 		 */
681 		plttable = (Elf32_Addr *)
682 		    (Elf32_Rela *)(object->Dyn.info[DT_PLTGOT]);
683 		buf.param[1].kb_addr = &plttable[index];
684 		buf.param[1].kb_size = sizeof(Elf_Addr);
685 		buf.newval[0] = value;
686 		param = &buf.param[1];
687 		psize = sizeof(struct __kbind) + sizeof(Elf_Addr);
688 	}
689 
690 	{
691 		register long syscall_num __asm("r0") = SYS_kbind;
692 		register void *arg1 __asm("r3") = param;
693 		register long  arg2 __asm("r4") = psize;
694 		register long  arg3 __asm("r5") = 0xffffffff & (cookie >> 32);
695 		register long  arg4 __asm("r6") = 0xffffffff &  cookie;
696 
697 		__asm volatile("sc" : "+r" (syscall_num), "+r" (arg1),
698 		    "+r" (arg2) : "r" (arg3), "r" (arg4) : "cc", "memory");
699 	}
700 
701 	return (value);
702 }
703 
704 /* should not be defined here, but it is 32 for all powerpc 603-G4 */
705 #define CACHELINESIZE 32
706 void
707 _dl_syncicache(char *from, size_t len)
708 {
709 	unsigned int off = 0;
710 	int l = len + ((int)from & (CACHELINESIZE-1));
711 
712 	while (off < l) {
713 		asm volatile ("dcbst %1,%0" :: "r"(from), "r"(off));
714 		asm volatile ("sync");
715 		asm volatile ("icbi %1, %0" :: "r"(from), "r"(off));
716 		asm volatile ("sync");
717 		asm volatile ("isync");
718 
719 		off += CACHELINESIZE;
720 	}
721 }
722