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