1/* $NetBSD: mdreloc.c,v 1.9 2023/06/04 01:24:58 joerg 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.9 2023/06/04 01:24:58 joerg Exp $"); 35#endif /* not lint */ 36 37#include <sys/types.h> 38#include <sys/endian.h> 39#include <sys/tls.h> 40 41#include <stdlib.h> 42#include <string.h> 43 44#include "debug.h" 45#include "rtld.h" 46 47void _rtld_bind_start(void); 48void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 49void *_rtld_bind(const Obj_Entry *, Elf_Word); 50 51void 52_rtld_setup_pltgot(const Obj_Entry *obj) 53{ 54 obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start; 55 obj->pltgot[1] = (Elf_Addr) obj; 56} 57 58void 59_rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 60{ 61 const Elf_Rela *rela = NULL, *relalim; 62 Elf_Addr relasz = 0; 63 64 for (; dynp->d_tag != DT_NULL; dynp++) { 65 switch (dynp->d_tag) { 66 case DT_RELA: 67 rela = (const Elf_Rela *)(relocbase + dynp->d_un.d_ptr); 68 break; 69 case DT_RELASZ: 70 relasz = dynp->d_un.d_val; 71 break; 72 } 73 } 74 75 relalim = (const Elf_Rela *)((uintptr_t)rela + relasz); 76 for (; rela < relalim; rela++) { 77 Elf_Word r_type = ELF_R_TYPE(rela->r_info); 78 Elf_Addr *where = (Elf_Addr *)(relocbase + rela->r_offset); 79 80 switch (r_type) { 81 case R_TYPE(RELATIVE): { 82 Elf_Addr val = relocbase + rela->r_addend; 83 *where = val; 84 rdbg(("RELATIVE/L(%p) -> %p in <self>", 85 where, (void *)val)); 86 break; 87 } 88 89 case R_TYPE(NONE): 90 break; 91 92 default: 93 abort(); 94 } 95 } 96} 97 98int 99_rtld_relocate_nonplt_objects(Obj_Entry *obj) 100{ 101 const Elf_Rela *rela; 102 const Elf_Sym *def = NULL; 103 const Obj_Entry *defobj = NULL; 104 unsigned long last_symnum = ULONG_MAX; 105 106 for (rela = obj->rela; rela < obj->relalim; rela++) { 107 Elf_Addr * const where = 108 (Elf_Addr *)(obj->relocbase + rela->r_offset); 109 const Elf_Word r_type = ELF_R_TYPE(rela->r_info); 110 unsigned long symnum; 111 112 switch (r_type) { 113 case R_TYPESZ(ADDR): 114 case R_TYPESZ(TLS_DTPMOD): 115 case R_TYPESZ(TLS_DTPREL): 116 case R_TYPESZ(TLS_TPREL): 117 symnum = ELF_R_SYM(rela->r_info); 118 if (last_symnum != symnum) { 119 last_symnum = symnum; 120 def = _rtld_find_symdef(symnum, obj, &defobj, 121 false); 122 if (def == NULL) 123 return -1; 124 } 125 break; 126 default: 127 break; 128 } 129 130 switch (r_type) { 131 case R_TYPE(NONE): 132 break; 133 134 case R_TYPE(RELATIVE): { 135 const Elf_Addr val = (Elf_Addr)obj->relocbase + 136 rela->r_addend; 137 138 rdbg(("RELATIVE(%p) -> %p (%s) in %s", 139 where, (void *)val, 140 obj->strtab + 141 obj->symtab[ELF_R_SYM(rela->r_info)].st_name, 142 obj->path)); 143 144 *where = val; 145 break; 146 } 147 148 case R_TYPESZ(ADDR): { 149 const Elf_Addr val = (Elf_Addr)defobj->relocbase + 150 def->st_value + rela->r_addend; 151 152 rdbg(("ADDR(%p) -> %p (%s) in %s%s", 153 where, (void *)val, 154 obj->strtab + 155 obj->symtab[ELF_R_SYM(rela->r_info)].st_name, 156 obj->path, 157 def == &_rtld_sym_zero ? " (symzero)" : "")); 158 159 *where = val; 160 break; 161 } 162 163 case R_TYPE(COPY): 164 /* 165 * These are deferred until all other relocations have 166 * been done. All we do here is make sure that the 167 * COPY relocation is not in a shared library. They 168 * are allowed only in executable files. 169 */ 170 if (obj->isdynamic) { 171 _rtld_error("%s: Unexpected R_COPY relocation" 172 " in shared library", obj->path); 173 return -1; 174 } 175 rdbg(("COPY (avoid in main)")); 176 break; 177 178 case R_TYPESZ(TLS_DTPMOD): { 179 const Elf_Addr val = (Elf_Addr)defobj->tlsindex; 180 181 rdbg(("TLS_DTPMOD(%p) -> %p (%s) in %s", 182 where, (void *)val, 183 obj->strtab + 184 obj->symtab[ELF_R_SYM(rela->r_info)].st_name, 185 obj->path)); 186 187 *where = val; 188 break; 189 } 190 191 case R_TYPESZ(TLS_DTPREL): { 192 const Elf_Addr val = (Elf_Addr)(def->st_value + 193 rela->r_addend - TLS_DTV_OFFSET); 194 195 rdbg(("TLS_DTPREL(%p) -> %p (%s) in %s", 196 where, (void *)val, 197 obj->strtab + 198 obj->symtab[ELF_R_SYM(rela->r_info)].st_name, 199 defobj->path)); 200 201 *where = val; 202 break; 203 } 204 205 case R_TYPESZ(TLS_TPREL): 206 if (!defobj->tls_static && 207 _rtld_tls_offset_allocate(__UNCONST(defobj))) 208 return -1; 209 210 *where = (Elf_Addr)(def->st_value + defobj->tlsoffset + 211 rela->r_addend); 212 213 rdbg(("TLS_TPREL %s in %s --> %p in %s", 214 obj->strtab + 215 obj->symtab[ELF_R_SYM(rela->r_info)].st_name, 216 obj->path, (void *)*where, defobj->path)); 217 break; 218 219 default: 220 rdbg(("sym = %lu, type = %lu, offset = %p, " 221 "addend = %p, contents = %p", 222 (u_long)ELF_R_SYM(rela->r_info), 223 (u_long)ELF_R_TYPE(rela->r_info), 224 (void *)rela->r_offset, (void *)rela->r_addend, 225 (void *)*where)); 226 _rtld_error("%s: Unsupported relocation type %ld " 227 "in non-PLT relocations", 228 obj->path, (u_long)r_type); 229 return -1; 230 } 231 } 232 233 return 0; 234} 235 236int 237_rtld_relocate_plt_lazy(Obj_Entry *obj) 238{ 239 240 if (!obj->relocbase) 241 return 0; 242 243 for (const Elf_Rela *rela = obj->pltrela; rela < obj->pltrelalim; rela++) { 244 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 245 switch (ELF_R_TYPE(rela->r_info)) { 246 case R_TYPE(JMP_SLOT): 247 /* Just relocate the GOT slots pointing into the PLT */ 248 *where += (Elf_Addr)obj->relocbase; 249 rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where)); 250 break; 251 default: 252 rdbg(("not yet... %d", (int)ELF_R_TYPE(rela->r_info) )); 253 } 254 } 255 256 return 0; 257} 258 259static int 260_rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela, 261 Elf_Addr *tp) 262{ 263 Elf_Addr * const where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 264 const Obj_Entry *defobj; 265 Elf_Addr new_value; 266 267 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT)); 268 269 const Elf_Sym *def = _rtld_find_plt_symdef(ELF_R_SYM(rela->r_info), 270 obj, &defobj, tp != NULL); 271 if (__predict_false(def == NULL)) 272 return -1; 273 if (__predict_false(def == &_rtld_sym_zero)) 274 return -1; 275 276 if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { 277 if (tp == NULL) 278 return 0; 279 new_value = _rtld_resolve_ifunc(defobj, def); 280 } else { 281 new_value = (Elf_Addr)(defobj->relocbase + def->st_value); 282 } 283 rdbg(("bind now/fixup in %s --> old=%p new=%p", 284 defobj->strtab + def->st_name, (void *)*where, 285 (void *)new_value)); 286 if (*where != new_value) 287 *where = new_value; 288 if (tp) 289 *tp = new_value; 290 291 return 0; 292} 293 294void * 295_rtld_bind(const Obj_Entry *obj, Elf_Word gotoff) 296{ 297 const Elf_Addr relidx = (gotoff / sizeof(Elf_Addr)); 298 const Elf_Rela *pltrela = obj->pltrela + relidx; 299 300 Elf_Addr new_value = 0; 301 302 _rtld_shared_enter(); 303 const int err = _rtld_relocate_plt_object(obj, pltrela, &new_value); 304 if (err) 305 _rtld_die(); 306 _rtld_shared_exit(); 307 308 return (void *)new_value; 309} 310 311int 312_rtld_relocate_plt_objects(const Obj_Entry *obj) 313{ 314 315 for (const Elf_Rela *rela = obj->pltrela; rela < obj->pltrelalim; rela++) { 316 if (_rtld_relocate_plt_object(obj, rela, NULL) < 0) 317 return -1; 318 } 319 320 return 0; 321} 322