OSDN Git Service

bump version to 0.9.32-rc2-git
[uclinux-h8/uClibc.git] / ldso / ldso / sh64 / elfinterp.c
1 /* vi: set sw=8 ts=8: */
2 /*
3  * ldso/ldso/sh64/elfinterp.c
4  *
5  * SuperH (sh64) ELF shared library loader suppport
6  *
7  * Copyright (C) 2003, 2004, 2005  Paul Mundt <lethal@linux-sh.org>
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 /* Program to load an ELF binary on a linux system, and run it.
34    References to symbols in sharable libraries can be resolved by either
35    an ELF sharable library or a linux style of shared library. */
36
37 /* Disclaimer:  I have never seen any AT&T source code for SVr4, nor have
38    I ever taken any courses on internals.  This program was developed using
39    information available through the book "UNIX SYSTEM V RELEASE 4,
40    Programmers guide: Ansi C and Programming Support Tools", which did
41    a more than adequate job of explaining everything required to get this
42    working. */
43
44 #include "ldso.h"
45
46 extern int _dl_linux_resolve(void);
47
48 unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
49 {
50         ELF_RELOC *this_reloc;
51         char *strtab;
52         Elf32_Sym *symtab;
53         int symtab_index;
54         char *rel_addr;
55         char *new_addr;
56         char **got_addr;
57         unsigned long instr_addr;
58         char *symname;
59
60         rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
61
62         this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry);
63         symtab_index = ELF32_R_SYM(this_reloc->r_info);
64
65         symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
66         strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
67         symname = strtab + symtab[symtab_index].st_name;
68
69         /* Address of jump instruction to fix up */
70         instr_addr = ((unsigned long)this_reloc->r_offset +
71                         (unsigned long)tpnt->loadaddr);
72         got_addr = (char **)instr_addr;
73
74
75         /* Get the address of the GOT entry */
76         new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT, NULL);
77         if (unlikely(!new_addr)) {
78                 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n",
79                             _dl_progname, symname);
80                 _dl_exit(1);
81         }
82
83 #ifdef __SUPPORT_LD_DEBUG__
84         if ((unsigned long)got_addr < 0x20000000) {
85                 if (_dl_debug_bindings) {
86                         _dl_dprintf(_dl_debug_file, "\nresolve function: %s",
87                                     symname);
88
89                         if (_dl_debug_detail)
90                                 _dl_dprintf(_dl_debug_file,
91                                             "\n\tpatched %x ==> %x @ %x\n",
92                                             *got_addr, new_addr, got_addr);
93                 }
94         }
95
96         if (!_dl_debug_nofixups)
97                 *got_addr = new_addr;
98 #else
99         *got_addr = new_addr;
100 #endif
101
102         return (unsigned long)new_addr;
103 }
104
105 static int _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
106                      unsigned long rel_addr, unsigned long rel_size,
107                      int (*reloc_fnc)(struct elf_resolve *tpnt,
108                                       struct dyn_elf *scope,
109                                       ELF_RELOC *rpnt, Elf32_Sym *symtab,
110                                       char *strtab))
111 {
112         unsigned int i;
113         char *strtab;
114         Elf32_Sym *symtab;
115         ELF_RELOC *rpnt;
116         int symtab_index;
117
118         /* Now parse the relocation information */
119         rpnt = (ELF_RELOC *)(intptr_t)rel_addr;
120         rel_size = rel_size / sizeof(ELF_RELOC);
121
122         symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
123         strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
124
125         for (i = 0; i < rel_size; i++, rpnt++) {
126                 int res;
127
128                 symtab_index = ELF32_R_SYM(rpnt->r_info);
129                 debug_sym(symtab,strtab,symtab_index);
130                 debug_reloc(symtab,strtab,rpnt);
131
132                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
133                 if (res == 0)
134                         continue;
135
136                 _dl_dprintf(2, "\n%s: ",_dl_progname);
137
138                 if (symtab_index)
139                         _dl_dprintf(2, "symbol '%s': ",
140                                 strtab + symtab[symtab_index].st_name);
141
142                 if (unlikely(res < 0)) {
143                         int reloc_type = ELF32_R_TYPE(rpnt->r_info);
144
145                         _dl_dprintf(2, "can't handle reloc type "
146 #ifdef __SUPPORT_LD_DEBUG__
147                                         "%s\n", _dl_reltypes(reloc_type)
148 #else
149                                         "%x\n", reloc_type
150 #endif
151                         );
152
153                         _dl_exit(-res);
154                 }
155                 if (unlikely(res > 0)) {
156                         _dl_dprintf(2, "can't resolve symbol\n");
157
158                         return res;
159                 }
160         }
161
162         return 0;
163 }
164
165 static int _dl_do_reloc(struct elf_resolve *tpnt,struct dyn_elf *scope,
166                         ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
167 {
168         int reloc_type;
169         int symtab_index, lsb;
170         char *symname;
171         unsigned long *reloc_addr;
172         unsigned long symbol_addr;
173 #ifdef __SUPPORT_LD_DEBUG__
174         unsigned long old_val;
175 #endif
176         struct symbol_ref sym_ref;
177
178         reloc_type   = ELF32_R_TYPE(rpnt->r_info);
179         symtab_index = ELF32_R_SYM(rpnt->r_info);
180         symbol_addr  = 0;
181         lsb          = !!(symtab[symtab_index].st_other & STO_SH5_ISA32);
182         sym_ref.sym = &symtab[symtab_index];
183         sym_ref.tpnt = NULL;
184         symname      = strtab + symtab[symtab_index].st_name;
185         reloc_addr   = (unsigned long *)(intptr_t)
186                 (tpnt->loadaddr + (unsigned long)rpnt->r_offset);
187
188         if (symtab_index) {
189                 int stb;
190
191                 symbol_addr = (unsigned long)_dl_find_hash(symname, scope, tpnt,
192                                                            elf_machine_type_class(reloc_type), &sym_ref);
193
194                 /*
195                  * We want to allow undefined references to weak symbols - this
196                  * might have been intentional. We should not be linking local
197                  * symbols here, so all bases should be covered.
198                  */
199                 stb = ELF32_ST_BIND(symtab[symtab_index].st_info);
200
201                 if (stb != STB_WEAK && !symbol_addr) {
202                         _dl_dprintf (2, "%s: can't resolve symbol '%s'\n",
203                                      _dl_progname, symname);
204                         _dl_exit (1);
205                 }
206         }
207
208 #ifdef __SUPPORT_LD_DEBUG__
209         old_val = *reloc_addr;
210 #endif
211
212         switch (reloc_type) {
213         case R_SH_NONE:
214                 break;
215         case R_SH_COPY:
216                 _dl_memcpy((char *)reloc_addr,
217                            (char *)symbol_addr, symtab[symtab_index].st_size);
218                 break;
219         case R_SH_DIR32:
220         case R_SH_GLOB_DAT:
221         case R_SH_JMP_SLOT:
222                 *reloc_addr = (symbol_addr + rpnt->r_addend) | lsb;
223                 break;
224         case R_SH_REL32:
225                 *reloc_addr = symbol_addr + rpnt->r_addend -
226                         (unsigned long)reloc_addr;
227                 break;
228         case R_SH_RELATIVE:
229                 *reloc_addr = (unsigned long)tpnt->loadaddr + rpnt->r_addend;
230                 break;
231         case R_SH_RELATIVE_LOW16:
232         case R_SH_RELATIVE_MEDLOW16:
233             {
234                 unsigned long word, value;
235
236                 word = (unsigned long)reloc_addr & ~0x3fffc00;
237                 value = (unsigned long)tpnt->loadaddr + rpnt->r_addend;
238
239                 if (reloc_type == R_SH_RELATIVE_MEDLOW16)
240                         value >>= 16;
241
242                 word |= (value & 0xffff) << 10;
243                 *reloc_addr = word;
244
245                 break;
246             }
247         case R_SH_IMM_LOW16:
248         case R_SH_IMM_MEDLOW16:
249             {
250                 unsigned long word, value;
251
252                 word = (unsigned long)reloc_addr & ~0x3fffc00;
253                 value = (symbol_addr + rpnt->r_addend) | lsb;
254
255                 if (reloc_type == R_SH_IMM_MEDLOW16)
256                         value >>= 16;
257
258                 word |= (value & 0xffff) << 10;
259                 *reloc_addr = word;
260
261                 break;
262             }
263         case R_SH_IMM_LOW16_PCREL:
264         case R_SH_IMM_MEDLOW16_PCREL:
265             {
266                 unsigned long word, value;
267
268                 word = (unsigned long)reloc_addr & ~0x3fffc00;
269                 value = symbol_addr + rpnt->r_addend -
270                         (unsigned long)reloc_addr;
271
272                 if (reloc_type == R_SH_IMM_MEDLOW16_PCREL)
273                         value >>= 16;
274
275                 word |= (value & 0xffff) << 10;
276                 *reloc_addr = word;
277
278                 break;
279             }
280         default:
281                 return -1; /*call _dl_exit(1) */
282         }
283
284 #ifdef __SUPPORT_LD_DEBUG__
285         if (_dl_debug_reloc && _dl_debug_detail)
286                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n",
287                             old_val, *reloc_addr, reloc_addr);
288 #endif
289
290         return 0;
291 }
292
293 static int _dl_do_lazy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
294                              ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
295 {
296         int reloc_type, symtab_index, lsb;
297         unsigned long *reloc_addr;
298 #ifdef __SUPPORT_LD_DEBUG__
299         unsigned long old_val;
300 #endif
301
302         reloc_type   = ELF32_R_TYPE(rpnt->r_info);
303         symtab_index = ELF32_R_SYM(rpnt->r_info);
304         lsb          = !!(symtab[symtab_index].st_other & STO_SH5_ISA32);
305         reloc_addr   = (unsigned long *)(intptr_t)
306                 (tpnt->loadaddr + (unsigned long)rpnt->r_offset);
307
308 #ifdef __SUPPORT_LD_DEBUG__
309         old_val = *reloc_addr;
310 #endif
311
312         switch (reloc_type) {
313         case R_SH_NONE:
314                 break;
315         case R_SH_JMP_SLOT:
316                 *reloc_addr += (unsigned long)tpnt->loadaddr | lsb;
317                 break;
318         default:
319                 return -1; /*call _dl_exit(1) */
320         }
321
322 #ifdef __SUPPORT_LD_DEBUG__
323         if (_dl_debug_reloc && _dl_debug_detail)
324                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n",
325                             old_val, *reloc_addr, reloc_addr);
326 #endif
327
328         return 0;
329 }
330
331 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
332         unsigned long rel_addr, unsigned long rel_size)
333 {
334         (void)_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
335 }
336
337 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
338         unsigned long rel_addr, unsigned long rel_size)
339 {
340         return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
341 }