OSDN Git Service

mkostemp: fix implementation
[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         ElfW(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 = ELF_R_SYM(this_reloc->r_info);
64
65         symtab = (ElfW(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, &_dl_loaded_modules->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 r_scope_elem *scope,
106                      unsigned long rel_addr, unsigned long rel_size,
107                      int (*reloc_fnc)(struct elf_resolve *tpnt,
108                                       struct r_scope_elem *scope,
109                                       ELF_RELOC *rpnt, ElfW(Sym) *symtab,
110                                       char *strtab))
111 {
112         unsigned int i;
113         char *strtab;
114         ElfW(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 = (ElfW(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 = ELF_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 = ELF_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 r_scope_elem *scope,
166                         ELF_RELOC *rpnt, ElfW(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   = ELF_R_TYPE(rpnt->r_info);
179         symtab_index = ELF_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 = ELF_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                 if (_dl_trace_prelink) {
207                         _dl_debug_lookup (symname, tpnt, &symtab[symtab_index],
208                                 &sym_ref, elf_machine_type_class(reloc_type));
209                 }
210         }
211
212 #ifdef __SUPPORT_LD_DEBUG__
213         old_val = *reloc_addr;
214 #endif
215
216         switch (reloc_type) {
217         case R_SH_NONE:
218                 break;
219         case R_SH_COPY:
220                 _dl_memcpy((char *)reloc_addr,
221                            (char *)symbol_addr, symtab[symtab_index].st_size);
222                 break;
223         case R_SH_DIR32:
224         case R_SH_GLOB_DAT:
225         case R_SH_JMP_SLOT:
226                 *reloc_addr = (symbol_addr + rpnt->r_addend) | lsb;
227                 break;
228         case R_SH_REL32:
229                 *reloc_addr = symbol_addr + rpnt->r_addend -
230                         (unsigned long)reloc_addr;
231                 break;
232         case R_SH_RELATIVE:
233                 *reloc_addr = (unsigned long)tpnt->loadaddr + rpnt->r_addend;
234                 break;
235         case R_SH_RELATIVE_LOW16:
236         case R_SH_RELATIVE_MEDLOW16:
237             {
238                 unsigned long word, value;
239
240                 word = (unsigned long)reloc_addr & ~0x3fffc00;
241                 value = (unsigned long)tpnt->loadaddr + rpnt->r_addend;
242
243                 if (reloc_type == R_SH_RELATIVE_MEDLOW16)
244                         value >>= 16;
245
246                 word |= (value & 0xffff) << 10;
247                 *reloc_addr = word;
248
249                 break;
250             }
251         case R_SH_IMM_LOW16:
252         case R_SH_IMM_MEDLOW16:
253             {
254                 unsigned long word, value;
255
256                 word = (unsigned long)reloc_addr & ~0x3fffc00;
257                 value = (symbol_addr + rpnt->r_addend) | lsb;
258
259                 if (reloc_type == R_SH_IMM_MEDLOW16)
260                         value >>= 16;
261
262                 word |= (value & 0xffff) << 10;
263                 *reloc_addr = word;
264
265                 break;
266             }
267         case R_SH_IMM_LOW16_PCREL:
268         case R_SH_IMM_MEDLOW16_PCREL:
269             {
270                 unsigned long word, value;
271
272                 word = (unsigned long)reloc_addr & ~0x3fffc00;
273                 value = symbol_addr + rpnt->r_addend -
274                         (unsigned long)reloc_addr;
275
276                 if (reloc_type == R_SH_IMM_MEDLOW16_PCREL)
277                         value >>= 16;
278
279                 word |= (value & 0xffff) << 10;
280                 *reloc_addr = word;
281
282                 break;
283             }
284         default:
285                 return -1; /*call _dl_exit(1) */
286         }
287
288 #ifdef __SUPPORT_LD_DEBUG__
289         if (_dl_debug_reloc && _dl_debug_detail)
290                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n",
291                             old_val, *reloc_addr, reloc_addr);
292 #endif
293
294         return 0;
295 }
296
297 static int _dl_do_lazy_reloc(struct elf_resolve *tpnt, struct r_scope_elem *scope,
298                              ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)
299 {
300         int reloc_type, symtab_index, lsb;
301         unsigned long *reloc_addr;
302 #ifdef __SUPPORT_LD_DEBUG__
303         unsigned long old_val;
304 #endif
305
306         reloc_type   = ELF_R_TYPE(rpnt->r_info);
307         symtab_index = ELF_R_SYM(rpnt->r_info);
308         lsb          = !!(symtab[symtab_index].st_other & STO_SH5_ISA32);
309         reloc_addr   = (unsigned long *)(intptr_t)
310                 (tpnt->loadaddr + (unsigned long)rpnt->r_offset);
311
312 #ifdef __SUPPORT_LD_DEBUG__
313         old_val = *reloc_addr;
314 #endif
315
316         switch (reloc_type) {
317         case R_SH_NONE:
318                 break;
319         case R_SH_JMP_SLOT:
320                 *reloc_addr += (unsigned long)tpnt->loadaddr | lsb;
321                 break;
322         default:
323                 return -1; /*call _dl_exit(1) */
324         }
325
326 #ifdef __SUPPORT_LD_DEBUG__
327         if (_dl_debug_reloc && _dl_debug_detail)
328                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n",
329                             old_val, *reloc_addr, reloc_addr);
330 #endif
331
332         return 0;
333 }
334
335 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
336         unsigned long rel_addr, unsigned long rel_size)
337 {
338         (void)_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
339 }
340
341 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
342         struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size)
343 {
344         return _dl_parse(rpnt->dyn, scope, rel_addr, rel_size, _dl_do_reloc);
345 }