mips_reloc.c revision 1.38 1 /* $NetBSD: mips_reloc.c,v 1.38 2003/07/17 13:56:33 skrll Exp $ */
2
3 /*
4 * Copyright 1997 Michael L. Hitch <mhitch (at) montana.edu>
5 * Portions copyright 2002 Charles M. Hannum <root (at) ihack.net>
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. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <string.h>
34
35 #include "debug.h"
36 #include "rtld.h"
37
38 #define SUPPORT_OLD_BROKEN_LD
39
40 void _rtld_bind_start(void);
41 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
42 caddr_t _rtld_bind(Elf_Word, Elf_Addr, Elf_Addr, Elf_Addr);
43
44 void
45 _rtld_setup_pltgot(obj)
46 const Obj_Entry *obj;
47 {
48 obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start;
49 /* XXX only if obj->pltgot[1] & 0x80000000 ?? */
50 obj->pltgot[1] |= (Elf_Addr) obj;
51 }
52
53 void
54 _rtld_relocate_nonplt_self(dynp, relocbase)
55 Elf_Dyn *dynp;
56 Elf_Addr relocbase;
57 {
58 const Elf_Rel *rel = 0, *rellim;
59 Elf_Addr relsz = 0;
60 Elf_Addr *where;
61 const Elf_Sym *symtab, *sym;
62 Elf_Addr *got;
63 Elf_Word local_gotno, symtabno, gotsym;
64 int i;
65
66 for (; dynp->d_tag != DT_NULL; dynp++) {
67 switch (dynp->d_tag) {
68 case DT_REL:
69 rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
70 break;
71 case DT_RELSZ:
72 relsz = dynp->d_un.d_val;
73 break;
74 case DT_SYMTAB:
75 symtab = (const Elf_Sym *)(relocbase + dynp->d_un.d_ptr);
76 break;
77 case DT_PLTGOT:
78 got = (Elf_Addr *)(relocbase + dynp->d_un.d_ptr);
79 break;
80 case DT_MIPS_LOCAL_GOTNO:
81 local_gotno = dynp->d_un.d_val;
82 break;
83 case DT_MIPS_SYMTABNO:
84 symtabno = dynp->d_un.d_val;
85 break;
86 case DT_MIPS_GOTSYM:
87 gotsym = dynp->d_un.d_val;
88 break;
89 }
90 }
91
92 i = (got[1] & 0x80000000) ? 2 : 1;
93 /* Relocate the local GOT entries */
94 got += i;
95 for (; i < local_gotno; i++)
96 *got++ += relocbase;
97 sym = symtab + gotsym;
98 /* Now do the global GOT entries */
99 for (i = gotsym; i < symtabno; i++) {
100 *got = sym->st_value + relocbase;
101 ++sym;
102 ++got;
103 }
104
105 rellim = (const Elf_Rel *)((caddr_t)rel + relsz);
106 for (; rel < rellim; rel++) {
107 where = (Elf_Addr *)(relocbase + rel->r_offset);
108
109 switch (ELF_R_TYPE(rel->r_info)) {
110 case R_TYPE(NONE):
111 break;
112
113 case R_TYPE(REL32):
114 assert(ELF_R_SYM(rel->r_info) < gotsym);
115 sym = symtab + ELF_R_SYM(rel->r_info);
116 assert(sym->st_info ==
117 ELF_ST_INFO(STB_LOCAL, STT_SECTION));
118 *where += (Elf_Addr)(sym->st_value + relocbase);
119 break;
120
121 default:
122 abort();
123 }
124 }
125 }
126
127 /*
128 * It is possible for the compiler to emit relocations for unaligned data.
129 * We handle this situation with these inlines.
130 */
131 #define RELOC_ALIGNED_P(x) \
132 (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
133
134 static __inline Elf_Addr
135 load_ptr(void *where)
136 {
137 Elf_Addr res;
138
139 memcpy(&res, where, sizeof(res));
140
141 return (res);
142 }
143
144 static __inline void
145 store_ptr(void *where, Elf_Addr val)
146 {
147
148 memcpy(where, &val, sizeof(val));
149 }
150
151 int
152 _rtld_relocate_nonplt_objects(obj)
153 const Obj_Entry *obj;
154 {
155 const Elf_Rel *rel;
156 Elf_Addr *got = obj->pltgot;
157 const Elf_Sym *sym, *def;
158 const Obj_Entry *defobj;
159 int i;
160 #ifdef SUPPORT_OLD_BROKEN_LD
161 int broken;
162 #endif
163
164 #ifdef SUPPORT_OLD_BROKEN_LD
165 broken = 0;
166 sym = obj->symtab;
167 for (i = 1; i < 12; i++)
168 if (sym[i].st_info == ELF_ST_INFO(STB_LOCAL, STT_NOTYPE))
169 broken = 1;
170 dbg(("%s: broken=%d", obj->path, broken));
171 #endif
172
173 i = (got[1] & 0x80000000) ? 2 : 1;
174 /* Relocate the local GOT entries */
175 got += i;
176 for (; i < obj->local_gotno; i++)
177 *got++ += (Elf_Addr)obj->relocbase;
178 sym = obj->symtab + obj->gotsym;
179 /* Now do the global GOT entries */
180 for (i = obj->gotsym; i < obj->symtabno; i++) {
181 rdbg((" doing got %d sym %p (%s, %x)", i - obj->gotsym, sym,
182 sym->st_name + obj->strtab, *got));
183
184 #ifdef SUPPORT_OLD_BROKEN_LD
185 if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
186 broken && sym->st_shndx == SHN_UNDEF) {
187 /*
188 * XXX DANGER WILL ROBINSON!
189 * You might think this is stupid, as it intentionally
190 * defeats lazy binding -- and you'd be right.
191 * Unfortunately, for lazy binding to work right, we
192 * need to a way to force the GOT slots used for
193 * function pointers to be resolved immediately. This
194 * is supposed to be done automatically by the linker,
195 * by not outputting a PLT slot and setting st_value
196 * to 0 if there are non-PLT references, but older
197 * versions of GNU ld do not do this.
198 */
199 def = _rtld_find_symdef(i, obj, &defobj, false);
200 if (def == NULL)
201 return -1;
202 *got = def->st_value + (Elf_Addr)defobj->relocbase;
203 } else
204 #endif
205 if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
206 sym->st_value != 0) {
207 /*
208 * If there are non-PLT references to the function,
209 * st_value should be 0, forcing us to resolve the
210 * address immediately.
211 */
212 *got = sym->st_value + (Elf_Addr)obj->relocbase;
213 } else if (sym->st_info == ELF_ST_INFO(STB_GLOBAL, STT_SECTION)) {
214 /* Symbols with index SHN_ABS are not relocated. */
215 if (sym->st_shndx != SHN_ABS)
216 *got = sym->st_value +
217 (Elf_Addr)obj->relocbase;
218 } else {
219 def = _rtld_find_symdef(i, obj, &defobj, false);
220 if (def == NULL)
221 return -1;
222 *got = def->st_value + (Elf_Addr)defobj->relocbase;
223 }
224
225 rdbg((" --> now %x", *got));
226 ++sym;
227 ++got;
228 }
229
230 got = obj->pltgot;
231 for (rel = obj->rel; rel < obj->rellim; rel++) {
232 Elf_Addr *where, tmp;
233 unsigned long symnum;
234
235 where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
236 symnum = ELF_R_SYM(rel->r_info);
237
238 switch (ELF_R_TYPE(rel->r_info)) {
239 case R_TYPE(NONE):
240 break;
241
242 case R_TYPE(REL32):
243 /* 32-bit PC-relative reference */
244 def = obj->symtab + symnum;
245
246 if (symnum >= obj->gotsym) {
247 tmp = *where;
248 tmp += got[obj->local_gotno + symnum - obj->gotsym];
249 *where = tmp;
250
251 rdbg(("REL32/G %s in %s --> %p in %s",
252 obj->strtab + def->st_name, obj->path,
253 (void *)tmp, obj->path));
254 break;
255 } else {
256 /*
257 * XXX: ABI DIFFERENCE!
258 *
259 * Old NetBSD binutils would generate shared
260 * libs with section-relative relocations being
261 * already adjusted for the start address of
262 * the section.
263 *
264 * New binutils, OTOH, generate shared libs
265 * with the same relocations being based at
266 * zero, so we need to add in the start address
267 * of the section.
268 *
269 * --rkb, Oct 6, 2001
270 */
271 tmp = *where;
272
273 if (def->st_info ==
274 ELF_ST_INFO(STB_LOCAL, STT_SECTION)
275 #ifdef SUPPORT_OLD_BROKEN_LD
276 && !broken
277 #endif
278 )
279 tmp += (Elf_Addr)def->st_value;
280
281 tmp += (Elf_Addr)obj->relocbase;
282 *where = tmp;
283
284 rdbg(("REL32/L %s in %s --> %p in %s",
285 obj->strtab + def->st_name, obj->path,
286 (void *)tmp, obj->path));
287 }
288 break;
289
290 default:
291 rdbg(("sym = %lu, type = %lu, offset = %p, "
292 "contents = %p, symbol = %s",
293 symnum, (u_long)ELF_R_TYPE(rel->r_info),
294 (void *)rel->r_offset, (void *)load_ptr(where),
295 obj->strtab + obj->symtab[symnum].st_name));
296 _rtld_error("%s: Unsupported relocation type %ld "
297 "in non-PLT relocations\n",
298 obj->path, (u_long) ELF_R_TYPE(rel->r_info));
299 return -1;
300 }
301 }
302
303 return 0;
304 }
305
306 int
307 _rtld_relocate_plt_lazy(obj)
308 const Obj_Entry *obj;
309 {
310 /* PLT fixups were done above in the GOT relocation. */
311 return 0;
312 }
313
314 caddr_t
315 _rtld_bind(a0, a1, a2, a3)
316 Elf_Word a0;
317 Elf_Addr a1, a2, a3;
318 {
319 Elf_Addr *got = (Elf_Addr *)(a2 - 0x7ff0);
320 const Obj_Entry *obj = (Obj_Entry *)(got[1] & 0x7fffffff);
321 const Elf_Sym *def;
322 const Obj_Entry *defobj;
323 Elf_Addr new_value;
324
325 def = _rtld_find_symdef(a0, obj, &defobj, true);
326 if (def == NULL)
327 _rtld_die();
328
329 new_value = (Elf_Addr)(defobj->relocbase + def->st_value);
330 rdbg(("bind now/fixup in %s --> new=%p",
331 defobj->strtab + def->st_name, (void *)new_value));
332 got[obj->local_gotno + a0 - obj->gotsym] = new_value;
333 return ((caddr_t)new_value);
334 }
335