xref: /openbsd-src/libexec/ld.so/powerpc/rtld_machine.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: rtld_machine.c,v 1.5 2001/08/05 15:31:35 drahn 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  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed under OpenBSD by
17  *	Dale Rahn.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
22  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  */
34 
35 #define _DYN_LOADER
36 
37 #include <sys/types.h>
38 #include <sys/mman.h>
39 
40 #include <nlist.h>
41 #include <link.h>
42 
43 #include "syscall.h"
44 #include "archdep.h"
45 #include "resolve.h"
46 
47 void
48 _dl_bcopy(void *src, void *dest, int size)
49 {
50 	unsigned char *psrc, *pdest;
51 	int i;
52 	psrc = src;
53 	pdest = dest;
54 	for (i = 0; i < size; i++) {
55 		pdest[i] = psrc[i];
56 	}
57 }
58 
59 int
60 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
61 {
62 	int	i;
63 	int	numrela;
64 	int	fails = 0;
65 	load_list_t *load_list;
66 	Elf32_Addr loff;
67 	Elf32_Rela  *relas;
68 	/* for jmp table relocations */
69 	Elf32_Addr *pltcall;
70 	Elf32_Addr *plttable;
71 
72 	Elf32_Addr * first_rela;
73 
74 	loff   = object->load_offs;
75 	numrela = object->Dyn.info[relasz] / sizeof(Elf32_Rela);
76 	relas = (Elf32_Rela *)(object->Dyn.info[rel]);
77 
78 #ifdef DL_PRINTF_DEBUG
79 _dl_printf("object relocation size %x, numrela %x\n",
80 	object->Dyn.info[relasz], numrela);
81 #endif
82 
83 	if((object->status & STAT_RELOC_DONE) || !relas) {
84 		return(0);
85 	}
86 	/* for plt relocation usage */
87 	if (object->Dyn.info[DT_JMPREL] != 0) {
88 		/* resolver stub not set up */
89 		Elf32_Addr val;
90 
91 		first_rela = (Elf32_Addr *)
92 		(((Elf32_Rela *)(object->Dyn.info[DT_JMPREL]))->r_offset
93 			+ loff);
94 		/* Need to construct table to do jumps */
95 		pltcall = (Elf32_Addr *)(first_rela) - 12;
96 #ifdef DL_PRINTF_DEBUG
97 _dl_printf("creating pltcall at %x\n", pltcall);
98 _dl_printf("md_reloc( jumprel %x\n", first_rela );
99 #endif
100 		plttable = (Elf32_Addr *)
101 			((Elf32_Addr)first_rela) + (2 *
102 			(object->Dyn.info[DT_PLTRELSZ]/sizeof(Elf32_Rela))
103 			);
104 
105 #ifdef DL_PRINTF_DEBUG
106 _dl_printf("md_reloc:  plttbl size %x\n",
107 			(object->Dyn.info[DT_PLTRELSZ]/sizeof(Elf32_Rela))
108 );
109 _dl_printf("md_reloc: plttable %x\n", plttable);
110 #endif
111 		pltcall[-1]= 0x504c5400; /* PLT tag :-) */
112 		val = ((Elf32_Addr)plttable >> 16) +
113 			(((Elf32_Addr)plttable & 0x00008000) >> 15);
114 		pltcall[0] = 0x3d6b0000 | val;  /* addis r11,r11,.PLTtable@ha*/
115 		val  = (Elf32_Addr)plttable & 0x0000ffff;
116 		pltcall[1] = 0x816b0000 | val;	/* lwz r11,plttable@l(r11) */
117 		pltcall[2] = 0x7d6903a6;	/* mtctr r12 */
118 		pltcall[3] = 0x4e800420;	/* bctr */
119 		_dl_dcbf(pltcall);
120 		_dl_dcbf(&pltcall[3]);
121 	} else {
122 		first_rela = NULL;
123 	}
124 
125 	/*
126 	 * Change protection of all write protected segments in the object
127 	 * so we can do relocations such as REL24, REL16 etc. After
128 	 * relocation restore protection.
129 	 */
130 	load_list = object->load_list;
131 	while(load_list != NULL) {
132 		_dl_mprotect(load_list->start, load_list->size,
133 			load_list->prot|PROT_WRITE);
134 		load_list = load_list->next;
135 	}
136 
137 
138 	for(i = 0; i < numrela; i++, relas++) {
139 		Elf32_Addr *r_addr = (Elf32_Addr *)(relas->r_offset + loff);
140 		Elf32_Addr ooff;
141 		const Elf32_Sym *sym, *this;
142 		const char *symn;
143 
144 		if(ELF32_R_SYM(relas->r_info) == 0xffffff) {
145 			continue;
146 		}
147 
148 		sym = object->dyn.symtab;
149 		sym += ELF32_R_SYM(relas->r_info);
150 		this = sym;
151 		symn = object->dyn.strtab + sym->st_name;
152 
153 		if(ELF32_R_SYM(relas->r_info) &&
154 		   !(ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
155 		     ELF32_ST_TYPE (sym->st_info) == STT_NOTYPE)) {
156 
157 			ooff = _dl_find_symbol(symn, _dl_objects, &this, 0, 0);
158 			if(!this && ELF32_ST_BIND(sym->st_info) == STB_GLOBAL) {
159 				_dl_printf("%s:"
160 					" %s :can't resolve reference '%s'\n",
161 					_dl_progname, object->load_name,
162 					symn);
163 				fails++;
164 			}
165 
166 		}
167 
168 		switch(ELF32_R_TYPE(relas->r_info)) {
169 #if 1
170 		case RELOC_32:
171 			if(ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
172 			   (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
173 			    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
174 				*r_addr = ooff + relas->r_addend;
175 			} else {
176 				*r_addr = ooff + this->st_value +
177 					relas->r_addend;
178 			}
179 			break;
180 #endif
181 		case RELOC_RELATIVE:
182 			if(ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
183 			   (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
184 			    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
185 				*r_addr = loff + relas->r_addend;
186 
187 #ifdef DL_PRINTF_DEBUG
188 _dl_printf("rel1 r_addr %x val %x loff %x ooff %x addend %x\n", r_addr,
189 loff + relas->r_addend, loff, ooff, relas->r_addend);
190 #endif
191 
192 			} else {
193 				*r_addr = loff + this->st_value +
194 					relas->r_addend;
195 			}
196 			break;
197 		case RELOC_JMP_SLOT:
198 		   {
199 			Elf32_Addr val = ooff + this->st_value +
200 				relas->r_addend - (Elf32_Addr)r_addr;
201 			if (!(((val & 0xfe000000) == 0x00000000) ||
202 				((val &  0xfe000000) == 0xfe000000)))
203 			{
204 				int index;
205 #ifdef DL_PRINTF_DEBUG
206 _dl_printf(" ooff %x, sym val %x, addend %x"
207 	" r_addr %x symn [%s] -> %x\n",
208 	ooff, this->st_value, relas->r_addend,
209 	r_addr, symn, val);
210 #endif
211 				/* if offset is > RELOC_24 deal with it */
212 				index = (r_addr - first_rela) >> 1;
213 
214 				if (index > (2 << 14)) {
215 
216 					/* addis r11,r11,.PLTtable@ha*/
217 					val = (index*4 >> 16) +
218 						((index*4 & 0x00008000) >> 15);
219 					r_addr[0] = 0x3d600000 | val;
220 					val = (Elf32_Addr)pltcall -
221 						(Elf32_Addr)&r_addr[2];
222 					r_addr[1] = 0x396b0000 | val;
223 					val &= ~0xfc000000;
224 					val |=  0x48000000;
225 					r_addr[2] = val;
226 
227 				} else {
228 #ifdef DL_PRINTF_DEBUG
229 	_dl_printf("  index %d, pltcall %x r_addr %x\n",
230 		index, pltcall, r_addr);
231 #endif
232 
233 					r_addr[0] = 0x39600000 | (index * 4);
234 					val = (Elf32_Addr)pltcall -
235 						(Elf32_Addr)&r_addr[1];
236 					val &= ~0xfc000000;
237 					val |=  0x48000000;
238 					r_addr[1] = val;
239 
240 				}
241 				_dl_dcbf(r_addr);
242 				_dl_dcbf(&r_addr[2]);
243 				val= ooff + this->st_value +
244 					relas->r_addend;
245 #ifdef DL_PRINTF_DEBUG
246 		_dl_printf(" symn [%s] val 0x%x\n", symn, val);
247 #endif
248 				plttable[index] = val;
249 			} else {
250 				/* if the offset is small enough,
251 				 * branch directy to the dest
252 				 */
253 				val &= ~0xfc000000;
254 				val |=  0x48000000;
255 				*r_addr = val;
256 				_dl_dcbf(r_addr);
257 			}
258 		   }
259 
260 			break;
261 		case RELOC_GLOB_DAT:
262 			*r_addr = ooff + this->st_value + relas->r_addend;
263 			break;
264 #if 1
265 		/* should not be supported ??? */
266 		case RELOC_REL24:
267 		  {
268 			Elf32_Addr val = ooff + this->st_value +
269 				relas->r_addend - (Elf32_Addr)r_addr;
270 			if ((val & 0xfe000000 != 0) &&
271 				(val & 0xfe000000 != 0xfe000000)) {
272 				/* invalid offset */
273 				_dl_exit(20);
274 			}
275 			val &= ~0xfc000003;
276 			val |=  (*r_addr & 0xfc000003);
277 			*r_addr = val;
278 
279 _dl_dcbf(r_addr);
280 		  }
281 		break;
282 #endif
283 #if 1
284 		case RELOC_16_LO:
285 		  {
286 			Elf32_Addr val;
287 
288 			val = loff + relas->r_addend;
289 			*(Elf32_Half *)r_addr = val;
290 
291 			_dl_dcbf(r_addr);
292 		  }
293 		break;
294 #endif
295 #if 1
296 		case RELOC_16_HI:
297 		  {
298 			Elf32_Addr val;
299 
300 			val = loff + relas->r_addend;
301 			*(Elf32_Half *)r_addr = (val >> 16);
302 
303 			_dl_dcbf(r_addr);
304 		  }
305 		break;
306 #endif
307 #if 1
308 		case RELOC_16_HA:
309 		  {
310 			Elf32_Addr val;
311 
312 			val = loff + relas->r_addend;
313 			*(Elf32_Half *)r_addr = ((val + 0x8000) >> 16);
314 
315 			_dl_dcbf(r_addr);
316 		  }
317 		break;
318 #endif
319 		case RELOC_REL14_TAKEN:
320 			/* val |= 1 << (31-10) XXX? */
321 		case RELOC_REL14:
322 		case RELOC_REL14_NTAKEN:
323 			{
324 			Elf32_Addr val = ooff + this->st_value +
325 				relas->r_addend - (Elf32_Addr)r_addr;
326 			if (((val & 0xffff8000) != 0) &&
327 				((val & 0xffff8000) != 0xffff8000))
328 			{
329 				/* invalid offset */
330 				_dl_exit(20);
331 			}
332 			val &= ~0xffff0003;
333 			val |=  (*r_addr & 0xffff0003);
334 			*r_addr = val;
335 #ifdef DL_PRINTF_DEBUG
336 			_dl_printf("rel 14 %x val %x\n",
337 				r_addr, val);
338 #endif
339 
340 			_dl_dcbf(r_addr);
341 			}
342 			break;
343 		case RELOC_COPY:
344 #ifdef DL_PRINTF_DEBUG
345 			_dl_printf("copy r_addr %x, sym %x [%s] size %d val %x\n",
346 				r_addr, sym, symn, sym->st_size,
347 				(ooff + this->st_value+
348 				relas->r_addend)
349 
350 				);
351 #endif
352 {
353 	/* we need to find a symbol, that is not in the current object,
354 	 * start looking at the beginning of the list, searching all objects
355 	 * but _not_ the current object, first one found wins.
356 	 */
357 	elf_object_t *cobj;
358 	const Elf32_Sym *cpysrc = NULL;
359 	Elf32_Addr src_loff;
360 	int size;
361 	for (cobj = _dl_objects;
362 		cobj != NULL && cpysrc == NULL;
363 		cobj = cobj->next)
364 	{
365 		if (object != cobj) {
366 
367 			/* only look in this object */
368 			src_loff = _dl_find_symbol(symn, cobj,
369 				&cpysrc, 1, 1);
370 		}
371 	}
372 	if (cpysrc == NULL) {
373 		_dl_printf("symbol not found [%s] \n", symn);
374 	} else {
375 		size  = sym->st_size;
376 		if (sym->st_size != cpysrc->st_size) {
377 			_dl_printf("symbols size differ [%s] \n", symn);
378 			size = sym->st_size < cpysrc->st_size ?
379 				sym->st_size : cpysrc->st_size;
380 		}
381 #ifdef DL_PRINTF_DEBUG
382 _dl_printf(" found other symbol at %x size %d\n",
383 		src_loff + cpysrc->st_value,  cpysrc->st_size);
384 #endif
385 		_dl_bcopy((void *)(src_loff + cpysrc->st_value),
386 			(void *)(ooff + this->st_value+ relas->r_addend),
387 			size);
388 	}
389 }
390 			break;
391 		case RELOC_NONE:
392 			break;
393 
394 		default:
395 			_dl_printf("%s:"
396 				" %s: unsupported relocation '%s' %d at %x\n",
397 					_dl_progname, object->load_name, symn,
398 					ELF32_R_TYPE(relas->r_info), r_addr );
399 			_dl_exit(1);
400 		}
401 	}
402 	object->status |= STAT_RELOC_DONE;
403 	load_list = object->load_list;
404 	while(load_list != NULL) {
405 		_dl_mprotect(load_list->start, load_list->size, load_list->prot);
406 		load_list = load_list->next;
407 	}
408 	return(fails);
409 }
410 
411 /*
412  *	Relocate the Global Offset Table (GOT). Currently we don't
413  *	do lazy evaluation here because the GNU linker doesn't
414  *	follow the ABI spec which says that if an external symbol
415  *	is referenced by other relocations than CALL16 and 26 it
416  *	should not be given a stub and have a zero value in the
417  *	symbol table. By not doing so, we can't use pointers to
418  *	external functions and use them in comparitions...
419  */
420 void
421 _dl_md_reloc_got(elf_object_t *object, int lazy)
422 {
423 	/* relocations all done via rela relocations above */
424 }
425 
426 /* should not be defined here, but is is 32 for all powerpc 603-G4 */
427 #define CACHELINESIZE 32
428 void
429 _dl_syncicache(char *from, size_t len)
430 {
431 	unsigned int off = 0;
432 	int l = len + ((int)from & (CACHELINESIZE-1));
433 
434 	while (off < l) {
435 		asm volatile ("dcbst %1,%0" :: "r"(from), "r"(off));
436 		asm volatile ("sync");
437 		asm volatile ("icbi %1, %0" :: "r"(from), "r"(off));
438 		asm volatile ("sync");
439 		asm volatile ("isync");
440 
441 		off += CACHELINESIZE;
442 	}
443 }
444