OSDN Git Service

Joakim Tjernlund writes:
[uclinux-h8/uclibc-ng.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, int type)
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, int type)
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, int type)
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
190         /* Now parse the relocation information */
191         rel_size = rel_size / sizeof(Elf32_Rel);
192         rpnt = (Elf32_Rel *) (rel_addr + tpnt->loadaddr);
193
194         symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
195         strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
196         got = (unsigned long *) (tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
197
198         for (i = 0; i < rel_size; i++, rpnt++) {
199                 reloc_addr = (unsigned long *) (tpnt->loadaddr +
200                         (unsigned long) rpnt->r_offset);
201                 reloc_type = ELF32_R_TYPE(rpnt->r_info);
202                 symtab_index = ELF32_R_SYM(rpnt->r_info);
203                 symbol_addr = 0;
204
205                 if (!symtab_index && tpnt->libtype == program_interpreter)
206                         continue;
207
208 #if defined (__SUPPORT_LD_DEBUG__)
209                 debug_sym(symtab,strtab,symtab_index);
210                 debug_reloc(symtab,strtab,rpnt);
211                 if (reloc_addr)
212                         old_val = *reloc_addr;
213 #endif
214
215                 switch (reloc_type) {
216                 case R_MIPS_REL32:
217                         if (symtab_index) {
218                                 if (symtab_index < tpnt->mips_gotsym)
219                                         *reloc_addr +=
220                                                 symtab[symtab_index].st_value +
221                                                 (unsigned long) tpnt->loadaddr;
222                                 else {
223                                         *reloc_addr += got[symtab_index + tpnt->mips_local_gotno -
224                                                 tpnt->mips_gotsym];
225                                 }
226                         }
227                         else {
228                                 *reloc_addr += (unsigned long) tpnt->loadaddr;
229                         }
230                         break;
231                 case R_MIPS_NONE:
232                         break;
233                 default:
234                         {
235                                 int reloc_type = ELF32_R_TYPE(rpnt->r_info);
236                                 _dl_dprintf(2, "\n%s: ",_dl_progname);
237
238                                 if (symtab_index)
239                                         _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
240
241 #if defined (__SUPPORT_LD_DEBUG__)
242                                 _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
243 #else
244                                 _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
245 #endif
246                                 _dl_exit(1);
247                         }
248                 };
249
250         };
251 #if defined (__SUPPORT_LD_DEBUG__)
252         if(_dl_debug_reloc && _dl_debug_detail && reloc_addr)
253                 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", old_val, *reloc_addr, reloc_addr);
254 #endif
255
256         return 0;
257 }
258
259 void _dl_perform_mips_global_got_relocations(struct elf_resolve *tpnt)
260 {
261         Elf32_Sym *sym;
262         char *strtab;
263         unsigned long i;
264         unsigned long *got_entry;
265
266         for (; tpnt ; tpnt = tpnt->next) {
267
268                 /* We don't touch the dynamic linker */
269                 if (tpnt->libtype == program_interpreter)
270                         continue;
271
272                 /* Setup the loop variables */
273                 got_entry = (unsigned long *) (tpnt->loadaddr +
274                         tpnt->dynamic_info[DT_PLTGOT]) + tpnt->mips_local_gotno;
275                 sym = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] +
276                         (unsigned long) tpnt->loadaddr) + tpnt->mips_gotsym;
277                 strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] +
278                         (unsigned long) tpnt->loadaddr);
279                 i = tpnt->mips_symtabno - tpnt->mips_gotsym;
280
281                 /* Relocate the global GOT entries for the object */
282                 while(i--) {
283                         if (sym->st_shndx == SHN_UNDEF) {
284                                 if (ELF32_ST_TYPE(sym->st_info) == STT_FUNC && sym->st_value)
285                                         *got_entry = sym->st_value + (unsigned long) tpnt->loadaddr;
286                                 else {
287                                         *got_entry = (unsigned long) _dl_find_hash(strtab +
288                                                 sym->st_name, tpnt->symbol_scope, ELF_RTYPE_CLASS_PLT);
289                                 }
290                         }
291                         else if (sym->st_shndx == SHN_COMMON) {
292                                 *got_entry = (unsigned long) _dl_find_hash(strtab +
293                                         sym->st_name, tpnt->symbol_scope, ELF_RTYPE_CLASS_PLT);
294                         }
295                         else if (ELF32_ST_TYPE(sym->st_info) == STT_FUNC &&
296                                 *got_entry != sym->st_value)
297                                 *got_entry += (unsigned long) tpnt->loadaddr;
298                         else if (ELF32_ST_TYPE(sym->st_info) == STT_SECTION) {
299                                 if (sym->st_other == 0)
300                                         *got_entry += (unsigned long) tpnt->loadaddr;
301                         }
302                         else {
303                                 *got_entry = (unsigned long) _dl_find_hash(strtab +
304                                         sym->st_name, tpnt->symbol_scope, ELF_RTYPE_CLASS_PLT);
305                         }
306
307                         got_entry++;
308                         sym++;
309                 }
310         }
311 }