OSDN Git Service

Merge remote branch 'origin/master' into prelink
[uclinux-h8/uClibc.git] / ldso / ldso / arm / elfinterp.c
1 /* vi: set sw=4 ts=4: */
2 /* ARM ELF shared library loader suppport
3  *
4  * Copyright (C) 2001-2004 Erik Andersen
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. The name of the above contributors may not be
14  *    used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 /* Program to load an ELF binary on a linux system, and run it.
31    References to symbols in sharable libraries can be resolved by either
32    an ELF sharable library or a linux style of shared library. */
33
34 /* Disclaimer:  I have never seen any AT&T source code for SVr4, nor have
35    I ever taken any courses on internals.  This program was developed using
36    information available through the book "UNIX SYSTEM V RELEASE 4,
37    Programmers guide: Ansi C and Programming Support Tools", which did
38    a more than adequate job of explaining everything required to get this
39    working. */
40
41 #include "ldso.h"
42
43 extern int _dl_linux_resolve(void);
44
45 unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
46 {
47         ELF_RELOC *this_reloc;
48         char *strtab;
49         char *symname;
50         Elf32_Sym *symtab;
51         ELF_RELOC *rel_addr;
52         int symtab_index;
53         unsigned long new_addr;
54         char **got_addr;
55         unsigned long instr_addr;
56
57         rel_addr = (ELF_RELOC *) tpnt->dynamic_info[DT_JMPREL];
58
59         this_reloc = rel_addr + reloc_entry;
60         symtab_index = ELF32_R_SYM(this_reloc->r_info);
61
62         symtab = (Elf32_Sym *) tpnt->dynamic_info[DT_SYMTAB];
63         strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
64         symname = strtab + symtab[symtab_index].st_name;
65
66         /* Address of jump instruction to fix up */
67         instr_addr = ((unsigned long) this_reloc->r_offset +
68                 (unsigned long) tpnt->loadaddr);
69         got_addr = (char **) instr_addr;
70
71         /* Get the address of the GOT entry */
72         new_addr = _dl_find_hash(symname, &_dl_loaded_modules->symbol_scope,
73                                  tpnt, ELF_RTYPE_CLASS_PLT, NULL);
74         if (unlikely(!new_addr)) {
75                 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n",
76                         _dl_progname, symname);
77                 _dl_exit(1);
78         }
79 #if defined (__SUPPORT_LD_DEBUG__)
80 #if !defined __SUPPORT_LD_DEBUG_EARLY__
81         if ((unsigned long) got_addr < 0x40000000)
82 #endif
83         {
84                 if (_dl_debug_bindings)
85                 {
86                         _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
87                         if (_dl_debug_detail) _dl_dprintf(_dl_debug_file,
88                                         "\tpatch %x ==> %x @ %x", *got_addr, new_addr, got_addr);
89                 }
90         }
91         if (!_dl_debug_nofixups) {
92                 *got_addr = (char *)new_addr;
93         }
94 #else
95         *got_addr = (char *)new_addr;
96 #endif
97
98         return new_addr;
99 }
100
101 static int
102 _dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope,
103           unsigned long rel_addr, unsigned long rel_size,
104           int (*reloc_fnc) (struct elf_resolve *tpnt, struct r_scope_elem *scope,
105                             ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
106 {
107         int i;
108         char *strtab;
109         int goof = 0;
110         Elf32_Sym *symtab;
111         ELF_RELOC *rpnt;
112         int symtab_index;
113
114         /* Now parse the relocation information */
115         rpnt = (ELF_RELOC *) rel_addr;
116         rel_size = rel_size / sizeof(ELF_RELOC);
117
118         symtab = (Elf32_Sym *) tpnt->dynamic_info[DT_SYMTAB];
119         strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
120
121           for (i = 0; i < rel_size; i++, rpnt++) {
122                 int res;
123
124                 symtab_index = ELF32_R_SYM(rpnt->r_info);
125
126                 debug_sym(symtab,strtab,symtab_index);
127                 debug_reloc(symtab,strtab,rpnt);
128
129                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
130
131                 if (res==0) continue;
132
133                 _dl_dprintf(2, "\n%s: ",_dl_progname);
134
135                 if (symtab_index)
136                   _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
137
138                 if (unlikely(res <0))
139                 {
140                         int reloc_type = ELF32_R_TYPE(rpnt->r_info);
141 #if defined (__SUPPORT_LD_DEBUG__)
142                         _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
143 #else
144                         _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
145 #endif
146                         _dl_exit(-res);
147                 }
148                 if (unlikely(res >0))
149                 {
150                         _dl_dprintf(2, "can't resolve symbol\n");
151                         goof += res;
152                 }
153           }
154           return goof;
155 }
156
157 #if 0
158 static unsigned long
159 fix_bad_pc24 (unsigned long *const reloc_addr, unsigned long value)
160 {
161   static void *fix_page;
162   static unsigned int fix_offset;
163   unsigned int *fix_address;
164   if (! fix_page)
165     {
166       fix_page = _dl_mmap (NULL,  PAGE_SIZE   , PROT_READ | PROT_WRITE | PROT_EXEC,
167                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
168       fix_offset = 0;
169     }
170
171   fix_address = (unsigned int *)(fix_page + fix_offset);
172   fix_address[0] = 0xe51ff004;  /* ldr pc, [pc, #-4] */
173   fix_address[1] = value;
174
175   fix_offset += 8;
176   if (fix_offset >= PAGE_SIZE)
177     fix_page = NULL;
178
179   return (unsigned long)fix_address;
180 }
181 #endif
182
183 static int
184 _dl_do_reloc (struct elf_resolve *tpnt,struct r_scope_elem *scope,
185               ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
186 {
187         int reloc_type;
188         int symtab_index;
189         unsigned long *reloc_addr;
190         unsigned long symbol_addr;
191         const Elf32_Sym *def = 0;
192         struct symbol_ref sym_ref;
193         struct elf_resolve *def_mod = 0;
194         int goof = 0;
195
196         reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
197
198         reloc_type = ELF32_R_TYPE(rpnt->r_info);
199         symtab_index = ELF32_R_SYM(rpnt->r_info);
200         symbol_addr = 0;
201         sym_ref.sym = &symtab[symtab_index];
202         sym_ref.tpnt = NULL;
203
204         if (symtab_index) {
205                 symbol_addr = _dl_find_hash(strtab + symtab[symtab_index].st_name,
206                         scope, tpnt, elf_machine_type_class(reloc_type), &sym_ref);
207
208                 /*
209                  * We want to allow undefined references to weak symbols - this might
210                  * have been intentional.  We should not be linking local symbols
211                  * here, so all bases should be covered.
212                  */
213                 if (!symbol_addr && (ELF_ST_TYPE(symtab[symtab_index].st_info) != STT_TLS)
214                         && (ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK)) {
215                         /* This may be non-fatal if called from dlopen.  */
216                         return 1;
217
218                 }
219                 if (_dl_trace_prelink)
220                         _dl_debug_lookup (symname, tpnt, &symtab[symtab_index],
221                                         &sym_ref, elf_machine_type_class(reloc_type));
222                 def_mod = sym_ref.tpnt;
223         } else {
224                 /*
225                  * Relocs against STN_UNDEF are usually treated as using a
226                  * symbol value of zero, and using the module containing the
227                  * reloc itself.
228                  */
229                 symbol_addr = symtab[symtab_index].st_value;
230                 def_mod = tpnt;
231         }
232
233 #if defined (__SUPPORT_LD_DEBUG__)
234         {
235                 unsigned long old_val = *reloc_addr;
236 #endif
237                 switch (reloc_type) {
238                         case R_ARM_NONE:
239                                 break;
240                         case R_ARM_ABS32:
241                                 *reloc_addr += symbol_addr;
242                                 break;
243                         case R_ARM_PC24:
244 #if 0
245                                 {
246                                         unsigned long addend;
247                                         long newvalue, topbits;
248
249                                         addend = *reloc_addr & 0x00ffffff;
250                                         if (addend & 0x00800000) addend |= 0xff000000;
251
252                                         newvalue = symbol_addr - (unsigned long)reloc_addr + (addend << 2);
253                                         topbits = newvalue & 0xfe000000;
254                                         if (topbits != 0xfe000000 && topbits != 0x00000000)
255                                         {
256                                                 newvalue = fix_bad_pc24(reloc_addr, symbol_addr)
257                                                         - (unsigned long)reloc_addr + (addend << 2);
258                                                 topbits = newvalue & 0xfe000000;
259                                                 if (unlikely(topbits != 0xfe000000 && topbits != 0x00000000))
260                                                 {
261                                                         _dl_dprintf(2,"symbol '%s': R_ARM_PC24 relocation out of range.",
262                                                                 symtab[symtab_index].st_name);
263                                                         _dl_exit(1);
264                                                 }
265                                         }
266                                         newvalue >>= 2;
267                                         symbol_addr = (*reloc_addr & 0xff000000) | (newvalue & 0x00ffffff);
268                                         *reloc_addr = symbol_addr;
269                                         break;
270                                 }
271 #else
272                                 _dl_dprintf(2,"R_ARM_PC24: Compile shared libraries with -fPIC!\n");
273                                 _dl_exit(1);
274 #endif
275                         case R_ARM_GLOB_DAT:
276                         case R_ARM_JUMP_SLOT:
277                                 *reloc_addr = symbol_addr;
278                                 break;
279                         case R_ARM_RELATIVE:
280                                 *reloc_addr += (unsigned long) tpnt->loadaddr;
281                                 break;
282                         case R_ARM_COPY:
283                                 _dl_memcpy((void *) reloc_addr,
284                                            (void *) symbol_addr, symtab[symtab_index].st_size);
285                                 break;
286 #if defined USE_TLS && USE_TLS
287                         case R_ARM_TLS_DTPMOD32:
288                                 *reloc_addr = def_mod->l_tls_modid;
289                                 break;
290
291                         case R_ARM_TLS_DTPOFF32:
292                                 *reloc_addr += symbol_addr;
293                                 break;
294
295                         case R_ARM_TLS_TPOFF32:
296                                 CHECK_STATIC_TLS ((struct link_map *) def_mod);
297                                 *reloc_addr += (symbol_addr + def_mod->l_tls_offset);
298                                 break;
299 #endif
300                         default:
301                                 return -1; /*call _dl_exit(1) */
302                 }
303 #if defined (__SUPPORT_LD_DEBUG__)
304                 if (_dl_debug_reloc && _dl_debug_detail)
305                         _dl_dprintf(_dl_debug_file, "\tpatch: %x ==> %x @ %x", old_val, *reloc_addr, reloc_addr);
306         }
307
308 #endif
309
310         return goof;
311 }
312
313 static int
314 _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
315                    ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
316 {
317         int reloc_type;
318         unsigned long *reloc_addr;
319
320         reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
321         reloc_type = ELF32_R_TYPE(rpnt->r_info);
322
323 #if defined (__SUPPORT_LD_DEBUG__)
324         {
325                 unsigned long old_val = *reloc_addr;
326 #endif
327                 switch (reloc_type) {
328                         case R_ARM_NONE:
329                                 break;
330                         case R_ARM_JUMP_SLOT:
331                                 *reloc_addr += (unsigned long) tpnt->loadaddr;
332                                 break;
333                         default:
334                                 return -1; /*call _dl_exit(1) */
335                 }
336 #if defined (__SUPPORT_LD_DEBUG__)
337                 if (_dl_debug_reloc && _dl_debug_detail)
338                         _dl_dprintf(_dl_debug_file, "\tpatch: %x ==> %x @ %x", old_val, *reloc_addr, reloc_addr);
339         }
340
341 #endif
342         return 0;
343
344 }
345
346 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
347         unsigned long rel_addr, unsigned long rel_size)
348 {
349         (void)_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
350 }
351
352 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
353         struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size)
354 {
355         return _dl_parse(rpnt->dyn, scope, rel_addr, rel_size, _dl_do_reloc);
356 }
357