]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/contrib/uclibc/ldso/ldso/bfin/elfinterp.c
update
[l4.git] / l4 / pkg / uclibc / lib / contrib / uclibc / ldso / ldso / bfin / elfinterp.c
1 /* Blackfin ELF shared library loader suppport
2    Copyright (C) 2003, 2004 Red Hat, Inc.
3    Contributed by Alexandre Oliva <aoliva@redhat.com>
4    Lots of code copied from ../i386/elfinterp.c, so:
5    Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald,
6                                 David Engel, Hongjiu Lu and Mitch D'Souza
7    Copyright (C) 2001-2002, Erik Andersen
8    All rights reserved.
9
10 This file is part of uClibc.
11
12 uClibc is free software; you can redistribute it and/or modify it
13 under the terms of the GNU Lesser General Public License as
14 published by the Free Software Foundation; either version 2.1 of the
15 License, or (at your option) any later version.
16
17 uClibc is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 Library General Public License for more details.
21
22 You should have received a copy of the GNU Lesser General Public
23 License along with uClibc; see the file COPYING.LIB.  If not, write to
24 the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
25 USA.  */
26
27 #include <sys/cdefs.h>      /* __attribute_used__ */
28
29 /* Program to load an ELF binary on a linux system, and run it.
30    References to symbols in sharable libraries can be resolved by either
31    an ELF sharable library or a linux style of shared library. */
32
33 /* Disclaimer:  I have never seen any AT&T source code for SVr4, nor have
34    I ever taken any courses on internals.  This program was developed using
35    information available through the book "UNIX SYSTEM V RELEASE 4,
36    Programmers guide: Ansi C and Programming Support Tools", which did
37    a more than adequate job of explaining everything required to get this
38    working. */
39
40 __attribute__((__visibility__("hidden")))
41 struct funcdesc_value volatile *
42 _dl_linux_resolver (struct elf_resolve *tpnt, int reloc_entry)
43 {
44         ELF_RELOC *this_reloc;
45         char *strtab;
46         ElfW(Sym) *symtab;
47         int symtab_index;
48         char *rel_addr;
49         char *new_addr;
50         struct funcdesc_value funcval;
51         struct funcdesc_value volatile *got_entry;
52         char *symname;
53         struct symbol_ref sym_ref;
54
55         rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
56
57         this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry);
58         symtab_index = ELF_R_SYM(this_reloc->r_info);
59
60         symtab = (Elf32_Sym *) tpnt->dynamic_info[DT_SYMTAB];
61         strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
62         sym_ref.sym = &symtab[symtab_index];
63         sym_ref.tpnt = NULL;
64         symname= strtab + symtab[symtab_index].st_name;
65
66         /* Address of GOT entry fix up */
67         got_entry = (struct funcdesc_value *) DL_RELOC_ADDR(tpnt->loadaddr, this_reloc->r_offset);
68
69         /* Get the address to be used to fill in the GOT entry.  */
70         new_addr = _dl_find_hash(symname, tpnt->symbol_scope, NULL, 0, &sym_ref);
71         if (!new_addr) {
72                 new_addr = _dl_find_hash(symname, NULL, NULL, 0, &sym_ref);
73                 if (!new_addr) {
74                         _dl_dprintf(2, "%s: can't resolve symbol '%s'\n",
75                                     _dl_progname, symname);
76                         _dl_exit(1);
77                 }
78         }
79
80         funcval.entry_point = new_addr;
81         funcval.got_value = sym_ref.tpnt->loadaddr.got_value;
82
83 #if defined (__SUPPORT_LD_DEBUG__)
84         if (_dl_debug_bindings) {
85                 _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
86                 if (_dl_debug_detail)
87                         _dl_dprintf(_dl_debug_file,
88                                     "\n\tpatched (%x,%x) ==> (%x,%x) @ %x\n",
89                                     got_entry->entry_point, got_entry->got_value,
90                                     funcval.entry_point, funcval.got_value,
91                                     got_entry);
92         }
93         if (1 || !_dl_debug_nofixups) {
94                 *got_entry = funcval;
95         }
96 #else
97         *got_entry = funcval;
98 #endif
99
100         return got_entry;
101 }
102
103 static int
104 _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
105           unsigned long rel_addr, unsigned long rel_size,
106           int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope,
107                             ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab))
108 {
109         unsigned int i;
110         char *strtab;
111         ElfW(Sym) *symtab;
112         ELF_RELOC *rpnt;
113         int symtab_index;
114
115         /* Now parse the relocation information */
116         rpnt = (ELF_RELOC *) rel_addr;
117         rel_size = rel_size / sizeof(ELF_RELOC);
118
119         symtab = (ElfW(Sym) *) tpnt->dynamic_info[DT_SYMTAB];
120         strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
121
122         for (i = 0; i < rel_size; i++, rpnt++) {
123                 int res;
124
125                 symtab_index = ELF_R_SYM(rpnt->r_info);
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 (res <0) {
139                         int reloc_type = ELF_R_TYPE(rpnt->r_info);
140 #if defined (__SUPPORT_LD_DEBUG__)
141                         _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
142 #else
143                         _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
144 #endif
145                         _dl_exit(-res);
146                 } else if (res >0) {
147                         _dl_dprintf(2, "can't resolve symbol\n");
148                         return res;
149                 }
150           }
151           return 0;
152 }
153
154 static int
155 _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
156               ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)
157 {
158         int reloc_type;
159         int symtab_index;
160         char *symname;
161         unsigned long reloc_value = 0, *reloc_addr;
162         struct { unsigned long v; } __attribute__((__packed__))
163                                             *reloc_addr_packed;
164         unsigned long symbol_addr;
165         struct elf_resolve *symbol_tpnt;
166         struct funcdesc_value funcval;
167 #if defined (__SUPPORT_LD_DEBUG__)
168         unsigned long old_val;
169 #endif
170         struct symbol_ref sym_ref;
171
172         reloc_addr   = (unsigned long *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset);
173         __asm__ ("" : "=r" (reloc_addr_packed) : "0" (reloc_addr));
174         reloc_type   = ELF_R_TYPE(rpnt->r_info);
175         symtab_index = ELF_R_SYM(rpnt->r_info);
176         symbol_addr  = 0;
177         sym_ref.sym =  &symtab[symtab_index];
178         sym_ref.tpnt =  NULL;
179         symname      = strtab + symtab[symtab_index].st_name;
180
181         if (ELF_ST_BIND (symtab[symtab_index].st_info) == STB_LOCAL) {
182                 symbol_addr = (unsigned long) DL_RELOC_ADDR(tpnt->loadaddr, symtab[symtab_index].st_value);
183                 symbol_tpnt = tpnt;
184         } else {
185
186                 symbol_addr = (unsigned long)
187                   _dl_find_hash(symname, scope, NULL, 0, &sym_ref);
188
189                 /*
190                  * We want to allow undefined references to weak symbols - this might
191                  * have been intentional.  We should not be linking local symbols
192                  * here, so all bases should be covered.
193                  */
194
195                 if (!symbol_addr && ELF_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK) {
196                         _dl_dprintf (2, "%s: can't resolve symbol '%s'\n",
197                                      _dl_progname, symname);
198                         _dl_exit (1);
199                 }
200                 symbol_tpnt = sym_ref.tpnt;
201         }
202
203 #if defined (__SUPPORT_LD_DEBUG__)
204         if (_dl_debug_reloc && _dl_debug_detail)
205           {
206             if ((long)reloc_addr_packed & 3)
207               old_val = reloc_addr_packed->v;
208             else
209               old_val = *reloc_addr;
210           }
211         else
212           old_val = 0;
213 #endif
214         switch (reloc_type) {
215         case R_BFIN_UNUSED0:
216                 break;
217         case R_BFIN_BYTE4_DATA:
218                 if ((long)reloc_addr_packed & 3)
219                         reloc_value = reloc_addr_packed->v += symbol_addr;
220                 else
221                         reloc_value = *reloc_addr += symbol_addr;
222                 break;
223         case R_BFIN_FUNCDESC_VALUE:
224                 funcval.entry_point = (void*)symbol_addr;
225                 /* The addend of FUNCDESC_VALUE
226                    relocations referencing global
227                    symbols must be ignored, because it
228                    may hold the address of a lazy PLT
229                    entry.  */
230                 if (ELF_ST_BIND(symtab[symtab_index].st_info) == STB_LOCAL)
231                         funcval.entry_point += *reloc_addr;
232                 reloc_value = (unsigned long)funcval.entry_point;
233                 if (symbol_addr)
234                         funcval.got_value
235                                 = symbol_tpnt->loadaddr.got_value;
236                 else
237                         funcval.got_value = 0;
238                 __asm__ ("%0 = %2; %1 = %H2;"
239                          : "=m" (*(struct funcdesc_value *)reloc_addr), "=m" (((long *)reloc_addr)[1])
240                          : "d" (funcval));
241                 break;
242         case R_BFIN_FUNCDESC:
243                 if ((long)reloc_addr_packed & 3)
244                         reloc_value = reloc_addr_packed->v;
245                 else
246                         reloc_value = *reloc_addr;
247                 if (symbol_addr)
248                         reloc_value = (unsigned long)_dl_funcdesc_for
249                                 ((char *)symbol_addr + reloc_value,
250                                  symbol_tpnt->loadaddr.got_value);
251                 else
252                         reloc_value = 0;
253                 if ((long)reloc_addr_packed & 3)
254                         reloc_addr_packed->v = reloc_value;
255                 else
256                         *reloc_addr = reloc_value;
257                 break;
258         default:
259                 return -1;
260         }
261 #if defined (__SUPPORT_LD_DEBUG__)
262         if (_dl_debug_reloc && _dl_debug_detail) {
263                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, reloc_value, reloc_addr);
264                 switch (reloc_type) {
265                 case R_BFIN_FUNCDESC_VALUE:
266                         _dl_dprintf(_dl_debug_file, " got %x", ((struct funcdesc_value *)reloc_value)->got_value);
267                         break;
268                 case R_BFIN_FUNCDESC:
269                         if (! reloc_value)
270                                 break;
271                         _dl_dprintf(_dl_debug_file, " funcdesc (%x,%x)",
272                                     ((struct funcdesc_value *)reloc_value)->entry_point,
273                                     ((struct funcdesc_value *)reloc_value)->got_value);
274                         break;
275                 }
276         }
277 #endif
278
279         return 0;
280 }
281
282 static int
283 _dl_do_lazy_reloc (struct elf_resolve *tpnt,
284                    struct dyn_elf *scope __attribute__((unused)),
285                    ELF_RELOC *rpnt, ElfW(Sym) *symtab __attribute__((unused)),
286                    char *strtab __attribute__((unused)))
287 {
288         int reloc_type;
289         struct funcdesc_value volatile *reloc_addr;
290         struct funcdesc_value funcval;
291 #if defined (__SUPPORT_LD_DEBUG__)
292         unsigned long old_val;
293 #endif
294
295         reloc_addr = (struct funcdesc_value *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset);
296         reloc_type = ELF_R_TYPE(rpnt->r_info);
297
298 #if defined (__SUPPORT_LD_DEBUG__)
299         old_val = (unsigned long)reloc_addr->entry_point;
300 #endif
301                 switch (reloc_type) {
302                         case R_BFIN_UNUSED0:
303                                 break;
304                         case R_BFIN_FUNCDESC_VALUE:
305                                 funcval = *reloc_addr;
306                                 funcval.entry_point = (void *) DL_RELOC_ADDR(tpnt->loadaddr, funcval.entry_point);
307                                 funcval.got_value = tpnt->loadaddr.got_value;
308                                 *reloc_addr = funcval;
309                                 break;
310                         default:
311                                 return -1;
312                 }
313 #if defined (__SUPPORT_LD_DEBUG__)
314         if (_dl_debug_reloc && _dl_debug_detail)
315                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", old_val, reloc_addr->entry_point, reloc_addr);
316 #endif
317         return 0;
318
319 }
320
321 void
322 _dl_parse_lazy_relocation_information
323 (struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size)
324 {
325   _dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
326 }
327
328 int
329 _dl_parse_relocation_information
330 (struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size)
331 {
332   return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
333 }
334
335 /* We don't have copy relocs.  */
336
337 int
338 _dl_parse_copy_information
339 (struct dyn_elf *rpnt __attribute__((unused)),
340  unsigned long rel_addr __attribute__((unused)),
341  unsigned long rel_size __attribute__((unused)))
342 {
343   return 0;
344 }
345
346 #ifndef IS_IN_libdl
347 # include "../../libc/sysdeps/linux/bfin/crtreloc.c"
348 #endif
349