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