reloc.c revision 1.6 1 /* $NetBSD: reloc.c,v 1.6 1999/02/07 17:24:05 christos 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 #ifdef RTLD_DEBUG_RELOC
92 dbg("COPY %s %s %s --> src=%p dst=%p *dst= %p size %d",
93 dstobj->path, srcobj->path, name, (void *)srcaddr, (void *)dstaddr,
94 (void *)*(long *)dstaddr, size);
95 #endif
96 return 0;
97 }
98 #endif /* __alpha__ || __powerpc__ || __i386__ */
99
100 /*
102 * Process the special R_xxx_COPY relocations in the main program. These
103 * copy data from a shared object into a region in the main program's BSS
104 * segment.
105 *
106 * Returns 0 on success, -1 on failure.
107 */
108 int
109 _rtld_do_copy_relocations(
110 const Obj_Entry *dstobj)
111 {
112 assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */
113
114 #if defined(__alpha__) || defined(__powerpc__) || defined(__i386__)
115 if (dstobj->rel != NULL) {
116 const Elf_Rel *rel;
117 for (rel = dstobj->rel; rel < dstobj->rellim; ++rel) {
118 if (ELF_R_TYPE(rel->r_info) == R_TYPE(COPY)) {
119 Elf_RelA ourrela;
120 ourrela.r_info = rel->r_info;
121 ourrela.r_offset = rel->r_offset;
122 ourrela.r_addend = 0;
123 if (_rtld_do_copy_relocation(dstobj, &ourrela) < 0)
124 return -1;
125 }
126 }
127 }
128
129 if (dstobj->rela != NULL) {
130 const Elf_RelA *rela;
131 for (rela = dstobj->rela; rela < dstobj->relalim; ++rela) {
132 if (ELF_R_TYPE(rela->r_info) == R_TYPE(COPY)) {
133 if (_rtld_do_copy_relocation(dstobj, rela) < 0)
134 return -1;
135 }
136 }
137 }
138 #endif /* __alpha__ || __powerpc__ || __i386__ */
139
140 return 0;
141 }
142
143 static int
145 _rtld_relocate_nonplt_object(
146 const Obj_Entry *obj,
147 const Elf_RelA *rela)
148 {
149 Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rela->r_offset);
150
151 switch (ELF_R_TYPE(rela->r_info)) {
152
153 case R_TYPE(NONE):
154 break;
155
156 #ifdef __i386__
157 case R_TYPE(GOT32): {
158 const Elf_Sym *def;
159 const Obj_Entry *defobj;
160
161 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false);
162 if (def == NULL)
163 return -1;
164
165 if (*where != (Elf_Addr) (defobj->relocbase + def->st_value))
166 *where = (Elf_Addr) (defobj->relocbase + def->st_value);
167 #ifdef RTLD_DEBUG_RELOC
168 dbg("GOT32 %s in %s --> %p in %s",
169 defobj->strtab + def->st_name, obj->path,
170 (void *)*where, defobj->path);
171 #endif
172 break;
173 }
174
175 case R_TYPE(PC32):
176 /*
177 * I don't think the dynamic linker should ever see this
178 * type of relocation. But the binutils-2.6 tools sometimes
179 * generate it.
180 */
181 {
182 const Elf_Sym *def;
183 const Obj_Entry *defobj;
184
185 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false);
186 if (def == NULL)
187 return -1;
188
189 *where += (Elf_Addr) (defobj->relocbase + def->st_value)
190 - (Elf_Addr) where;
191 #ifdef RTLD_DEBUG_RELOC
192 dbg("PC32 %s in %s --> %p in %s",
193 defobj->strtab + def->st_name, obj->path,
194 (void *)*where, defobj->path);
195 #endif
196 break;
197 }
198
199 case R_TYPE(32): {
200 const Elf_Sym *def;
201 const Obj_Entry *defobj;
202
203 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false);
204 if (def == NULL)
205 return -1;
206
207 *where += (Elf_Addr)(defobj->relocbase + def->st_value);
208 #ifdef RTLD_DEBUG_RELOC
209 dbg("32 %s in %s --> %p in %s",
210 defobj->strtab + def->st_name, obj->path,
211 (void *)*where, defobj->path);
212 #endif
213 break;
214 }
215 #endif /* __i386__ */
216
217 #ifdef __alpha__
218 case R_ALPHA_REFQUAD: {
219 const Elf_Sym *def;
220 const Obj_Entry *defobj;
221 Elf_Addr tmp_value;
222
223 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false);
224 if (def == NULL)
225 return -1;
226
227 tmp_value = (Elf_Addr) (defobj->relocbase + def->st_value)
228 + *where + rela->r_addend;
229 if (*where != tmp_value)
230 *where = tmp_value;
231 #ifdef RTLD_DEBUG_RELOC
232 dbg("REFQUAD %s in %s --> %p in %s",
233 defobj->strtab + def->st_name, obj->path,
234 (void *)*where, defobj->path);
235 #endif
236 break;
237 }
238 #endif /* __alpha__ */
239
240 #if defined(__i386__) || defined(__alpha__)
241 case R_TYPE(GLOB_DAT):
242 {
243 const Elf_Sym *def;
244 const Obj_Entry *defobj;
245
246 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false);
247 if (def == NULL)
248 return -1;
249
250 if (*where != (Elf_Addr) (defobj->relocbase + def->st_value))
251 *where = (Elf_Addr) (defobj->relocbase + def->st_value);
252 #ifdef RTLD_DEBUG_RELOC
253 dbg("GLOB_DAT %s in %s --> %p in %s",
254 defobj->strtab + def->st_name, obj->path,
255 (void *)*where, defobj->path);
256 #endif
257 break;
258 }
259
260 case R_TYPE(RELATIVE): {
261 extern Elf_Addr _GLOBAL_OFFSET_TABLE_[];
262 extern Elf_Dyn _DYNAMIC;
263
264 if (obj != &_rtld_objself ||
265 (caddr_t)where < (caddr_t)_GLOBAL_OFFSET_TABLE_ ||
266 (caddr_t)where >= (caddr_t)&_DYNAMIC) {
267 *where += (Elf_Addr) obj->relocbase;
268 #ifdef RTLD_DEBUG_RELOC
269 dbg("RELATIVE in %s --> %p", obj->path, (void *)*where);
270 #endif
271 }
272 #ifdef RTLD_DEBUG_RELOC
273 else
274 dbg("RELATIVE in %s stays at %p", obj->path, (void *)*where);
275 #endif
276 break;
277 }
278
279 case R_TYPE(COPY): {
280 /*
281 * These are deferred until all other relocations have
282 * been done. All we do here is make sure that the COPY
283 * relocation is not in a shared library. They are allowed
284 * only in executable files.
285 */
286 if (!obj->mainprog) {
287 _rtld_error("%s: Unexpected R_COPY relocation in shared library",
288 obj->path);
289 return -1;
290 }
291 #ifdef RTLD_DEBUG_RELOC
292 dbg("COPY (avoid in main)");
293 #endif
294 break;
295 }
296 #endif /* __i386__ || __alpha__ */
297
298 #ifdef __mips__
299 case R_TYPE(REL32): {
300 /* 32-bit PC-relative reference */
301
302 const Elf_Sym *def;
303 const Obj_Entry *defobj;
304
305 def = obj->symtab + ELF_R_SYM(rela->r_info);
306
307 if (ELF_SYM_BIND(def->st_info) == Elf_estb_local &&
308 (ELF_SYM_TYPE(def->st_info) == Elf_estt_section ||
309 ELF_SYM_TYPE(def->st_info) == Elf_estt_notype)) {
310 *where += (Elf_Addr) obj->relocbase;
311 #ifdef RTLD_DEBUG_RELOC
312 dbg("REL32 in %s --> %p", obj->path, (void *)*where);
313 #endif
314 } else {
315 /* XXX maybe do something re: bootstrapping? */
316 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj,
317 &defobj, false);
318 if (def == NULL)
319 return -1;
320 *where += (Elf_Addr)(defobj->relocbase + def->st_value);
321 #ifdef RTLD_DEBUG_RELOC
322 dbg("REL32 %s in %s --> %p in %s",
323 defobj->strtab + def->st_name, obj->path,
324 (void *)*where, defobj->path);
325 #endif
326 }
327 break;
328 }
329
330 #endif /* mips */
331
332 #ifdef __powerpc__
333 case R_TYPE(32): /* word32 S + A */
334 case R_TYPE(GLOB_DAT): { /* word32 S + A */
335 const Elf_Sym *def;
336 const Obj_Entry *defobj;
337 Elf_Addr x;
338
339 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false);
340 if (def == NULL)
341 return -1;
342
343 x = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend);
344
345 if (*where != x)
346 *where = x;
347 #ifdef RTLD_DEBUG_RELOC
348 dbg("32/GLOB_DAT %s in %s --> %p in %s",
349 defobj->strtab + def->st_name, obj->path,
350 (void *)*where, defobj->path);
351 #endif
352 break;
353 }
354
355 case R_TYPE(COPY):
356 #ifdef RTLD_DEBUG_RELOC
357 dbg("COPY");
358 #endif
359 break;
360
361 case R_TYPE(JMP_SLOT):
362 #ifdef RTLD_DEBUG_RELOC
363 dbg("JMP_SLOT");
364 #endif
365 break;
366
367 case R_TYPE(RELATIVE): { /* word32 B + A */
368 if (obj == &_rtld_objself &&
369 *where == (Elf_Addr)obj->relocbase + rela->r_addend)
370 break; /* GOT - already done */
371
372 *where = (Elf_Addr)obj->relocbase + rela->r_addend;
373 #ifdef RTLD_DEBUG_RELOC
374 dbg("RELATIVE in %s --> %p", obj->path, (void *)*where);
375 #endif
376 break;
377 }
378 #endif /* __powerpc__ */
379
380 default: {
381 const Elf_Sym *def;
382 const Obj_Entry *defobj;
383
384 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, true);
385 dbg("sym = %lu, type = %lu, offset = %p, addend = %p, contents = %p, symbol = %s",
386 (u_long)ELF_R_SYM(rela->r_info), (u_long)ELF_R_TYPE(rela->r_info),
387 (void *)rela->r_offset, (void *)rela->r_addend, (void *)*where,
388 def ? defobj->strtab + def->st_name : "??");
389 _rtld_error("%s: Unsupported relocation type %d in non-PLT relocations\n",
390 obj->path, ELF_R_TYPE(rela->r_info));
391 return -1;
392 }
393 }
394 return 0;
395 }
396
397 static int
399 _rtld_relocate_plt_object(
400 const Obj_Entry *obj,
401 const Elf_RelA *rela,
402 bool bind_now)
403 {
404 Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rela->r_offset);
405 Elf_Addr new_value;
406
407 /* Fully resolve procedure addresses now */
408
409 #if defined(__powerpc__)
410 return _rtld_reloc_powerpc_plt(obj, rela, bind_now);
411 #endif
412
413 #if defined(__alpha__) || defined(__i386__) /* (jrs) */
414 if (bind_now || obj->pltgot == NULL) {
415 const Elf_Sym *def;
416 const Obj_Entry *defobj;
417
418 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT));
419
420 def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, true);
421 if (def == NULL)
422 return -1;
423
424 new_value = (Elf_Addr) (defobj->relocbase + def->st_value);
425 #ifdef RTLD_DEBUG_RELOC
426 dbg("bind now %d/fixup in %s --> old=%p new=%p",
427 (int)bind_now,
428 defobj->strtab + def->st_name,
429 (void *)*where, (void *)new_value);
430 #endif
431 } else
432 #endif /* __alpha__ (jrs) */
433 if (!obj->mainprog) {
434 /* Just relocate the GOT slots pointing into the PLT */
435 new_value = *where + (Elf_Addr) (obj->relocbase);
436 #ifdef RTLD_DEBUG_RELOC
437 dbg("fixup !main in %s --> %p", obj->path, (void *)*where);
438 #endif
439 } else {
440 #ifdef __i386__
441 new_value = *where + (Elf_Addr) (obj->relocbase);
442 #ifdef RTLD_DEBUG_RELOC
443 dbg("fixup main in %s --> %p", obj->path, (void *)*where);
444 #endif
445 #endif
446 }
447 /*
448 * Since this page is probably copy-on-write, let's not write
449 * it unless we really really have to.
450 */
451 if (*where != new_value)
452 *where = new_value;
453 return 0;
454 }
455
456 caddr_t
458 _rtld_bind(
459 const Obj_Entry *obj,
460 Elf_Word reloff)
461 {
462 const Elf_RelA *rela;
463 Elf_RelA ourrela;
464
465 if (obj->pltrel != NULL) {
466 ourrela.r_info = ((const Elf_Rel *) ((caddr_t) obj->pltrel + reloff))->r_info;
467 ourrela.r_offset = ((const Elf_Rel *) ((caddr_t) obj->pltrel + reloff))->r_offset;
468 rela = &ourrela;
469 } else {
470 rela = (const Elf_RelA *) ((caddr_t) obj->pltrela + reloff);
471 }
472
473 if (_rtld_relocate_plt_object(obj, rela, true) < 0)
474 _rtld_die();
475
476 return *(caddr_t *)(obj->relocbase + rela->r_offset);
477 }
478
479 /*
481 * Relocate newly-loaded shared objects. The argument is a pointer to
482 * the Obj_Entry for the first such object. All objects from the first
483 * to the end of the list of objects are relocated. Returns 0 on success,
484 * or -1 on failure.
485 */
486 int
487 _rtld_relocate_objects(
488 Obj_Entry *first,
489 bool bind_now)
490 {
491 Obj_Entry *obj;
492 int ok = 1;
493
494 for (obj = first; obj != NULL; obj = obj->next) {
495
496 if (obj->nbuckets == 0 || obj->nchains == 0
497 || obj->buckets == NULL || obj->symtab == NULL
498 || obj->strtab == NULL) {
499 _rtld_error("%s: Shared object has no run-time symbol table",
500 obj->path);
501 return -1;
502 }
503
504 dbg(" relocating %s (%ld/%ld rel/rela, %ld/%ld plt rel/rela)",
505 obj->path,
506 (long)(obj->rellim - obj->rel), (long)(obj->relalim - obj->rela),
507 (long)(obj->pltrellim - obj->pltrel),
508 (long)(obj->pltrelalim - obj->pltrela));
509
510 if (obj->textrel) {
511 /* There are relocations to the write-protected text segment. */
512 if (mprotect(obj->mapbase, obj->textsize,
513 PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
514 _rtld_error("%s: Cannot write-enable text segment: %s",
515 obj->path, xstrerror(errno));
516 return -1;
517 }
518 }
519
520 if (obj->rel != NULL) {
521 /* Process the non-PLT relocations. */
522 const Elf_Rel *rel;
523 for (rel = obj->rel; rel < obj->rellim; ++rel) {
524 Elf_RelA ourrela;
525 ourrela.r_info = rel->r_info;
526 ourrela.r_offset = rel->r_offset;
527 #if defined(__mips__)
528 /* rel->r_offset is not valid on mips? */
529 if (ELF_R_TYPE(ourrela.r_info) == R_TYPE(NONE))
530 ourrela.r_addend = 0;
531 else
532 #endif
533 ourrela.r_addend = *(Elf_Word *) (obj->relocbase + rel->r_offset);
534
535 if (_rtld_relocate_nonplt_object(obj, &ourrela) < 0)
536 ok = 0;
537 }
538 }
539
540 if (obj->rela != NULL) {
541 /* Process the non-PLT relocations. */
542 const Elf_RelA *rela;
543 for (rela = obj->rela; rela < obj->relalim; ++rela) {
544 if (_rtld_relocate_nonplt_object(obj, rela) < 0)
545 ok = 0;
546 }
547 }
548
549 if (obj->textrel) { /* Re-protected the text segment. */
550 if (mprotect(obj->mapbase, obj->textsize,
551 PROT_READ|PROT_EXEC) == -1) {
552 _rtld_error("%s: Cannot write-protect text segment: %s",
553 obj->path, xstrerror(errno));
554 return -1;
555 }
556 }
557
558 /* Process the PLT relocations. */
559 if (obj->pltrel != NULL) {
560 const Elf_Rel *rel;
561 for (rel = obj->pltrel; rel < obj->pltrellim; ++rel) {
562 Elf_RelA ourrela;
563 ourrela.r_info = rel->r_info;
564 ourrela.r_offset = rel->r_offset;
565 ourrela.r_addend = *(Elf_Word *) (obj->relocbase + rel->r_offset);
566 if (_rtld_relocate_plt_object(obj, &ourrela, bind_now) < 0)
567 ok = 0;
568 }
569 }
570
571 if (obj->pltrela != NULL) {
572 const Elf_RelA *rela;
573 for (rela = obj->pltrela; rela < obj->pltrelalim; ++rela) {
574 if (_rtld_relocate_plt_object(obj, rela, bind_now) < 0)
575 ok = 0;
576 }
577 }
578
579 if (!ok)
580 return -1;
581
582
583 /* Set some sanity-checking numbers in the Obj_Entry. */
584 obj->magic = RTLD_MAGIC;
585 obj->version = RTLD_VERSION;
586
587 /* Fill in the dynamic linker entry points. */
588 obj->dlopen = _rtld_dlopen;
589 obj->dlsym = _rtld_dlsym;
590 obj->dlerror = _rtld_dlerror;
591 obj->dlclose = _rtld_dlclose;
592
593 /* Set the special PLTGOT entries. */
594 if (obj->pltgot != NULL) {
595 #if defined(__i386__)
596 obj->pltgot[1] = (Elf_Addr) obj;
597 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
598 #endif
599 #if defined(__alpha__)
600 /* This function will be called to perform the relocation. */
601 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
602 /* Identify this shared object */
603 obj->pltgot[3] = (Elf_Addr) obj;
604 #endif
605 #if defined(__mips__)
606 _rtld_relocate_mips_got(obj);
607
608 obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start;
609 /* XXX only if obj->pltgot[1] & 0x80000000 ?? */
610 obj->pltgot[1] |= (Elf_Addr) obj;
611 #endif
612 #if defined(__powerpc__)
613 _rtld_setup_powerpc_plt(obj);
614 #endif
615 }
616 }
617
618 return 0;
619 }
620