1 /* vi: set sw=4 ts=4: */
2 /* powerpc shared library loader suppport
3  *
4  * Copyright (C) 2001-2002 David A. Schleef
5  * Copyright (C) 2003-2004 Erik Andersen
6  * Copyright (C) 2004 Joakim Tjernlund
7  *
8  * All rights reserved.
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. The name of the above contributors may not be
16  *    used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include "ldso.h"
33 #define TLS_DTV_OFFSET 0x8000
34 #define TLS_TP_OFFSET 0x7000
35 
36 extern int _dl_linux_resolve(void);
37 
_dl_init_got(unsigned long * plt,struct elf_resolve * tpnt)38 void _dl_init_got(unsigned long *plt,struct elf_resolve *tpnt)
39 {
40 	Elf32_Word *tramp;
41 	Elf32_Word num_plt_entries;
42 	Elf32_Word data_words;
43 	Elf32_Word rel_offset_words;
44 	Elf32_Word dlrr = (Elf32_Word) _dl_linux_resolve;
45 
46 	if (tpnt->dynamic_info[DT_JMPREL] == 0)
47 		return;
48 	if (tpnt->dynamic_info[DT_PPC_GOT_IDX] != 0) {
49 		tpnt->dynamic_info[DT_PPC_GOT_IDX] += tpnt->loadaddr;
50 		return;
51 	}
52 	num_plt_entries = tpnt->dynamic_info[DT_PLTRELSZ] / sizeof(ELF_RELOC);
53 	rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
54 	data_words = (Elf32_Word) (plt + rel_offset_words);
55 	tpnt->data_words = data_words;
56 
57 	plt[PLT_LONGBRANCH_ENTRY_WORDS] = OPCODE_ADDIS_HI(11, 11, data_words);
58 	plt[PLT_LONGBRANCH_ENTRY_WORDS+1] = OPCODE_LWZ(11,data_words,11);
59 
60 	plt[PLT_LONGBRANCH_ENTRY_WORDS+2] = OPCODE_MTCTR(11);
61 	plt[PLT_LONGBRANCH_ENTRY_WORDS+3] = OPCODE_BCTR();
62 
63 	/* [4] */
64 	/* [5] */
65 	tramp = (Elf32_Word *) (plt + PLT_TRAMPOLINE_ENTRY_WORDS);
66 
67 	/* For the long entries, subtract off data_words.  */
68 	tramp[0] = OPCODE_ADDIS_HI(11,11,-data_words);
69 	tramp[1] = OPCODE_ADDI(11,11,-data_words);
70 
71 	/* Multiply index of entry by 3 (in r11).  */
72 	tramp[2] = OPCODE_SLWI(12,11,1);
73 	tramp[3] = OPCODE_ADD(11,12,11);
74 	if (dlrr <= 0x01fffffc || dlrr >= 0xfe000000) {
75 		/* Load address of link map in r12.  */
76 		tramp[4] = OPCODE_LI (12, (Elf32_Word) tpnt);
77 		tramp[5] = OPCODE_ADDIS_HI (12, 12, (Elf32_Word) tpnt);
78 
79 		/* Call _dl_linux_resolve .  */
80 		tramp[6] = OPCODE_BA (dlrr);
81 	} else {
82 		/* Get address of _dl_linux_resolve in CTR.  */
83 		tramp[4] = OPCODE_LI(12,dlrr);
84 		tramp[5] = OPCODE_ADDIS_HI(12,12,dlrr);
85 		tramp[6] = OPCODE_MTCTR(12);
86 
87 		/* Load address of link map in r12.  */
88 		tramp[7] = OPCODE_LI(12,(Elf32_Word) tpnt);
89 		tramp[8] = OPCODE_ADDIS_HI(12,12,(Elf32_Word) tpnt);
90 
91 		/* Call _dl_linux_resolve.  */
92 		tramp[9] = OPCODE_BCTR();
93 	}
94 	/* [16] unused */
95 	/* [17] unused */
96 
97 	PPC_DCBST(plt);
98 	PPC_DCBST(plt+4);
99 	PPC_DCBST(plt+8);
100 	PPC_DCBST(plt+12);
101 	PPC_DCBST(plt+16-1);
102 	PPC_SYNC;
103 	PPC_ICBI(plt);
104 	PPC_ICBI(plt+16-1);
105 	PPC_ISYNC;
106 }
107 
_dl_linux_resolver(struct elf_resolve * tpnt,int reloc_entry)108 unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
109 {
110 	ELF_RELOC *this_reloc;
111 	char *strtab;
112 	ElfW(Sym) *symtab;
113 	ELF_RELOC *rel_addr;
114 	int symtab_index;
115 	char *symname;
116 	ElfW(Addr) *reloc_addr;
117 	ElfW(Addr)  finaladdr;
118 	Elf32_Sword delta;
119 
120 	rel_addr = (ELF_RELOC *)tpnt->dynamic_info[DT_JMPREL];
121 
122 	this_reloc = (void *)rel_addr + reloc_entry;
123 	symtab_index = ELF_R_SYM(this_reloc->r_info);
124 
125 	symtab = (ElfW(Sym) *)tpnt->dynamic_info[DT_SYMTAB];
126 	strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
127 	symname = strtab + symtab[symtab_index].st_name;
128 
129 	debug_sym(symtab,strtab,symtab_index);
130 	debug_reloc(symtab,strtab,this_reloc);
131 
132 	/* Address of dump instruction to fix up */
133 	reloc_addr = (ElfW(Addr) *) (tpnt->loadaddr + this_reloc->r_offset);
134 
135 #if defined (__SUPPORT_LD_DEBUG__)
136 	if (_dl_debug_reloc && _dl_debug_detail)
137 		_dl_dprintf(_dl_debug_file, "\n\tResolving symbol %s %x --> ", symname, (ElfW(Addr))reloc_addr);
138 #endif
139 
140 	/* Get the address of the GOT entry */
141 	finaladdr = (ElfW(Addr)) _dl_find_hash(symname,
142 			&_dl_loaded_modules->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT, NULL);
143 	if (unlikely(!finaladdr)) {
144 		_dl_dprintf(2, "%s: can't resolve symbol '%s' in lib '%s'.\n", _dl_progname, symname, tpnt->libname);
145 		_dl_exit(1);
146 	}
147 	finaladdr += this_reloc->r_addend;
148 #if defined (__SUPPORT_LD_DEBUG__)
149 	if (_dl_debug_reloc && _dl_debug_detail)
150 		_dl_dprintf(_dl_debug_file, "%x\n", finaladdr);
151 #endif
152 	if (tpnt->dynamic_info[DT_PPC_GOT_IDX] != 0) {
153 		*reloc_addr = finaladdr;
154 	} else {
155 		delta = finaladdr - (Elf32_Word)reloc_addr;
156 		if (delta<<6>>6 == delta) {
157 			*reloc_addr = OPCODE_B(delta);
158 		} else if (finaladdr <= 0x01fffffc) {
159 			*reloc_addr = OPCODE_BA (finaladdr);
160 		} else {
161 			/* Warning: we don't handle double-sized PLT entries */
162 			Elf32_Word *plt, *data_words, idx, offset;
163 
164 			plt = (Elf32_Word *)tpnt->dynamic_info[DT_PLTGOT];
165 			offset = reloc_addr - plt;
166 			idx = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
167 			data_words = (Elf32_Word *)tpnt->data_words;
168 			reloc_addr += 1;
169 
170 			data_words[idx] = finaladdr;
171 			PPC_SYNC;
172 			*reloc_addr =  OPCODE_B ((PLT_LONGBRANCH_ENTRY_WORDS - (offset+1)) * 4);
173 		}
174 
175 		/* instructions were modified */
176 		PPC_DCBST(reloc_addr);
177 		PPC_SYNC;
178 		PPC_ICBI(reloc_addr);
179 		PPC_ISYNC;
180 	}
181 	return finaladdr;
182 }
183 
184 static __inline__ int
_dl_do_reloc(struct elf_resolve * tpnt,struct r_scope_elem * scope,ELF_RELOC * rpnt,ElfW (Sym)* symtab,char * strtab)185 _dl_do_reloc (struct elf_resolve *tpnt,struct r_scope_elem *scope,
186 	      ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)
187 {
188 	int reloc_type;
189 	int symtab_index;
190 	struct symbol_ref sym_ref;
191 	ElfW(Addr) *reloc_addr;
192 	ElfW(Addr) finaladdr;
193 	struct elf_resolve *tls_tpnt = NULL;
194 	unsigned long symbol_addr;
195 	char *symname;
196 #if defined (__SUPPORT_LD_DEBUG__)
197 	unsigned long old_val;
198 #endif
199 
200 	symbol_addr  = tpnt->loadaddr; /* For R_PPC_RELATIVE */
201 	reloc_addr   = (ElfW(Addr) *)(intptr_t) (symbol_addr + (unsigned long) rpnt->r_offset);
202 	reloc_type   = ELF_R_TYPE(rpnt->r_info);
203 	symtab_index = ELF_R_SYM(rpnt->r_info);
204 	sym_ref.sym  = &symtab[symtab_index];
205 	sym_ref.tpnt = NULL;
206 	symname      = strtab + sym_ref.sym->st_name;
207 	if (symtab_index) {
208 		symbol_addr = (unsigned long) _dl_find_hash(symname, scope, tpnt,
209 							    elf_machine_type_class(reloc_type),  &sym_ref);
210 		/* We want to allow undefined references to weak symbols - this might
211 		 * have been intentional.  We should not be linking local symbols
212 		 * here, so all bases should be covered.
213 		 */
214 		if (unlikely(!symbol_addr
215 			&& (ELF_ST_TYPE(sym_ref.sym->st_info) != STT_TLS
216 				&& ELF_ST_BIND(sym_ref.sym->st_info) != STB_WEAK)))
217 			return 1;
218 		if (_dl_trace_prelink) {
219 			_dl_debug_lookup (symname, tpnt, &symtab[symtab_index],
220 						&sym_ref, elf_machine_type_class(reloc_type));
221 		}
222 		tls_tpnt = sym_ref.tpnt;
223 	} else {
224 		symbol_addr = sym_ref.sym->st_value;
225 		tls_tpnt = tpnt;
226 	}
227 #if defined (__SUPPORT_LD_DEBUG__)
228 	old_val = *reloc_addr;
229 #endif
230 	finaladdr = (ElfW(Addr)) (symbol_addr + rpnt->r_addend);
231 
232 	switch (reloc_type) {
233 	case R_PPC_RELATIVE:
234 	case R_PPC_ADDR32:
235 	case R_PPC_GLOB_DAT:
236 		*reloc_addr = finaladdr;
237 		goto out_nocode; /* No code modified */
238 	case R_PPC_JMP_SLOT:
239 	{
240 		if (tpnt->dynamic_info[DT_PPC_GOT_IDX] != 0) {
241 			*reloc_addr = finaladdr;
242 			goto out_nocode; /* No code modified */
243 		} else {
244 			Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
245 			if (delta<<6>>6 == delta) {
246 				*reloc_addr = OPCODE_B(delta);
247 			} else if (finaladdr <= 0x01fffffc) {
248 				*reloc_addr = OPCODE_BA (finaladdr);
249 			} else {
250 				/* Warning: we don't handle double-sized PLT entries */
251 				Elf32_Word *plt, *data_words, idx, offset;
252 
253 				plt = (Elf32_Word *)tpnt->dynamic_info[DT_PLTGOT];
254 				offset = reloc_addr - plt;
255 				idx = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
256 				data_words = (Elf32_Word *)tpnt->data_words;
257 
258 				data_words[idx] = finaladdr;
259 				reloc_addr[0] = OPCODE_LI(11,idx*4);
260 				reloc_addr[1] = OPCODE_B((PLT_LONGBRANCH_ENTRY_WORDS - (offset+1)) * 4);
261 
262 				/* instructions were modified */
263 				PPC_DCBST(reloc_addr+1);
264 				PPC_SYNC;
265 				PPC_ICBI(reloc_addr+1);
266 			}
267 		}
268 		break;
269 	}
270 	case R_PPC_COPY:
271 #if defined (__SUPPORT_LD_DEBUG__)
272 		if (_dl_debug_move)
273 			_dl_dprintf(_dl_debug_file,"\n%s move %x bytes from %x to %x",
274 				    symname, sym_ref.sym->st_size,
275 				    symbol_addr, reloc_addr);
276 #endif
277 		_dl_memcpy((char *) reloc_addr, (char *) finaladdr, sym_ref.sym->st_size);
278 		goto out_nocode; /* No code modified */
279 	case R_PPC_ADDR16_HA:
280 		finaladdr += 0x8000; /* fall through. */
281 	case R_PPC_ADDR16_HI:
282 		finaladdr >>= 16; /* fall through. */
283 	case R_PPC_ADDR16_LO:
284 		*(short *)reloc_addr = finaladdr;
285 		break;
286 #if USE_TLS
287 	case R_PPC_DTPMOD32:
288 		*reloc_addr = tls_tpnt->l_tls_modid;
289 		break;
290 	case R_PPC_DTPREL32:
291 		/* During relocation all TLS symbols are defined and used.
292 		   Therefore the offset is already correct.  */
293 		*reloc_addr = finaladdr - TLS_DTV_OFFSET;
294 		break;
295 	case R_PPC_TPREL32:
296 		*reloc_addr = tls_tpnt->l_tls_offset + finaladdr - TLS_TP_OFFSET;
297 		break;
298 #endif
299 	case R_PPC_REL24:
300 #if 0
301 		{
302 			Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
303 			if (unlikely(delta<<6>>6 != delta)) {
304 				_dl_dprintf(2, "%s: symbol '%s' R_PPC_REL24 is out of range.\n\t"
305 						"Compile shared libraries with -fPIC!\n",
306 						_dl_progname, symname);
307 				_dl_exit(1);
308 			}
309 			*reloc_addr = (*reloc_addr & 0xfc000003) | (delta & 0x3fffffc);
310 			break;
311 		}
312 #else
313 		_dl_dprintf(2,"R_PPC_REL24: Compile shared libraries with -fPIC!\n");
314 		return -1;
315 #endif
316 	case R_PPC_NONE:
317 		goto out_nocode; /* No code modified */
318 	default:
319 		_dl_dprintf(2, "%s: can't handle reloc type ", _dl_progname);
320 #if defined (__SUPPORT_LD_DEBUG__)
321 		_dl_dprintf(2, "%s ", _dl_reltypes(reloc_type));
322 #endif
323 		if (symtab_index)
324 			_dl_dprintf(2, "'%s'\n", symname);
325 		return -1;
326 	}
327 
328 	/* instructions were modified */
329 	PPC_DCBST(reloc_addr);
330 	PPC_SYNC;
331 	PPC_ICBI(reloc_addr);
332 	PPC_ISYNC;
333  out_nocode:
334 #if defined (__SUPPORT_LD_DEBUG__)
335 	if (_dl_debug_reloc && _dl_debug_detail)
336 		_dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", old_val, *reloc_addr, reloc_addr);
337 #endif
338 	return 0;
339 }
340 
_dl_parse_lazy_relocation_information(struct dyn_elf * rpnt,unsigned long rel_addr,unsigned long rel_size)341 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
342 	unsigned long rel_addr, unsigned long rel_size)
343 {
344 	struct elf_resolve *tpnt = rpnt->dyn;
345 	Elf32_Word *plt, offset, i,  num_plt_entries, rel_offset_words;
346 
347 	num_plt_entries = rel_size / sizeof(ELF_RELOC);
348 	plt = (Elf32_Word *)tpnt->dynamic_info[DT_PLTGOT];
349 	if (tpnt->dynamic_info[DT_PPC_GOT_IDX] != 0) {
350 		/* Secure PLT */
351 		ElfW(Addr) *got = (ElfW(Addr) *)tpnt->dynamic_info[DT_PPC_GOT_IDX];
352 		Elf32_Word dlrr = (Elf32_Word) _dl_linux_resolve;
353 
354 		got[1] = (ElfW(Addr)) dlrr;
355 		got[2] = (ElfW(Addr)) tpnt;
356 
357 		/* Relocate everything in .plt by the load address offset.  */
358 		while (num_plt_entries-- != 0)
359 			*plt++ += tpnt->loadaddr;
360 		return;
361 	}
362 
363 	rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
364 
365 	/* Set up the lazy PLT entries.  */
366 	offset = PLT_INITIAL_ENTRY_WORDS;
367 	i = 0;
368 	/* Warning: we don't handle double-sized PLT entries */
369 	while (i < num_plt_entries) {
370 		plt[offset  ] = OPCODE_LI(11, i * 4);
371 		plt[offset+1] = OPCODE_B((PLT_TRAMPOLINE_ENTRY_WORDS + 2 - (offset+1)) * 4);
372 		i++;
373 		offset += 2;
374 	}
375 	/* Now, we've modified code.  We need to write the changes from
376 	   the data cache to a second-level unified cache, then make
377 	   sure that stale data in the instruction cache is removed.
378 	   (In a multiprocessor system, the effect is more complex.)
379 	   Most of the PLT shouldn't be in the instruction cache, but
380 	   there may be a little overlap at the start and the end.
381 
382 	   Assumes that dcbst and icbi apply to lines of 16 bytes or
383 	   more.  Current known line sizes are 16, 32, and 128 bytes.  */
384 	for (i = 0; i < rel_offset_words; i += 4)
385 		PPC_DCBST (plt + i);
386 	PPC_DCBST (plt + rel_offset_words - 1);
387 	PPC_SYNC;
388 	PPC_ICBI (plt);
389 	PPC_ICBI (plt + rel_offset_words - 1);
390 	PPC_ISYNC;
391 }
392 
393 static __inline__ int
_dl_parse(struct elf_resolve * tpnt,struct r_scope_elem * scope,unsigned long rel_addr,unsigned long rel_size,int (* reloc_fnc)(struct elf_resolve * tpnt,struct r_scope_elem * scope,ELF_RELOC * rpnt,ElfW (Sym)* symtab,char * strtab))394 _dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope,
395 	  unsigned long rel_addr, unsigned long rel_size,
396 	  int (*reloc_fnc) (struct elf_resolve *tpnt, struct r_scope_elem *scope,
397 			    ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab))
398 {
399 	unsigned int i;
400 	char *strtab;
401 	ElfW(Sym) *symtab;
402 	ELF_RELOC *rpnt;
403 	int symtab_index;
404 
405 	/* Now parse the relocation information */
406 	rpnt = (ELF_RELOC *)(intptr_t)rel_addr;
407 	rel_size = rel_size / sizeof(ELF_RELOC);
408 
409 	symtab = (ElfW(Sym) *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
410 	strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
411 
412 	  for (i = 0; i < rel_size; i++, rpnt++) {
413 	        int res;
414 
415 		symtab_index = ELF_R_SYM(rpnt->r_info);
416 
417 		debug_sym(symtab,strtab,symtab_index);
418 		debug_reloc(symtab,strtab,rpnt);
419 
420 		res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
421 
422 		if (res==0) continue;
423 
424 		_dl_dprintf(2, "\n%s: ",_dl_progname);
425 
426 		if (symtab_index)
427 		  _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
428 
429 		if (unlikely(res <0))
430 		{
431 		        int reloc_type = ELF_R_TYPE(rpnt->r_info);
432 #if defined (__SUPPORT_LD_DEBUG__)
433 			_dl_dprintf(2, "can't handle reloc type '%s' in lib '%s'\n", _dl_reltypes(reloc_type), tpnt->libname);
434 #else
435 			_dl_dprintf(2, "can't handle reloc type %x in lib '%s'\n", reloc_type, tpnt->libname);
436 #endif
437 			return res;
438 		}
439 		if (unlikely(res >0))
440 		{
441 			_dl_dprintf(2, "can't resolve symbol in lib '%s'.\n", tpnt->libname);
442 			return res;
443 		}
444 	  }
445 	  return 0;
446 }
447 
_dl_parse_relocation_information(struct dyn_elf * rpnt,struct r_scope_elem * scope,unsigned long rel_addr,unsigned long rel_size)448 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
449 	struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size)
450 {
451 	return _dl_parse(rpnt->dyn, scope, rel_addr, rel_size, _dl_do_reloc);
452 }
453 
454 static __always_inline void
elf_machine_setup(ElfW (Addr)load_off,unsigned long const * dynamic_info,struct elf_resolve * tpnt,int lazy)455 elf_machine_setup(ElfW(Addr) load_off, unsigned long const *dynamic_info,
456                   struct elf_resolve *tpnt, int lazy)
457 {
458 	(void) load_off;
459 	(void) lazy;
460 	unsigned long *lpnt = (unsigned long *) dynamic_info[DT_PLTGOT];
461 #ifdef ALLOW_ZERO_PLTGOT
462 	if (lpnt)
463 #endif
464 		INIT_GOT(lpnt, tpnt);
465 }
466 
467