OSDN Git Service

fe7afe54e3bdc3602e9be54d4efbd777b4946b79
[uclinux-h8/uClibc.git] / ldso / ldso / powerpc / elfinterp.c
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
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
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
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 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 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 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 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
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
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
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 }