OSDN Git Service

Use __always_inline instead of __inline__
[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 struct funcdesc_value volatile *__attribute__((__visibility__("hidden")))
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         struct elf_resolve *new_tpnt;
49         char *new_addr;
50         struct funcdesc_value funcval;
51         struct funcdesc_value volatile *got_entry;
52         char *symname;
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 = (Elf32_Sym *) tpnt->dynamic_info[DT_SYMTAB];
60         strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
61         symname= strtab + symtab[symtab_index].st_name;
62
63         /* Address of GOT entry fix up */
64         got_entry = (struct funcdesc_value *) DL_RELOC_ADDR(tpnt->loadaddr, this_reloc->r_offset);
65
66         /* Get the address to be used to fill in the GOT entry.  */
67         new_addr = _dl_lookup_hash(symname, tpnt->symbol_scope, NULL, 0, &new_tpnt);
68         if (!new_addr) {
69                 new_addr = _dl_lookup_hash(symname, NULL, NULL, 0, &new_tpnt);
70                 if (!new_addr) {
71                         _dl_dprintf(2, "%s: can't resolve symbol '%s'\n",
72                                     _dl_progname, symname);
73                         _dl_exit(1);
74                 }
75         }
76
77         funcval.entry_point = new_addr;
78         funcval.got_value = new_tpnt->loadaddr.got_value;
79
80 #if defined (__SUPPORT_LD_DEBUG__)
81         if (_dl_debug_bindings) {
82                 _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
83                 if (_dl_debug_detail)
84                         _dl_dprintf(_dl_debug_file,
85                                     "\n\tpatched (%x,%x) ==> (%x,%x) @ %x\n",
86                                     got_entry->entry_point, got_entry->got_value,
87                                     funcval.entry_point, funcval.got_value,
88                                     got_entry);
89         }
90         if (1 || !_dl_debug_nofixups) {
91                 *got_entry = funcval;
92         }
93 #else
94         *got_entry = funcval;
95 #endif
96
97         return got_entry;
98 }
99
100 static int
101 _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
102           unsigned long rel_addr, unsigned long rel_size,
103           int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope,
104                             ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab))
105 {
106         unsigned int i;
107         char *strtab;
108         ElfW(Sym) *symtab;
109         ELF_RELOC *rpnt;
110         int symtab_index;
111
112         /* Now parse the relocation information */
113         rpnt = (ELF_RELOC *) rel_addr;
114         rel_size = rel_size / sizeof(ELF_RELOC);
115
116         symtab = (ElfW(Sym) *) tpnt->dynamic_info[DT_SYMTAB];
117         strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
118
119         for (i = 0; i < rel_size; i++, rpnt++) {
120                 int res;
121
122                 symtab_index = ELF_R_SYM(rpnt->r_info);
123                 debug_sym(symtab,strtab,symtab_index);
124                 debug_reloc(symtab,strtab,rpnt);
125
126                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
127
128                 if (res==0) continue;
129
130                 _dl_dprintf(2, "\n%s: ",_dl_progname);
131
132                 if (symtab_index)
133                         _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
134
135                 if (res <0) {
136                         int reloc_type = ELF_R_TYPE(rpnt->r_info);
137 #if defined (__SUPPORT_LD_DEBUG__)
138                         _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
139 #else
140                         _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
141 #endif
142                         _dl_exit(-res);
143                 } else if (res >0) {
144                         _dl_dprintf(2, "can't resolve symbol\n");
145                         return res;
146                 }
147           }
148           return 0;
149 }
150
151 static int
152 _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
153               ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)
154 {
155         int reloc_type;
156         int symtab_index;
157         char *symname;
158         unsigned long reloc_value = 0, *reloc_addr;
159         struct { unsigned long v; } __attribute__((__packed__))
160                                             *reloc_addr_packed;
161         unsigned long symbol_addr;
162         struct elf_resolve *symbol_tpnt;
163         struct funcdesc_value funcval;
164 #if defined (__SUPPORT_LD_DEBUG__)
165         unsigned long old_val;
166 #endif
167
168         reloc_addr   = (unsigned long *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset);
169         __asm__ ("" : "=r" (reloc_addr_packed) : "0" (reloc_addr));
170         reloc_type   = ELF_R_TYPE(rpnt->r_info);
171         symtab_index = ELF_R_SYM(rpnt->r_info);
172         symbol_addr  = 0;
173         symname      = strtab + symtab[symtab_index].st_name;
174
175         if (ELF_ST_BIND (symtab[symtab_index].st_info) == STB_LOCAL) {
176                 symbol_addr = (unsigned long) DL_RELOC_ADDR(tpnt->loadaddr, symtab[symtab_index].st_value);
177                 symbol_tpnt = tpnt;
178         } else {
179
180                 symbol_addr = (unsigned long)
181                   _dl_lookup_hash(symname, scope, NULL, 0, &symbol_tpnt);
182
183                 /*
184                  * We want to allow undefined references to weak symbols - this might
185                  * have been intentional.  We should not be linking local symbols
186                  * here, so all bases should be covered.
187                  */
188
189                 if (!symbol_addr && ELF_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK) {
190                         _dl_dprintf (2, "%s: can't resolve symbol '%s'\n",
191                                      _dl_progname, strtab + symtab[symtab_index].st_name);
192                         _dl_exit (1);
193                 }
194         }
195
196 #if defined (__SUPPORT_LD_DEBUG__)
197         if (_dl_debug_reloc && _dl_debug_detail)
198           {
199             if ((long)reloc_addr_packed & 3)
200               old_val = reloc_addr_packed->v;
201             else
202               old_val = *reloc_addr;
203           }
204         else
205           old_val = 0;
206 #endif
207         switch (reloc_type) {
208         case R_BFIN_unused0:
209                 break;
210         case R_BFIN_byte4_data:
211                 if ((long)reloc_addr_packed & 3)
212                         reloc_value = reloc_addr_packed->v += symbol_addr;
213                 else
214                         reloc_value = *reloc_addr += symbol_addr;
215                 break;
216         case R_BFIN_FUNCDESC_VALUE:
217                 funcval.entry_point = (void*)symbol_addr;
218                 /* The addend of FUNCDESC_VALUE
219                    relocations referencing global
220                    symbols must be ignored, because it
221                    may hold the address of a lazy PLT
222                    entry.  */
223                 if (ELF_ST_BIND(symtab[symtab_index].st_info) == STB_LOCAL)
224                         funcval.entry_point += *reloc_addr;
225                 reloc_value = (unsigned long)funcval.entry_point;
226                 if (symbol_addr)
227                         funcval.got_value
228                                 = symbol_tpnt->loadaddr.got_value;
229                 else
230                         funcval.got_value = 0;
231                 __asm__ ("%0 = %2; %1 = %H2;"
232                          : "=m" (*(struct funcdesc_value *)reloc_addr), "=m" (((long *)reloc_addr)[1])
233                          : "d" (funcval));
234                 break;
235         case R_BFIN_FUNCDESC:
236                 if ((long)reloc_addr_packed & 3)
237                         reloc_value = reloc_addr_packed->v;
238                 else
239                         reloc_value = *reloc_addr;
240                 if (symbol_addr)
241                         reloc_value = (unsigned long)_dl_funcdesc_for
242                                 ((char *)symbol_addr + reloc_value,
243                                  symbol_tpnt->loadaddr.got_value);
244                 else
245                         reloc_value = 0;
246                 if ((long)reloc_addr_packed & 3)
247                         reloc_addr_packed->v = reloc_value;
248                 else
249                         *reloc_addr = reloc_value;
250                 break;
251         default:
252                 return -1;
253         }
254 #if defined (__SUPPORT_LD_DEBUG__)
255         if (_dl_debug_reloc && _dl_debug_detail) {
256                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, reloc_value, reloc_addr);
257                 switch (reloc_type) {
258                 case R_BFIN_FUNCDESC_VALUE:
259                         _dl_dprintf(_dl_debug_file, " got %x", ((struct funcdesc_value *)reloc_value)->got_value);
260                         break;
261                 case R_BFIN_FUNCDESC:
262                         if (! reloc_value)
263                                 break;
264                         _dl_dprintf(_dl_debug_file, " funcdesc (%x,%x)",
265                                     ((struct funcdesc_value *)reloc_value)->entry_point,
266                                     ((struct funcdesc_value *)reloc_value)->got_value);
267                         break;
268                 }
269         }
270 #endif
271
272         return 0;
273 }
274
275 static int
276 _dl_do_lazy_reloc (struct elf_resolve *tpnt,
277                    struct dyn_elf *scope __attribute__((unused)),
278                    ELF_RELOC *rpnt, ElfW(Sym) *symtab __attribute__((unused)),
279                    char *strtab __attribute__((unused)))
280 {
281         int reloc_type;
282         struct funcdesc_value volatile *reloc_addr;
283         struct funcdesc_value funcval;
284 #if defined (__SUPPORT_LD_DEBUG__)
285         unsigned long old_val;
286 #endif
287
288         reloc_addr = (struct funcdesc_value *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset);
289         reloc_type = ELF_R_TYPE(rpnt->r_info);
290
291 #if defined (__SUPPORT_LD_DEBUG__)
292         old_val = (unsigned long)reloc_addr->entry_point;
293 #endif
294                 switch (reloc_type) {
295                         case R_BFIN_unused0:
296                                 break;
297                         case R_BFIN_FUNCDESC_VALUE:
298                                 funcval = *reloc_addr;
299                                 funcval.entry_point = DL_RELOC_ADDR(tpnt->loadaddr, funcval.entry_point);
300                                 funcval.got_value = tpnt->loadaddr.got_value;
301                                 *reloc_addr = funcval;
302                                 break;
303                         default:
304                                 return -1;
305                 }
306 #if defined (__SUPPORT_LD_DEBUG__)
307         if (_dl_debug_reloc && _dl_debug_detail)
308                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, reloc_addr->entry_point, reloc_addr);
309 #endif
310         return 0;
311
312 }
313
314 void
315 _dl_parse_lazy_relocation_information
316 (struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size)
317 {
318   _dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
319 }
320
321 int
322 _dl_parse_relocation_information
323 (struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size)
324 {
325   return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
326 }
327
328 /* We don't have copy relocs.  */
329
330 int
331 _dl_parse_copy_information
332 (struct dyn_elf *rpnt __attribute__((unused)),
333  unsigned long rel_addr __attribute__((unused)),
334  unsigned long rel_size __attribute__((unused)))
335 {
336   return 0;
337 }
338
339 #ifndef IS_IN_libdl
340 # include "../../libc/sysdeps/linux/bfin/crtreloc.c"
341 #endif
342