OSDN Git Service

Give gcc branch prediction some hits on obviously unlikely branches
[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 #if defined (__SUPPORT_LD_DEBUG__)
33 static const char *_dl_reltypes_tab[] =
34         { "R_PPC_NONE", "R_PPC_ADDR32", "R_PPC_ADDR24", "R_PPC_ADDR16",
35         "R_PPC_ADDR16_LO", "R_PPC_ADDR16_HI", "R_PPC_ADDR16_HA",
36         "R_PPC_ADDR14", "R_PPC_ADDR14_BRTAKEN", "R_PPC_ADDR14_BRNTAKEN",
37         "R_PPC_REL24", "R_PPC_REL14", "R_PPC_REL14_BRTAKEN",
38         "R_PPC_REL14_BRNTAKEN", "R_PPC_GOT16", "R_PPC_GOT16_LO",
39         "R_PPC_GOT16_HI", "R_PPC_GOT16_HA", "R_PPC_PLTREL24",
40         "R_PPC_COPY", "R_PPC_GLOB_DAT", "R_PPC_JMP_SLOT", "R_PPC_RELATIVE",
41         "R_PPC_LOCAL24PC", "R_PPC_UADDR32", "R_PPC_UADDR16", "R_PPC_REL32",
42         "R_PPC_PLT32", "R_PPC_PLTREL32", "R_PPC_PLT16_LO", "R_PPC_PLT16_HI",
43         "R_PPC_PLT16_HA", "R_PPC_SDAREL16", "R_PPC_SECTOFF",
44         "R_PPC_SECTOFF_LO", "R_PPC_SECTOFF_HI", "R_PPC_SECTOFF_HA",
45 };
46
47 static const char *
48 _dl_reltypes(int type)
49 {
50   static char buf[22];
51   const char *str;
52
53   if (type >= (int)(sizeof (_dl_reltypes_tab)/sizeof(_dl_reltypes_tab[0])) ||
54       NULL == (str = _dl_reltypes_tab[type]))
55   {
56     str =_dl_simple_ltoa( buf, (unsigned long)(type));
57   }
58   return str;
59 }
60
61 static
62 void debug_sym(Elf32_Sym *symtab,char *strtab,int symtab_index)
63 {
64   if(_dl_debug_symbols)
65   {
66     if(symtab_index){
67       _dl_dprintf(_dl_debug_file, "\n%s\n\tvalue=%x\tsize=%x\tinfo=%x\tother=%x\tshndx=%x",
68                   strtab + symtab[symtab_index].st_name,
69                   symtab[symtab_index].st_value,
70                   symtab[symtab_index].st_size,
71                   symtab[symtab_index].st_info,
72                   symtab[symtab_index].st_other,
73                   symtab[symtab_index].st_shndx);
74     }
75   }
76 }
77
78 static
79 void debug_reloc(Elf32_Sym *symtab,char *strtab, ELF_RELOC *rpnt)
80 {
81   if(_dl_debug_reloc)
82   {
83     int symtab_index;
84     const char *sym;
85     symtab_index = ELF32_R_SYM(rpnt->r_info);
86     sym = symtab_index ? strtab + symtab[symtab_index].st_name : "sym=0x0";
87
88   if(_dl_debug_symbols)
89           _dl_dprintf(_dl_debug_file, "\n\t");
90   else
91           _dl_dprintf(_dl_debug_file, "\n%s\n\t", sym);
92 #ifdef ELF_USES_RELOCA
93     _dl_dprintf(_dl_debug_file, "%s\toffset=%x\taddend=%x",
94                 _dl_reltypes(ELF32_R_TYPE(rpnt->r_info)),
95                 rpnt->r_offset,
96                 rpnt->r_addend);
97 #else
98     _dl_dprintf(_dl_debug_file, "%s\toffset=%x\n",
99                 _dl_reltypes(ELF32_R_TYPE(rpnt->r_info)),
100                 rpnt->r_offset);
101 #endif
102   }
103 }
104 #endif
105
106 extern int _dl_linux_resolve(void);
107
108 void _dl_init_got(unsigned long *plt,struct elf_resolve *tpnt)
109 {
110         Elf32_Word *tramp;
111         Elf32_Word num_plt_entries;
112         Elf32_Word data_words;
113         Elf32_Word rel_offset_words;
114         Elf32_Word dlrr = (Elf32_Word) _dl_linux_resolve;
115
116         num_plt_entries = tpnt->dynamic_info[DT_PLTRELSZ] / sizeof(ELF_RELOC);
117         rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
118         data_words = (Elf32_Word) (plt + rel_offset_words);
119         tpnt->data_words = data_words;
120
121         plt[PLT_LONGBRANCH_ENTRY_WORDS] = OPCODE_ADDIS_HI(11, 11, data_words);
122         plt[PLT_LONGBRANCH_ENTRY_WORDS+1] = OPCODE_LWZ(11,data_words,11);
123
124         plt[PLT_LONGBRANCH_ENTRY_WORDS+2] = OPCODE_MTCTR(11);
125         plt[PLT_LONGBRANCH_ENTRY_WORDS+3] = OPCODE_BCTR();
126
127         /* [4] */
128         /* [5] */
129         tramp = (Elf32_Word *) (plt + PLT_TRAMPOLINE_ENTRY_WORDS);
130
131         /* For the long entries, subtract off data_words.  */
132         tramp[0] = OPCODE_ADDIS_HI(11,11,-data_words);
133         tramp[1] = OPCODE_ADDI(11,11,-data_words);
134
135         /* Multiply index of entry by 3 (in r11).  */
136         tramp[2] = OPCODE_SLWI(12,11,1);
137         tramp[3] = OPCODE_ADD(11,12,11);
138         if (dlrr <= 0x01fffffc || dlrr >= 0xfe000000) {
139                 /* Load address of link map in r12.  */
140                 tramp[4] = OPCODE_LI (12, (Elf32_Word) tpnt);
141                 tramp[5] = OPCODE_ADDIS_HI (12, 12, (Elf32_Word) tpnt);
142
143                 /* Call _dl_linux_resolve .  */
144                 tramp[6] = OPCODE_BA (dlrr);
145         } else {
146                 /* Get address of _dl_linux_resolve in CTR.  */
147                 tramp[4] = OPCODE_LI(12,dlrr);
148                 tramp[5] = OPCODE_ADDIS_HI(12,12,dlrr);
149                 tramp[6] = OPCODE_MTCTR(12);
150
151                 /* Load address of link map in r12.  */
152                 tramp[7] = OPCODE_LI(12,(Elf32_Word) tpnt);
153                 tramp[8] = OPCODE_ADDIS_HI(12,12,(Elf32_Word) tpnt);
154
155                 /* Call _dl_linux_resolve.  */
156                 tramp[9] = OPCODE_BCTR();
157         }
158         /* [16] unused */
159         /* [17] unused */
160
161         PPC_DCBST(plt);
162         PPC_DCBST(plt+4);
163         PPC_DCBST(plt+8);
164         PPC_DCBST(plt+12);
165         PPC_DCBST(plt+16-1);
166         PPC_SYNC;
167         PPC_ICBI(plt);
168         PPC_ICBI(plt+16-1);
169         PPC_ISYNC;
170 }
171
172 unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
173 {
174         ELF_RELOC *this_reloc;
175         char *strtab;
176         Elf32_Sym *symtab;
177         ELF_RELOC *rel_addr;
178         int symtab_index;
179         char *symname;
180         Elf32_Addr *reloc_addr;
181         Elf32_Addr  finaladdr;
182         Elf32_Sword delta;
183
184         rel_addr = (ELF_RELOC *) (tpnt->dynamic_info[DT_JMPREL] + tpnt->loadaddr);
185
186         this_reloc = (void *)rel_addr + reloc_entry;
187         symtab_index = ELF32_R_SYM(this_reloc->r_info);
188
189         symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
190         strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
191         symname      = strtab + symtab[symtab_index].st_name;
192
193 #if defined (__SUPPORT_LD_DEBUG__)
194         debug_sym(symtab,strtab,symtab_index);
195         debug_reloc(symtab,strtab,this_reloc);
196
197         if (unlikely(ELF32_R_TYPE(this_reloc->r_info) != R_PPC_JMP_SLOT)) {
198                 _dl_dprintf(2, "%s: Incorrect relocation type in jump relocation\n", _dl_progname);
199                 _dl_exit(1);
200         };
201 #endif
202
203         /* Address of dump instruction to fix up */
204         reloc_addr = (Elf32_Addr *) (tpnt->loadaddr + this_reloc->r_offset);
205
206 #if defined (__SUPPORT_LD_DEBUG__)
207         if(_dl_debug_reloc && _dl_debug_detail)
208                 _dl_dprintf(_dl_debug_file, "\n\tResolving symbol %s %x --> ", symname, (Elf32_Addr)reloc_addr);
209 #endif
210
211         /* Get the address of the GOT entry */
212         finaladdr = (Elf32_Addr) _dl_find_hash(symname,
213                         tpnt->symbol_scope, ELF_RTYPE_CLASS_PLT);
214         if (unlikely(!finaladdr)) {
215                 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname);
216                 _dl_exit(1);
217         };
218
219 #if defined (__SUPPORT_LD_DEBUG__)
220         if(_dl_debug_reloc && _dl_debug_detail)
221                 _dl_dprintf(_dl_debug_file, "%x\n", finaladdr);
222 #endif
223         delta = finaladdr - (Elf32_Word)reloc_addr;
224         if (delta<<6>>6 == delta) {
225                 *reloc_addr = OPCODE_B(delta);
226         } else if (finaladdr <= 0x01fffffc) {
227                 *reloc_addr = OPCODE_BA (finaladdr);
228         } else {
229                 /* Warning: we don't handle double-sized PLT entries */
230                 Elf32_Word *plt, *data_words, index, offset;
231
232                 plt = (Elf32_Word *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
233                 offset = reloc_addr - plt;
234                 index = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
235                 data_words = (Elf32_Word *)tpnt->data_words;
236                 reloc_addr += 1;
237
238                 data_words[index] = finaladdr;
239                 PPC_SYNC;
240                 *reloc_addr =  OPCODE_B ((PLT_LONGBRANCH_ENTRY_WORDS - (offset+1)) * 4);
241         }
242
243         /* instructions were modified */
244         PPC_DCBST(reloc_addr);
245         PPC_SYNC;
246         PPC_ICBI(reloc_addr);
247         PPC_ISYNC;
248
249         return finaladdr;
250 }
251
252 static inline int
253 _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
254               ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
255 {
256         int reloc_type;
257         int symtab_index;
258         char *symname;
259         Elf32_Addr *reloc_addr;
260         Elf32_Addr finaladdr;
261
262         unsigned long symbol_addr;
263 #if defined (__SUPPORT_LD_DEBUG__)
264         unsigned long old_val;
265 #endif
266         reloc_addr   = (Elf32_Addr *)(intptr_t) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
267         reloc_type   = ELF32_R_TYPE(rpnt->r_info);
268         if (reloc_type == R_PPC_RELATIVE) {
269                 *reloc_addr = tpnt->loadaddr + rpnt->r_addend;
270                 return 0;
271         }
272         if (reloc_type == R_PPC_NONE)
273                 return 0;
274         symtab_index = ELF32_R_SYM(rpnt->r_info);
275         symname      = strtab + symtab[symtab_index].st_name;
276
277         symbol_addr = (unsigned long) _dl_find_hash(symname, scope->dyn->symbol_scope,
278                                                     elf_machine_type_class(reloc_type));
279         /*
280          * We want to allow undefined references to weak symbols - this might
281          * have been intentional.  We should not be linking local symbols
282          * here, so all bases should be covered.
283          */
284         if (!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) == STB_GLOBAL) {
285 #if defined (__SUPPORT_LD_DEBUG__)
286                 _dl_dprintf(2, "\tglobal symbol '%s' already defined in '%s', rel type: %s\n",
287                             symname, tpnt->libname, _dl_reltypes(reloc_type));
288 #endif
289                 return 0;
290         }
291 #if defined (__SUPPORT_LD_DEBUG__)
292         old_val = *reloc_addr;
293 #endif
294         finaladdr = (Elf32_Addr) (symbol_addr + rpnt->r_addend);
295
296         switch (reloc_type) {
297         case R_PPC_ADDR32:
298         case R_PPC_GLOB_DAT:
299                 *reloc_addr = finaladdr;
300                 return 0; /* No code code modified */
301                 break;
302         case R_PPC_JMP_SLOT:
303         {
304                 Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
305
306                 if (delta<<6>>6 == delta) {
307                         *reloc_addr = OPCODE_B(delta);
308                 } else if (finaladdr <= 0x01fffffc) {
309                         *reloc_addr = OPCODE_BA (finaladdr);
310                 } else {
311                         /* Warning: we don't handle double-sized PLT entries */
312                         Elf32_Word *plt, *data_words, index, offset;
313
314                         plt = (Elf32_Word *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
315                         offset = reloc_addr - plt;
316                         index = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
317                         data_words = (Elf32_Word *)tpnt->data_words;
318
319                         data_words[index] = finaladdr;
320                         reloc_addr[0] = OPCODE_LI(11,index*4);
321                         reloc_addr[1] = OPCODE_B((PLT_LONGBRANCH_ENTRY_WORDS - (offset+1)) * 4);
322
323                         /* instructions were modified */
324                         PPC_DCBST(reloc_addr+1);
325                         PPC_SYNC;
326                         PPC_ICBI(reloc_addr+1);
327                 }
328                 break;
329         }
330         case R_PPC_COPY:
331                 if (symbol_addr) {
332 #if defined (__SUPPORT_LD_DEBUG__)
333                         if(_dl_debug_move)
334                                 _dl_dprintf(_dl_debug_file,"\n%s move %x bytes from %x to %x",
335                                             symname, symtab[symtab_index].st_size,
336                                             symbol_addr, symtab[symtab_index].st_value);
337 #endif
338                         _dl_memcpy((char *) reloc_addr, (char *) finaladdr, symtab[symtab_index].st_size);
339                 }
340                 return 0; /* No code code modified */
341                 break;
342         case R_PPC_ADDR16_HA:
343                 *(short *)reloc_addr = (finaladdr + 0x8000)>>16;
344                 break;
345         case R_PPC_ADDR16_HI:
346                 *(short *)reloc_addr = finaladdr >> 16;
347                 break;
348         case R_PPC_ADDR16_LO:
349                 *(short *)reloc_addr = finaladdr;
350                 break;
351         case R_PPC_REL24:
352         {
353                 Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
354                 if(unlikely(delta<<6>>6 != delta)) {
355                         _dl_dprintf(2, "%s: symbol '%s' R_PPC_REL24 is out of range.\n\t"
356                                         "Compile shared libraries with -fPIC!\n",
357                                     _dl_progname, symname);
358                         _dl_exit(1);
359                 }
360                 *reloc_addr = (*reloc_addr & 0xfc000003) | (delta & 0x3fffffc);
361                 break;
362         }
363         default:
364                 _dl_dprintf(2, "%s: can't handle reloc type ", _dl_progname);
365 #if defined (__SUPPORT_LD_DEBUG__)
366                 _dl_dprintf(2, "%s ", _dl_reltypes(reloc_type));
367 #endif
368                 if (symtab_index)
369                         _dl_dprintf(2, "'%s'\n", symname);
370                 return -1;
371         };
372
373         /* instructions were modified */
374         PPC_DCBST(reloc_addr);
375         PPC_SYNC;
376         PPC_ICBI(reloc_addr);
377         PPC_ISYNC;
378 #if defined (__SUPPORT_LD_DEBUG__)
379         if(_dl_debug_reloc && _dl_debug_detail)
380                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, *reloc_addr, reloc_addr);
381 #endif
382         return 0;
383 }
384
385 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
386         unsigned long rel_addr, unsigned long rel_size, int type)
387 {
388         struct elf_resolve *tpnt = rpnt->dyn;
389         Elf32_Word *plt, offset, i,  num_plt_entries, rel_offset_words;
390
391         (void) type;
392         num_plt_entries = rel_size / sizeof(ELF_RELOC);
393
394         /* When the dynamic linker bootstrapped itself, it resolved some symbols.
395            Make sure we do not do them again */
396         if (tpnt->libtype == program_interpreter)
397                 return;
398         rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
399         plt = (Elf32_Word *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
400
401         /* Set up the lazy PLT entries.  */
402         offset = PLT_INITIAL_ENTRY_WORDS;
403         i = 0;
404         /* Warning: we don't handle double-sized PLT entries */
405         while (i < num_plt_entries) {
406                 plt[offset  ] = OPCODE_LI(11, i * 4);
407                 plt[offset+1] = OPCODE_B((PLT_TRAMPOLINE_ENTRY_WORDS + 2 - (offset+1)) * 4);
408                 i++;
409                 offset += 2;
410         }
411         /* Now, we've modified code.  We need to write the changes from
412            the data cache to a second-level unified cache, then make
413            sure that stale data in the instruction cache is removed.
414            (In a multiprocessor system, the effect is more complex.)
415            Most of the PLT shouldn't be in the instruction cache, but
416            there may be a little overlap at the start and the end.
417
418            Assumes that dcbst and icbi apply to lines of 16 bytes or
419            more.  Current known line sizes are 16, 32, and 128 bytes.  */
420         for (i = 0; i < rel_offset_words; i += 4)
421                 PPC_DCBST (plt + i);
422         PPC_DCBST (plt + rel_offset_words - 1);
423         PPC_SYNC;
424         PPC_ICBI (plt);
425         PPC_ICBI (plt + rel_offset_words - 1);
426         PPC_ISYNC;
427 }
428
429 static inline int
430 _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
431           unsigned long rel_addr, unsigned long rel_size,
432           int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope,
433                             ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
434 {
435         unsigned int i;
436         char *strtab;
437         Elf32_Sym *symtab;
438         ELF_RELOC *rpnt;
439         int symtab_index;
440
441         /* Now parse the relocation information */
442         rpnt = (ELF_RELOC *)(intptr_t) (rel_addr + tpnt->loadaddr);
443         rel_size = rel_size / sizeof(ELF_RELOC);
444
445         symtab = (Elf32_Sym *)(intptr_t) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
446         strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
447
448           for (i = 0; i < rel_size; i++, rpnt++) {
449                 int res;
450
451                 symtab_index = ELF32_R_SYM(rpnt->r_info);
452
453                 /* When the dynamic linker bootstrapped itself, it resolved some symbols.
454                    Make sure we do not do them again */
455                 if (!symtab_index && tpnt->libtype == program_interpreter)
456                         continue;
457                 if (symtab_index && tpnt->libtype == program_interpreter &&
458                     _dl_symbol(strtab + symtab[symtab_index].st_name))
459                         continue;
460
461 #if defined (__SUPPORT_LD_DEBUG__)
462                 debug_sym(symtab,strtab,symtab_index);
463                 debug_reloc(symtab,strtab,rpnt);
464 #endif
465
466                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
467
468                 if (res==0) continue;
469
470                 _dl_dprintf(2, "\n%s: ",_dl_progname);
471
472                 if (symtab_index)
473                   _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
474
475                 if (unlikely(res <0))
476                 {
477                         int reloc_type = ELF32_R_TYPE(rpnt->r_info);
478 #if defined (__SUPPORT_LD_DEBUG__)
479                         _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
480 #else
481                         _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
482 #endif
483                         _dl_exit(-res);
484                 }
485                 if (unlikely(res >0))
486                 {
487                         _dl_dprintf(2, "can't resolve symbol\n");
488                         return res;
489                 }
490           }
491           return 0;
492 }
493
494 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
495         unsigned long rel_addr, unsigned long rel_size, int type)
496 {
497         return _dl_parse(rpnt->dyn, rpnt, rel_addr, rel_size, _dl_do_reloc);
498 }
499
500 /* Should be a static inline instead, but that conflicts with ld_elf.h */
501 int _dl_parse_copy_information(struct dyn_elf *rpnt,
502         unsigned long rel_addr, unsigned long rel_size, int type)
503 {
504         /* Not used! */
505         return 0;
506 }
507