OSDN Git Service

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