1 /* vi: set sw=8 ts=8: */
3 * ldso/ldso/sh64/elfinterp.c
5 * SuperH (sh64) ELF shared library loader suppport
7 * Copyright (C) 2003, 2004, 2005 Paul Mundt <lethal@linux-sh.org>
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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
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. */
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
46 extern int _dl_linux_resolve(void);
48 unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
50 ELF_RELOC *this_reloc;
57 unsigned long instr_addr;
60 rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
62 this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry);
63 symtab_index = ELF_R_SYM(this_reloc->r_info);
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;
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;
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);
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",
90 _dl_dprintf(_dl_debug_file,
91 "\n\tpatched %x ==> %x @ %x\n",
92 *got_addr, new_addr, got_addr);
96 if (!_dl_debug_nofixups)
102 return (unsigned long)new_addr;
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,
118 /* Now parse the relocation information */
119 rpnt = (ELF_RELOC *)(intptr_t)rel_addr;
120 rel_size = rel_size / sizeof(ELF_RELOC);
122 symtab = (ElfW(Sym) *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
123 strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
125 for (i = 0; i < rel_size; i++, rpnt++) {
128 symtab_index = ELF_R_SYM(rpnt->r_info);
129 debug_sym(symtab,strtab,symtab_index);
130 debug_reloc(symtab,strtab,rpnt);
132 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
136 _dl_dprintf(2, "\n%s: ",_dl_progname);
139 _dl_dprintf(2, "symbol '%s': ",
140 strtab + symtab[symtab_index].st_name);
142 if (unlikely(res < 0)) {
143 int reloc_type = ELF_R_TYPE(rpnt->r_info);
145 _dl_dprintf(2, "can't handle reloc type "
146 #ifdef __SUPPORT_LD_DEBUG__
147 "%s\n", _dl_reltypes(reloc_type)
155 if (unlikely(res > 0)) {
156 _dl_dprintf(2, "can't resolve symbol\n");
165 static int _dl_do_reloc(struct elf_resolve *tpnt,struct r_scope_elem *scope,
166 ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)
169 int symtab_index, lsb;
171 unsigned long *reloc_addr;
172 unsigned long symbol_addr;
173 #ifdef __SUPPORT_LD_DEBUG__
174 unsigned long old_val;
176 struct symbol_ref sym_ref;
178 reloc_type = ELF_R_TYPE(rpnt->r_info);
179 symtab_index = ELF_R_SYM(rpnt->r_info);
181 lsb = !!(symtab[symtab_index].st_other & STO_SH5_ISA32);
182 sym_ref.sym = &symtab[symtab_index];
184 symname = strtab + symtab[symtab_index].st_name;
185 reloc_addr = (unsigned long *)(intptr_t)
186 (tpnt->loadaddr + (unsigned long)rpnt->r_offset);
191 symbol_addr = (unsigned long)_dl_find_hash(symname, scope, tpnt,
192 elf_machine_type_class(reloc_type), &sym_ref);
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.
199 stb = ELF_ST_BIND(symtab[symtab_index].st_info);
201 if (stb != STB_WEAK && !symbol_addr) {
202 _dl_dprintf (2, "%s: can't resolve symbol '%s'\n",
203 _dl_progname, symname);
206 if (_dl_trace_prelink) {
207 _dl_debug_lookup (symname, tpnt, &symtab[symtab_index],
208 &sym_ref, elf_machine_type_class(reloc_type));
212 #ifdef __SUPPORT_LD_DEBUG__
213 old_val = *reloc_addr;
216 switch (reloc_type) {
220 _dl_memcpy((char *)reloc_addr,
221 (char *)symbol_addr, symtab[symtab_index].st_size);
226 *reloc_addr = (symbol_addr + rpnt->r_addend) | lsb;
229 *reloc_addr = symbol_addr + rpnt->r_addend -
230 (unsigned long)reloc_addr;
233 *reloc_addr = (unsigned long)tpnt->loadaddr + rpnt->r_addend;
235 case R_SH_RELATIVE_LOW16:
236 case R_SH_RELATIVE_MEDLOW16:
238 unsigned long word, value;
240 word = (unsigned long)reloc_addr & ~0x3fffc00;
241 value = (unsigned long)tpnt->loadaddr + rpnt->r_addend;
243 if (reloc_type == R_SH_RELATIVE_MEDLOW16)
246 word |= (value & 0xffff) << 10;
252 case R_SH_IMM_MEDLOW16:
254 unsigned long word, value;
256 word = (unsigned long)reloc_addr & ~0x3fffc00;
257 value = (symbol_addr + rpnt->r_addend) | lsb;
259 if (reloc_type == R_SH_IMM_MEDLOW16)
262 word |= (value & 0xffff) << 10;
267 case R_SH_IMM_LOW16_PCREL:
268 case R_SH_IMM_MEDLOW16_PCREL:
270 unsigned long word, value;
272 word = (unsigned long)reloc_addr & ~0x3fffc00;
273 value = symbol_addr + rpnt->r_addend -
274 (unsigned long)reloc_addr;
276 if (reloc_type == R_SH_IMM_MEDLOW16_PCREL)
279 word |= (value & 0xffff) << 10;
285 return -1; /*call _dl_exit(1) */
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);
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)
300 int reloc_type, symtab_index, lsb;
301 unsigned long *reloc_addr;
302 #ifdef __SUPPORT_LD_DEBUG__
303 unsigned long old_val;
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);
312 #ifdef __SUPPORT_LD_DEBUG__
313 old_val = *reloc_addr;
316 switch (reloc_type) {
320 *reloc_addr += (unsigned long)tpnt->loadaddr | lsb;
323 return -1; /*call _dl_exit(1) */
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);
335 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
336 unsigned long rel_addr, unsigned long rel_size)
338 (void)_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
341 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
342 struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size)
344 return _dl_parse(rpnt->dyn, scope, rel_addr, rel_size, _dl_do_reloc);