OSDN Git Service

8eb60091627ae9aaea8dac989f38f42f24d71744
[uclinux-h8/uClibc.git] / ldso / ldso / xtensa / elfinterp.c
1 /* vi: set sw=4 ts=4: */
2 /* Xtensa ELF shared library loader suppport
3  *
4  * Copyright (C) 2007 Tensilica Inc.
5  * Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald,
6  *                              David Engel, Hongjiu Lu and Mitch D'Souza
7  * Copyright (C) 2001-2004 Erik Andersen
8  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. The name of the above contributors may not be
17  *    used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #include "ldso.h"
34
35 unsigned long
36 _dl_linux_resolver (struct elf_resolve *tpnt, int reloc_entry)
37 {
38         ELF_RELOC *this_reloc;
39         char *strtab;
40         Elf32_Sym *symtab;
41         int symtab_index;
42         char *rel_addr;
43         char *new_addr;
44         char **got_addr;
45         char *symname;
46
47         rel_addr = (char *) tpnt->dynamic_info[DT_JMPREL];
48         this_reloc = (ELF_RELOC *) (rel_addr + reloc_entry);
49         symtab_index = ELF32_R_SYM (this_reloc->r_info);
50
51         symtab = (Elf32_Sym *) tpnt->dynamic_info[DT_SYMTAB];
52         strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
53         symname = strtab + symtab[symtab_index].st_name;
54
55         /* Address of the literal to fix up.  */
56         got_addr = (char **) (this_reloc->r_offset + tpnt->loadaddr);
57
58         /* Get the address of the GOT entry.  */
59         new_addr = _dl_find_hash (symname, tpnt->symbol_scope, tpnt,
60                                                           ELF_RTYPE_CLASS_PLT, NULL);
61         if (unlikely (!new_addr)) {
62                 _dl_dprintf (2, "%s: can't resolve symbol '%s'\n",
63                                          _dl_progname, symname);
64                 _dl_exit (1);
65         }
66
67 #if defined (__SUPPORT_LD_DEBUG__)
68         if (_dl_debug_bindings) {
69                 _dl_dprintf (_dl_debug_file, "\nresolve function: %s", symname);
70                 if (_dl_debug_detail)
71                         _dl_dprintf (_dl_debug_file, "\n\tpatched %x ==> %x @ %x\n",
72                                                  *got_addr, new_addr, got_addr);
73         }
74         if (!_dl_debug_nofixups)
75                 *got_addr = new_addr;
76 #else
77         *got_addr = new_addr;
78 #endif
79
80         return (unsigned long) new_addr;
81 }
82
83
84 static int
85 _dl_parse (struct elf_resolve *tpnt, struct dyn_elf *scope,
86                    unsigned long rel_addr, unsigned long rel_size,
87                    int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope,
88                                                          ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
89 {
90         unsigned int i;
91         char *strtab;
92         Elf32_Sym *symtab;
93         ELF_RELOC *rpnt;
94         int symtab_index;
95
96         /* Parse the relocation information.  */
97         rpnt = (ELF_RELOC *) rel_addr;
98         rel_size /= sizeof (ELF_RELOC);
99
100         symtab = (Elf32_Sym *) tpnt->dynamic_info[DT_SYMTAB];
101         strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
102
103         for (i = 0; i < rel_size; i++, rpnt++) {
104                 int res;
105
106                 symtab_index = ELF32_R_SYM (rpnt->r_info);
107
108                 debug_sym (symtab, strtab, symtab_index);
109                 debug_reloc (symtab, strtab, rpnt);
110
111                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
112
113                 if (res == 0)
114                         continue;
115
116                 _dl_dprintf (2, "\n%s: ", _dl_progname);
117
118                 if (symtab_index)
119                         _dl_dprintf (2, "symbol '%s': ",
120                                                  strtab + symtab[symtab_index].st_name);
121
122                 if (unlikely (res < 0)) {
123                         int reloc_type = ELF32_R_TYPE (rpnt->r_info);
124 #if defined (__SUPPORT_LD_DEBUG__)
125                         _dl_dprintf (2, "can't handle reloc type %s\n",
126                                                  _dl_reltypes (reloc_type));
127 #else
128                         _dl_dprintf (2, "can't handle reloc type %x\n", reloc_type);
129 #endif
130                         _dl_exit (-res);
131                 }
132                 if (unlikely (res > 0)) {
133                         _dl_dprintf (2, "can't resolve symbol\n");
134                         return res;
135                 }
136         }
137
138         return 0;
139 }
140
141
142 static int
143 _dl_do_reloc (struct elf_resolve *tpnt, struct dyn_elf *scope,
144                           ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
145 {
146         int reloc_type;
147         int symtab_index;
148         char *symname;
149         struct symbol_ref sym_ref;
150         Elf32_Addr *reloc_addr;
151         Elf32_Addr symbol_addr;
152 #if defined (__SUPPORT_LD_DEBUG__)
153         Elf32_Addr old_val;
154 #endif
155
156         reloc_addr = (Elf32_Addr *) (tpnt->loadaddr + rpnt->r_offset);
157         reloc_type = ELF32_R_TYPE (rpnt->r_info);
158         symtab_index = ELF32_R_SYM (rpnt->r_info);
159         sym_ref.sym = &symtab[symtab_index];
160         sym_ref.tpnt = NULL;
161         symbol_addr = 0;
162         symname = strtab + sym_ref.sym->st_name;
163
164         if (symtab_index) {
165                 symbol_addr = (Elf32_Addr)
166                         _dl_find_hash (symname, scope, tpnt,
167                                                    elf_machine_type_class (reloc_type), &sym_ref);
168
169                 /*
170                  * We want to allow undefined references to weak symbols - this might
171                  * have been intentional.  We should not be linking local symbols
172                  * here, so all bases should be covered.
173                  */
174                 if (unlikely (!symbol_addr &&
175                                           ELF32_ST_BIND (sym_ref.sym->st_info) != STB_WEAK)) {
176                         _dl_dprintf (2, "%s: can't resolve symbol '%s'\n",
177                                                  _dl_progname, symname);
178                         _dl_exit (1);
179                 }
180         }
181
182 #if defined (__SUPPORT_LD_DEBUG__)
183         old_val = *reloc_addr;
184 #endif
185
186         switch (reloc_type) {
187         case R_XTENSA_NONE:
188                 break;
189
190         case R_XTENSA_GLOB_DAT:
191         case R_XTENSA_JMP_SLOT:
192                 *reloc_addr = symbol_addr + rpnt->r_addend;
193                 break;
194
195         case R_XTENSA_RTLD:
196                 if (rpnt->r_addend == 1) {
197                         /* Grab the function pointer stashed at the beginning of the
198                            GOT by the GOT_INIT function.  */
199                         *reloc_addr = *(Elf32_Addr *) tpnt->dynamic_info[DT_PLTGOT];
200                 } else if (rpnt->r_addend == 2) {
201                         /* Store the link map for the object.  */
202                         *reloc_addr = (Elf32_Addr) tpnt;
203                 } else {
204                         _dl_exit (1);
205                 }
206                 break;
207
208         case R_XTENSA_RELATIVE:
209                 *reloc_addr += tpnt->loadaddr + rpnt->r_addend;
210                 break;
211
212         default:
213                 return -1; /* Calls _dl_exit(1).  */
214         }
215 #if defined (__SUPPORT_LD_DEBUG__)
216         if (_dl_debug_reloc && _dl_debug_detail)
217                 _dl_dprintf (_dl_debug_file, "\tpatched: %x ==> %x @ %x\n",
218                                          old_val, *reloc_addr, reloc_addr);
219 #endif
220
221         return 0;
222 }
223
224
225 static int
226 _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct dyn_elf *scope,
227                                    ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
228 {
229         int reloc_type;
230         Elf32_Addr *reloc_addr;
231 #if defined (__SUPPORT_LD_DEBUG__)
232         Elf32_Addr old_val;
233 #endif
234
235         reloc_addr = (Elf32_Addr *) (tpnt->loadaddr + rpnt->r_offset);
236         reloc_type = ELF32_R_TYPE (rpnt->r_info);
237
238 #if defined (__SUPPORT_LD_DEBUG__)
239         old_val = *reloc_addr;
240 #endif
241
242         switch (reloc_type) {
243         case R_XTENSA_JMP_SLOT:
244                 /* Perform a RELATIVE reloc on the GOT entry that transfers
245                    to the stub function.  */
246                 *reloc_addr += tpnt->loadaddr;
247                 break;
248         case R_XTENSA_NONE:
249                 break;
250         default:
251                 _dl_exit (1);
252         }
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\n",
257                                          old_val, *reloc_addr, reloc_addr);
258 #endif
259         return 0;
260
261 }
262
263 void
264 _dl_parse_lazy_relocation_information (struct dyn_elf *rpnt,
265                                                                            unsigned long rel_addr,
266                                                                            unsigned long rel_size)
267 {
268         (void) _dl_parse (rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
269 }
270
271 int
272 _dl_parse_relocation_information (struct dyn_elf *rpnt,
273                                                                   unsigned long rel_addr,
274                                                                   unsigned long rel_size)
275 {
276         return _dl_parse (rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size,
277                                           _dl_do_reloc);
278 }