OSDN Git Service

Modify interfaces for _dl_parse_relocation_information()
[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  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 #ifdef __SUPPORT_LD_DEBUG__
34 static const char *_dl_reltypes_tab[] = {
35         /* SHcompact relocs */
36           [0] = "R_SH_NONE",            "R_SH_DIR32",
37                 "R_SH_REL32",           "R_SH_DIR8WPN",
38           [4] = "R_SH_IND12W",          "R_SH_DIR8WPL",
39                 "R_SH_DIR8WPZ",         "R_SH_DIR8BP",
40           [8] = "R_SH_DIR8W",           "R_SH_DIR8L",
41          [25] = "R_SH_SWITCH16",        "R_SH_SWITCH32",
42                 "R_SH_USES",            "R_SH_COUNT",
43          [29] = "R_SH_ALIGN",           "R_SH_CODE",
44                 "R_SH_DATA",            "R_SH_LABEL",
45          [33] = "R_SH_SWITCH8",         "R_SH_GNU_VTINHERIT",
46                 "R_SH_GNU_VTENTRY",
47         [160] = "R_SH_GOT32",           "R_SH_PLT32",
48                 "R_SH_COPY",            "R_SH_GLOB_DAT",
49         [164] = "R_SH_JMP_SLOT",        "R_SH_RELATIVE",
50                 "R_SH_GOTOFF",          "R_SH_GOTPC",
51
52         /* SHmedia relocs */
53          [45] = "R_SH_DIR5U",           "R_SH_DIR6U",
54                 "R_SH_DIR6S",           "R_SH_DIR10S",
55          [49] = "R_SH_DIR10SW",         "R_SH_DIR10SL",
56                 "R_SH_DIR10SQ",
57         [169] = "R_SH_GOT_LOW16",       "R_SH_GOT_MEDLOW16",
58                 "R_SH_GOT_MEDHI16",     "R_SH_GOT_HI16",
59         [173] = "R_SH_GOTPLT_LOW16",    "R_SH_GOTPLT_MEDLOW16",
60                 "R_SH_GOTPLT_MEDHI16",  "R_SH_GOTPLT_HI16",
61         [177] = "R_SH_PLT_LOW16",       "R_SH_PLT_MEDLOW16",
62                 "R_SH_PLT_MEDHI16",     "R_SH_PLT_HI16",
63         [181] = "R_SH_GOTOFF_LOW16",    "R_SH_GOTOFF_MEDLOW16",
64                 "R_SH_GOTOFF_MEDHI16",  "R_SH_GOTOFF_HI16",
65         [185] = "R_SH_GOTPC_LOW16",     "R_SH_GOTPC_MEDLOW16",
66                 "R_SH_GOTPC_MEDHI16",   "R_SH_GOTPC_HI16",
67         [189] = "R_SH_GOT10BY4",        "R_SH_GOTPLT10BY4",
68                 "R_SH_GOT10BY8",        "R_SH_GOTPLT10BY8",
69         [193] = "R_SH_COPY64",          "R_SH_GLOB_DAT64",
70                 "R_SH_JMP_SLOT64",      "R_SH_RELATIVE64",
71         [197] = "R_SH_RELATIVE_LOW16",  "R_SH_RELATIVE_MEDLOW16",
72                 "R_SH_RELATIVE_MEDHI16","R_SH_RELATIVE_HI16",
73         [242] = "R_SH_SHMEDIA_CODE",    "R_SH_PT_16",
74                 "R_SH_IMMS16",          "R_SH_IMMU16",
75         [246] = "R_SH_IMM_LOW16",       "R_SH_IMM_LOW16_PCREL",
76                 "R_SH_IMM_MEDLOW16",    "R_SH_IMM_MEDLOW16_PCREL",
77         [250] = "R_SH_IMM_MEDHI16",     "R_SH_IMM_MEDHI16_PCREL",
78                 "R_SH_IMM_HI16",        "R_SH_IMM_HI16_PCREL",
79         [254] = "R_SH_64",              "R_SH_64_PCREL",
80 };
81
82 static const char *_dl_reltypes(int type)
83 {
84         static char buf[22];
85         const char *str;
86         int tabsize;
87
88         tabsize = sizeof(_dl_reltypes_tab)/sizeof(_dl_reltypes_tab[0]);
89         str     = _dl_reltypes_tab[type];
90
91         if (type >= tabsize || str == NULL)
92                 str =_dl_simple_ltoa(buf, (unsigned long)(type));
93
94         return str;
95 }
96
97 static void debug_sym(Elf32_Sym *symtab, char *strtab, int symtab_index)
98 {
99         if (!_dl_debug_symbols || !symtab_index)
100                 return;
101
102         _dl_dprintf(_dl_debug_file,
103                 "\n%s\tvalue=%x\tsize=%x\tinfo=%x\tother=%x\tshndx=%x",
104                 strtab + symtab[symtab_index].st_name,
105                 symtab[symtab_index].st_value,
106                 symtab[symtab_index].st_size,
107                 symtab[symtab_index].st_info,
108                 symtab[symtab_index].st_other,
109                 symtab[symtab_index].st_shndx);
110 }
111
112 static void debug_reloc(Elf32_Sym *symtab, char *strtab, ELF_RELOC *rpnt)
113 {
114         if (!_dl_debug_reloc)
115                 return;
116
117         if (_dl_debug_symbols) {
118                 _dl_dprintf(_dl_debug_file, "\n\t");
119         } else {
120                 int symtab_index;
121                 const char *sym;
122
123                 symtab_index = ELF32_R_SYM(rpnt->r_info);
124                 sym = symtab_index ? strtab + symtab[symtab_index].st_name
125                                    : "sym=0x0";
126
127                 _dl_dprintf(_dl_debug_file, "\n%s\n\t", sym);
128         }
129
130         _dl_dprintf(_dl_debug_file, "%s\toffset=%x",
131                     _dl_reltypes(ELF32_R_TYPE(rpnt->r_info)),
132                     rpnt->r_offset);
133
134 #ifdef ELF_USES_RELOCA
135         _dl_dprintf(_dl_debug_file, "\taddend=%x", rpnt->r_addend);
136 #endif
137
138         _dl_dprintf(_dl_debug_file, "\n");
139
140 }
141 #endif /* __SUPPORT_LD_DEBUG__ */
142
143 /* Program to load an ELF binary on a linux system, and run it.
144    References to symbols in sharable libraries can be resolved by either
145    an ELF sharable library or a linux style of shared library. */
146
147 /* Disclaimer:  I have never seen any AT&T source code for SVr4, nor have
148    I ever taken any courses on internals.  This program was developed using
149    information available through the book "UNIX SYSTEM V RELEASE 4,
150    Programmers guide: Ansi C and Programming Support Tools", which did
151    a more than adequate job of explaining everything required to get this
152    working. */
153
154 extern int _dl_linux_resolve(void);
155
156 unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
157 {
158         int reloc_type;
159         ELF_RELOC *this_reloc;
160         char *strtab;
161         Elf32_Sym *symtab;
162         int symtab_index;
163         char *rel_addr;
164         char *new_addr;
165         char **got_addr;
166         unsigned long instr_addr;
167         char *symname;
168
169         rel_addr = (char *)(tpnt->dynamic_info[DT_JMPREL] + tpnt->loadaddr);
170
171         this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry);
172         reloc_type = ELF32_R_TYPE(this_reloc->r_info);
173         symtab_index = ELF32_R_SYM(this_reloc->r_info);
174
175         symtab = (Elf32_Sym *)(intptr_t)
176                 (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
177         strtab = (char *)(tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
178         symname = strtab + symtab[symtab_index].st_name;
179
180         if (reloc_type != R_SH_JMP_SLOT) {
181                 _dl_dprintf(2, "%s: Incorrect relocation type in jump reloc\n",
182                             _dl_progname);
183                 _dl_exit(1);
184         }
185
186         /* Address of jump instruction to fix up */
187         instr_addr = ((unsigned long)this_reloc->r_offset +
188                         (unsigned long)tpnt->loadaddr);
189         got_addr = (char **)instr_addr;
190
191
192         /* Get the address of the GOT entry */
193         new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt, resolver);
194         if (!new_addr) {
195                 new_addr = _dl_find_hash(symname, NULL, NULL, resolver);
196
197                 if (new_addr)
198                         return (unsigned long)new_addr;
199
200                 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n",
201                             _dl_progname, symname);
202                 _dl_exit(1);
203         }
204
205 #ifdef __SUPPORT_LD_DEBUG__
206         if ((unsigned long)got_addr < 0x20000000) {
207                 if (_dl_debug_bindings) {
208                         _dl_dprintf(_dl_debug_file, "\nresolve function: %s",
209                                     symname);
210
211                         if (_dl_debug_detail)
212                                 _dl_dprintf(_dl_debug_file,
213                                             "\n\tpatched %x ==> %x @ %x\n",
214                                             *got_addr, new_addr, got_addr);
215                 }
216         }
217
218         if (!_dl_debug_nofixups)
219                 *got_addr = new_addr;
220 #else
221         *got_addr = new_addr;
222 #endif
223
224         return (unsigned long)new_addr;
225 }
226
227 static int _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
228                      unsigned long rel_addr, unsigned long rel_size,
229                      int (*reloc_fnc)(struct elf_resolve *tpnt,
230                                       struct dyn_elf *scope,
231                                       ELF_RELOC *rpnt, Elf32_Sym *symtab,
232                                       char *strtab))
233 {
234         unsigned int i;
235         char *strtab;
236         Elf32_Sym *symtab;
237         ELF_RELOC *rpnt;
238         int symtab_index;
239
240         /* Now parse the relocation information */
241         rpnt = (ELF_RELOC *)(intptr_t)(rel_addr + tpnt->loadaddr);
242         rel_size = rel_size / sizeof(ELF_RELOC);
243
244         symtab = (Elf32_Sym *)(intptr_t)
245                 (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
246         strtab = (char *)(tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
247
248         for (i = 0; i < rel_size; i++, rpnt++) {
249                 int res;
250
251                 symtab_index = ELF32_R_SYM(rpnt->r_info);
252
253                 /* When the dynamic linker bootstrapped itself, it resolved
254                    some symbols. Make sure we do not do them again */
255                 if (!symtab_index && tpnt->libtype == program_interpreter)
256                         continue;
257                 if (symtab_index && tpnt->libtype == program_interpreter &&
258                     _dl_symbol(strtab + symtab[symtab_index].st_name))
259                         continue;
260
261 #ifdef __SUPPORT_LD_DEBUG__
262                 debug_sym(symtab,strtab,symtab_index);
263                 debug_reloc(symtab,strtab,rpnt);
264 #endif
265
266                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
267                 if (res == 0)
268                         continue;
269
270                 _dl_dprintf(2, "\n%s: ",_dl_progname);
271
272                 if (symtab_index)
273                         _dl_dprintf(2, "symbol '%s': ",
274                                 strtab + symtab[symtab_index].st_name);
275
276                 if (res < 0) {
277                         int reloc_type = ELF32_R_TYPE(rpnt->r_info);
278
279                         _dl_dprintf(2, "can't handle reloc type "
280 #ifdef __SUPPORT_LD_DEBUG__
281                                         "%s\n", _dl_reltypes(reloc_type)
282 #else
283                                         "%x\n", reloc_type
284 #endif
285                         );
286
287                         _dl_exit(-res);
288                 } else if (res > 0) {
289                         _dl_dprintf(2, "can't resolve symbol\n");
290
291                         return res;
292                 }
293         }
294
295         return 0;
296 }
297
298 static int _dl_do_reloc(struct elf_resolve *tpnt,struct dyn_elf *scope,
299                         ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
300 {
301         int reloc_type;
302         int symtab_index, lsb;
303         char *symname;
304         unsigned long *reloc_addr;
305         unsigned long symbol_addr;
306 #ifdef __SUPPORT_LD_DEBUG__
307         unsigned long old_val;
308 #endif
309
310         reloc_type   = ELF32_R_TYPE(rpnt->r_info);
311         symtab_index = ELF32_R_SYM(rpnt->r_info);
312         symbol_addr  = 0;
313         lsb          = symtab[symtab_index].st_other & 4;
314         symname      = strtab + symtab[symtab_index].st_name;
315         reloc_addr   = (unsigned long *)(intptr_t)
316                 (tpnt->loadaddr + (unsigned long)rpnt->r_offset);
317
318         if (symtab_index) {
319                 int stb;
320
321                 symbol_addr = (unsigned long)_dl_find_hash(symname, scope,
322                                 (reloc_type == R_SH_JMP_SLOT ? tpnt : NULL),
323                                  symbolrel);
324
325                 /*
326                  * We want to allow undefined references to weak symbols - this
327                  * might have been intentional. We should not be linking local
328                  * symbols here, so all bases should be covered.
329                  */
330                 stb = ELF32_ST_BIND(symtab[symtab_index].st_info);
331
332                 if (stb == STB_GLOBAL && !symbol_addr) {
333 #ifdef __SUPPORT_LD_DEBUG__
334                         _dl_dprintf(2, "\tglobal symbol '%s' "
335                                     "already defined in '%s'\n",
336                                     symname, tpnt->libname);
337 #endif
338                         return 0;
339                 }
340         }
341
342 #ifdef __SUPPORT_LD_DEBUG__
343         old_val = *reloc_addr;
344 #endif
345
346         switch (reloc_type) {
347         case R_SH_NONE:
348                 break;
349         case R_SH_COPY:
350                 /* handled later on */
351                 break;
352         case R_SH_DIR32:
353         case R_SH_GLOB_DAT:
354         case R_SH_JMP_SLOT:
355                 *reloc_addr = (symbol_addr + rpnt->r_addend) | lsb;
356                 break;
357         case R_SH_REL32:
358                 *reloc_addr = symbol_addr + rpnt->r_addend -
359                         (unsigned long)reloc_addr;
360                 break;
361         case R_SH_RELATIVE:
362                 *reloc_addr = (unsigned long)tpnt->loadaddr + rpnt->r_addend;
363                 break;
364         case R_SH_RELATIVE_LOW16:
365         case R_SH_RELATIVE_MEDLOW16:
366             {
367                 unsigned long word, value;
368
369                 word = (unsigned long)reloc_addr & ~0x3fffc00;
370                 value = (unsigned long)tpnt->loadaddr + rpnt->r_addend;
371
372                 if (reloc_type == R_SH_RELATIVE_MEDLOW16)
373                         value >>= 16;
374
375                 word |= (value & 0xffff) << 10;
376                 *reloc_addr = word;
377
378                 break;
379             }
380         case R_SH_IMM_LOW16:
381         case R_SH_IMM_MEDLOW16:
382             {
383                 unsigned long word, value;
384
385                 word = (unsigned long)reloc_addr & ~0x3fffc00;
386                 value = (symbol_addr + rpnt->r_addend) | lsb;
387
388                 if (reloc_type == R_SH_IMM_MEDLOW16)
389                         value >>= 16;
390
391                 word |= (value & 0xffff) << 10;
392                 *reloc_addr = word;
393
394                 break;
395             }
396         case R_SH_IMM_LOW16_PCREL:
397         case R_SH_IMM_MEDLOW16_PCREL:
398             {
399                 unsigned long word, value;
400
401                 word = (unsigned long)reloc_addr & ~0x3fffc00;
402                 value = symbol_addr + rpnt->r_addend -
403                         (unsigned long)reloc_addr;
404
405                 if (reloc_type == R_SH_IMM_MEDLOW16_PCREL)
406                         value >>= 16;
407
408                 word |= (value & 0xffff) << 10;
409                 *reloc_addr = word;
410
411                 break;
412             }
413         default:
414                 return -1; /*call _dl_exit(1) */
415         }
416
417 #ifdef __SUPPORT_LD_DEBUG__
418         if (_dl_debug_reloc && _dl_debug_detail)
419                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x",
420                             old_val, *reloc_addr, reloc_addr);
421 #endif
422
423         return 0;
424 }
425
426 static int _dl_do_lazy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
427                              ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
428 {
429         int reloc_type, symtab_index, lsb;
430         unsigned long *reloc_addr;
431 #ifdef __SUPPORT_LD_DEBUG__
432         unsigned long old_val;
433 #endif
434
435         reloc_type   = ELF32_R_TYPE(rpnt->r_info);
436         symtab_index = ELF32_R_SYM(rpnt->r_info);
437         lsb          = symtab[symtab_index].st_other & 4;
438         reloc_addr   = (unsigned long *)(intptr_t)
439                 (tpnt->loadaddr + (unsigned long)rpnt->r_offset);
440
441 #ifdef __SUPPORT_LD_DEBUG__
442         old_val = *reloc_addr;
443 #endif
444
445         switch (reloc_type) {
446         case R_SH_NONE:
447                 break;
448         case R_SH_JMP_SLOT:
449                 *reloc_addr += (unsigned long)tpnt->loadaddr | lsb;
450                 break;
451         default:
452                 return -1; /*call _dl_exit(1) */
453         }
454
455 #ifdef __SUPPORT_LD_DEBUG__
456         if (_dl_debug_reloc && _dl_debug_detail)
457                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x",
458                             old_val, *reloc_addr, reloc_addr);
459 #endif
460
461         return 0;
462 }
463
464 /* This is done as a separate step, because there are cases where
465    information is first copied and later initialized.  This results in
466    the wrong information being copied.  Someone at Sun was complaining about
467    a bug in the handling of _COPY by SVr4, and this may in fact be what he
468    was talking about.  Sigh. */
469
470 /* No, there are cases where the SVr4 linker fails to emit COPY relocs
471    at all */
472 static int _dl_do_copy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
473                        ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
474 {
475         int reloc_type;
476         int symtab_index;
477         unsigned long *reloc_addr;
478         unsigned long symbol_addr;
479         char *symname;
480         int goof = 0;
481
482         reloc_addr = (unsigned long *)(intptr_t)
483                 (tpnt->loadaddr + (unsigned long)rpnt->r_offset);
484         reloc_type = ELF32_R_TYPE(rpnt->r_info);
485
486         if (reloc_type != R_SH_COPY)
487                 return 0;
488
489         symtab_index = ELF32_R_SYM(rpnt->r_info);
490         symbol_addr  = 0;
491         symname      = strtab + symtab[symtab_index].st_name;
492
493         if (symtab_index) {
494                 symbol_addr = (unsigned long)
495                         _dl_find_hash(symname, scope, NULL, copyrel);
496
497                 if (!symbol_addr)
498                         goof++;
499         }
500
501         if (!goof) {
502 #ifdef __SUPPORT_LD_DEBUG__
503                 if (_dl_debug_move)
504                         _dl_dprintf(_dl_debug_file,
505                                     "\n%s move %x bytes from %x to %x",
506                                     symname, symtab[symtab_index].st_size,
507                                     symbol_addr, symtab[symtab_index].st_value);
508 #endif
509
510                 _dl_memcpy((char *)symtab[symtab_index].st_value,
511                            (char *)symbol_addr, symtab[symtab_index].st_size);
512         }
513
514         return goof;
515 }
516
517 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
518         unsigned long rel_addr, unsigned long rel_size, int type)
519 {
520         (void)_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
521 }
522
523 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
524         unsigned long rel_addr, unsigned long rel_size, int type)
525 {
526         return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
527 }
528
529 int _dl_parse_copy_information(struct dyn_elf *rpnt,
530         unsigned long rel_addr, unsigned long rel_size, int type)
531 {
532         return _dl_parse(rpnt->dyn, rpnt->next, rel_addr, rel_size, _dl_do_copy_reloc);
533 }
534