OSDN Git Service

Joakim Tjernlund writes:
[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 #if 0
353                 {
354                         Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
355                         if(unlikely(delta<<6>>6 != delta)) {
356                                 _dl_dprintf(2, "%s: symbol '%s' R_PPC_REL24 is out of range.\n\t"
357                                                 "Compile shared libraries with -fPIC!\n",
358                                                 _dl_progname, symname);
359                                 _dl_exit(1);
360                         }
361                         *reloc_addr = (*reloc_addr & 0xfc000003) | (delta & 0x3fffffc);
362                         break;
363                 }
364 #else
365                 _dl_dprintf(2,"R_PPC_REL24: Compile shared libraries with -fPIC!\n");
366                 _dl_exit(1);
367 #endif
368         default:
369                 _dl_dprintf(2, "%s: can't handle reloc type ", _dl_progname);
370 #if defined (__SUPPORT_LD_DEBUG__)
371                 _dl_dprintf(2, "%s ", _dl_reltypes(reloc_type));
372 #endif
373                 if (symtab_index)
374                         _dl_dprintf(2, "'%s'\n", symname);
375                 return -1;
376         };
377
378         /* instructions were modified */
379         PPC_DCBST(reloc_addr);
380         PPC_SYNC;
381         PPC_ICBI(reloc_addr);
382         PPC_ISYNC;
383 #if defined (__SUPPORT_LD_DEBUG__)
384         if(_dl_debug_reloc && _dl_debug_detail)
385                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, *reloc_addr, reloc_addr);
386 #endif
387         return 0;
388 }
389
390 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
391         unsigned long rel_addr, unsigned long rel_size, int type)
392 {
393         struct elf_resolve *tpnt = rpnt->dyn;
394         Elf32_Word *plt, offset, i,  num_plt_entries, rel_offset_words;
395
396         (void) type;
397         num_plt_entries = rel_size / sizeof(ELF_RELOC);
398
399         /* When the dynamic linker bootstrapped itself, it resolved some symbols.
400            Make sure we do not do them again */
401         if (tpnt->libtype == program_interpreter)
402                 return;
403         rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
404         plt = (Elf32_Word *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
405
406         /* Set up the lazy PLT entries.  */
407         offset = PLT_INITIAL_ENTRY_WORDS;
408         i = 0;
409         /* Warning: we don't handle double-sized PLT entries */
410         while (i < num_plt_entries) {
411                 plt[offset  ] = OPCODE_LI(11, i * 4);
412                 plt[offset+1] = OPCODE_B((PLT_TRAMPOLINE_ENTRY_WORDS + 2 - (offset+1)) * 4);
413                 i++;
414                 offset += 2;
415         }
416         /* Now, we've modified code.  We need to write the changes from
417            the data cache to a second-level unified cache, then make
418            sure that stale data in the instruction cache is removed.
419            (In a multiprocessor system, the effect is more complex.)
420            Most of the PLT shouldn't be in the instruction cache, but
421            there may be a little overlap at the start and the end.
422
423            Assumes that dcbst and icbi apply to lines of 16 bytes or
424            more.  Current known line sizes are 16, 32, and 128 bytes.  */
425         for (i = 0; i < rel_offset_words; i += 4)
426                 PPC_DCBST (plt + i);
427         PPC_DCBST (plt + rel_offset_words - 1);
428         PPC_SYNC;
429         PPC_ICBI (plt);
430         PPC_ICBI (plt + rel_offset_words - 1);
431         PPC_ISYNC;
432 }
433
434 static inline int
435 _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
436           unsigned long rel_addr, unsigned long rel_size,
437           int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope,
438                             ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
439 {
440         unsigned int i;
441         char *strtab;
442         Elf32_Sym *symtab;
443         ELF_RELOC *rpnt;
444         int symtab_index;
445
446         /* When the dynamic linker bootstrapped itself, it resolved some symbols.
447            Make sure we do not do them again */
448         if (tpnt->libtype == program_interpreter)
449                 return 0;
450
451         /* Now parse the relocation information */
452         rpnt = (ELF_RELOC *)(intptr_t) (rel_addr + tpnt->loadaddr);
453         rel_size = rel_size / sizeof(ELF_RELOC);
454
455         symtab = (Elf32_Sym *)(intptr_t) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
456         strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
457
458           for (i = 0; i < rel_size; i++, rpnt++) {
459                 int res;
460
461                 symtab_index = ELF32_R_SYM(rpnt->r_info);
462
463 #if defined (__SUPPORT_LD_DEBUG__)
464                 debug_sym(symtab,strtab,symtab_index);
465                 debug_reloc(symtab,strtab,rpnt);
466 #endif
467
468                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
469
470                 if (res==0) continue;
471
472                 _dl_dprintf(2, "\n%s: ",_dl_progname);
473
474                 if (symtab_index)
475                   _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
476
477                 if (unlikely(res <0))
478                 {
479                         int reloc_type = ELF32_R_TYPE(rpnt->r_info);
480 #if defined (__SUPPORT_LD_DEBUG__)
481                         _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
482 #else
483                         _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
484 #endif
485                         _dl_exit(-res);
486                 }
487                 if (unlikely(res >0))
488                 {
489                         _dl_dprintf(2, "can't resolve symbol\n");
490                         return res;
491                 }
492           }
493           return 0;
494 }
495
496 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
497         unsigned long rel_addr, unsigned long rel_size, int type)
498 {
499         return _dl_parse(rpnt->dyn, rpnt, rel_addr, rel_size, _dl_do_reloc);
500 }
501
502 /* Should be a static inline instead, but that conflicts with ld_elf.h */
503 int _dl_parse_copy_information(struct dyn_elf *rpnt,
504         unsigned long rel_addr, unsigned long rel_size, int type)
505 {
506         /* Not used! */
507         return 0;
508 }
509