OSDN Git Service

rip out all the duplicated debug code and move into a shared file
[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 extern int _dl_linux_resolve(void);
33
34 void _dl_init_got(unsigned long *plt,struct elf_resolve *tpnt)
35 {
36         Elf32_Word *tramp;
37         Elf32_Word num_plt_entries;
38         Elf32_Word data_words;
39         Elf32_Word rel_offset_words;
40         Elf32_Word dlrr = (Elf32_Word) _dl_linux_resolve;
41
42         num_plt_entries = tpnt->dynamic_info[DT_PLTRELSZ] / sizeof(ELF_RELOC);
43         rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
44         data_words = (Elf32_Word) (plt + rel_offset_words);
45         tpnt->data_words = data_words;
46
47         plt[PLT_LONGBRANCH_ENTRY_WORDS] = OPCODE_ADDIS_HI(11, 11, data_words);
48         plt[PLT_LONGBRANCH_ENTRY_WORDS+1] = OPCODE_LWZ(11,data_words,11);
49
50         plt[PLT_LONGBRANCH_ENTRY_WORDS+2] = OPCODE_MTCTR(11);
51         plt[PLT_LONGBRANCH_ENTRY_WORDS+3] = OPCODE_BCTR();
52
53         /* [4] */
54         /* [5] */
55         tramp = (Elf32_Word *) (plt + PLT_TRAMPOLINE_ENTRY_WORDS);
56
57         /* For the long entries, subtract off data_words.  */
58         tramp[0] = OPCODE_ADDIS_HI(11,11,-data_words);
59         tramp[1] = OPCODE_ADDI(11,11,-data_words);
60
61         /* Multiply index of entry by 3 (in r11).  */
62         tramp[2] = OPCODE_SLWI(12,11,1);
63         tramp[3] = OPCODE_ADD(11,12,11);
64         if (dlrr <= 0x01fffffc || dlrr >= 0xfe000000) {
65                 /* Load address of link map in r12.  */
66                 tramp[4] = OPCODE_LI (12, (Elf32_Word) tpnt);
67                 tramp[5] = OPCODE_ADDIS_HI (12, 12, (Elf32_Word) tpnt);
68
69                 /* Call _dl_linux_resolve .  */
70                 tramp[6] = OPCODE_BA (dlrr);
71         } else {
72                 /* Get address of _dl_linux_resolve in CTR.  */
73                 tramp[4] = OPCODE_LI(12,dlrr);
74                 tramp[5] = OPCODE_ADDIS_HI(12,12,dlrr);
75                 tramp[6] = OPCODE_MTCTR(12);
76
77                 /* Load address of link map in r12.  */
78                 tramp[7] = OPCODE_LI(12,(Elf32_Word) tpnt);
79                 tramp[8] = OPCODE_ADDIS_HI(12,12,(Elf32_Word) tpnt);
80
81                 /* Call _dl_linux_resolve.  */
82                 tramp[9] = OPCODE_BCTR();
83         }
84         /* [16] unused */
85         /* [17] unused */
86
87         PPC_DCBST(plt);
88         PPC_DCBST(plt+4);
89         PPC_DCBST(plt+8);
90         PPC_DCBST(plt+12);
91         PPC_DCBST(plt+16-1);
92         PPC_SYNC;
93         PPC_ICBI(plt);
94         PPC_ICBI(plt+16-1);
95         PPC_ISYNC;
96 }
97
98 unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
99 {
100         ELF_RELOC *this_reloc;
101         char *strtab;
102         Elf32_Sym *symtab;
103         ELF_RELOC *rel_addr;
104         int symtab_index;
105         char *symname;
106         Elf32_Addr *reloc_addr;
107         Elf32_Addr  finaladdr;
108         Elf32_Sword delta;
109
110         rel_addr = (ELF_RELOC *)tpnt->dynamic_info[DT_JMPREL];
111
112         this_reloc = (void *)rel_addr + reloc_entry;
113         symtab_index = ELF32_R_SYM(this_reloc->r_info);
114
115         symtab = (Elf32_Sym *)tpnt->dynamic_info[DT_SYMTAB];
116         strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
117         symname = strtab + symtab[symtab_index].st_name;
118
119         debug_sym(symtab,strtab,symtab_index);
120         debug_reloc(symtab,strtab,this_reloc);
121
122 #if defined (__SUPPORT_LD_DEBUG__)
123         if (unlikely(ELF32_R_TYPE(this_reloc->r_info) != R_PPC_JMP_SLOT)) {
124                 _dl_dprintf(2, "%s: Incorrect relocation type in jump relocation\n", _dl_progname);
125                 _dl_exit(1);
126         };
127 #endif
128
129         /* Address of dump instruction to fix up */
130         reloc_addr = (Elf32_Addr *) (tpnt->loadaddr + this_reloc->r_offset);
131
132 #if defined (__SUPPORT_LD_DEBUG__)
133         if(_dl_debug_reloc && _dl_debug_detail)
134                 _dl_dprintf(_dl_debug_file, "\n\tResolving symbol %s %x --> ", symname, (Elf32_Addr)reloc_addr);
135 #endif
136
137         /* Get the address of the GOT entry */
138         finaladdr = (Elf32_Addr) _dl_find_hash(symname,
139                         tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT);
140         if (unlikely(!finaladdr)) {
141                 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname);
142                 _dl_exit(1);
143         };
144         finaladdr += this_reloc->r_addend;
145 #if defined (__SUPPORT_LD_DEBUG__)
146         if(_dl_debug_reloc && _dl_debug_detail)
147                 _dl_dprintf(_dl_debug_file, "%x\n", finaladdr);
148 #endif
149         delta = finaladdr - (Elf32_Word)reloc_addr;
150         if (delta<<6>>6 == delta) {
151                 *reloc_addr = OPCODE_B(delta);
152         } else if (finaladdr <= 0x01fffffc) {
153                 *reloc_addr = OPCODE_BA (finaladdr);
154         } else {
155                 /* Warning: we don't handle double-sized PLT entries */
156                 Elf32_Word *plt, *data_words, index, offset;
157
158                 plt = (Elf32_Word *)tpnt->dynamic_info[DT_PLTGOT];
159                 offset = reloc_addr - plt;
160                 index = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
161                 data_words = (Elf32_Word *)tpnt->data_words;
162                 reloc_addr += 1;
163
164                 data_words[index] = finaladdr;
165                 PPC_SYNC;
166                 *reloc_addr =  OPCODE_B ((PLT_LONGBRANCH_ENTRY_WORDS - (offset+1)) * 4);
167         }
168
169         /* instructions were modified */
170         PPC_DCBST(reloc_addr);
171         PPC_SYNC;
172         PPC_ICBI(reloc_addr);
173         PPC_ISYNC;
174
175         return finaladdr;
176 }
177
178 static inline int
179 _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
180               ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
181 {
182         int reloc_type;
183         int symtab_index;
184         char *symname;
185         Elf32_Addr *reloc_addr;
186         Elf32_Addr finaladdr;
187
188         unsigned long symbol_addr;
189 #if defined (__SUPPORT_LD_DEBUG__)
190         unsigned long old_val;
191 #endif
192         reloc_addr   = (Elf32_Addr *)(intptr_t) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
193         reloc_type   = ELF32_R_TYPE(rpnt->r_info);
194         symbol_addr  = tpnt->loadaddr; /* For R_PPC_RELATIVE */ 
195         symtab_index = ELF32_R_SYM(rpnt->r_info);
196         symname      = strtab + symtab[symtab_index].st_name;
197         if (symtab_index) {
198                 symbol_addr = (unsigned long) _dl_find_hash(symname, scope, tpnt,
199                                                             elf_machine_type_class(reloc_type));
200                 /* We want to allow undefined references to weak symbols - this might
201                  * have been intentional.  We should not be linking local symbols
202                  * here, so all bases should be covered.
203                  */
204                 if (unlikely(!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK))
205                         return -1;
206         }
207 #if defined (__SUPPORT_LD_DEBUG__)
208         old_val = *reloc_addr;
209 #endif
210         finaladdr = (Elf32_Addr) (symbol_addr + rpnt->r_addend);
211
212         switch (reloc_type) {
213         case R_PPC_RELATIVE:
214         case R_PPC_ADDR32:
215         case R_PPC_GLOB_DAT:
216                 *reloc_addr = finaladdr;
217                 goto out_nocode; /* No code code modified */
218         case R_PPC_JMP_SLOT:
219         {
220                 Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
221                 if (delta<<6>>6 == delta) {
222                         *reloc_addr = OPCODE_B(delta);
223                 } else if (finaladdr <= 0x01fffffc) {
224                         *reloc_addr = OPCODE_BA (finaladdr);
225                 } else {
226                         /* Warning: we don't handle double-sized PLT entries */
227                         Elf32_Word *plt, *data_words, index, offset;
228
229                         plt = (Elf32_Word *)tpnt->dynamic_info[DT_PLTGOT];
230                         offset = reloc_addr - plt;
231                         index = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
232                         data_words = (Elf32_Word *)tpnt->data_words;
233
234                         data_words[index] = finaladdr;
235                         reloc_addr[0] = OPCODE_LI(11,index*4);
236                         reloc_addr[1] = OPCODE_B((PLT_LONGBRANCH_ENTRY_WORDS - (offset+1)) * 4);
237
238                         /* instructions were modified */
239                         PPC_DCBST(reloc_addr+1);
240                         PPC_SYNC;
241                         PPC_ICBI(reloc_addr+1);
242                 }
243                 break;
244         }
245         case R_PPC_COPY:
246 #if defined (__SUPPORT_LD_DEBUG__)
247                 if(_dl_debug_move)
248                         _dl_dprintf(_dl_debug_file,"\n%s move %x bytes from %x to %x",
249                                     symname, symtab[symtab_index].st_size,
250                                     symbol_addr, reloc_addr);
251 #endif
252                 _dl_memcpy((char *) reloc_addr, (char *) finaladdr, symtab[symtab_index].st_size);
253                 goto out_nocode; /* No code code modified */
254         case R_PPC_ADDR16_HA:
255                 finaladdr += 0x8000; /* fall through. */
256         case R_PPC_ADDR16_HI:
257                 finaladdr >>= 16; /* fall through. */
258         case R_PPC_ADDR16_LO:
259                 *(short *)reloc_addr = finaladdr;
260                 break;
261         case R_PPC_REL24:
262 #if 0
263                 {
264                         Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
265                         if(unlikely(delta<<6>>6 != delta)) {
266                                 _dl_dprintf(2, "%s: symbol '%s' R_PPC_REL24 is out of range.\n\t"
267                                                 "Compile shared libraries with -fPIC!\n",
268                                                 _dl_progname, symname);
269                                 _dl_exit(1);
270                         }
271                         *reloc_addr = (*reloc_addr & 0xfc000003) | (delta & 0x3fffffc);
272                         break;
273                 }
274 #else
275                 _dl_dprintf(2,"R_PPC_REL24: Compile shared libraries with -fPIC!\n");
276                 return -1;
277 #endif
278         case R_PPC_NONE:
279                 goto out_nocode; /* No code code modified */
280         default:
281                 _dl_dprintf(2, "%s: can't handle reloc type ", _dl_progname);
282 #if defined (__SUPPORT_LD_DEBUG__)
283                 _dl_dprintf(2, "%s ", _dl_reltypes(reloc_type));
284 #endif
285                 if (symtab_index)
286                         _dl_dprintf(2, "'%s'\n", symname);
287                 return -1;
288         };
289
290         /* instructions were modified */
291         PPC_DCBST(reloc_addr);
292         PPC_SYNC;
293         PPC_ICBI(reloc_addr);
294         PPC_ISYNC;
295  out_nocode:
296 #if defined (__SUPPORT_LD_DEBUG__)
297         if(_dl_debug_reloc && _dl_debug_detail)
298                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, *reloc_addr, reloc_addr);
299 #endif
300         return 0;
301 }
302
303 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
304         unsigned long rel_addr, unsigned long rel_size)
305 {
306         struct elf_resolve *tpnt = rpnt->dyn;
307         Elf32_Word *plt, offset, i,  num_plt_entries, rel_offset_words;
308
309         num_plt_entries = rel_size / sizeof(ELF_RELOC);
310
311         rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
312         plt = (Elf32_Word *)tpnt->dynamic_info[DT_PLTGOT];
313
314         /* Set up the lazy PLT entries.  */
315         offset = PLT_INITIAL_ENTRY_WORDS;
316         i = 0;
317         /* Warning: we don't handle double-sized PLT entries */
318         while (i < num_plt_entries) {
319                 plt[offset  ] = OPCODE_LI(11, i * 4);
320                 plt[offset+1] = OPCODE_B((PLT_TRAMPOLINE_ENTRY_WORDS + 2 - (offset+1)) * 4);
321                 i++;
322                 offset += 2;
323         }
324         /* Now, we've modified code.  We need to write the changes from
325            the data cache to a second-level unified cache, then make
326            sure that stale data in the instruction cache is removed.
327            (In a multiprocessor system, the effect is more complex.)
328            Most of the PLT shouldn't be in the instruction cache, but
329            there may be a little overlap at the start and the end.
330
331            Assumes that dcbst and icbi apply to lines of 16 bytes or
332            more.  Current known line sizes are 16, 32, and 128 bytes.  */
333         for (i = 0; i < rel_offset_words; i += 4)
334                 PPC_DCBST (plt + i);
335         PPC_DCBST (plt + rel_offset_words - 1);
336         PPC_SYNC;
337         PPC_ICBI (plt);
338         PPC_ICBI (plt + rel_offset_words - 1);
339         PPC_ISYNC;
340 }
341
342 static inline int
343 _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
344           unsigned long rel_addr, unsigned long rel_size,
345           int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope,
346                             ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
347 {
348         unsigned int i;
349         char *strtab;
350         Elf32_Sym *symtab;
351         ELF_RELOC *rpnt;
352         int symtab_index;
353
354         /* Now parse the relocation information */
355         rpnt = (ELF_RELOC *)(intptr_t)rel_addr;
356         rel_size = rel_size / sizeof(ELF_RELOC);
357
358         symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
359         strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
360         
361           for (i = 0; i < rel_size; i++, rpnt++) {
362                 int res;
363
364                 symtab_index = ELF32_R_SYM(rpnt->r_info);
365
366                 debug_sym(symtab,strtab,symtab_index);
367                 debug_reloc(symtab,strtab,rpnt);
368
369                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
370
371                 if (res==0) continue;
372
373                 _dl_dprintf(2, "\n%s: ",_dl_progname);
374
375                 if (symtab_index)
376                   _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
377
378                 if (unlikely(res <0))
379                 {
380                         int reloc_type = ELF32_R_TYPE(rpnt->r_info);
381 #if defined (__SUPPORT_LD_DEBUG__)
382                         _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
383 #else
384                         _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
385 #endif
386                         _dl_exit(-res);
387                 }
388                 if (unlikely(res >0))
389                 {
390                         _dl_dprintf(2, "can't resolve symbol\n");
391                         return res;
392                 }
393           }
394           return 0;
395 }
396
397 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
398         unsigned long rel_addr, unsigned long rel_size)
399 {
400         return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
401 }