]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/contrib/uclibc/ldso/ldso/c6x/elfinterp.c
update
[l4.git] / l4 / pkg / uclibc / lib / contrib / uclibc / ldso / ldso / c6x / elfinterp.c
1 /* TI C64X DSBT ELF shared library loader suppport
2  * Copyright (C) 2010 Texas Instruments Incorporated
3  * Contributed by Mark Salter <msalter@redhat.com>
4  *
5  * Borrowed heavily from frv arch:
6  * Copyright (C) 2003, 2004 Red Hat, Inc.
7  * Contributed by Alexandre Oliva <aoliva@redhat.com>
8  * Lots of code copied from ../i386/elfinterp.c, so:
9  * Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald,
10  *               David Engel, Hongjiu Lu and Mitch D'Souza
11  * Copyright (C) 2001-2002, Erik Andersen
12  * All rights reserved.
13  *
14  * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
15  */
16
17 #include <features.h>
18
19 /* Program to load an ELF binary on a linux system, and run it.
20    References to symbols in sharable libraries can be resolved by either
21    an ELF sharable library or a linux style of shared library. */
22
23 /* Disclaimer:  I have never seen any AT&T source code for SVr4, nor have
24    I ever taken any courses on internals.  This program was developed using
25    information available through the book "UNIX SYSTEM V RELEASE 4,
26    Programmers guide: Ansi C and Programming Support Tools", which did
27    a more than adequate job of explaining everything required to get this
28    working. */
29
30 extern void __c6x_cache_sync(unsigned long start, unsigned long end)
31     attribute_hidden;
32
33 static void
34 _dl_c6x_flush_relocs(struct elf32_dsbt_loadmap *map)
35 {
36         unsigned long s, e;
37         s = map->segs[0].addr;
38         e = s + map->segs[0].p_memsz;
39         __c6x_cache_sync(s, e);
40         s = map->segs[1].addr;
41         e = s + map->segs[1].p_memsz;
42         __c6x_cache_sync(s, e);
43 }
44
45
46 attribute_hidden
47 char *
48 _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         char *symname;
58
59         rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
60
61         this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry);
62         symtab_index = ELF_R_SYM(this_reloc->r_info);
63
64         symtab = (Elf32_Sym *) tpnt->dynamic_info[DT_SYMTAB];
65         strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
66         symname = strtab + symtab[symtab_index].st_name;
67
68         /* Address of GOT entry fix up */
69         got_addr = (char **) DL_RELOC_ADDR(tpnt->loadaddr, this_reloc->r_offset);
70
71         /* Get the address to be used to fill in the GOT entry.  */
72         new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt,
73                                  ELF_RTYPE_CLASS_PLT, NULL);
74         if (unlikely(!new_addr)) {
75                 _dl_dprintf(2, "%s: can't resolve symbol '%s' in lib '%s'.\n", _dl_progname, symname, tpnt->libname);
76                 _dl_exit(1);
77         }
78
79
80 #if defined (__SUPPORT_LD_DEBUG__)
81         if (_dl_debug_bindings) {
82                 _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
83                 if (_dl_debug_detail)
84                         _dl_dprintf(_dl_debug_file,
85                                     "\n\tpatched %x ==> %x @ %x\n",
86                                     *got_addr, new_addr, got_addr);
87         }
88         if (!_dl_debug_nofixups) {
89                 *got_addr = new_addr;
90         }
91 #else
92         *got_addr = new_addr;
93 #endif
94
95         return new_addr;
96 }
97
98 static int
99 _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
100           unsigned long rel_addr, unsigned long rel_size,
101           int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope,
102                             ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
103 {
104         unsigned int i;
105         char *strtab;
106         Elf32_Sym *symtab;
107         ELF_RELOC *rpnt;
108         int symtab_index;
109
110         /* Now parse the relocation information */
111         rpnt = (ELF_RELOC *)rel_addr;
112         rel_size = rel_size / sizeof(ELF_RELOC);
113
114         symtab = (Elf32_Sym *)tpnt->dynamic_info[DT_SYMTAB];
115         strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
116
117         for (i = 0; i < rel_size; i++, rpnt++) {
118                 int res;
119
120                 symtab_index = ELF32_R_SYM(rpnt->r_info);
121                 debug_sym(symtab,strtab,symtab_index);
122                 debug_reloc(symtab,strtab,rpnt);
123
124                 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
125
126                 if (res==0) continue;
127
128                 _dl_dprintf(2, "\n%s: ",_dl_progname);
129
130                 if (symtab_index)
131                         _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
132
133                 if (res <0) {
134                         int reloc_type = ELF32_R_TYPE(rpnt->r_info);
135 #if defined (__SUPPORT_LD_DEBUG__)
136                         _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
137 #else
138                         _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
139 #endif
140                         _dl_exit(-res);
141                 } else if (res >0) {
142                         _dl_dprintf(2, "can't resolve symbol\n");
143                         return res;
144                 }
145         }
146         _dl_c6x_flush_relocs(tpnt->loadaddr.map);
147         return 0;
148 }
149
150 static int
151 _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
152               ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
153 {
154         int reloc_type;
155         int symtab_index;
156         char *symname;
157         unsigned long *reloc_addr;
158         unsigned long symbol_addr, sym_val;
159         long reloc_addend;
160         unsigned long old_val, new_val;
161
162         reloc_addr = (unsigned long *)(intptr_t)
163                 DL_RELOC_ADDR (tpnt->loadaddr, rpnt->r_offset);
164
165         reloc_type   = ELF32_R_TYPE(rpnt->r_info);
166         reloc_addend = rpnt->r_addend;
167         symtab_index = ELF32_R_SYM(rpnt->r_info);
168         symbol_addr  = 0;
169         symname      = strtab + symtab[symtab_index].st_name;
170
171         if (ELF32_ST_BIND (symtab[symtab_index].st_info) == STB_LOCAL) {
172                 symbol_addr = (unsigned long)
173                         DL_RELOC_ADDR (tpnt->loadaddr, symtab[symtab_index].st_value);
174         } else {
175                 symbol_addr = (unsigned long) _dl_find_hash(strtab + symtab[symtab_index].st_name,
176                                                             scope, tpnt, elf_machine_type_class(reloc_type),
177                                                             NULL);
178                 /*
179                  * We want to allow undefined references to weak symbols - this might
180                  * have been intentional.  We should not be linking local symbols
181                  * here, so all bases should be covered.
182                  */
183
184                 if (!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK) {
185                         _dl_dprintf (2, "%s: can't resolve symbol '%s'\n",
186                                      _dl_progname, strtab + symtab[symtab_index].st_name);
187                         _dl_exit (1);
188                 }
189         }
190         old_val = *reloc_addr;
191         sym_val = symbol_addr + reloc_addend;
192
193         switch (reloc_type) {
194         case R_C6000_NONE:
195                 break;
196         case R_C6000_ABS32:
197         case R_C6000_JUMP_SLOT:
198                 new_val = sym_val;
199                 *reloc_addr = sym_val;
200                 break;
201         case R_C6000_ABS_L16:
202                 new_val = (old_val & ~0x007fff80) | ((sym_val & 0xffff) << 7);
203                 *reloc_addr = new_val;
204                 break;
205         case R_C6000_ABS_H16:
206                 new_val = (old_val & ~0x007fff80) | ((sym_val >> 9) & 0x007fff80);
207                 *reloc_addr = new_val;
208                 break;
209         case R_C6000_PCR_S21:
210                 new_val = sym_val - (((unsigned long)reloc_addr) & ~31);
211                 *reloc_addr = (old_val & ~0x0fffff80) | (((new_val >> 2) & 0x1fffff) << 7);
212                 break;
213         case R_C6000_COPY:
214                 if (symbol_addr) {
215 #if defined (__SUPPORT_LD_DEBUG__)
216                         if (_dl_debug_move)
217                                 _dl_dprintf(_dl_debug_file,
218                                             "\n%s move %d bytes from %x to %x",
219                                             symname, symtab[symtab_index].st_size,
220                                             symbol_addr, reloc_addr);
221 #endif
222                         
223                         _dl_memcpy((char *)reloc_addr,
224                                    (char *)symbol_addr,
225                                    symtab[symtab_index].st_size);
226                 }
227                 break;
228         default:
229                 return -1; /*call _dl_exit(1) */
230         }
231 #if defined (__SUPPORT_LD_DEBUG__)
232         if (_dl_debug_reloc && _dl_debug_detail && reloc_type != R_C6000_NONE) {
233                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", old_val, new_val, reloc_addr);
234         }
235 #endif
236         return 0;
237 }
238
239 static int
240 _dl_do_lazy_reloc (struct elf_resolve *tpnt,
241                    struct dyn_elf *scope attribute_unused,
242                    ELF_RELOC *rpnt, ElfW(Sym) *symtab attribute_unused,
243                    char *strtab attribute_unused)
244 {
245         int reloc_type;
246         unsigned long *reloc_addr;
247         unsigned long old_val;
248
249         reloc_addr = (unsigned long *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset);
250         reloc_type = ELF_R_TYPE(rpnt->r_info);
251
252         old_val = *reloc_addr;
253
254         switch (reloc_type) {
255                 case R_C6000_NONE:
256                         break;
257                 case R_C6000_JUMP_SLOT:
258                         *reloc_addr = DL_RELOC_ADDR(tpnt->loadaddr, old_val);
259                         break;
260                 default:
261                         return -1;
262         }
263
264 #if defined (__SUPPORT_LD_DEBUG__)
265         if (_dl_debug_reloc && _dl_debug_detail)
266                 _dl_dprintf(_dl_debug_file, "\n\tpatched: %x ==> %x @ %x\n",
267                             old_val, *reloc_addr, reloc_addr);
268 #endif
269
270         return 0;
271 }
272
273 void
274 _dl_parse_lazy_relocation_information
275 (struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size)
276 {
277         _dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
278 }
279
280 int
281 _dl_parse_relocation_information
282 (struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size)
283 {
284         return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
285 }
286
287 /* We don't have copy relocs.  */
288 int
289 _dl_parse_copy_information
290 (struct dyn_elf *rpnt,
291  unsigned long rel_addr,
292  unsigned long rel_size)
293 {
294         return 0;
295 }
296