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