OSDN Git Service

Merge remote branch 'origin/master' into prelink
[uclinux-h8/uClibc.git] / 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, &_dl_loaded_modules->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 r_scope_elem *scope,
105           unsigned long rel_addr, unsigned long rel_size,
106           int (*reloc_fnc) (struct elf_resolve *tpnt, struct r_scope_elem *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 r_scope_elem *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                 if (_dl_trace_prelink)
201                         _dl_debug_lookup (symname, tpnt, &symtab[symtab_index],
202                                 &sym_ref, elf_machine_type_class(reloc_type));
203                 symbol_tpnt = sym_ref.tpnt;
204         }
205
206 #if defined (__SUPPORT_LD_DEBUG__)
207         if (_dl_debug_reloc && _dl_debug_detail)
208           {
209             if ((long)reloc_addr_packed & 3)
210               old_val = reloc_addr_packed->v;
211             else
212               old_val = *reloc_addr;
213           }
214         else
215           old_val = 0;
216 #endif
217         switch (reloc_type) {
218         case R_BFIN_UNUSED0:
219                 break;
220         case R_BFIN_BYTE4_DATA:
221                 if ((long)reloc_addr_packed & 3)
222                         reloc_value = reloc_addr_packed->v += symbol_addr;
223                 else
224                         reloc_value = *reloc_addr += symbol_addr;
225                 break;
226         case R_BFIN_FUNCDESC_VALUE:
227                 funcval.entry_point = (void*)symbol_addr;
228                 /* The addend of FUNCDESC_VALUE
229                    relocations referencing global
230                    symbols must be ignored, because it
231                    may hold the address of a lazy PLT
232                    entry.  */
233                 if (ELF_ST_BIND(symtab[symtab_index].st_info) == STB_LOCAL)
234                         funcval.entry_point += *reloc_addr;
235                 reloc_value = (unsigned long)funcval.entry_point;
236                 if (symbol_addr)
237                         funcval.got_value
238                                 = symbol_tpnt->loadaddr.got_value;
239                 else
240                         funcval.got_value = 0;
241                 __asm__ ("%0 = %2; %1 = %H2;"
242                          : "=m" (*(struct funcdesc_value *)reloc_addr), "=m" (((long *)reloc_addr)[1])
243                          : "d" (funcval));
244                 break;
245         case R_BFIN_FUNCDESC:
246                 if ((long)reloc_addr_packed & 3)
247                         reloc_value = reloc_addr_packed->v;
248                 else
249                         reloc_value = *reloc_addr;
250                 if (symbol_addr)
251                         reloc_value = (unsigned long)_dl_funcdesc_for
252                                 ((char *)symbol_addr + reloc_value,
253                                  symbol_tpnt->loadaddr.got_value);
254                 else
255                         reloc_value = 0;
256                 if ((long)reloc_addr_packed & 3)
257                         reloc_addr_packed->v = reloc_value;
258                 else
259                         *reloc_addr = reloc_value;
260                 break;
261         default:
262                 return -1;
263         }
264 #if defined (__SUPPORT_LD_DEBUG__)
265         if (_dl_debug_reloc && _dl_debug_detail) {
266                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, reloc_value, reloc_addr);
267                 switch (reloc_type) {
268                 case R_BFIN_FUNCDESC_VALUE:
269                         _dl_dprintf(_dl_debug_file, " got %x", ((struct funcdesc_value *)reloc_value)->got_value);
270                         break;
271                 case R_BFIN_FUNCDESC:
272                         if (! reloc_value)
273                                 break;
274                         _dl_dprintf(_dl_debug_file, " funcdesc (%x,%x)",
275                                     ((struct funcdesc_value *)reloc_value)->entry_point,
276                                     ((struct funcdesc_value *)reloc_value)->got_value);
277                         break;
278                 }
279         }
280 #endif
281
282         return 0;
283 }
284
285 static int
286 _dl_do_lazy_reloc (struct elf_resolve *tpnt,
287                    struct r_scope_elem *scope __attribute__((unused)),
288                    ELF_RELOC *rpnt, ElfW(Sym) *symtab __attribute__((unused)),
289                    char *strtab __attribute__((unused)))
290 {
291         int reloc_type;
292         struct funcdesc_value volatile *reloc_addr;
293         struct funcdesc_value funcval;
294 #if defined (__SUPPORT_LD_DEBUG__)
295         unsigned long old_val;
296 #endif
297
298         reloc_addr = (struct funcdesc_value *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset);
299         reloc_type = ELF_R_TYPE(rpnt->r_info);
300
301 #if defined (__SUPPORT_LD_DEBUG__)
302         old_val = (unsigned long)reloc_addr->entry_point;
303 #endif
304                 switch (reloc_type) {
305                         case R_BFIN_UNUSED0:
306                                 break;
307                         case R_BFIN_FUNCDESC_VALUE:
308                                 funcval = *reloc_addr;
309                                 funcval.entry_point = (void *) DL_RELOC_ADDR(tpnt->loadaddr, funcval.entry_point);
310                                 funcval.got_value = tpnt->loadaddr.got_value;
311                                 *reloc_addr = funcval;
312                                 break;
313                         default:
314                                 return -1;
315                 }
316 #if defined (__SUPPORT_LD_DEBUG__)
317         if (_dl_debug_reloc && _dl_debug_detail)
318                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", old_val, reloc_addr->entry_point, reloc_addr);
319 #endif
320         return 0;
321
322 }
323
324 void
325 _dl_parse_lazy_relocation_information
326 (struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size)
327 {
328   _dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
329 }
330
331 int
332 _dl_parse_relocation_information
333 (struct dyn_elf *rpnt, struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size)
334 {
335   return _dl_parse(rpnt->dyn, scope, rel_addr, rel_size, _dl_do_reloc);
336 }
337
338 /* We don't have copy relocs.  */
339
340 int
341 _dl_parse_copy_information
342 (struct dyn_elf *rpnt __attribute__((unused)),
343  unsigned long rel_addr __attribute__((unused)),
344  unsigned long rel_size __attribute__((unused)))
345 {
346   return 0;
347 }
348
349 #ifndef IS_IN_libdl
350 # include "../../libc/sysdeps/linux/bfin/crtreloc.c"
351 #endif
352