Home | History | Annotate | Line # | Download | only in ld.elf_so
reloc.c revision 1.4
      1 /*	$NetBSD: reloc.c,v 1.4 1999/01/10 18:18:56 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     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 = %d, type = %d, offset = %p, addend = %p, contents = %p, symbol = %s",
    323 	    ELF_R_SYM(rela->r_info), 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 (%d/%d rel/rela, %d/%d plt rel/rela)",
    443 	    obj->path,
    444 	    obj->rellim - obj->rel, obj->relalim - obj->rela,
    445 	    obj->pltrellim - obj->pltrel, obj->pltrelalim - obj->pltrela);
    446 
    447 	if (obj->textrel) {
    448 	    /* There are relocations to the write-protected text segment. */
    449 	    if (mprotect(obj->mapbase, obj->textsize,
    450 			 PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
    451 		_rtld_error("%s: Cannot write-enable text segment: %s",
    452 			    obj->path, xstrerror(errno));
    453 		return -1;
    454 	    }
    455 	}
    456 
    457 	if (obj->rel != NULL) {
    458 	    /* Process the non-PLT relocations. */
    459 	    const Elf_Rel *rel;
    460 	    for (rel = obj->rel;  rel < obj->rellim;  ++rel) {
    461 		Elf_RelA ourrela;
    462 		ourrela.r_info   = rel->r_info;
    463 		ourrela.r_offset = rel->r_offset;
    464 #if defined(__mips__)
    465 		/* rel->r_offset is not valid on mips? */
    466 		if (ELF_R_TYPE(ourrela.r_info) == R_TYPE(NONE))
    467 		    ourrela.r_addend = 0;
    468 		else
    469 #endif
    470 		ourrela.r_addend = *(Elf_Word *) (obj->relocbase + rel->r_offset);
    471 
    472 		if (_rtld_relocate_nonplt_object(obj, &ourrela) < 0)
    473 		    ok = 0;
    474 	    }
    475 	}
    476 
    477 	if (obj->rela != NULL) {
    478 	    /* Process the non-PLT relocations. */
    479 	    const Elf_RelA *rela;
    480 	    for (rela = obj->rela;  rela < obj->relalim;  ++rela) {
    481 		if (_rtld_relocate_nonplt_object(obj, rela) < 0)
    482 		    ok = 0;
    483 	    }
    484 	}
    485 
    486 	if (obj->textrel) {	/* Re-protected the text segment. */
    487 	    if (mprotect(obj->mapbase, obj->textsize,
    488 			 PROT_READ|PROT_EXEC) == -1) {
    489 		_rtld_error("%s: Cannot write-protect text segment: %s",
    490 			    obj->path, xstrerror(errno));
    491 		return -1;
    492 	    }
    493 	}
    494 
    495 	/* Process the PLT relocations. */
    496 	if (obj->pltrel != NULL) {
    497 	    const Elf_Rel *rel;
    498 	    for (rel = obj->pltrel; rel < obj->pltrellim;  ++rel) {
    499 		Elf_RelA ourrela;
    500 		ourrela.r_info   = rel->r_info;
    501 		ourrela.r_offset = rel->r_offset;
    502 		ourrela.r_addend = *(Elf_Word *) (obj->relocbase + rel->r_offset);
    503 		if (_rtld_relocate_plt_object(obj, &ourrela, bind_now) < 0)
    504 		    ok = 0;
    505 	    }
    506 	}
    507 
    508 	if (obj->pltrela != NULL) {
    509 	    const Elf_RelA *rela;
    510 	    for (rela = obj->pltrela;  rela < obj->pltrelalim;  ++rela) {
    511 		if (_rtld_relocate_plt_object(obj, rela, bind_now) < 0)
    512 		    ok = 0;
    513 	    }
    514 	}
    515 
    516 	if (!ok)
    517 	    return -1;
    518 
    519 
    520 	/* Set some sanity-checking numbers in the Obj_Entry. */
    521 	obj->magic = RTLD_MAGIC;
    522 	obj->version = RTLD_VERSION;
    523 
    524 	/* Fill in the dynamic linker entry points. */
    525 	obj->dlopen  = _rtld_dlopen;
    526 	obj->dlsym   = _rtld_dlsym;
    527 	obj->dlerror = _rtld_dlerror;
    528 	obj->dlclose = _rtld_dlclose;
    529 
    530 	/* Set the special PLTGOT entries. */
    531 	if (obj->pltgot != NULL) {
    532 #if defined(__i386__)
    533 	    obj->pltgot[1] = (Elf_Addr) obj;
    534 	    obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
    535 #endif
    536 #if defined(__alpha__)
    537 	    /* This function will be called to perform the relocation.  */
    538 	    obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
    539 	    /* Identify this shared object */
    540 	    obj->pltgot[3] = (Elf_Addr) obj;
    541 #endif
    542 #if defined(__mips__)
    543 	    _rtld_relocate_mips_got(obj);
    544 
    545 	    obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start;
    546 	    /* XXX only if obj->pltgot[1] & 0x80000000 ?? */
    547 	    obj->pltgot[1] |= (Elf_Addr) obj;
    548 #endif
    549 #if defined(__powerpc__)
    550 	    _rtld_setup_powerpc_plt(obj);
    551 #endif
    552 	}
    553     }
    554 
    555     return 0;
    556 }
    557