]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/contrib/uclibc/ldso/ldso/arm/elfinterp.c
update
[l4.git] / l4 / pkg / uclibc / lib / contrib / uclibc / 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, tpnt->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 dyn_elf *scope,
103           unsigned long rel_addr, unsigned long rel_size,
104           int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *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 dyn_elf *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                 def_mod = sym_ref.tpnt;
220         } else {
221                 /*
222                  * Relocs against STN_UNDEF are usually treated as using a
223                  * symbol value of zero, and using the module containing the
224                  * reloc itself.
225                  */
226                 symbol_addr = symtab[symtab_index].st_value;
227                 def_mod = tpnt;
228         }
229
230 #if defined (__SUPPORT_LD_DEBUG__)
231         {
232                 unsigned long old_val = *reloc_addr;
233 #endif
234                 switch (reloc_type) {
235                         case R_ARM_NONE:
236                                 break;
237                         case R_ARM_ABS32:
238                                 *reloc_addr += symbol_addr;
239                                 break;
240                         case R_ARM_PC24:
241 #if 0
242                                 {
243                                         unsigned long addend;
244                                         long newvalue, topbits;
245
246                                         addend = *reloc_addr & 0x00ffffff;
247                                         if (addend & 0x00800000) addend |= 0xff000000;
248
249                                         newvalue = symbol_addr - (unsigned long)reloc_addr + (addend << 2);
250                                         topbits = newvalue & 0xfe000000;
251                                         if (topbits != 0xfe000000 && topbits != 0x00000000)
252                                         {
253                                                 newvalue = fix_bad_pc24(reloc_addr, symbol_addr)
254                                                         - (unsigned long)reloc_addr + (addend << 2);
255                                                 topbits = newvalue & 0xfe000000;
256                                                 if (unlikely(topbits != 0xfe000000 && topbits != 0x00000000))
257                                                 {
258                                                         _dl_dprintf(2,"symbol '%s': R_ARM_PC24 relocation out of range.",
259                                                                 symtab[symtab_index].st_name);
260                                                         _dl_exit(1);
261                                                 }
262                                         }
263                                         newvalue >>= 2;
264                                         symbol_addr = (*reloc_addr & 0xff000000) | (newvalue & 0x00ffffff);
265                                         *reloc_addr = symbol_addr;
266                                         break;
267                                 }
268 #else
269                                 _dl_dprintf(2,"R_ARM_PC24: Compile shared libraries with -fPIC!\n");
270                                 _dl_exit(1);
271 #endif
272                         case R_ARM_GLOB_DAT:
273                         case R_ARM_JUMP_SLOT:
274                                 *reloc_addr = symbol_addr;
275                                 break;
276                         case R_ARM_RELATIVE:
277                                 *reloc_addr += (unsigned long) tpnt->loadaddr;
278                                 break;
279                         case R_ARM_COPY:
280                                 _dl_memcpy((void *) reloc_addr,
281                                            (void *) symbol_addr, symtab[symtab_index].st_size);
282                                 break;
283 #if defined USE_TLS && USE_TLS
284                         case R_ARM_TLS_DTPMOD32:
285                                 *reloc_addr = def_mod->l_tls_modid;
286                                 break;
287
288                         case R_ARM_TLS_DTPOFF32:
289                                 *reloc_addr += symbol_addr;
290                                 break;
291
292                         case R_ARM_TLS_TPOFF32:
293                                 CHECK_STATIC_TLS ((struct link_map *) def_mod);
294                                 *reloc_addr += (symbol_addr + def_mod->l_tls_offset);
295                                 break;
296 #endif
297                         default:
298                                 return -1; /*call _dl_exit(1) */
299                 }
300 #if defined (__SUPPORT_LD_DEBUG__)
301                 if (_dl_debug_reloc && _dl_debug_detail)
302                         _dl_dprintf(_dl_debug_file, "\tpatch: %x ==> %x @ %x", old_val, *reloc_addr, reloc_addr);
303         }
304
305 #endif
306
307         return goof;
308 }
309
310 static int
311 _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct dyn_elf *scope,
312                    ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
313 {
314         int reloc_type;
315         unsigned long *reloc_addr;
316
317         reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
318         reloc_type = ELF32_R_TYPE(rpnt->r_info);
319
320 #if defined (__SUPPORT_LD_DEBUG__)
321         {
322                 unsigned long old_val = *reloc_addr;
323 #endif
324                 switch (reloc_type) {
325                         case R_ARM_NONE:
326                                 break;
327                         case R_ARM_JUMP_SLOT:
328                                 *reloc_addr += (unsigned long) tpnt->loadaddr;
329                                 break;
330                         default:
331                                 return -1; /*call _dl_exit(1) */
332                 }
333 #if defined (__SUPPORT_LD_DEBUG__)
334                 if (_dl_debug_reloc && _dl_debug_detail)
335                         _dl_dprintf(_dl_debug_file, "\tpatch: %x ==> %x @ %x", old_val, *reloc_addr, reloc_addr);
336         }
337
338 #endif
339         return 0;
340
341 }
342
343 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
344         unsigned long rel_addr, unsigned long rel_size)
345 {
346         (void)_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
347 }
348
349 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
350         unsigned long rel_addr, unsigned long rel_size)
351 {
352         return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
353 }
354