OSDN Git Service

21bda6f969283456e1d5de97d612bf0ebf4a3129
[uclinux-h8/uClibc.git] / ldso / ldso / mips / elfinterp.c
1 /* vi: set sw=4 ts=4: */
2 /* mips/mipsel ELF shared library loader suppport
3  *
4    Copyright (C) 2002, Steven J. Hill (sjhill@realitydiluted.com)
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. The name of the above contributors may not be
14  *    used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #if defined (__SUPPORT_LD_DEBUG__)
31 static const char *_dl_reltypes_tab[] =
32 {
33                 [0]             "R_MIPS_NONE",  "R_MIPS_16",    "R_MIPS_32",
34                 [3]             "R_MIPS_REL32", "R_MIPS_26",    "R_MIPS_HI16",
35                 [6]             "R_MIPS_LO16",  "R_MIPS_GPREL16",       "R_MIPS_LITERAL",
36                 [9]             "R_MIPS_GOT16", "R_MIPS_PC16",  "R_MIPS_CALL16",
37                 [12]    "R_MIPS_GPREL32",
38                 [16]    "R_MIPS_SHIFT5",        "R_MIPS_SHIFT6",        "R_MIPS_64",
39                 [19]    "R_MIPS_GOT_DISP",      "R_MIPS_GOT_PAGE",      "R_MIPS_GOT_OFST",
40                 [22]    "R_MIPS_GOT_HI16",      "R_MIPS_GOT_LO16",      "R_MIPS_SUB",
41                 [25]    "R_MIPS_INSERT_A",      "R_MIPS_INSERT_B",      "R_MIPS_DELETE",
42                 [28]    "R_MIPS_HIGHER",        "R_MIPS_HIGHEST",       "R_MIPS_CALL_HI16",
43                 [31]    "R_MIPS_CALL_LO16",     "R_MIPS_SCN_DISP",      "R_MIPS_REL16",
44                 [34]    "R_MIPS_ADD_IMMEDIATE", "R_MIPS_PJUMP", "R_MIPS_RELGOT",
45                 [37]    "R_MIPS_JALR",
46 };
47
48 static const char *
49 _dl_reltypes(int type)
50 {
51   static char buf[22];
52   const char *str;
53
54   if (type >= (int)(sizeof (_dl_reltypes_tab)/sizeof(_dl_reltypes_tab[0])) ||
55       NULL == (str = _dl_reltypes_tab[type]))
56   {
57     str =_dl_simple_ltoa( buf, (unsigned long)(type));
58   }
59   return str;
60 }
61
62 static
63 void debug_sym(Elf32_Sym *symtab,char *strtab,int symtab_index)
64 {
65   if(_dl_debug_symbols)
66   {
67     if(symtab_index){
68       _dl_dprintf(_dl_debug_file, "\n%s\n\tvalue=%x\tsize=%x\tinfo=%x\tother=%x\tshndx=%x",
69                   strtab + symtab[symtab_index].st_name,
70                   symtab[symtab_index].st_value,
71                   symtab[symtab_index].st_size,
72                   symtab[symtab_index].st_info,
73                   symtab[symtab_index].st_other,
74                   symtab[symtab_index].st_shndx);
75     }
76   }
77 }
78
79 static void debug_reloc(Elf32_Sym *symtab,char *strtab, ELF_RELOC *rpnt)
80 {
81   if(_dl_debug_reloc)
82   {
83     int symtab_index;
84     const char *sym;
85     symtab_index = ELF32_R_SYM(rpnt->r_info);
86     sym = symtab_index ? strtab + symtab[symtab_index].st_name : "sym=0x0";
87
88   if(_dl_debug_symbols)
89           _dl_dprintf(_dl_debug_file, "\n\t");
90   else
91           _dl_dprintf(_dl_debug_file, "\n%s\n\t", sym);
92 #ifdef ELF_USES_RELOCA
93     _dl_dprintf(_dl_debug_file, "%s\toffset=%x\taddend=%x",
94                 _dl_reltypes(ELF32_R_TYPE(rpnt->r_info)),
95                 rpnt->r_offset,
96                 rpnt->r_addend);
97 #else
98     _dl_dprintf(_dl_debug_file, "%s\toffset=%x\n",
99                 _dl_reltypes(ELF32_R_TYPE(rpnt->r_info)),
100                 rpnt->r_offset);
101 #endif
102   }
103 }
104 #endif
105
106 extern int _dl_linux_resolve(void);
107
108 #define OFFSET_GP_GOT 0x7ff0
109
110 unsigned long _dl_linux_resolver(unsigned long sym_index,
111         unsigned long old_gpreg)
112 {
113         unsigned long *got = (unsigned long *) (old_gpreg - OFFSET_GP_GOT);
114         struct elf_resolve *tpnt = (struct elf_resolve *) got[1];
115         Elf32_Sym *sym;
116         char *strtab;
117         unsigned long local_gotno;
118         unsigned long gotsym;
119         unsigned long new_addr;
120         unsigned long instr_addr;
121         char **got_addr;
122         char *symname;
123
124         gotsym = tpnt->mips_gotsym;
125         local_gotno = tpnt->mips_local_gotno;
126
127         sym = ((Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr)) + sym_index;
128         strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
129         symname = strtab + sym->st_name;
130
131         new_addr = (unsigned long) _dl_find_hash(symname,
132                         tpnt->symbol_scope, ELF_RTYPE_CLASS_PLT);
133         if (unlikely(!new_addr)) {
134                 _dl_dprintf (2, "%s: can't resolve symbol '%s'\n",
135                                 _dl_progname, symname);
136                 _dl_exit (1);
137         }
138
139         /* Address of jump instruction to fix up */
140         instr_addr = (unsigned long) (got + local_gotno + sym_index - gotsym);
141         got_addr = (char **) instr_addr;
142
143 #if defined (__SUPPORT_LD_DEBUG__)
144         if (_dl_debug_bindings)
145         {
146                 _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
147                 if(_dl_debug_detail) _dl_dprintf(_dl_debug_file,
148                                 "\n\tpatched %x ==> %x @ %x\n", *got_addr, new_addr, got_addr);
149         }
150         if (!_dl_debug_nofixups) {
151                 *got_addr = (char*)new_addr;
152         }
153 #else
154         *got_addr = (char*)new_addr;
155 #endif
156
157         return new_addr;
158 }
159
160 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
161         unsigned long rel_addr, unsigned long rel_size)
162 {
163         /* Nothing to do */
164         return;
165 }
166
167 int _dl_parse_copy_information(struct dyn_elf *rpnt,
168         unsigned long rel_addr, unsigned long rel_size)
169 {
170         /* Nothing to do */
171         return 0;
172 }
173
174
175 int _dl_parse_relocation_information(struct dyn_elf *xpnt,
176         unsigned long rel_addr, unsigned long rel_size)
177 {
178         Elf32_Sym *symtab;
179         Elf32_Rel *rpnt;
180         char *strtab;
181         unsigned long *got;
182         unsigned long *reloc_addr=NULL;
183         unsigned long symbol_addr;
184         int i, reloc_type, symtab_index;
185         struct elf_resolve *tpnt = xpnt->dyn;
186 #if defined (__SUPPORT_LD_DEBUG__)
187         unsigned long old_val=0;
188 #endif
189         /* Now parse the relocation information */
190         rel_size = rel_size / sizeof(Elf32_Rel);
191         rpnt = (Elf32_Rel *) (rel_addr + tpnt->loadaddr);
192
193         symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
194         strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
195         got = (unsigned long *) (tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
196
197         for (i = 0; i < rel_size; i++, rpnt++) {
198                 reloc_addr = (unsigned long *) (tpnt->loadaddr +
199                         (unsigned long) rpnt->r_offset);
200                 reloc_type = ELF32_R_TYPE(rpnt->r_info);
201                 symtab_index = ELF32_R_SYM(rpnt->r_info);
202                 symbol_addr = 0;
203
204 #if defined (__SUPPORT_LD_DEBUG__)
205                 debug_sym(symtab,strtab,symtab_index);
206                 debug_reloc(symtab,strtab,rpnt);
207                 if (reloc_addr)
208                         old_val = *reloc_addr;
209 #endif
210
211                 switch (reloc_type) {
212                 case R_MIPS_REL32:
213                         if (symtab_index) {
214                                 if (symtab_index < tpnt->mips_gotsym)
215                                         *reloc_addr +=
216                                                 symtab[symtab_index].st_value +
217                                                 (unsigned long) tpnt->loadaddr;
218                                 else {
219                                         *reloc_addr += got[symtab_index + tpnt->mips_local_gotno -
220                                                 tpnt->mips_gotsym];
221                                 }
222                         }
223                         else {
224                                 *reloc_addr += (unsigned long) tpnt->loadaddr;
225                         }
226                         break;
227                 case R_MIPS_NONE:
228                         break;
229                 default:
230                         {
231                                 int reloc_type = ELF32_R_TYPE(rpnt->r_info);
232                                 _dl_dprintf(2, "\n%s: ",_dl_progname);
233
234                                 if (symtab_index)
235                                         _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
236
237 #if defined (__SUPPORT_LD_DEBUG__)
238                                 _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
239 #else
240                                 _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
241 #endif
242                                 _dl_exit(1);
243                         }
244                 };
245
246         };
247 #if defined (__SUPPORT_LD_DEBUG__)
248         if(_dl_debug_reloc && _dl_debug_detail && reloc_addr)
249                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", old_val, *reloc_addr, reloc_addr);
250 #endif
251
252         return 0;
253 }
254
255 void _dl_perform_mips_global_got_relocations(struct elf_resolve *tpnt)
256 {
257         Elf32_Sym *sym;
258         char *strtab;
259         unsigned long i;
260         unsigned long *got_entry;
261
262         for (; tpnt ; tpnt = tpnt->next) {
263                 /* Setup the loop variables */
264                 got_entry = (unsigned long *) (tpnt->loadaddr +
265                         tpnt->dynamic_info[DT_PLTGOT]) + tpnt->mips_local_gotno;
266                 sym = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] +
267                         (unsigned long) tpnt->loadaddr) + tpnt->mips_gotsym;
268                 strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] +
269                         (unsigned long) tpnt->loadaddr);
270                 i = tpnt->mips_symtabno - tpnt->mips_gotsym;
271
272                 /* Relocate the global GOT entries for the object */
273                 while(i--) {
274                         if (sym->st_shndx == SHN_UNDEF) {
275                                 if (ELF32_ST_TYPE(sym->st_info) == STT_FUNC && sym->st_value)
276                                         *got_entry = sym->st_value + (unsigned long) tpnt->loadaddr;
277                                 else {
278                                         *got_entry = (unsigned long) _dl_find_hash(strtab +
279                                                 sym->st_name, tpnt->symbol_scope, ELF_RTYPE_CLASS_PLT);
280                                 }
281                         }
282                         else if (sym->st_shndx == SHN_COMMON) {
283                                 *got_entry = (unsigned long) _dl_find_hash(strtab +
284                                         sym->st_name, tpnt->symbol_scope, ELF_RTYPE_CLASS_PLT);
285                         }
286                         else if (ELF32_ST_TYPE(sym->st_info) == STT_FUNC &&
287                                 *got_entry != sym->st_value)
288                                 *got_entry += (unsigned long) tpnt->loadaddr;
289                         else if (ELF32_ST_TYPE(sym->st_info) == STT_SECTION) {
290                                 if (sym->st_other == 0)
291                                         *got_entry += (unsigned long) tpnt->loadaddr;
292                         }
293                         else {
294                                 *got_entry = (unsigned long) _dl_find_hash(strtab +
295                                         sym->st_name, tpnt->symbol_scope, ELF_RTYPE_CLASS_PLT);
296                         }
297
298                         got_entry++;
299                         sym++;
300                 }
301         }
302 }