Home | History | Annotate | Line # | Download | only in ld.elf_so
      1 /*	$NetBSD: reloc.c,v 1.120 2025/05/02 23:03: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.120 2025/05/02 23:03: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  * _rtld_relocate_relr(obj)
    182  *
    183  *	Relocate the RELR entries of obj.  The RELR table is encoded as
    184  *	a sequence of alternating addresses and bitmaps.  Each address
    185  *	entry has the low-order bit clear, and each bitmap has the
    186  *	low-order bit set:
    187  *
    188  *		AAAAAAA0
    189  *		BBBBBBB1
    190  *		BBBBBBB1
    191  *		BBBBBBB1
    192  *		AAAAAAA0
    193  *		BBBBBBB1
    194  *		...
    195  *
    196  *	Each address A is taken relative to obj->relocbase, and has
    197  *	obj->relocbase added to the Elf_Addr it points at.  For each
    198  *	bit i in the following bitmaps concatenated starting at 1,
    199  *	excluding the low-order bit used to distinguish bitmaps from
    200  *	addresses, the Elf_Addr at the address
    201  *
    202  *		A + sizeof(Elf_Addr)*i
    203  *
    204  *	(again, relative to obj->relocbase) has obj->relocbase added
    205  *	too.
    206  *
    207  *	DT_RELR relocations are processed before any DT_REL or DT_RELA
    208  *	relocations.
    209  *
    210  *	References:
    211  *
    212  *	Rahul Chaudhry, `Re: Proposal for a new section type SHT_RELR',
    213  *	generic-abi mailing list, 2018-02-07.
    214  *
    215  *	https://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ
    216  *	https://web.archive.org/web/20241213012330/https://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ
    217  */
    218 static void
    219 _rtld_relocate_relr(Obj_Entry *obj)
    220 {
    221 	const Elf_Relr *relr;
    222 
    223 	if (obj->relr == obj->relrlim)
    224 		return;
    225 
    226 	for (relr = obj->relr; relr < obj->relrlim;) {
    227 		Elf_Addr *where;
    228 
    229 		/*
    230 		 * At an address entry.  Relocate the address.
    231 		 */
    232 		assert((*relr & 1) == 0);
    233 		where = (Elf_Addr *)(obj->relocbase + *relr);
    234 		*where++ += (Elf_Addr)obj->relocbase;
    235 
    236 		/*
    237 		 * Process every bitmap entry after the address.
    238 		 */
    239 		while (++relr < obj->relrlim && *relr & 1) {
    240 			unsigned i;
    241 
    242 			/*
    243 			 * Process every set bit in the bitmap.  Note
    244 			 * that the first bit (i=0) is not processed
    245 			 * here -- it's just metadata to mark a bitmap
    246 			 * entry.
    247 			 */
    248 			for (i = 1; i < CHAR_BIT*sizeof(*relr); i++, where++) {
    249 				if (*relr & ((Elf_Relr)1 << i))
    250 					*where += (Elf_Addr)obj->relocbase;
    251 			}
    252 		}
    253 	}
    254 }
    255 
    256 /*
    257  * Relocate newly-loaded shared objects.  The argument is a pointer to
    258  * the Obj_Entry for the first such object.  All objects from the first
    259  * to the end of the list of objects are relocated.  Returns 0 on success,
    260  * or -1 on failure.
    261  */
    262 int
    263 _rtld_relocate_objects(Obj_Entry *first, bool bind_now)
    264 {
    265 	Obj_Entry *obj;
    266 	int ok = 1;
    267 
    268 	for (obj = first; obj != NULL; obj = obj->next) {
    269 		if ((!obj->sysv_hash && !obj->gnu_hash) ||
    270 		    obj->symtab == NULL || obj->strtab == NULL) {
    271 			_rtld_error("%s: Shared object has no run-time"
    272 			    " symbol table", obj->path);
    273 			return -1;
    274 		}
    275 		if (obj->nbuckets == UINT32_MAX) {
    276 			_rtld_error("%s: Symbol table too large", obj->path);
    277 			return -1;
    278 		}
    279 		rdbg((" relocating %s (%ld/%ld rel/rela, %ld/%ld plt rel/rela)",
    280 		    obj->path,
    281 		    (long)(obj->rellim - obj->rel),
    282 		    (long)(obj->relalim - obj->rela),
    283 		    (long)(obj->pltrellim - obj->pltrel),
    284 		    (long)(obj->pltrelalim - obj->pltrela)));
    285 
    286 		if (obj->textrel) {
    287 			xwarnx("%s: text relocations", obj->path);
    288 			/*
    289 			 * There are relocations to the write-protected text
    290 			 * segment.
    291 			 */
    292 			if (mprotect(obj->mapbase, obj->textsize,
    293 				PROT_READ | PROT_WRITE) == -1) {
    294 				_rtld_error("%s: Cannot write-enable text "
    295 				    "segment: %s", obj->path, xstrerror(errno));
    296 				return -1;
    297 			}
    298 		}
    299 		dbg(("doing relative relocations"));
    300 		_rtld_relocate_relr(obj);
    301 		dbg(("doing non-PLT relocations"));
    302 		if (_rtld_relocate_nonplt_objects(obj) < 0)
    303 			ok = 0;
    304 		if (obj->textrel) {	/* Re-protected the text segment. */
    305 			if (mprotect(obj->mapbase, obj->textsize,
    306 				     PROT_READ | PROT_EXEC) == -1) {
    307 				_rtld_error("%s: Cannot write-protect text "
    308 				    "segment: %s", obj->path, xstrerror(errno));
    309 				return -1;
    310 			}
    311 		}
    312 		dbg(("doing lazy PLT binding"));
    313 		if (_rtld_relocate_plt_lazy(obj) < 0)
    314 			ok = 0;
    315 		if (obj->z_now || bind_now) {
    316 			dbg(("doing immediate PLT binding"));
    317 			if (_rtld_relocate_plt_objects(obj) < 0)
    318 				ok = 0;
    319 		}
    320 		if (!ok)
    321 			return -1;
    322 
    323 		dbg(("fixing up PLTGOT"));
    324 		/* Set the special PLTGOT entries. */
    325 		if (obj->pltgot != NULL)
    326 			_rtld_setup_pltgot(obj);
    327 #ifdef GNU_RELRO
    328 		if (_rtld_relro(obj, false) == -1)
    329 			return -1;
    330 #endif
    331 	}
    332 	return 0;
    333 }
    334 
    335 Elf_Addr
    336 _rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)
    337 {
    338 	Elf_Addr target;
    339 
    340 	_rtld_shared_exit();
    341 	target = _rtld_resolve_ifunc2(obj,
    342 	    (Elf_Addr)obj->relocbase + def->st_value);
    343 	_rtld_shared_enter();
    344 	return target;
    345 }
    346 
    347 Elf_Addr
    348 _rtld_resolve_ifunc2(const Obj_Entry *obj, Elf_Addr addr)
    349 {
    350 	Elf_Addr target;
    351 
    352 	target = _rtld_call_function_addr(obj, addr);
    353 
    354 	return target;
    355 }
    356 
    357 #if \
    358     !defined(RTLD_COMMON_CALL_IFUNC_RELA) && \
    359     !defined(RTLD_COMMON_CALL_IFUNC_REL) && \
    360     !defined(RTLD_ARCH_CALL_IFUNC)
    361 void
    362 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
    363 {
    364 }
    365 #endif
    366 
    367 #ifdef RTLD_COMMON_CALL_IFUNC_RELA
    368 #  ifdef __sparc__
    369 #  include <machine/elf_support.h>
    370 #  endif
    371 
    372 void
    373 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
    374 {
    375 	const Elf_Rela *rela;
    376 	Elf_Addr *where;
    377 #ifdef __sparc__
    378 	Elf_Word *where2;
    379 #endif
    380 	Elf_Addr target;
    381 
    382 	while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
    383 		rela = obj->pltrelalim - obj->ifunc_remaining--;
    384 #ifdef __sparc__
    385 #define PLT_IRELATIVE R_TYPE(JMP_IREL)
    386 #else
    387 #define PLT_IRELATIVE R_TYPE(IRELATIVE)
    388 #endif
    389 		if (ELF_R_TYPE(rela->r_info) != PLT_IRELATIVE)
    390 			continue;
    391 #ifdef __sparc__
    392 		where2 = (Elf_Word *)(obj->relocbase + rela->r_offset);
    393 #else
    394 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
    395 #endif
    396 		target = (Elf_Addr)(obj->relocbase + rela->r_addend);
    397 		_rtld_exclusive_exit(mask);
    398 		target = _rtld_resolve_ifunc2(obj, target);
    399 		_rtld_exclusive_enter(mask);
    400 #ifdef __sparc__
    401 		sparc_write_branch(where2 + 1, (void *)target);
    402 #else
    403 		if (*where != target)
    404 			*where = target;
    405 #endif
    406 	}
    407 
    408 	while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
    409 		rela = obj->relalim - obj->ifunc_remaining_nonplt--;
    410 		if (ELF_R_TYPE(rela->r_info) != R_TYPE(IRELATIVE))
    411 			continue;
    412 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
    413 		target = (Elf_Addr)(obj->relocbase + rela->r_addend);
    414 		_rtld_exclusive_exit(mask);
    415 		target = _rtld_resolve_ifunc2(obj, target);
    416 		_rtld_exclusive_enter(mask);
    417 		if (*where != target)
    418 			*where = target;
    419 	}
    420 }
    421 #endif
    422 
    423 #ifdef RTLD_COMMON_CALL_IFUNC_REL
    424 void
    425 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
    426 {
    427 	const Elf_Rel *rel;
    428 	Elf_Addr *where, target;
    429 
    430 	while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
    431 		rel = obj->pltrellim - obj->ifunc_remaining;
    432 		--obj->ifunc_remaining;
    433 		if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
    434 			where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
    435 			_rtld_exclusive_exit(mask);
    436 			target = _rtld_resolve_ifunc2(obj, *where);
    437 			_rtld_exclusive_enter(mask);
    438 			if (*where != target)
    439 				*where = target;
    440 		}
    441 	}
    442 
    443 	while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
    444 		rel = obj->rellim - obj->ifunc_remaining_nonplt--;
    445 		if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
    446 			where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
    447 			_rtld_exclusive_exit(mask);
    448 			target = _rtld_resolve_ifunc2(obj, *where);
    449 			_rtld_exclusive_enter(mask);
    450 			if (*where != target)
    451 				*where = target;
    452 		}
    453 	}
    454 }
    455 #endif
    456