reloc.c revision 1.5 1 /* $NetBSD: reloc.c,v 1.5 1999/01/11 23:12:16 thorpej Exp $ */
2
3 /*
4 * Copyright 1996 John D. Polstra.
5 * Copyright 1996 Matt Thomas <matt (at) 3am-software.com>
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. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by John Polstra.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /*
35 * Dynamic linker for ELF.
36 *
37 * John Polstra <jdp (at) polstra.com>.
38 */
39
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <sys/types.h>
49 #include <sys/mman.h>
50 #include <dirent.h>
51
52 #include "debug.h"
53 #include "rtld.h"
54
55 #if defined(__alpha__) || defined(__powerpc__) || defined(__i386__)
56 /*
57 * XXX: These don't work for the alpha and i386; don't know about powerpc
58 * The alpha and the i386 avoid the problem by compiling everything PIC.
59 * These relocation are supposed to be writing the address of the
60 * function to be called on the bss.rel or bss.rela segment, but:
61 * - st_size == 0
62 * - on the i386 at least the call instruction is a direct call
63 * not an indirect call.
64 */
65 static int
66 _rtld_do_copy_relocation(
67 const Obj_Entry *dstobj,
68 const Elf_RelA *rela)
69 {
70 void *dstaddr = (void *) (dstobj->relocbase + rela->r_offset);
71 const Elf_Sym *dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
72 const char *name = dstobj->strtab + dstsym->st_name;
73 unsigned long hash = _rtld_elf_hash(name);
74 size_t size = dstsym->st_size;
75 const void *srcaddr;
76 const Elf_Sym *srcsym;
77 Obj_Entry *srcobj;
78
79 for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next)
80 if ((srcsym = _rtld_symlook_obj(name, hash, srcobj, false)) != NULL)
81 break;
82
83 if (srcobj == NULL) {
84 _rtld_error("Undefined symbol \"%s\" referenced from COPY"
85 " relocation in %s", name, dstobj->path);
86 return -1;
87 }
88
89 srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value);
90 memcpy(dstaddr, srcaddr, size);
91 return 0;
92 }
93 #endif /* __alpha__ || __powerpc__ || __i386__ */
94
95 /*
97 * Process the special R_xxx_COPY relocations in the main program. These
98 * copy data from a shared object into a region in the main program's BSS
99 * segment.
100 *
101 * Returns 0 on success, -1 on failure.
102 */
103 int
104 _rtld_do_copy_relocations(
105 const Obj_Entry *dstobj)
106 {
107 assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */
108
109 #if defined(__alpha__) || defined(__powerpc__) || defined(__i386__)
110 if (dstobj->rel != NULL) {
111 const Elf_Rel *rel;
112 for (rel = dstobj->rel; rel < dstobj->rellim; ++rel) {
113 if (ELF_R_TYPE(rel->r_info) == R_TYPE(COPY)) {
114 Elf_RelA ourrela;
115 ourrela.r_info = rel->r_info;
116 ourrela.r_offset = rel->r_offset;
117 ourrela.r_addend = 0;
118 if (_rtld_do_copy_relocation(dstobj, &ourrela) < 0)
119 return -1;
120 }
121 }
122 }
123
124 if (dstobj->rela != NULL) {
125 const Elf_RelA *rela;
126 for (rela = dstobj->rela; rela < dstobj->relalim; ++rela) {
127 if (ELF_R_TYPE(rela->r_info) == R_TYPE(COPY)) {
128 if (_rtld_do_copy_relocation(dstobj, rela) < 0)
129 return -1;
130 }
131 }
132 }
133 #endif /* __alpha__ || __powerpc__ || __i386__ */
134
135 return 0;
136 }
137
138 static int
140 _rtld_relocate_nonplt_object(
141 const Obj_Entry *obj,
142 const Elf_RelA *rela)
143 {
144 Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rela->r_offset);
145
146 switch (ELF_R_TYPE(rela->r_info)) {
147
148 case R_TYPE(NONE):
149 break;
150
151 #ifdef __i386__
152 case R_TYPE(GOT32): {
153 const Elf_Sym *def;
154 const Obj_Entry *defobj;
155
156 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false);
157 if (def == NULL)
158 return -1;
159
160 if (*where != (Elf_Addr) (defobj->relocbase + def->st_value + rela->r_addend))
161 *where = (Elf_Addr) (defobj->relocbase + def->st_value + rela->r_addend);
162 break;
163 }
164
165 case R_TYPE(PC32):
166 /*
167 * I don't think the dynamic linker should ever see this
168 * type of relocation. But the binutils-2.6 tools sometimes
169 * generate it.
170 */
171 {
172 const Elf_Sym *def;
173 const Obj_Entry *defobj;
174
175 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false);
176 if (def == NULL)
177 return -1;
178
179 *where += (Elf_Addr) (defobj->relocbase + def->st_value)
180 - (Elf_Addr) where;
181 break;
182 }
183
184 case R_TYPE(32): {
185 const Elf_Sym *def;
186 const Obj_Entry *defobj;
187
188 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false);
189 if (def == NULL)
190 return -1;
191
192 *where += (Elf_Addr)(defobj->relocbase + def->st_value);
193 break;
194 }
195 #endif /* __i386__ */
196
197 #ifdef __alpha__
198 case R_ALPHA_REFQUAD: {
199 const Elf_Sym *def;
200 const Obj_Entry *defobj;
201 Elf_Addr tmp_value;
202
203 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false);
204 if (def == NULL)
205 return -1;
206
207 tmp_value = (Elf_Addr) (defobj->relocbase + def->st_value)
208 + *where + rela->r_addend;
209 if (*where != tmp_value)
210 *where = tmp_value;
211 break;
212 }
213 #endif /* __alpha__ */
214
215 #if defined(__i386__) || defined(__alpha__)
216 case R_TYPE(GLOB_DAT):
217 {
218 const Elf_Sym *def;
219 const Obj_Entry *defobj;
220
221 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false);
222 if (def == NULL)
223 return -1;
224
225 if (*where != (Elf_Addr) (defobj->relocbase + def->st_value))
226 *where = (Elf_Addr) (defobj->relocbase + def->st_value);
227 break;
228 }
229
230 case R_TYPE(RELATIVE): {
231 extern Elf_Addr _GLOBAL_OFFSET_TABLE_[];
232 extern Elf_Dyn _DYNAMIC;
233
234 if (obj != &_rtld_objself ||
235 (caddr_t)where < (caddr_t)_GLOBAL_OFFSET_TABLE_ ||
236 (caddr_t)where >= (caddr_t)&_DYNAMIC)
237 *where += (Elf_Addr) obj->relocbase;
238 break;
239 }
240
241 case R_TYPE(COPY): {
242 /*
243 * These are deferred until all other relocations have
244 * been done. All we do here is make sure that the COPY
245 * relocation is not in a shared library. They are allowed
246 * only in executable files.
247 */
248 if (!obj->mainprog) {
249 _rtld_error("%s: Unexpected R_COPY relocation in shared library",
250 obj->path);
251 return -1;
252 }
253 break;
254 }
255 #endif /* __i386__ || __alpha__ */
256
257 #ifdef __mips__
258 case R_TYPE(REL32): {
259 /* 32-bit PC-relative reference */
260
261 const Elf_Sym *def;
262 const Obj_Entry *defobj;
263
264 def = obj->symtab + ELF_R_SYM(rela->r_info);
265
266 if (ELF_SYM_BIND(def->st_info) == Elf_estb_local &&
267 (ELF_SYM_TYPE(def->st_info) == Elf_estt_section ||
268 ELF_SYM_TYPE(def->st_info) == Elf_estt_notype)) {
269 *where += (Elf_Addr) obj->relocbase;
270 } else {
271 /* XXX maybe do something re: bootstrapping? */
272 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj,
273 &defobj, false);
274 if (def == NULL)
275 return -1;
276 *where += (Elf_Addr)(defobj->relocbase + def->st_value);
277 }
278 break;
279 }
280
281 #endif /* mips */
282
283 #ifdef __powerpc__
284 case R_TYPE(32): /* word32 S + A */
285 case R_TYPE(GLOB_DAT): { /* word32 S + A */
286 const Elf_Sym *def;
287 const Obj_Entry *defobj;
288 Elf_Addr x;
289
290 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false);
291 if (def == NULL)
292 return -1;
293
294 x = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend);
295
296 if (*where != x)
297 *where = x;
298 break;
299 }
300
301 case R_TYPE(COPY):
302 break;
303
304 case R_TYPE(JMP_SLOT):
305 break;
306
307 case R_TYPE(RELATIVE): { /* word32 B + A */
308 if (obj == &_rtld_objself &&
309 *where == (Elf_Addr)obj->relocbase + rela->r_addend)
310 break; /* GOT - already done */
311
312 *where = (Elf_Addr)obj->relocbase + rela->r_addend;
313 break;
314 }
315 #endif /* __powerpc__ */
316
317 default: {
318 const Elf_Sym *def;
319 const Obj_Entry *defobj;
320
321 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, true);
322 dbg("sym = %lu, type = %lu, offset = %p, addend = %p, contents = %p, symbol = %s",
323 (u_long)ELF_R_SYM(rela->r_info), (u_long)ELF_R_TYPE(rela->r_info),
324 (void *)rela->r_offset, (void *)rela->r_addend, (void *)*where,
325 def ? defobj->strtab + def->st_name : "??");
326 _rtld_error("%s: Unsupported relocation type %d in non-PLT relocations\n",
327 obj->path, ELF_R_TYPE(rela->r_info));
328 return -1;
329 }
330 }
331 return 0;
332 }
333
334 static int
336 _rtld_relocate_plt_object(
337 const Obj_Entry *obj,
338 const Elf_RelA *rela,
339 bool bind_now)
340 {
341 Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rela->r_offset);
342 Elf_Addr new_value;
343
344 /* Fully resolve procedure addresses now */
345
346 #if defined(__powerpc__)
347 return _rtld_reloc_powerpc_plt(obj, rela, bind_now);
348 #endif
349
350 #if defined(__alpha__) || defined(__i386__) /* (jrs) */
351 if (bind_now || obj->pltgot == NULL) {
352 const Elf_Sym *def;
353 const Obj_Entry *defobj;
354
355 #if defined(__alpha__)
356 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT));
357 #endif
358
359 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, true);
360 if (def == NULL)
361 return -1;
362
363 new_value = (Elf_Addr) (defobj->relocbase + def->st_value);
364 #if 0
365 dbg("fixup %s in %s --> %p in %s",
366 defobj->strtab + def->st_name, obj->path,
367 (void *)new_value, defobj->path);
368 #endif
369 } else
370 #endif /* __alpha__ (jrs) */
371 if (!obj->mainprog) {
372 /* Just relocate the GOT slots pointing into the PLT */
373 new_value = *where + (Elf_Addr) (obj->relocbase);
374 #if 0
375 new_value += rela->r_offset;
376 #endif
377 } else {
378 #ifdef __i386__
379 new_value = *where + (Elf_Addr) (obj->relocbase);
380 new_value += rela->r_offset;
381 #endif
382 return 0;
383 }
384 /*
385 * Since this page is probably copy-on-write, let's not write
386 * it unless we really really have to.
387 */
388 if (*where != new_value)
389 *where = new_value;
390 return 0;
391 }
392
393 caddr_t
395 _rtld_bind(
396 const Obj_Entry *obj,
397 Elf_Word reloff)
398 {
399 const Elf_RelA *rela;
400 Elf_RelA ourrela;
401
402 if (obj->pltrel != NULL) {
403 ourrela.r_info = ((const Elf_Rel *) ((caddr_t) obj->pltrel + reloff))->r_info;
404 ourrela.r_offset = ((const Elf_Rel *) ((caddr_t) obj->pltrel + reloff))->r_offset;
405 rela = &ourrela;
406 } else {
407 rela = (const Elf_RelA *) ((caddr_t) obj->pltrela + reloff);
408 }
409
410
411 if (_rtld_relocate_plt_object(obj, rela, true) < 0)
412 _rtld_die();
413
414 return *(caddr_t *)(obj->relocbase + rela->r_offset);
415 }
416
417 /*
419 * Relocate newly-loaded shared objects. The argument is a pointer to
420 * the Obj_Entry for the first such object. All objects from the first
421 * to the end of the list of objects are relocated. Returns 0 on success,
422 * or -1 on failure.
423 */
424 int
425 _rtld_relocate_objects(
426 Obj_Entry *first,
427 bool bind_now)
428 {
429 Obj_Entry *obj;
430 int ok = 1;
431
432 for (obj = first; obj != NULL; obj = obj->next) {
433
434 if (obj->nbuckets == 0 || obj->nchains == 0
435 || obj->buckets == NULL || obj->symtab == NULL
436 || obj->strtab == NULL) {
437 _rtld_error("%s: Shared object has no run-time symbol table",
438 obj->path);
439 return -1;
440 }
441
442 dbg(" relocating %s (%ld/%ld rel/rela, %ld/%ld plt rel/rela)",
443 obj->path,
444 (long)(obj->rellim - obj->rel), (long)(obj->relalim - obj->rela),
445 (long)(obj->pltrellim - obj->pltrel),
446 (long)(obj->pltrelalim - obj->pltrela));
447
448 if (obj->textrel) {
449 /* There are relocations to the write-protected text segment. */
450 if (mprotect(obj->mapbase, obj->textsize,
451 PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
452 _rtld_error("%s: Cannot write-enable text segment: %s",
453 obj->path, xstrerror(errno));
454 return -1;
455 }
456 }
457
458 if (obj->rel != NULL) {
459 /* Process the non-PLT relocations. */
460 const Elf_Rel *rel;
461 for (rel = obj->rel; rel < obj->rellim; ++rel) {
462 Elf_RelA ourrela;
463 ourrela.r_info = rel->r_info;
464 ourrela.r_offset = rel->r_offset;
465 #if defined(__mips__)
466 /* rel->r_offset is not valid on mips? */
467 if (ELF_R_TYPE(ourrela.r_info) == R_TYPE(NONE))
468 ourrela.r_addend = 0;
469 else
470 #endif
471 ourrela.r_addend = *(Elf_Word *) (obj->relocbase + rel->r_offset);
472
473 if (_rtld_relocate_nonplt_object(obj, &ourrela) < 0)
474 ok = 0;
475 }
476 }
477
478 if (obj->rela != NULL) {
479 /* Process the non-PLT relocations. */
480 const Elf_RelA *rela;
481 for (rela = obj->rela; rela < obj->relalim; ++rela) {
482 if (_rtld_relocate_nonplt_object(obj, rela) < 0)
483 ok = 0;
484 }
485 }
486
487 if (obj->textrel) { /* Re-protected the text segment. */
488 if (mprotect(obj->mapbase, obj->textsize,
489 PROT_READ|PROT_EXEC) == -1) {
490 _rtld_error("%s: Cannot write-protect text segment: %s",
491 obj->path, xstrerror(errno));
492 return -1;
493 }
494 }
495
496 /* Process the PLT relocations. */
497 if (obj->pltrel != NULL) {
498 const Elf_Rel *rel;
499 for (rel = obj->pltrel; rel < obj->pltrellim; ++rel) {
500 Elf_RelA ourrela;
501 ourrela.r_info = rel->r_info;
502 ourrela.r_offset = rel->r_offset;
503 ourrela.r_addend = *(Elf_Word *) (obj->relocbase + rel->r_offset);
504 if (_rtld_relocate_plt_object(obj, &ourrela, bind_now) < 0)
505 ok = 0;
506 }
507 }
508
509 if (obj->pltrela != NULL) {
510 const Elf_RelA *rela;
511 for (rela = obj->pltrela; rela < obj->pltrelalim; ++rela) {
512 if (_rtld_relocate_plt_object(obj, rela, bind_now) < 0)
513 ok = 0;
514 }
515 }
516
517 if (!ok)
518 return -1;
519
520
521 /* Set some sanity-checking numbers in the Obj_Entry. */
522 obj->magic = RTLD_MAGIC;
523 obj->version = RTLD_VERSION;
524
525 /* Fill in the dynamic linker entry points. */
526 obj->dlopen = _rtld_dlopen;
527 obj->dlsym = _rtld_dlsym;
528 obj->dlerror = _rtld_dlerror;
529 obj->dlclose = _rtld_dlclose;
530
531 /* Set the special PLTGOT entries. */
532 if (obj->pltgot != NULL) {
533 #if defined(__i386__)
534 obj->pltgot[1] = (Elf_Addr) obj;
535 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
536 #endif
537 #if defined(__alpha__)
538 /* This function will be called to perform the relocation. */
539 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
540 /* Identify this shared object */
541 obj->pltgot[3] = (Elf_Addr) obj;
542 #endif
543 #if defined(__mips__)
544 _rtld_relocate_mips_got(obj);
545
546 obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start;
547 /* XXX only if obj->pltgot[1] & 0x80000000 ?? */
548 obj->pltgot[1] |= (Elf_Addr) obj;
549 #endif
550 #if defined(__powerpc__)
551 _rtld_setup_powerpc_plt(obj);
552 #endif
553 }
554 }
555
556 return 0;
557 }
558