]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/contrib/uclibc/ldso/ldso/i386/elfinterp.c
update
[l4.git] / l4 / pkg / uclibc / lib / contrib / uclibc / ldso / ldso / i386 / elfinterp.c
1 /* vi: set sw=4 ts=4: */
2 /* i386 ELF shared library loader suppport
3  *
4  * Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald,
5  *                              David Engel, Hongjiu Lu and Mitch D'Souza
6  * Copyright (C) 2001-2004 Erik Andersen
7  *
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. The name of the above contributors may not be
16  *    used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include "ldso.h"
33
34 /* Program to load an ELF binary on a linux system, and run it.
35    References to symbols in sharable libraries can be resolved by either
36    an ELF sharable library or a linux style of shared library. */
37
38 /* Disclaimer:  I have never seen any AT&T source code for SVr4, nor have
39    I ever taken any courses on internals.  This program was developed using
40    information available through the book "UNIX SYSTEM V RELEASE 4,
41    Programmers guide: Ansi C and Programming Support Tools", which did
42    a more than adequate job of explaining everything required to get this
43    working. */
44
45 extern int _dl_linux_resolve(void);
46
47 unsigned long
48 _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
49 {
50         ELF_RELOC *this_reloc;
51         char *strtab;
52         Elf32_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         this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry);
62         symtab_index = ELF32_R_SYM(this_reloc->r_info);
63
64         symtab = (Elf32_Sym *)(intptr_t)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 the jump instruction to fix up. */
69         instr_addr = ((unsigned long)this_reloc->r_offset +
70                       (unsigned long)tpnt->loadaddr);
71         got_addr = (char **)instr_addr;
72
73         /* Get the address of the GOT entry. */
74         new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT, NULL);
75         if (unlikely(!new_addr)) {
76                 _dl_dprintf(2, "%s: can't resolve symbol '%s' in lib '%s'.\n", _dl_progname, symname, tpnt->libname);
77                 _dl_exit(1);
78         }
79
80 #if defined (__SUPPORT_LD_DEBUG__)
81         if ((unsigned long)got_addr < 0x40000000) {
82                 if (_dl_debug_bindings) {
83                         _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
84                         if (_dl_debug_detail)
85                                 _dl_dprintf(_dl_debug_file,
86                                             "\n\tpatched: %x ==> %x @ %x",
87                                             *got_addr, new_addr, got_addr);
88                 }
89         }
90         if (!_dl_debug_nofixups) {
91                 *got_addr = new_addr;
92         }
93 #else
94         *got_addr = new_addr;
95 #endif
96
97         return (unsigned long)new_addr;
98 }
99
100 static int
101 _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
102           unsigned long rel_addr, unsigned long rel_size,
103           int (*reloc_fnc)(struct elf_resolve *tpnt, struct dyn_elf *scope,
104                            ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
105 {
106         unsigned int i;
107         char *strtab;
108         Elf32_Sym *symtab;
109         ELF_RELOC *rpnt;
110         int symtab_index;
111
112         /* Parse the relocation information. */
113         rpnt = (ELF_RELOC *)(intptr_t)rel_addr;
114         rel_size /= sizeof(ELF_RELOC);
115
116         symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
117         strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
118
119         for (i = 0; i < rel_size; i++, rpnt++) {
120                 int res;
121
122                 symtab_index = ELF32_R_SYM(rpnt->r_info);
123
124                 debug_sym(symtab, strtab, symtab_index);
125                 debug_reloc(symtab, strtab, rpnt);
126
127                 res = reloc_fnc(tpnt, scope, rpnt, symtab, strtab);
128
129                 if (res == 0)
130                         continue;
131
132                 _dl_dprintf(2, "\n%s: ", _dl_progname);
133
134                 if (symtab_index)
135                         _dl_dprintf(2, "symbol '%s': ",
136                                     strtab + symtab[symtab_index].st_name);
137
138                 if (unlikely(res < 0)) {
139                         int reloc_type = ELF32_R_TYPE(rpnt->r_info);
140
141 #if defined (__SUPPORT_LD_DEBUG__)
142                         _dl_dprintf(2, "can't handle reloc type '%s' in lib '%s'\n",
143                                     _dl_reltypes(reloc_type), tpnt->libname);
144 #else
145                         _dl_dprintf(2, "can't handle reloc type %x in lib '%s'\n",
146                                     reloc_type, tpnt->libname);
147 #endif
148                         return res;
149                 } else if (unlikely(res > 0)) {
150                         _dl_dprintf(2, "can't resolve symbol in lib '%s'.\n", tpnt->libname);
151                         return res;
152                 }
153         }
154
155         return 0;
156 }
157
158 static int
159 _dl_do_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
160              ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
161 {
162         int reloc_type;
163         int symtab_index;
164         char *symname;
165         struct elf_resolve *tls_tpnt = NULL;
166         unsigned long *reloc_addr;
167         unsigned long symbol_addr;
168 #if defined (__SUPPORT_LD_DEBUG__)
169         unsigned long old_val;
170 #endif
171         struct symbol_ref sym_ref;
172
173         reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset);
174         reloc_type = ELF32_R_TYPE(rpnt->r_info);
175         symtab_index = ELF32_R_SYM(rpnt->r_info);
176         symbol_addr = 0;
177         sym_ref.sym = &symtab[symtab_index];
178         sym_ref.tpnt = NULL;
179         symname = strtab + symtab[symtab_index].st_name;
180
181         if (symtab_index) {
182                 symbol_addr = (unsigned long)_dl_find_hash(symname, scope, tpnt,
183                                                            elf_machine_type_class(reloc_type), &sym_ref);
184
185                 /*
186                  * We want to allow undefined references to weak symbols - this
187                  * might have been intentional.  We should not be linking local
188                  * symbols here, so all bases should be covered.
189                  */
190                 if (unlikely(!symbol_addr && (ELF_ST_TYPE(symtab[symtab_index].st_info) != STT_TLS)
191                                         && ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK))
192                         return 1;
193                 tls_tpnt = sym_ref.tpnt;
194         } else {
195                 symbol_addr = symtab[symtab_index].st_value;
196                 tls_tpnt = tpnt;
197         }
198         
199
200 #if defined (__SUPPORT_LD_DEBUG__)
201         /* Start: aw11: fixed pagefualt with empty relocations */
202         if (reloc_type != R_386_NONE) old_val = *reloc_addr; else old_val = 0;
203         /* End: aw11 */
204 #endif
205
206         switch (reloc_type) {
207                 case R_386_NONE:
208                         break;
209                 case R_386_32:
210                         *reloc_addr += symbol_addr;
211                         break;
212                 case R_386_PC32:
213                         *reloc_addr += symbol_addr - (unsigned long)reloc_addr;
214                         break;
215                 case R_386_GLOB_DAT:
216                 case R_386_JMP_SLOT:
217                         *reloc_addr = symbol_addr;
218                         break;
219                 case R_386_RELATIVE:
220                         *reloc_addr += (unsigned long)tpnt->loadaddr;
221                         break;
222                 case R_386_COPY:
223                         if (symbol_addr) {
224 #if defined (__SUPPORT_LD_DEBUG__)
225                                 if (_dl_debug_move)
226                                         _dl_dprintf(_dl_debug_file,
227                                                     "\n%s move %d bytes from %x to %x",
228                                                     symname, symtab[symtab_index].st_size,
229                                                     symbol_addr, reloc_addr);
230 #endif
231
232                                 _dl_memcpy((char *)reloc_addr,
233                                            (char *)symbol_addr,
234                                            symtab[symtab_index].st_size);
235                         }
236                         break;
237 #if defined USE_TLS && USE_TLS
238                 case R_386_TLS_DTPMOD32:
239                         *reloc_addr = tls_tpnt->l_tls_modid;
240                         break;
241                 case R_386_TLS_DTPOFF32:
242                         /* During relocation all TLS symbols are defined and used.
243                          * Therefore the offset is already correct. */
244                         *reloc_addr = symbol_addr;
245                         break;
246                 case R_386_TLS_TPOFF32:
247                         /* The offset is positive, backward from the thread pointer. */
248                         CHECK_STATIC_TLS((struct link_map*) tls_tpnt);
249                         *reloc_addr += tls_tpnt->l_tls_offset - symbol_addr;
250                         break;
251                 case R_386_TLS_TPOFF:
252                         /* The offset is negative, forward from the thread pointer. */
253                         CHECK_STATIC_TLS((struct link_map*) tls_tpnt);
254                         *reloc_addr += symbol_addr - tls_tpnt->l_tls_offset;
255                         break;
256 #endif
257                 default:
258                         return -1;
259         }
260
261 #if defined (__SUPPORT_LD_DEBUG__)
262         if (_dl_debug_reloc && _dl_debug_detail)
263                 _dl_dprintf(_dl_debug_file, "\n\tpatched: %x ==> %x @ %x\n",
264                             old_val, *reloc_addr, reloc_addr);
265 #endif
266
267         return 0;
268 }
269
270 static int
271 _dl_do_lazy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
272                   ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
273 {
274         int reloc_type;
275         unsigned long *reloc_addr;
276 #if defined (__SUPPORT_LD_DEBUG__)
277         unsigned long old_val;
278 #endif
279
280         (void)scope;
281         (void)symtab;
282         (void)strtab;
283
284         reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset);
285         reloc_type = ELF32_R_TYPE(rpnt->r_info);
286
287 #if defined (__SUPPORT_LD_DEBUG__)
288         old_val = *reloc_addr;
289 #endif
290
291         switch (reloc_type) {
292                 case R_386_NONE:
293                         break;
294                 case R_386_JMP_SLOT:
295                         *reloc_addr += (unsigned long)tpnt->loadaddr;
296                         break;
297                 default:
298                         return -1;
299         }
300
301 #if defined (__SUPPORT_LD_DEBUG__)
302         if (_dl_debug_reloc && _dl_debug_detail)
303                 _dl_dprintf(_dl_debug_file, "\n\tpatched: %x ==> %x @ %x\n",
304                             old_val, *reloc_addr, reloc_addr);
305 #endif
306
307         return 0;
308 }
309
310 void
311 _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
312                                       unsigned long rel_addr,
313                                       unsigned long rel_size)
314 {
315         (void)_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
316 }
317
318 int
319 _dl_parse_relocation_information(struct dyn_elf *rpnt,
320                                  unsigned long rel_addr,
321                                  unsigned long rel_size)
322 {
323         return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
324 }