OSDN Git Service

Add RTLD_LOCAL support for dlopened libs. Reported by
[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, tpnt, 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         finaladdr += this_reloc->r_addend;
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         symbol_addr = tpnt->loadaddr; /* For R_PPC_RELATIVE */ 
269         symtab_index = ELF32_R_SYM(rpnt->r_info);
270         symname      = strtab + symtab[symtab_index].st_name;
271         if (symtab_index) {
272                 symbol_addr = (unsigned long) _dl_find_hash(symname, scope, tpnt,
273                                                             elf_machine_type_class(reloc_type));
274                 /* We want to allow undefined references to weak symbols - this might
275                  * have been intentional.  We should not be linking local symbols
276                  * here, so all bases should be covered.
277                  */
278                 if (unlikely(!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK)) {
279                         _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname);
280                         _dl_exit(1);
281                 };
282         }
283 #if defined (__SUPPORT_LD_DEBUG__)
284         old_val = *reloc_addr;
285 #endif
286         finaladdr = (Elf32_Addr) (symbol_addr + rpnt->r_addend);
287
288         switch (reloc_type) {
289         case R_PPC_RELATIVE:
290         case R_PPC_ADDR32:
291         case R_PPC_GLOB_DAT:
292                 *reloc_addr = finaladdr;
293                 goto out_nocode; /* No code code modified */
294         case R_PPC_JMP_SLOT:
295         {
296                 Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
297                 if (delta<<6>>6 == delta) {
298                         *reloc_addr = OPCODE_B(delta);
299                 } else if (finaladdr <= 0x01fffffc) {
300                         *reloc_addr = OPCODE_BA (finaladdr);
301                 } else {
302                         /* Warning: we don't handle double-sized PLT entries */
303                         Elf32_Word *plt, *data_words, index, offset;
304
305                         plt = (Elf32_Word *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
306                         offset = reloc_addr - plt;
307                         index = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
308                         data_words = (Elf32_Word *)tpnt->data_words;
309
310                         data_words[index] = finaladdr;
311                         reloc_addr[0] = OPCODE_LI(11,index*4);
312                         reloc_addr[1] = OPCODE_B((PLT_LONGBRANCH_ENTRY_WORDS - (offset+1)) * 4);
313
314                         /* instructions were modified */
315                         PPC_DCBST(reloc_addr+1);
316                         PPC_SYNC;
317                         PPC_ICBI(reloc_addr+1);
318                 }
319                 break;
320         }
321         case R_PPC_COPY:
322 #if defined (__SUPPORT_LD_DEBUG__)
323                 if(_dl_debug_move)
324                         _dl_dprintf(_dl_debug_file,"\n%s move %x bytes from %x to %x",
325                                     symname, symtab[symtab_index].st_size,
326                                     symbol_addr, reloc_addr);
327 #endif
328                 _dl_memcpy((char *) reloc_addr, (char *) finaladdr, symtab[symtab_index].st_size);
329                 goto out_nocode; /* No code code modified */
330         case R_PPC_ADDR16_HA:
331                 finaladdr += 0x8000; /* fall through. */
332         case R_PPC_ADDR16_HI:
333                 finaladdr >>= 16; /* fall through. */
334         case R_PPC_ADDR16_LO:
335                 *(short *)reloc_addr = finaladdr;
336                 break;
337         case R_PPC_REL24:
338 #if 0
339                 {
340                         Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
341                         if(unlikely(delta<<6>>6 != delta)) {
342                                 _dl_dprintf(2, "%s: symbol '%s' R_PPC_REL24 is out of range.\n\t"
343                                                 "Compile shared libraries with -fPIC!\n",
344                                                 _dl_progname, symname);
345                                 _dl_exit(1);
346                         }
347                         *reloc_addr = (*reloc_addr & 0xfc000003) | (delta & 0x3fffffc);
348                         break;
349                 }
350 #else
351                 _dl_dprintf(2,"R_PPC_REL24: Compile shared libraries with -fPIC!\n");
352                 _dl_exit(1);
353 #endif
354         case R_PPC_NONE:
355                 goto out_nocode; /* No code code modified */
356         default:
357                 _dl_dprintf(2, "%s: can't handle reloc type ", _dl_progname);
358 #if defined (__SUPPORT_LD_DEBUG__)
359                 _dl_dprintf(2, "%s ", _dl_reltypes(reloc_type));
360 #endif
361                 if (symtab_index)
362                         _dl_dprintf(2, "'%s'\n", symname);
363                 return -1;
364         };
365
366         /* instructions were modified */
367         PPC_DCBST(reloc_addr);
368         PPC_SYNC;
369         PPC_ICBI(reloc_addr);
370         PPC_ISYNC;
371  out_nocode:
372 #if defined (__SUPPORT_LD_DEBUG__)
373         if(_dl_debug_reloc && _dl_debug_detail)
374                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, *reloc_addr, reloc_addr);
375 #endif
376         return 0;
377 }
378
379 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
380         unsigned long rel_addr, unsigned long rel_size)
381 {
382         struct elf_resolve *tpnt = rpnt->dyn;
383         Elf32_Word *plt, offset, i,  num_plt_entries, rel_offset_words;
384
385         num_plt_entries = rel_size / sizeof(ELF_RELOC);
386
387         rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
388         plt = (Elf32_Word *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
389
390         /* Set up the lazy PLT entries.  */
391         offset = PLT_INITIAL_ENTRY_WORDS;
392         i = 0;
393         /* Warning: we don't handle double-sized PLT entries */
394         while (i < num_plt_entries) {
395                 plt[offset  ] = OPCODE_LI(11, i * 4);
396                 plt[offset+1] = OPCODE_B((PLT_TRAMPOLINE_ENTRY_WORDS + 2 - (offset+1)) * 4);
397                 i++;
398                 offset += 2;
399         }
400         /* Now, we've modified code.  We need to write the changes from
401            the data cache to a second-level unified cache, then make
402            sure that stale data in the instruction cache is removed.
403            (In a multiprocessor system, the effect is more complex.)
404            Most of the PLT shouldn't be in the instruction cache, but
405            there may be a little overlap at the start and the end.
406
407            Assumes that dcbst and icbi apply to lines of 16 bytes or
408            more.  Current known line sizes are 16, 32, and 128 bytes.  */
409         for (i = 0; i < rel_offset_words; i += 4)
410                 PPC_DCBST (plt + i);
411         PPC_DCBST (plt + rel_offset_words - 1);
412         PPC_SYNC;
413         PPC_ICBI (plt);
414         PPC_ICBI (plt + rel_offset_words - 1);
415         PPC_ISYNC;
416 }
417
418 static inline int
419 _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
420           unsigned long rel_addr, unsigned long rel_size,
421           int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope,
422                             ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
423 {
424         unsigned int i;
425         char *strtab;
426         Elf32_Sym *symtab;
427         ELF_RELOC *rpnt;
428         int symtab_index;
429
430         /* Now parse the relocation information */
431         rpnt = (ELF_RELOC *)(intptr_t) (rel_addr + tpnt->loadaddr);
432         rel_size = rel_size / sizeof(ELF_RELOC);
433
434         symtab = (Elf32_Sym *)(intptr_t) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
435         strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
436
437           for (i = 0; i < rel_size; i++, rpnt++) {
438                 int res;
439
440                 symtab_index = ELF32_R_SYM(rpnt->r_info);
441
442 #if defined (__SUPPORT_LD_DEBUG__)
443                 debug_sym(symtab,strtab,symtab_index);
444                 debug_reloc(symtab,strtab,rpnt);
445 #endif
446
447                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
448
449                 if (res==0) continue;
450
451                 _dl_dprintf(2, "\n%s: ",_dl_progname);
452
453                 if (symtab_index)
454                   _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
455
456                 if (unlikely(res <0))
457                 {
458                         int reloc_type = ELF32_R_TYPE(rpnt->r_info);
459 #if defined (__SUPPORT_LD_DEBUG__)
460                         _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
461 #else
462                         _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
463 #endif
464                         _dl_exit(-res);
465                 }
466                 if (unlikely(res >0))
467                 {
468                         _dl_dprintf(2, "can't resolve symbol\n");
469                         return res;
470                 }
471           }
472           return 0;
473 }
474
475 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
476         unsigned long rel_addr, unsigned long rel_size)
477 {
478         return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
479 }