OSDN Git Service

psm missed a spot :)
[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         int reloc_type;
51         ELF_RELOC *this_reloc;
52         char *strtab;
53         Elf32_Sym *symtab;
54         int symtab_index;
55         char *rel_addr;
56         char *new_addr;
57         char **got_addr;
58         unsigned long instr_addr;
59         char *symname;
60
61         rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
62         this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry);
63         reloc_type = ELF32_R_TYPE(this_reloc->r_info);
64         symtab_index = ELF32_R_SYM(this_reloc->r_info);
65
66         symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
67         strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
68         symname = strtab + symtab[symtab_index].st_name;
69
70         if (unlikely(reloc_type != R_386_JMP_SLOT)) {
71                 _dl_dprintf(2, "%s: Incorrect relocation type in jump relocations\n",
72                             _dl_progname);
73                 _dl_exit(1);
74         }
75
76         /* Address of the jump instruction to fix up. */
77         instr_addr = ((unsigned long)this_reloc->r_offset +
78                       (unsigned long)tpnt->loadaddr);
79         got_addr = (char **)instr_addr;
80
81         /* Get the address of the GOT entry. */
82         new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT);
83         if (unlikely(!new_addr)) {
84                 _dl_dprintf(2, "%s: can't resolve symbol '%s' in lib '%s'.\n", _dl_progname, symname, tpnt->libname);
85                 _dl_exit(1);
86         }
87
88 #if defined (__SUPPORT_LD_DEBUG__)
89         if ((unsigned long)got_addr < 0x40000000) {
90                 if (_dl_debug_bindings) {
91                         _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
92                         if (_dl_debug_detail)
93                                 _dl_dprintf(_dl_debug_file,
94                                             "\n\tpatched: %x ==> %x @ %x",
95                                             *got_addr, new_addr, got_addr);
96                 }
97         }
98         if (!_dl_debug_nofixups) {
99                 *got_addr = new_addr;
100         }
101 #else
102         *got_addr = new_addr;
103 #endif
104
105         return (unsigned long)new_addr;
106 }
107
108 static int
109 _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
110           unsigned long rel_addr, unsigned long rel_size,
111           int (*reloc_fnc)(struct elf_resolve *tpnt, struct dyn_elf *scope,
112                            ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
113 {
114         unsigned int i;
115         char *strtab;
116         Elf32_Sym *symtab;
117         ELF_RELOC *rpnt;
118         int symtab_index;
119
120         /* Parse the relocation information. */
121         rpnt = (ELF_RELOC *)(intptr_t)rel_addr;
122         rel_size /= sizeof(ELF_RELOC);
123
124         symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
125         strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
126
127         for (i = 0; i < rel_size; i++, rpnt++) {
128                 int res;
129
130                 symtab_index = ELF32_R_SYM(rpnt->r_info);
131
132                 debug_sym(symtab, strtab, symtab_index);
133                 debug_reloc(symtab, strtab, rpnt);
134
135                 res = reloc_fnc(tpnt, scope, rpnt, symtab, strtab);
136
137                 if (res == 0)
138                         continue;
139
140                 _dl_dprintf(2, "\n%s: ", _dl_progname);
141
142                 if (symtab_index)
143                         _dl_dprintf(2, "symbol '%s': ",
144                                     strtab + symtab[symtab_index].st_name);
145
146                 if (unlikely(res < 0)) {
147                         int reloc_type = ELF32_R_TYPE(rpnt->r_info);
148
149 #if defined (__SUPPORT_LD_DEBUG__)
150                         _dl_dprintf(2, "can't handle reloc type '%s' in lib '%s'\n",
151                                     _dl_reltypes(reloc_type), tpnt->libname);
152 #else
153                         _dl_dprintf(2, "can't handle reloc type %x in lib '%s'\n",
154                                     reloc_type, tpnt->libname);
155 #endif
156                         return res;
157                 } else if (unlikely(res > 0)) {
158                         _dl_dprintf(2, "can't resolve symbol in lib '%s'.\n", tpnt->libname);
159                         return res;
160                 }
161         }
162
163         return 0;
164 }
165
166 static int
167 _dl_do_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
168              ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
169 {
170         int reloc_type;
171         int symtab_index;
172         char *symname;
173         unsigned long *reloc_addr;
174         unsigned long symbol_addr;
175 #if defined (__SUPPORT_LD_DEBUG__)
176         unsigned long old_val;
177 #endif
178
179         reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset);
180         reloc_type = ELF32_R_TYPE(rpnt->r_info);
181         symtab_index = ELF32_R_SYM(rpnt->r_info);
182         symbol_addr = 0;
183         symname = strtab + symtab[symtab_index].st_name;
184
185         if (symtab_index) {
186                 symbol_addr = (unsigned long)_dl_find_hash(symname, scope, tpnt,
187                                                            elf_machine_type_class(reloc_type));
188
189                 /*
190                  * We want to allow undefined references to weak symbols - this
191                  * might have been intentional.  We should not be linking local
192                  * symbols here, so all bases should be covered.
193                  */
194                 if (unlikely(!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK))
195                         return 1;
196         }
197
198 #if defined (__SUPPORT_LD_DEBUG__)
199         old_val = *reloc_addr;
200 #endif
201
202         switch (reloc_type) {
203                 case R_386_NONE:
204                         break;
205                 case R_386_32:
206                         *reloc_addr += symbol_addr;
207                         break;
208                 case R_386_PC32:
209                         *reloc_addr += symbol_addr - (unsigned long)reloc_addr;
210                         break;
211                 case R_386_GLOB_DAT:
212                 case R_386_JMP_SLOT:
213                         *reloc_addr = symbol_addr;
214                         break;
215                 case R_386_RELATIVE:
216                         *reloc_addr += (unsigned long)tpnt->loadaddr;
217                         break;
218                 case R_386_COPY:
219                         if (symbol_addr) {
220 #if defined (__SUPPORT_LD_DEBUG__)
221                                 if (_dl_debug_move)
222                                         _dl_dprintf(_dl_debug_file,
223                                                     "\n%s move %d bytes from %x to %x",
224                                                     symname, symtab[symtab_index].st_size,
225                                                     symbol_addr, reloc_addr);
226 #endif
227
228                                 _dl_memcpy((char *)reloc_addr,
229                                            (char *)symbol_addr,
230                                            symtab[symtab_index].st_size);
231                         }
232                         break;
233                 default:
234                         return -1;
235         }
236
237 #if defined (__SUPPORT_LD_DEBUG__)
238         if (_dl_debug_reloc && _dl_debug_detail)
239                 _dl_dprintf(_dl_debug_file, "\n\tpatched: %x ==> %x @ %x",
240                             old_val, *reloc_addr, reloc_addr);
241 #endif
242
243         return 0;
244 }
245
246 static int
247 _dl_do_lazy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
248                   ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
249 {
250         int reloc_type;
251         unsigned long *reloc_addr;
252 #if defined (__SUPPORT_LD_DEBUG__)
253         unsigned long old_val;
254 #endif
255
256         (void)scope;
257         (void)symtab;
258         (void)strtab;
259
260         reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset);
261         reloc_type = ELF32_R_TYPE(rpnt->r_info);
262
263 #if defined (__SUPPORT_LD_DEBUG__)
264         old_val = *reloc_addr;
265 #endif
266
267         switch (reloc_type) {
268                 case R_386_NONE:
269                         break;
270                 case R_386_JMP_SLOT:
271                         *reloc_addr += (unsigned long)tpnt->loadaddr;
272                         break;
273                 default:
274                         return -1;
275         }
276
277 #if defined (__SUPPORT_LD_DEBUG__)
278         if (_dl_debug_reloc && _dl_debug_detail)
279                 _dl_dprintf(_dl_debug_file, "\n\tpatched: %x ==> %x @ %x",
280                             old_val, *reloc_addr, reloc_addr);
281 #endif
282
283         return 0;
284 }
285
286 void
287 _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
288                                       unsigned long rel_addr,
289                                       unsigned long rel_size)
290 {
291         (void)_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
292 }
293
294 int
295 _dl_parse_relocation_information(struct dyn_elf *rpnt,
296                                  unsigned long rel_addr,
297                                  unsigned long rel_size)
298 {
299         return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
300 }