OSDN Git Service

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