OSDN Git Service

0017c239b9baeac4ecd698793cd1957909391227
[uclinux-h8/uClibc.git] / 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 = 0;
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         old_val = *reloc_addr;
202 #endif
203
204         switch (reloc_type) {
205                 case R_386_NONE:
206                         break;
207                 case R_386_32:
208                         *reloc_addr += symbol_addr;
209                         break;
210                 case R_386_PC32:
211                         *reloc_addr += symbol_addr - (unsigned long)reloc_addr;
212                         break;
213                 case R_386_GLOB_DAT:
214                 case R_386_JMP_SLOT:
215                         *reloc_addr = symbol_addr;
216                         break;
217                 case R_386_RELATIVE:
218                         *reloc_addr += (unsigned long)tpnt->loadaddr;
219                         break;
220                 case R_386_COPY:
221                         if (symbol_addr) {
222 #if defined (__SUPPORT_LD_DEBUG__)
223                                 if (_dl_debug_move)
224                                         _dl_dprintf(_dl_debug_file,
225                                                     "\n%s move %d bytes from %x to %x",
226                                                     symname, symtab[symtab_index].st_size,
227                                                     symbol_addr, reloc_addr);
228 #endif
229
230                                 _dl_memcpy((char *)reloc_addr,
231                                            (char *)symbol_addr,
232                                            symtab[symtab_index].st_size);
233                         }
234                         break;
235 #if defined USE_TLS && USE_TLS
236                 case R_386_TLS_DTPMOD32:
237                         *reloc_addr = tls_tpnt->l_tls_modid;
238                         break;
239                 case R_386_TLS_DTPOFF32:
240                         /* During relocation all TLS symbols are defined and used.
241                          * Therefore the offset is already correct. */
242                         *reloc_addr = symbol_addr;
243                         break;
244                 case R_386_TLS_TPOFF32:
245                         /* The offset is positive, backward from the thread pointer. */
246                         CHECK_STATIC_TLS((struct link_map*) tls_tpnt);
247                         *reloc_addr += tls_tpnt->l_tls_offset - symbol_addr;
248                         break;
249                 case R_386_TLS_TPOFF:
250                         /* The offset is negative, forward from the thread pointer. */
251                         CHECK_STATIC_TLS((struct link_map*) tls_tpnt);
252                         *reloc_addr += symbol_addr - tls_tpnt->l_tls_offset;
253                         break;
254 #endif
255                 default:
256                         return -1;
257         }
258
259 #if defined (__SUPPORT_LD_DEBUG__)
260         if (_dl_debug_reloc && _dl_debug_detail)
261                 _dl_dprintf(_dl_debug_file, "\n\tpatched: %x ==> %x @ %x\n",
262                             old_val, *reloc_addr, reloc_addr);
263 #endif
264
265         return 0;
266 }
267
268 static int
269 _dl_do_lazy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
270                   ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
271 {
272         int reloc_type;
273         unsigned long *reloc_addr;
274 #if defined (__SUPPORT_LD_DEBUG__)
275         unsigned long old_val;
276 #endif
277
278         (void)scope;
279         (void)symtab;
280         (void)strtab;
281
282         reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset);
283         reloc_type = ELF32_R_TYPE(rpnt->r_info);
284
285 #if defined (__SUPPORT_LD_DEBUG__)
286         old_val = *reloc_addr;
287 #endif
288
289         switch (reloc_type) {
290                 case R_386_NONE:
291                         break;
292                 case R_386_JMP_SLOT:
293                         *reloc_addr += (unsigned long)tpnt->loadaddr;
294                         break;
295                 default:
296                         return -1;
297         }
298
299 #if defined (__SUPPORT_LD_DEBUG__)
300         if (_dl_debug_reloc && _dl_debug_detail)
301                 _dl_dprintf(_dl_debug_file, "\n\tpatched: %x ==> %x @ %x\n",
302                             old_val, *reloc_addr, reloc_addr);
303 #endif
304
305         return 0;
306 }
307
308 void
309 _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
310                                       unsigned long rel_addr,
311                                       unsigned long rel_size)
312 {
313         (void)_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
314 }
315
316 int
317 _dl_parse_relocation_information(struct dyn_elf *rpnt,
318                                  unsigned long rel_addr,
319                                  unsigned long rel_size)
320 {
321         return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
322 }