Home | History | Annotate | Line # | Download | only in ld.elf_so
reloc.c revision 1.115
      1 /*	$NetBSD: reloc.c,v 1.115 2020/02/29 04:23:05 kamil 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 <sys/cdefs.h>
     41 #ifndef lint
     42 __RCSID("$NetBSD: reloc.c,v 1.115 2020/02/29 04:23:05 kamil Exp $");
     43 #endif /* not lint */
     44 
     45 #include <err.h>
     46 #include <errno.h>
     47 #include <fcntl.h>
     48 #include <stdarg.h>
     49 #include <stdio.h>
     50 #include <stdlib.h>
     51 #include <string.h>
     52 #include <unistd.h>
     53 #include <sys/types.h>
     54 #include <sys/mman.h>
     55 #include <sys/bitops.h>
     56 #include <dirent.h>
     57 
     58 #include "debug.h"
     59 #include "rtld.h"
     60 
     61 #ifndef RTLD_INHIBIT_COPY_RELOCS
     62 static int _rtld_do_copy_relocation(const Obj_Entry *, const Elf_Rela *);
     63 
     64 static int
     65 _rtld_do_copy_relocation(const Obj_Entry *dstobj, const Elf_Rela *rela)
     66 {
     67 	void           *dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
     68 	const Elf_Sym  *dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
     69 	const char     *name = dstobj->strtab + dstsym->st_name;
     70 	Elf_Hash        hash;
     71 	size_t          size = dstsym->st_size;
     72 	const void     *srcaddr;
     73 	const Elf_Sym  *srcsym = NULL;
     74 	Obj_Entry      *srcobj;
     75 
     76 	hash.sysv = _rtld_sysv_hash(name);
     77 	hash.gnu = _rtld_gnu_hash(name);
     78 
     79 	if (__predict_false(size == 0)) {
     80 #if defined(__powerpc__) && !defined(__LP64) /* PR port-macppc/47464 */
     81 		if (strcmp(name, "_SDA_BASE_") == 0
     82 		    || strcmp(name, "_SDA2_BASE_") == 0)
     83 		{
     84 			rdbg(("COPY %s %s --> ignoring old binutils bug",
     85 			      dstobj->path, name));
     86 			return 0;
     87 		}
     88 #endif
     89 #if 0 /* shall we warn? */
     90 		xwarnx("%s: zero size COPY relocation for \"%s\"",
     91 		       dstobj->path, name);
     92 #endif
     93 	}
     94 
     95 	for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) {
     96 		srcsym = _rtld_symlook_obj(name, &hash, srcobj, 0,
     97 		    _rtld_fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)));
     98 		if (srcsym != NULL)
     99 			break;
    100 	}
    101 
    102 	if (srcobj == NULL) {
    103 		_rtld_error("Undefined symbol \"%s\" referenced from COPY"
    104 		    " relocation in %s", name, dstobj->path);
    105 		return (-1);
    106 	}
    107 	srcaddr = (const void *)(srcobj->relocbase + srcsym->st_value);
    108 	rdbg(("COPY %s %s %s --> src=%p dst=%p size %ld",
    109 	    dstobj->path, srcobj->path, name, srcaddr,
    110 	    (void *)dstaddr, (long)size));
    111 	(void)memcpy(dstaddr, srcaddr, size);
    112 	return (0);
    113 }
    114 #endif /* RTLD_INHIBIT_COPY_RELOCS */
    115 
    116 
    117 /*
    118  * Process the special R_xxx_COPY relocations in the main program.  These
    119  * copy data from a shared object into a region in the main program's BSS
    120  * segment.
    121  *
    122  * Returns 0 on success, -1 on failure.
    123  */
    124 int
    125 _rtld_do_copy_relocations(const Obj_Entry *dstobj)
    126 {
    127 #ifndef RTLD_INHIBIT_COPY_RELOCS
    128 
    129 	/* COPY relocations are invalid elsewhere */
    130 	assert(!dstobj->isdynamic);
    131 
    132 	if (dstobj->rel != NULL) {
    133 		const Elf_Rel  *rel;
    134 		for (rel = dstobj->rel; rel < dstobj->rellim; ++rel) {
    135 			if (ELF_R_TYPE(rel->r_info) == R_TYPE(COPY)) {
    136 				Elf_Rela        ourrela;
    137 				ourrela.r_info = rel->r_info;
    138 				ourrela.r_offset = rel->r_offset;
    139 				ourrela.r_addend = 0;
    140 				if (_rtld_do_copy_relocation(dstobj,
    141 				    &ourrela) < 0)
    142 					return (-1);
    143 			}
    144 		}
    145 	}
    146 	if (dstobj->rela != NULL) {
    147 		const Elf_Rela *rela;
    148 		for (rela = dstobj->rela; rela < dstobj->relalim; ++rela) {
    149 			if (ELF_R_TYPE(rela->r_info) == R_TYPE(COPY)) {
    150 				if (_rtld_do_copy_relocation(dstobj, rela) < 0)
    151 					return (-1);
    152 			}
    153 		}
    154 	}
    155 #ifdef GNU_RELRO
    156 	if (_rtld_relro(dstobj, true) == -1)
    157 		return -1;
    158 #endif
    159 #endif /* RTLD_INHIBIT_COPY_RELOCS */
    160 
    161 	return (0);
    162 }
    163 
    164 /*
    165  * Relocate newly-loaded shared objects.  The argument is a pointer to
    166  * the Obj_Entry for the first such object.  All objects from the first
    167  * to the end of the list of objects are relocated.  Returns 0 on success,
    168  * or -1 on failure.
    169  */
    170 int
    171 _rtld_relocate_objects(Obj_Entry *first, bool bind_now)
    172 {
    173 	Obj_Entry *obj;
    174 	int ok = 1;
    175 
    176 	for (obj = first; obj != NULL; obj = obj->next) {
    177 		if (obj->nbuckets == 0 || obj->nchains == 0 ||
    178 		    obj->buckets == NULL || obj->symtab == NULL ||
    179 		    obj->strtab == NULL) {
    180 			_rtld_error("%s: Shared object has no run-time"
    181 			    " symbol table", obj->path);
    182 			return -1;
    183 		}
    184 		if (obj->nbuckets == UINT32_MAX) {
    185 			_rtld_error("%s: Symbol table too large", obj->path);
    186 			return -1;
    187 		}
    188 		rdbg((" relocating %s (%ld/%ld rel/rela, %ld/%ld plt rel/rela)",
    189 		    obj->path,
    190 		    (long)(obj->rellim - obj->rel),
    191 		    (long)(obj->relalim - obj->rela),
    192 		    (long)(obj->pltrellim - obj->pltrel),
    193 		    (long)(obj->pltrelalim - obj->pltrela)));
    194 
    195 		if (obj->textrel) {
    196 			xwarnx("%s: text relocations", obj->path);
    197 			/*
    198 			 * There are relocations to the write-protected text
    199 			 * segment.
    200 			 */
    201 			if (mprotect(obj->mapbase, obj->textsize,
    202 				PROT_READ | PROT_WRITE) == -1) {
    203 				_rtld_error("%s: Cannot write-enable text "
    204 				    "segment: %s", obj->path, xstrerror(errno));
    205 				return -1;
    206 			}
    207 		}
    208 		dbg(("doing non-PLT relocations"));
    209 		if (_rtld_relocate_nonplt_objects(obj) < 0)
    210 			ok = 0;
    211 		if (obj->textrel) {	/* Re-protected the text segment. */
    212 			if (mprotect(obj->mapbase, obj->textsize,
    213 				     PROT_READ | PROT_EXEC) == -1) {
    214 				_rtld_error("%s: Cannot write-protect text "
    215 				    "segment: %s", obj->path, xstrerror(errno));
    216 				return -1;
    217 			}
    218 		}
    219 		dbg(("doing lazy PLT binding"));
    220 		if (_rtld_relocate_plt_lazy(obj) < 0)
    221 			ok = 0;
    222 		if (obj->z_now || bind_now) {
    223 			dbg(("doing immediate PLT binding"));
    224 			if (_rtld_relocate_plt_objects(obj) < 0)
    225 				ok = 0;
    226 		}
    227 		if (!ok)
    228 			return -1;
    229 
    230 		dbg(("fixing up PLTGOT"));
    231 		/* Set the special PLTGOT entries. */
    232 		if (obj->pltgot != NULL)
    233 			_rtld_setup_pltgot(obj);
    234 #ifdef GNU_RELRO
    235 		if (_rtld_relro(obj, false) == -1)
    236 			return -1;
    237 #endif
    238 	}
    239 	return 0;
    240 }
    241 
    242 Elf_Addr
    243 _rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)
    244 {
    245 	Elf_Addr target;
    246 
    247 	_rtld_shared_exit();
    248 	target = _rtld_resolve_ifunc2(obj,
    249 	    (Elf_Addr)obj->relocbase + def->st_value);
    250 	_rtld_shared_enter();
    251 	return target;
    252 }
    253 
    254 Elf_Addr
    255 _rtld_resolve_ifunc2(const Obj_Entry *obj, Elf_Addr addr)
    256 {
    257 	Elf_Addr target;
    258 
    259 	target = _rtld_call_function_addr(obj, addr);
    260 
    261 	return target;
    262 }
    263 
    264 #ifdef RTLD_COMMON_CALL_IFUNC_RELA
    265 #  ifdef __sparc__
    266 #  include <machine/elf_support.h>
    267 #  endif
    268 
    269 void
    270 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
    271 {
    272 	const Elf_Rela *rela;
    273 	Elf_Addr *where;
    274 #ifdef __sparc__
    275 	Elf_Word *where2;
    276 #endif
    277 	Elf_Addr target;
    278 
    279 	while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
    280 		rela = obj->pltrelalim - obj->ifunc_remaining--;
    281 #ifdef __sparc__
    282 #define PLT_IRELATIVE R_TYPE(JMP_IREL)
    283 #else
    284 #define PLT_IRELATIVE R_TYPE(IRELATIVE)
    285 #endif
    286 		if (ELF_R_TYPE(rela->r_info) != PLT_IRELATIVE)
    287 			continue;
    288 #ifdef __sparc__
    289 		where2 = (Elf_Word *)(obj->relocbase + rela->r_offset);
    290 #else
    291 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
    292 #endif
    293 		target = (Elf_Addr)(obj->relocbase + rela->r_addend);
    294 		_rtld_exclusive_exit(mask);
    295 		target = _rtld_resolve_ifunc2(obj, target);
    296 		_rtld_exclusive_enter(mask);
    297 #ifdef __sparc__
    298 		sparc_write_branch(where2 + 1, (void *)target);
    299 #else
    300 		if (*where != target)
    301 			*where = target;
    302 #endif
    303 	}
    304 
    305 	while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
    306 		rela = obj->relalim - obj->ifunc_remaining_nonplt--;
    307 		if (ELF_R_TYPE(rela->r_info) != R_TYPE(IRELATIVE))
    308 			continue;
    309 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
    310 		target = (Elf_Addr)(obj->relocbase + rela->r_addend);
    311 		_rtld_exclusive_exit(mask);
    312 		target = _rtld_resolve_ifunc2(obj, target);
    313 		_rtld_exclusive_enter(mask);
    314 		if (*where != target)
    315 			*where = target;
    316 	}
    317 }
    318 #endif
    319 
    320 #ifdef RTLD_COMMON_CALL_IFUNC_REL
    321 void
    322 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
    323 {
    324 	const Elf_Rel *rel;
    325 	Elf_Addr *where, target;
    326 
    327 	while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
    328 		rel = obj->pltrellim - obj->ifunc_remaining;
    329 		--obj->ifunc_remaining;
    330 		if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
    331 			where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
    332 			_rtld_exclusive_exit(mask);
    333 			target = _rtld_resolve_ifunc2(obj, *where);
    334 			_rtld_exclusive_enter(mask);
    335 			if (*where != target)
    336 				*where = target;
    337 		}
    338 	}
    339 
    340 	while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
    341 		rel = obj->rellim - obj->ifunc_remaining_nonplt--;
    342 		if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
    343 			where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
    344 			_rtld_exclusive_exit(mask);
    345 			target = _rtld_resolve_ifunc2(obj, *where);
    346 			_rtld_exclusive_enter(mask);
    347 			if (*where != target)
    348 				*where = target;
    349 		}
    350 	}
    351 }
    352 #endif
    353