Home | History | Annotate | Line # | Download | only in ld.elf_so
      1 /*	$NetBSD: reloc.c,v 1.122 2026/07/18 04:26:42 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.122 2026/07/18 04:26:42 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->relocstate != OBJRELOC_READY)
    270 			continue;
    271 		_rtld_objrelocpending--;
    272 		if ((!obj->sysv_hash && !obj->gnu_hash) ||
    273 		    obj->symtab == NULL || obj->strtab == NULL) {
    274 			_rtld_error("%s: Shared object has no run-time"
    275 			    " symbol table", obj->path);
    276 			obj->relocstate = OBJRELOC_FAILED;
    277 			return -1;
    278 		}
    279 		if (obj->nbuckets == UINT32_MAX) {
    280 			_rtld_error("%s: Symbol table too large", obj->path);
    281 			obj->relocstate = OBJRELOC_FAILED;
    282 			return -1;
    283 		}
    284 		rdbg((" relocating %s (%ld/%ld rel/rela, %ld/%ld plt rel/rela)",
    285 		    obj->path,
    286 		    (long)(obj->rellim - obj->rel),
    287 		    (long)(obj->relalim - obj->rela),
    288 		    (long)(obj->pltrellim - obj->pltrel),
    289 		    (long)(obj->pltrelalim - obj->pltrela)));
    290 
    291 		if (obj->textrel) {
    292 			xwarnx("%s: text relocations", obj->path);
    293 			/*
    294 			 * There are relocations to the write-protected text
    295 			 * segment.
    296 			 */
    297 			if (mprotect(obj->mapbase, obj->textsize,
    298 				PROT_READ | PROT_WRITE) == -1) {
    299 				_rtld_error("%s: Cannot write-enable text "
    300 				    "segment: %s", obj->path, xstrerror(errno));
    301 				obj->relocstate = OBJRELOC_FAILED;
    302 				return -1;
    303 			}
    304 		}
    305 		dbg(("doing relative relocations"));
    306 		_rtld_relocate_relr(obj);
    307 		dbg(("doing non-PLT relocations"));
    308 		if (_rtld_relocate_nonplt_objects(obj) < 0)
    309 			ok = 0;
    310 		if (obj->textrel) {	/* Re-protected the text segment. */
    311 			if (mprotect(obj->mapbase, obj->textsize,
    312 				     PROT_READ | PROT_EXEC) == -1) {
    313 				_rtld_error("%s: Cannot write-protect text "
    314 				    "segment: %s", obj->path, xstrerror(errno));
    315 				obj->relocstate = OBJRELOC_FAILED;
    316 				return -1;
    317 			}
    318 		}
    319 		dbg(("doing lazy PLT binding"));
    320 		if (_rtld_relocate_plt_lazy(obj) < 0)
    321 			ok = 0;
    322 		if (obj->z_now || bind_now) {
    323 			dbg(("doing immediate PLT binding"));
    324 			if (_rtld_relocate_plt_objects(obj) < 0)
    325 				ok = 0;
    326 		}
    327 		if (!ok) {
    328 			obj->relocstate = OBJRELOC_FAILED;
    329 			return -1;
    330 		}
    331 
    332 		dbg(("fixing up PLTGOT"));
    333 		/* Set the special PLTGOT entries. */
    334 		if (obj->pltgot != NULL)
    335 			_rtld_setup_pltgot(obj);
    336 #ifdef GNU_RELRO
    337 		if (_rtld_relro(obj, false) == -1) {
    338 			obj->relocstate = OBJRELOC_FAILED;
    339 			return -1;
    340 		}
    341 #endif
    342 		obj->relocstate = OBJRELOC_DONE;
    343 	}
    344 	return 0;
    345 }
    346 
    347 Elf_Addr
    348 _rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)
    349 {
    350 	Elf_Addr target;
    351 
    352 	_rtld_shared_exit();
    353 	target = _rtld_resolve_ifunc2(obj,
    354 	    (Elf_Addr)obj->relocbase + def->st_value);
    355 	_rtld_shared_enter();
    356 	return target;
    357 }
    358 
    359 Elf_Addr
    360 _rtld_resolve_ifunc2(const Obj_Entry *obj, Elf_Addr addr)
    361 {
    362 	Elf_Addr target;
    363 
    364 	target = _rtld_call_function_addr(obj, addr);
    365 
    366 	return target;
    367 }
    368 
    369 #if \
    370     !defined(RTLD_COMMON_CALL_IFUNC_RELA) && \
    371     !defined(RTLD_COMMON_CALL_IFUNC_REL) && \
    372     !defined(RTLD_ARCH_CALL_IFUNC)
    373 void
    374 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
    375 {
    376 }
    377 #endif
    378 
    379 #ifdef RTLD_COMMON_CALL_IFUNC_RELA
    380 #  ifdef __sparc__
    381 #  include <machine/elf_support.h>
    382 #  endif
    383 
    384 void
    385 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
    386 {
    387 	const Elf_Rela *rela;
    388 	Elf_Addr *where;
    389 #ifdef __sparc__
    390 	Elf_Word *where2;
    391 #endif
    392 	Elf_Addr target;
    393 
    394 	while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
    395 		rela = obj->pltrelalim - obj->ifunc_remaining--;
    396 #ifdef __sparc__
    397 #define PLT_IRELATIVE R_TYPE(JMP_IREL)
    398 #else
    399 #define PLT_IRELATIVE R_TYPE(IRELATIVE)
    400 #endif
    401 		if (ELF_R_TYPE(rela->r_info) != PLT_IRELATIVE)
    402 			continue;
    403 #ifdef __sparc__
    404 		where2 = (Elf_Word *)(obj->relocbase + rela->r_offset);
    405 #else
    406 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
    407 #endif
    408 		target = (Elf_Addr)(obj->relocbase + rela->r_addend);
    409 		_rtld_exclusive_exit(mask);
    410 		target = _rtld_resolve_ifunc2(obj, target);
    411 		_rtld_exclusive_enter(mask);
    412 #ifdef __sparc__
    413 		sparc_write_branch(where2 + 1, (void *)target);
    414 #else
    415 		if (*where != target)
    416 			*where = target;
    417 #endif
    418 	}
    419 
    420 	while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
    421 		rela = obj->relalim - obj->ifunc_remaining_nonplt--;
    422 		if (ELF_R_TYPE(rela->r_info) != R_TYPE(IRELATIVE))
    423 			continue;
    424 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
    425 		target = (Elf_Addr)(obj->relocbase + rela->r_addend);
    426 		_rtld_exclusive_exit(mask);
    427 		target = _rtld_resolve_ifunc2(obj, target);
    428 		_rtld_exclusive_enter(mask);
    429 		if (*where != target)
    430 			*where = target;
    431 	}
    432 }
    433 #endif
    434 
    435 #ifdef RTLD_COMMON_CALL_IFUNC_REL
    436 void
    437 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
    438 {
    439 	const Elf_Rel *rel;
    440 	Elf_Addr *where, target;
    441 
    442 	while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
    443 		rel = obj->pltrellim - obj->ifunc_remaining;
    444 		--obj->ifunc_remaining;
    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 	while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
    456 		rel = obj->rellim - obj->ifunc_remaining_nonplt--;
    457 		if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
    458 			where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
    459 			_rtld_exclusive_exit(mask);
    460 			target = _rtld_resolve_ifunc2(obj, *where);
    461 			_rtld_exclusive_enter(mask);
    462 			if (*where != target)
    463 				*where = target;
    464 		}
    465 	}
    466 }
    467 #endif
    468