OSDN Git Service

Let ldso decide if it should relocate itselft a second time. This
[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         rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
400         plt = (Elf32_Word *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
401
402         /* Set up the lazy PLT entries.  */
403         offset = PLT_INITIAL_ENTRY_WORDS;
404         i = 0;
405         /* Warning: we don't handle double-sized PLT entries */
406         while (i < num_plt_entries) {
407                 plt[offset  ] = OPCODE_LI(11, i * 4);
408                 plt[offset+1] = OPCODE_B((PLT_TRAMPOLINE_ENTRY_WORDS + 2 - (offset+1)) * 4);
409                 i++;
410                 offset += 2;
411         }
412         /* Now, we've modified code.  We need to write the changes from
413            the data cache to a second-level unified cache, then make
414            sure that stale data in the instruction cache is removed.
415            (In a multiprocessor system, the effect is more complex.)
416            Most of the PLT shouldn't be in the instruction cache, but
417            there may be a little overlap at the start and the end.
418
419            Assumes that dcbst and icbi apply to lines of 16 bytes or
420            more.  Current known line sizes are 16, 32, and 128 bytes.  */
421         for (i = 0; i < rel_offset_words; i += 4)
422                 PPC_DCBST (plt + i);
423         PPC_DCBST (plt + rel_offset_words - 1);
424         PPC_SYNC;
425         PPC_ICBI (plt);
426         PPC_ICBI (plt + rel_offset_words - 1);
427         PPC_ISYNC;
428 }
429
430 static inline int
431 _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
432           unsigned long rel_addr, unsigned long rel_size,
433           int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope,
434                             ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
435 {
436         unsigned int i;
437         char *strtab;
438         Elf32_Sym *symtab;
439         ELF_RELOC *rpnt;
440         int symtab_index;
441
442         /* Now parse the relocation information */
443         rpnt = (ELF_RELOC *)(intptr_t) (rel_addr + tpnt->loadaddr);
444         rel_size = rel_size / sizeof(ELF_RELOC);
445
446         symtab = (Elf32_Sym *)(intptr_t) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
447         strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
448
449           for (i = 0; i < rel_size; i++, rpnt++) {
450                 int res;
451
452                 symtab_index = ELF32_R_SYM(rpnt->r_info);
453
454 #if defined (__SUPPORT_LD_DEBUG__)
455                 debug_sym(symtab,strtab,symtab_index);
456                 debug_reloc(symtab,strtab,rpnt);
457 #endif
458
459                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
460
461                 if (res==0) continue;
462
463                 _dl_dprintf(2, "\n%s: ",_dl_progname);
464
465                 if (symtab_index)
466                   _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
467
468                 if (unlikely(res <0))
469                 {
470                         int reloc_type = ELF32_R_TYPE(rpnt->r_info);
471 #if defined (__SUPPORT_LD_DEBUG__)
472                         _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
473 #else
474                         _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
475 #endif
476                         _dl_exit(-res);
477                 }
478                 if (unlikely(res >0))
479                 {
480                         _dl_dprintf(2, "can't resolve symbol\n");
481                         return res;
482                 }
483           }
484           return 0;
485 }
486
487 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
488         unsigned long rel_addr, unsigned long rel_size, int type)
489 {
490         return _dl_parse(rpnt->dyn, rpnt, rel_addr, rel_size, _dl_do_reloc);
491 }
492
493 /* Should be a static inline instead, but that conflicts with ld_elf.h */
494 int _dl_parse_copy_information(struct dyn_elf *rpnt,
495         unsigned long rel_addr, unsigned long rel_size, int type)
496 {
497         /* Not used! */
498         return 0;
499 }
500