OSDN Git Service

Replace FSF snail mail address with URLs
[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, see
24 <http://www.gnu.org/licenses/>.  */
25
26 #include <sys/cdefs.h>      /* __attribute_used__ */
27
28 /* Program to load an ELF binary on a linux system, and run it.
29    References to symbols in sharable libraries can be resolved by either
30    an ELF sharable library or a linux style of shared library. */
31
32 /* Disclaimer:  I have never seen any AT&T source code for SVr4, nor have
33    I ever taken any courses on internals.  This program was developed using
34    information available through the book "UNIX SYSTEM V RELEASE 4,
35    Programmers guide: Ansi C and Programming Support Tools", which did
36    a more than adequate job of explaining everything required to get this
37    working. */
38
39 __attribute__((__visibility__("hidden")))
40 struct funcdesc_value volatile *
41 _dl_linux_resolver (struct elf_resolve *tpnt, int reloc_entry)
42 {
43         ELF_RELOC *this_reloc;
44         char *strtab;
45         ElfW(Sym) *symtab;
46         int symtab_index;
47         char *rel_addr;
48         char *new_addr;
49         struct funcdesc_value funcval;
50         struct funcdesc_value volatile *got_entry;
51         char *symname;
52         struct symbol_ref sym_ref;
53
54         rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
55
56         this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry);
57         symtab_index = ELF_R_SYM(this_reloc->r_info);
58
59         symtab = (ElfW(Sym) *) tpnt->dynamic_info[DT_SYMTAB];
60         strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
61         sym_ref.sym = &symtab[symtab_index];
62         sym_ref.tpnt = NULL;
63         symname= strtab + symtab[symtab_index].st_name;
64
65         /* Address of GOT entry fix up */
66         got_entry = (struct funcdesc_value *) DL_RELOC_ADDR(tpnt->loadaddr, this_reloc->r_offset);
67
68         /* Get the address to be used to fill in the GOT entry.  */
69         new_addr = _dl_find_hash(symname, &_dl_loaded_modules->symbol_scope, NULL, 0, &sym_ref);
70         if (!new_addr) {
71                 new_addr = _dl_find_hash(symname, NULL, NULL, 0, &sym_ref);
72                 if (!new_addr) {
73                         _dl_dprintf(2, "%s: can't resolve symbol '%s'\n",
74                                     _dl_progname, symname);
75                         _dl_exit(1);
76                 }
77         }
78
79         funcval.entry_point = new_addr;
80         funcval.got_value = sym_ref.tpnt->loadaddr.got_value;
81
82 #if defined (__SUPPORT_LD_DEBUG__)
83         if (_dl_debug_bindings) {
84                 _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
85                 if (_dl_debug_detail)
86                         _dl_dprintf(_dl_debug_file,
87                                     "\n\tpatched (%x,%x) ==> (%x,%x) @ %x\n",
88                                     got_entry->entry_point, got_entry->got_value,
89                                     funcval.entry_point, funcval.got_value,
90                                     got_entry);
91         }
92         if (1 || !_dl_debug_nofixups) {
93                 *got_entry = funcval;
94         }
95 #else
96         *got_entry = funcval;
97 #endif
98
99         return got_entry;
100 }
101
102 static int
103 _dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope,
104           unsigned long rel_addr, unsigned long rel_size,
105           int (*reloc_fnc) (struct elf_resolve *tpnt, struct r_scope_elem *scope,
106                             ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab))
107 {
108         unsigned int i;
109         char *strtab;
110         ElfW(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 = (ElfW(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 = ELF_R_SYM(rpnt->r_info);
125                 debug_sym(symtab,strtab,symtab_index);
126                 debug_reloc(symtab,strtab,rpnt);
127
128                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
129
130                 if (res==0) continue;
131
132                 _dl_dprintf(2, "\n%s: ",_dl_progname);
133
134                 if (symtab_index)
135                         _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
136
137                 if (res <0) {
138                         int reloc_type = ELF_R_TYPE(rpnt->r_info);
139 #if defined (__SUPPORT_LD_DEBUG__)
140                         _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
141 #else
142                         _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
143 #endif
144                         _dl_exit(-res);
145                 } else if (res >0) {
146                         _dl_dprintf(2, "can't resolve symbol\n");
147                         return res;
148                 }
149           }
150           return 0;
151 }
152
153 static int
154 _dl_do_reloc (struct elf_resolve *tpnt,struct r_scope_elem *scope,
155               ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)
156 {
157         int reloc_type;
158         int symtab_index;
159         char *symname;
160         unsigned long reloc_value = 0, *reloc_addr;
161         struct { unsigned long v; } __attribute__((__packed__))
162                                             *reloc_addr_packed;
163         unsigned long symbol_addr;
164         struct elf_resolve *symbol_tpnt;
165         struct funcdesc_value funcval;
166 #if defined (__SUPPORT_LD_DEBUG__)
167         unsigned long old_val;
168 #endif
169         struct symbol_ref sym_ref;
170
171         reloc_addr   = (unsigned long *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset);
172         __asm__ ("" : "=r" (reloc_addr_packed) : "0" (reloc_addr));
173         reloc_type   = ELF_R_TYPE(rpnt->r_info);
174         symtab_index = ELF_R_SYM(rpnt->r_info);
175         symbol_addr  = 0;
176         sym_ref.sym =  &symtab[symtab_index];
177         sym_ref.tpnt =  NULL;
178         symname      = strtab + symtab[symtab_index].st_name;
179
180         if (ELF_ST_BIND (symtab[symtab_index].st_info) == STB_LOCAL) {
181                 symbol_addr = (unsigned long) DL_RELOC_ADDR(tpnt->loadaddr, symtab[symtab_index].st_value);
182                 symbol_tpnt = tpnt;
183         } else {
184
185                 symbol_addr = (unsigned long)
186                   _dl_find_hash(symname, scope, NULL, 0, &sym_ref);
187
188                 /*
189                  * We want to allow undefined references to weak symbols - this might
190                  * have been intentional.  We should not be linking local symbols
191                  * here, so all bases should be covered.
192                  */
193
194                 if (!symbol_addr && ELF_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK) {
195                         _dl_dprintf (2, "%s: can't resolve symbol '%s'\n",
196                                      _dl_progname, symname);
197                         _dl_exit (1);
198                 }
199                 if (_dl_trace_prelink) {
200                         _dl_debug_lookup (symname, tpnt, &symtab[symtab_index],
201                                 &sym_ref, elf_machine_type_class(reloc_type));
202                 }
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