Home | History | Annotate | Line # | Download | only in riscv
mdreloc.c revision 1.10
      1 /*	$NetBSD: mdreloc.c,v 1.10 2024/07/22 23:11:05 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2014 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Matt Thomas of 3am Software Foundry.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __RCSID("$NetBSD: mdreloc.c,v 1.10 2024/07/22 23:11:05 riastradh Exp $");
     35 #endif /* not lint */
     36 
     37 /*
     38  * RISC-V ELF relocations.
     39  *
     40  * Reference:
     41  *
     42  *	[RISCVELF] RISC-V ELF Specification, 2024-07-17.
     43  *	https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/9fec6080d15e7f009c9e714d1e9b8dd7177b0b67/riscv-elf.adoc
     44  */
     45 
     46 #include <sys/types.h>
     47 #include <sys/endian.h>
     48 #include <sys/tls.h>
     49 
     50 #include <stdlib.h>
     51 #include <string.h>
     52 
     53 #include "debug.h"
     54 #include "rtld.h"
     55 
     56 void _rtld_bind_start(void);
     57 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
     58 void *_rtld_bind(const Obj_Entry *, Elf_Word);
     59 
     60 void
     61 _rtld_setup_pltgot(const Obj_Entry *obj)
     62 {
     63 	obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start;
     64 	obj->pltgot[1] = (Elf_Addr) obj;
     65 }
     66 
     67 void
     68 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
     69 {
     70 	const Elf_Rela *rela = NULL, *relalim;
     71 	Elf_Addr relasz = 0;
     72 
     73 	for (; dynp->d_tag != DT_NULL; dynp++) {
     74 		switch (dynp->d_tag) {
     75 		case DT_RELA:
     76 			rela = (const Elf_Rela *)(relocbase + dynp->d_un.d_ptr);
     77 			break;
     78 		case DT_RELASZ:
     79 			relasz = dynp->d_un.d_val;
     80 			break;
     81 		}
     82 	}
     83 
     84 	relalim = (const Elf_Rela *)((uintptr_t)rela + relasz);
     85 	for (; rela < relalim; rela++) {
     86 		Elf_Word r_type = ELF_R_TYPE(rela->r_info);
     87 		Elf_Addr *where = (Elf_Addr *)(relocbase + rela->r_offset);
     88 
     89 		switch (r_type) {
     90 		case R_TYPE(RELATIVE): {
     91 			Elf_Addr val = relocbase + rela->r_addend;
     92 			*where = val;
     93 			rdbg(("RELATIVE/L(%p) -> %p in <self>",
     94 			    where, (void *)val));
     95 			break;
     96 		    }
     97 
     98 		case R_TYPE(NONE):
     99 			break;
    100 
    101 		default:
    102 			abort();
    103 		}
    104 	}
    105 }
    106 
    107 int
    108 _rtld_relocate_nonplt_objects(Obj_Entry *obj)
    109 {
    110 	const Elf_Rela *rela;
    111 	const Elf_Sym *def = NULL;
    112 	const Obj_Entry *defobj = NULL;
    113 	unsigned long last_symnum = ULONG_MAX;
    114 
    115 	for (rela = obj->rela; rela < obj->relalim; rela++) {
    116 		Elf_Addr * const where =
    117 		    (Elf_Addr *)(obj->relocbase + rela->r_offset);
    118 		const Elf_Word r_type = ELF_R_TYPE(rela->r_info);
    119 		unsigned long symnum;
    120 
    121 		switch (r_type) {
    122 		case R_TYPESZ(ADDR):
    123 		case R_TYPESZ(TLS_DTPMOD):
    124 		case R_TYPESZ(TLS_DTPREL):
    125 		case R_TYPESZ(TLS_TPREL):
    126 			symnum = ELF_R_SYM(rela->r_info);
    127 			if (last_symnum != symnum) {
    128 				last_symnum = symnum;
    129 				def = _rtld_find_symdef(symnum, obj, &defobj,
    130 				    false);
    131 				if (def == NULL)
    132 					return -1;
    133 			}
    134 			break;
    135 		default:
    136 			break;
    137 		}
    138 
    139 		switch (r_type) {
    140 		case R_TYPE(NONE):
    141 			break;
    142 
    143 		case R_TYPE(RELATIVE): {
    144 			const Elf_Addr val = (Elf_Addr)obj->relocbase +
    145 			    rela->r_addend;
    146 
    147 			rdbg(("RELATIVE(%p) -> %p (%s) in %s",
    148 			    where, (void *)val,
    149 			    obj->strtab +
    150 				obj->symtab[ELF_R_SYM(rela->r_info)].st_name,
    151 			    obj->path));
    152 
    153 			*where = val;
    154 			break;
    155 		    }
    156 
    157 		case R_TYPESZ(ADDR): {
    158 			const Elf_Addr val = (Elf_Addr)defobj->relocbase +
    159 			    def->st_value + rela->r_addend;
    160 
    161 			rdbg(("ADDR(%p) -> %p (%s) in %s%s",
    162 			    where, (void *)val,
    163 			    obj->strtab +
    164 				obj->symtab[ELF_R_SYM(rela->r_info)].st_name,
    165 			    obj->path,
    166 			    def == &_rtld_sym_zero ? " (symzero)" : ""));
    167 
    168 			*where = val;
    169 			break;
    170 		    }
    171 
    172 		case R_TYPE(COPY):
    173 			/*
    174 			 * These are deferred until all other relocations have
    175 			 * been done.  All we do here is make sure that the
    176 			 * COPY relocation is not in a shared library.  They
    177 			 * are allowed only in executable files.
    178 			 */
    179 			if (obj->isdynamic) {
    180 				_rtld_error("%s: Unexpected R_COPY relocation"
    181 				    " in shared library", obj->path);
    182 				return -1;
    183 			}
    184 			rdbg(("COPY (avoid in main)"));
    185 			break;
    186 
    187 		case R_TYPESZ(TLS_DTPMOD): {
    188 			const Elf_Addr val = (Elf_Addr)defobj->tlsindex;
    189 
    190 			rdbg(("TLS_DTPMOD(%p) -> %p (%s) in %s",
    191 			    where, (void *)val,
    192 			    obj->strtab +
    193 				obj->symtab[ELF_R_SYM(rela->r_info)].st_name,
    194 			    obj->path));
    195 
    196 			*where = val;
    197 			break;
    198 		    }
    199 
    200 		case R_TYPESZ(TLS_DTPREL): {
    201 			const Elf_Addr val = (Elf_Addr)(def->st_value +
    202 			    rela->r_addend - TLS_DTV_OFFSET);
    203 
    204 			rdbg(("TLS_DTPREL(%p) -> %p (%s) in %s",
    205 			    where, (void *)val,
    206 			    obj->strtab +
    207 				obj->symtab[ELF_R_SYM(rela->r_info)].st_name,
    208 			    defobj->path));
    209 
    210 			*where = val;
    211 			break;
    212 		    }
    213 
    214 		case R_TYPESZ(TLS_TPREL):
    215 			if (!defobj->tls_static &&
    216 			    _rtld_tls_offset_allocate(__UNCONST(defobj)))
    217 				return -1;
    218 
    219 			*where = (Elf_Addr)(def->st_value + defobj->tlsoffset +
    220 			    rela->r_addend);
    221 
    222 			rdbg(("TLS_TPREL %s in %s --> %p in %s",
    223 			    obj->strtab +
    224 				obj->symtab[ELF_R_SYM(rela->r_info)].st_name,
    225 			    obj->path, (void *)*where, defobj->path));
    226 			break;
    227 
    228 		default:
    229 			rdbg(("sym = %lu, type = %lu, offset = %p, "
    230 			    "addend = %p, contents = %p",
    231 			    (u_long)ELF_R_SYM(rela->r_info),
    232 			    (u_long)ELF_R_TYPE(rela->r_info),
    233 			    (void *)rela->r_offset, (void *)rela->r_addend,
    234 			    (void *)*where));
    235 			_rtld_error("%s: Unsupported relocation type %ld "
    236 			    "in non-PLT relocations",
    237 			    obj->path, (u_long)r_type);
    238 			return -1;
    239 		}
    240 	}
    241 
    242 	return 0;
    243 }
    244 
    245 int
    246 _rtld_relocate_plt_lazy(Obj_Entry *obj)
    247 {
    248 
    249 	if (!obj->relocbase)
    250 		return 0;
    251 
    252 	for (const Elf_Rela *rela = obj->pltrela; rela < obj->pltrelalim; rela++) {
    253 		Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
    254 		switch (ELF_R_TYPE(rela->r_info)) {
    255 		case R_TYPE(JMP_SLOT):
    256 			/* Just relocate the GOT slots pointing into the PLT */
    257 			*where += (Elf_Addr)obj->relocbase;
    258 			rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where));
    259 			break;
    260 		default:
    261 			rdbg(("not yet... %d", (int)ELF_R_TYPE(rela->r_info) ));
    262 		}
    263 	}
    264 
    265 	return 0;
    266 }
    267 
    268 static int
    269 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela,
    270     Elf_Addr *tp)
    271 {
    272 	Elf_Addr * const where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
    273 	const Obj_Entry *defobj;
    274 	Elf_Addr new_value;
    275 
    276         assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT));
    277 
    278 	const Elf_Sym *def = _rtld_find_plt_symdef(ELF_R_SYM(rela->r_info),
    279 	    obj, &defobj, tp != NULL);
    280 	if (__predict_false(def == NULL))
    281 		return -1;
    282 	if (__predict_false(def == &_rtld_sym_zero))
    283 		return -1;
    284 
    285 	if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
    286 		if (tp == NULL)
    287 			return 0;
    288 		new_value = _rtld_resolve_ifunc(defobj, def);
    289 	} else {
    290 		new_value = (Elf_Addr)(defobj->relocbase + def->st_value);
    291 	}
    292 	rdbg(("bind now/fixup in %s --> old=%p new=%p",
    293 	    defobj->strtab + def->st_name, (void *)*where,
    294 	    (void *)new_value));
    295 	if (*where != new_value)
    296 		*where = new_value;
    297 	if (tp)
    298 		*tp = new_value;
    299 
    300 	return 0;
    301 }
    302 
    303 void *
    304 _rtld_bind(const Obj_Entry *obj, Elf_Word gotoff)
    305 {
    306 	const Elf_Addr relidx = (gotoff / sizeof(Elf_Addr));
    307 	const Elf_Rela *pltrela = obj->pltrela + relidx;
    308 
    309 	Elf_Addr new_value = 0;
    310 
    311 	_rtld_shared_enter();
    312 	const int err = _rtld_relocate_plt_object(obj, pltrela, &new_value);
    313 	if (err)
    314 		_rtld_die();
    315 	_rtld_shared_exit();
    316 
    317 	return (void *)new_value;
    318 }
    319 
    320 int
    321 _rtld_relocate_plt_objects(const Obj_Entry *obj)
    322 {
    323 
    324 	for (const Elf_Rela *rela = obj->pltrela; rela < obj->pltrelalim; rela++) {
    325 		if (_rtld_relocate_plt_object(obj, rela, NULL) < 0)
    326 			return -1;
    327 	}
    328 
    329 	return 0;
    330 }
    331