OSDN Git Service

Joakim Tjernlund writes:
[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, ELF_RTYPE_CLASS_PLT);
194         if (!new_addr) {
195                 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n",
196                             _dl_progname, symname);
197                 _dl_exit(1);
198         }
199
200 #ifdef __SUPPORT_LD_DEBUG__
201         if ((unsigned long)got_addr < 0x20000000) {
202                 if (_dl_debug_bindings) {
203                         _dl_dprintf(_dl_debug_file, "\nresolve function: %s",
204                                     symname);
205
206                         if (_dl_debug_detail)
207                                 _dl_dprintf(_dl_debug_file,
208                                             "\n\tpatched %x ==> %x @ %x\n",
209                                             *got_addr, new_addr, got_addr);
210                 }
211         }
212
213         if (!_dl_debug_nofixups)
214                 *got_addr = new_addr;
215 #else
216         *got_addr = new_addr;
217 #endif
218
219         return (unsigned long)new_addr;
220 }
221
222 static int _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
223                      unsigned long rel_addr, unsigned long rel_size,
224                      int (*reloc_fnc)(struct elf_resolve *tpnt,
225                                       struct dyn_elf *scope,
226                                       ELF_RELOC *rpnt, Elf32_Sym *symtab,
227                                       char *strtab))
228 {
229         unsigned int i;
230         char *strtab;
231         Elf32_Sym *symtab;
232         ELF_RELOC *rpnt;
233         int symtab_index;
234
235         /* Now parse the relocation information */
236         rpnt = (ELF_RELOC *)(intptr_t)(rel_addr + tpnt->loadaddr);
237         rel_size = rel_size / sizeof(ELF_RELOC);
238
239         symtab = (Elf32_Sym *)(intptr_t)
240                 (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
241         strtab = (char *)(tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
242
243         for (i = 0; i < rel_size; i++, rpnt++) {
244                 int res;
245
246                 symtab_index = ELF32_R_SYM(rpnt->r_info);
247
248                 /* When the dynamic linker bootstrapped itself, it resolved
249                    some symbols. Make sure we do not do them again */
250                 if (!symtab_index && tpnt->libtype == program_interpreter)
251                         continue;
252                 if (symtab_index && tpnt->libtype == program_interpreter &&
253                     _dl_symbol(strtab + symtab[symtab_index].st_name))
254                         continue;
255
256 #ifdef __SUPPORT_LD_DEBUG__
257                 debug_sym(symtab,strtab,symtab_index);
258                 debug_reloc(symtab,strtab,rpnt);
259 #endif
260
261                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
262                 if (res == 0)
263                         continue;
264
265                 _dl_dprintf(2, "\n%s: ",_dl_progname);
266
267                 if (symtab_index)
268                         _dl_dprintf(2, "symbol '%s': ",
269                                 strtab + symtab[symtab_index].st_name);
270
271                 if (res < 0) {
272                         int reloc_type = ELF32_R_TYPE(rpnt->r_info);
273
274                         _dl_dprintf(2, "can't handle reloc type "
275 #ifdef __SUPPORT_LD_DEBUG__
276                                         "%s\n", _dl_reltypes(reloc_type)
277 #else
278                                         "%x\n", reloc_type
279 #endif
280                         );
281
282                         _dl_exit(-res);
283                 } else if (res > 0) {
284                         _dl_dprintf(2, "can't resolve symbol\n");
285
286                         return res;
287                 }
288         }
289
290         return 0;
291 }
292
293 static int _dl_do_reloc(struct elf_resolve *tpnt,struct dyn_elf *scope,
294                         ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
295 {
296         int reloc_type;
297         int symtab_index, lsb;
298         char *symname;
299         unsigned long *reloc_addr;
300         unsigned long symbol_addr;
301 #ifdef __SUPPORT_LD_DEBUG__
302         unsigned long old_val;
303 #endif
304
305         reloc_type   = ELF32_R_TYPE(rpnt->r_info);
306         symtab_index = ELF32_R_SYM(rpnt->r_info);
307         symbol_addr  = 0;
308         lsb          = symtab[symtab_index].st_other & 4;
309         symname      = strtab + symtab[symtab_index].st_name;
310         reloc_addr   = (unsigned long *)(intptr_t)
311                 (tpnt->loadaddr + (unsigned long)rpnt->r_offset);
312
313         if (symtab_index) {
314                 int stb;
315
316                 symbol_addr = (unsigned long)_dl_find_hash(symname, scope,
317                                                            elf_machine_type_class(reloc_type));
318
319                 /*
320                  * We want to allow undefined references to weak symbols - this
321                  * might have been intentional. We should not be linking local
322                  * symbols here, so all bases should be covered.
323                  */
324                 stb = ELF32_ST_BIND(symtab[symtab_index].st_info);
325
326                 if (stb == STB_GLOBAL && !symbol_addr) {
327 #ifdef __SUPPORT_LD_DEBUG__
328                         _dl_dprintf(2, "\tglobal symbol '%s' "
329                                     "already defined in '%s'\n",
330                                     symname, tpnt->libname);
331 #endif
332                         return 0;
333                 }
334         }
335
336 #ifdef __SUPPORT_LD_DEBUG__
337         old_val = *reloc_addr;
338 #endif
339
340         switch (reloc_type) {
341         case R_SH_NONE:
342                 break;
343         case R_SH_COPY:
344                 /* handled later on */
345                 break;
346         case R_SH_DIR32:
347         case R_SH_GLOB_DAT:
348         case R_SH_JMP_SLOT:
349                 *reloc_addr = (symbol_addr + rpnt->r_addend) | lsb;
350                 break;
351         case R_SH_REL32:
352                 *reloc_addr = symbol_addr + rpnt->r_addend -
353                         (unsigned long)reloc_addr;
354                 break;
355         case R_SH_RELATIVE:
356                 *reloc_addr = (unsigned long)tpnt->loadaddr + rpnt->r_addend;
357                 break;
358         case R_SH_RELATIVE_LOW16:
359         case R_SH_RELATIVE_MEDLOW16:
360             {
361                 unsigned long word, value;
362
363                 word = (unsigned long)reloc_addr & ~0x3fffc00;
364                 value = (unsigned long)tpnt->loadaddr + rpnt->r_addend;
365
366                 if (reloc_type == R_SH_RELATIVE_MEDLOW16)
367                         value >>= 16;
368
369                 word |= (value & 0xffff) << 10;
370                 *reloc_addr = word;
371
372                 break;
373             }
374         case R_SH_IMM_LOW16:
375         case R_SH_IMM_MEDLOW16:
376             {
377                 unsigned long word, value;
378
379                 word = (unsigned long)reloc_addr & ~0x3fffc00;
380                 value = (symbol_addr + rpnt->r_addend) | lsb;
381
382                 if (reloc_type == R_SH_IMM_MEDLOW16)
383                         value >>= 16;
384
385                 word |= (value & 0xffff) << 10;
386                 *reloc_addr = word;
387
388                 break;
389             }
390         case R_SH_IMM_LOW16_PCREL:
391         case R_SH_IMM_MEDLOW16_PCREL:
392             {
393                 unsigned long word, value;
394
395                 word = (unsigned long)reloc_addr & ~0x3fffc00;
396                 value = symbol_addr + rpnt->r_addend -
397                         (unsigned long)reloc_addr;
398
399                 if (reloc_type == R_SH_IMM_MEDLOW16_PCREL)
400                         value >>= 16;
401
402                 word |= (value & 0xffff) << 10;
403                 *reloc_addr = word;
404
405                 break;
406             }
407         default:
408                 return -1; /*call _dl_exit(1) */
409         }
410
411 #ifdef __SUPPORT_LD_DEBUG__
412         if (_dl_debug_reloc && _dl_debug_detail)
413                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x",
414                             old_val, *reloc_addr, reloc_addr);
415 #endif
416
417         return 0;
418 }
419
420 static int _dl_do_lazy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
421                              ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
422 {
423         int reloc_type, symtab_index, lsb;
424         unsigned long *reloc_addr;
425 #ifdef __SUPPORT_LD_DEBUG__
426         unsigned long old_val;
427 #endif
428
429         reloc_type   = ELF32_R_TYPE(rpnt->r_info);
430         symtab_index = ELF32_R_SYM(rpnt->r_info);
431         lsb          = symtab[symtab_index].st_other & 4;
432         reloc_addr   = (unsigned long *)(intptr_t)
433                 (tpnt->loadaddr + (unsigned long)rpnt->r_offset);
434
435 #ifdef __SUPPORT_LD_DEBUG__
436         old_val = *reloc_addr;
437 #endif
438
439         switch (reloc_type) {
440         case R_SH_NONE:
441                 break;
442         case R_SH_JMP_SLOT:
443                 *reloc_addr += (unsigned long)tpnt->loadaddr | lsb;
444                 break;
445         default:
446                 return -1; /*call _dl_exit(1) */
447         }
448
449 #ifdef __SUPPORT_LD_DEBUG__
450         if (_dl_debug_reloc && _dl_debug_detail)
451                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x",
452                             old_val, *reloc_addr, reloc_addr);
453 #endif
454
455         return 0;
456 }
457
458 /* This is done as a separate step, because there are cases where
459    information is first copied and later initialized.  This results in
460    the wrong information being copied.  Someone at Sun was complaining about
461    a bug in the handling of _COPY by SVr4, and this may in fact be what he
462    was talking about.  Sigh. */
463
464 /* No, there are cases where the SVr4 linker fails to emit COPY relocs
465    at all */
466 static int _dl_do_copy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
467                        ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
468 {
469         int reloc_type;
470         int symtab_index;
471         unsigned long *reloc_addr;
472         unsigned long symbol_addr;
473         char *symname;
474         int goof = 0;
475
476         reloc_addr = (unsigned long *)(intptr_t)
477                 (tpnt->loadaddr + (unsigned long)rpnt->r_offset);
478         reloc_type = ELF32_R_TYPE(rpnt->r_info);
479
480         if (reloc_type != R_SH_COPY)
481                 return 0;
482
483         symtab_index = ELF32_R_SYM(rpnt->r_info);
484         symbol_addr  = 0;
485         symname      = strtab + symtab[symtab_index].st_name;
486
487         if (symtab_index) {
488                 symbol_addr = (unsigned long)
489                         _dl_find_hash(symname, scope, ELF_RTYPE_CLASS_COPY);
490
491                 if (!symbol_addr)
492                         goof++;
493         }
494
495         if (!goof) {
496 #ifdef __SUPPORT_LD_DEBUG__
497                 if (_dl_debug_move)
498                         _dl_dprintf(_dl_debug_file,
499                                     "\n%s move %x bytes from %x to %x",
500                                     symname, symtab[symtab_index].st_size,
501                                     symbol_addr, symtab[symtab_index].st_value);
502 #endif
503
504                 _dl_memcpy((char *)symtab[symtab_index].st_value,
505                            (char *)symbol_addr, symtab[symtab_index].st_size);
506         }
507
508         return goof;
509 }
510
511 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
512         unsigned long rel_addr, unsigned long rel_size, int type)
513 {
514         (void)_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
515 }
516
517 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
518         unsigned long rel_addr, unsigned long rel_size, int type)
519 {
520         return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
521 }
522
523 int _dl_parse_copy_information(struct dyn_elf *rpnt,
524         unsigned long rel_addr, unsigned long rel_size, int type)
525 {
526         return _dl_parse(rpnt->dyn, rpnt->next, rel_addr, rel_size, _dl_do_copy_reloc);
527 }
528