1 /* $NetBSD: mdreloc.c,v 1.19 2024/07/22 23:10:35 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 /*- 33 * Copyright (c) 2014-2015 The FreeBSD Foundation 34 * All rights reserved. 35 * 36 * Portions of this software were developed by Andrew Turner 37 * under sponsorship from the FreeBSD Foundation. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 * SUCH DAMAGE. 59 */ 60 61 #include <sys/cdefs.h> 62 #ifndef lint 63 __RCSID("$NetBSD: mdreloc.c,v 1.19 2024/07/22 23:10:35 riastradh Exp $"); 64 #endif /* not lint */ 65 66 /* 67 * AArch64 ELF relocations. 68 * 69 * References: 70 * 71 * [AAELF64] ELF for the Arm 64-bit Architecture (AArch64), 72 * 2022Q3. Arm Ltd. 73 * https://github.com/ARM-software/abi-aa/blob/2982a9f3b512a5bfdc9e3fea5d3b298f9165c36b/aaelf64/aaelf64.rst 74 * 75 * [TLSDESC] Glauber de Oliveira Costa and Alexandre Oliva, 76 * `Thread-Local Storage Access in Dynamic Libraries in the ARM 77 * Platform', 2006. 78 * https://www.fsfla.org/~lxoliva/writeups/TLS/paper-lk2006.pdf 79 */ 80 81 #include <sys/types.h> 82 #include <string.h> 83 84 #include "debug.h" 85 #include "rtld.h" 86 87 struct tls_data { 88 size_t td_tlsindex; 89 Elf_Addr td_tlsoffs; 90 }; 91 92 void _rtld_bind_start(void); 93 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 94 Elf_Addr _rtld_bind(const Obj_Entry *, Elf_Word); 95 void *_rtld_tlsdesc_static(void *); 96 void *_rtld_tlsdesc_undef(void *); 97 void *_rtld_tlsdesc_dynamic(void *); 98 99 /* 100 * AARCH64 PLT looks like this; 101 * 102 * PLT HEADER <8 instructions> 103 * PLT ENTRY #0 <4 instructions> 104 * PLT ENTRY #1 <4 instructions> 105 * . 106 * . 107 * PLT ENTRY #n <4 instructions> 108 * 109 * PLT HEADER 110 * stp x16, x30, [sp, #-16]! 111 * adrp x16, (GOT+16) 112 * ldr x17, [x16, #PLT_GOT+0x10] 113 * add x16, x16, #PLT_GOT+0x10 114 * br x17 115 * nop 116 * nop 117 * nop 118 * 119 * PLT ENTRY #n 120 * adrp x16, PLTGOT + n * 8 121 * ldr x17, [x16, PLTGOT + n * 8] 122 * add x16, x16, :lo12:PLTGOT + n * 8 123 * br x17 124 */ 125 void 126 _rtld_setup_pltgot(const Obj_Entry *obj) 127 { 128 129 obj->pltgot[1] = (Elf_Addr) obj; 130 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; 131 } 132 133 static struct tls_data * 134 _rtld_tlsdesc_alloc(size_t tlsindex, Elf_Addr offs) 135 { 136 struct tls_data *tlsdesc; 137 138 tlsdesc = xmalloc(sizeof(*tlsdesc)); 139 tlsdesc->td_tlsindex = tlsindex; 140 tlsdesc->td_tlsoffs = offs; 141 142 return tlsdesc; 143 } 144 145 static void 146 _rtld_tlsdesc_fill(const Obj_Entry *obj, const Elf_Rela *rela, Elf_Addr *where, u_int flags) 147 { 148 const Elf_Sym *def; 149 const Obj_Entry *defobj; 150 Elf_Addr offs = 0; 151 unsigned long symnum = ELF_R_SYM(rela->r_info); 152 153 if (symnum != 0) { 154 def = _rtld_find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, 155 flags); 156 if (def == NULL) 157 _rtld_die(); 158 if (def == &_rtld_sym_zero) { 159 /* Weak undefined thread variable */ 160 where[0] = (Elf_Addr)_rtld_tlsdesc_undef; 161 where[1] = rela->r_addend; 162 163 rdbg(("TLSDESC %s (weak) in %s --> %p", 164 obj->strtab + obj->symtab[symnum].st_name, 165 obj->path, (void *)where[1])); 166 167 return; 168 } 169 offs = def->st_value; 170 } else { 171 defobj = obj; 172 } 173 offs += rela->r_addend; 174 175 if (defobj->tls_static) { 176 /* Variable is in initially allocated TLS segment */ 177 where[0] = (Elf_Addr)_rtld_tlsdesc_static; 178 where[1] = defobj->tlsoffset + offs + 179 sizeof(struct tls_tcb); 180 181 rdbg(("TLSDESC %s --> %p static", 182 obj->path, (void *)where[1])); 183 } else { 184 /* TLS offset is unknown at load time, use dynamic resolving */ 185 where[0] = (Elf_Addr)_rtld_tlsdesc_dynamic; 186 where[1] = (Elf_Addr)_rtld_tlsdesc_alloc(defobj->tlsindex, offs); 187 188 rdbg(("TLSDESC %s in %s --> %p dynamic (%zu, %p)", 189 obj->strtab + obj->symtab[symnum].st_name, 190 obj->path, (void *)where[1], defobj->tlsindex, (void *)offs)); 191 } 192 } 193 194 void 195 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 196 { 197 const Elf_Rela *rela = 0, *relalim; 198 Elf_Addr relasz = 0; 199 Elf_Addr *where; 200 201 for (; dynp->d_tag != DT_NULL; dynp++) { 202 switch (dynp->d_tag) { 203 case DT_RELA: 204 rela = (const Elf_Rela *)(relocbase + dynp->d_un.d_ptr); 205 break; 206 case DT_RELASZ: 207 relasz = dynp->d_un.d_val; 208 break; 209 } 210 } 211 relalim = (const Elf_Rela *)((const uint8_t *)rela + relasz); 212 for (; rela < relalim; rela++) { 213 where = (Elf_Addr *)(relocbase + rela->r_offset); 214 *where += (Elf_Addr)relocbase; 215 } 216 } 217 218 int 219 _rtld_relocate_nonplt_objects(Obj_Entry *obj) 220 { 221 const Elf_Sym *def = NULL; 222 const Obj_Entry *defobj = NULL; 223 unsigned long last_symnum = ULONG_MAX; 224 225 for (const Elf_Rela *rela = obj->rela; rela < obj->relalim; rela++) { 226 Elf_Addr *where; 227 Elf_Addr tmp; 228 unsigned long symnum = ULONG_MAX; 229 230 where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 231 232 switch (ELF_R_TYPE(rela->r_info)) { 233 case R_TYPE(ABS64): /* word S + A */ 234 case R_TYPE(GLOB_DAT): /* word S + A */ 235 case R_TLS_TYPE(TLS_DTPREL): 236 case R_TLS_TYPE(TLS_DTPMOD): 237 case R_TLS_TYPE(TLS_TPREL): 238 symnum = ELF_R_SYM(rela->r_info); 239 if (last_symnum != symnum) { 240 last_symnum = symnum; 241 def = _rtld_find_symdef(symnum, obj, &defobj, 242 false); 243 if (def == NULL) 244 return -1; 245 } 246 247 default: 248 break; 249 } 250 251 switch (ELF_R_TYPE(rela->r_info)) { 252 case R_TYPE(NONE): 253 break; 254 255 case R_TYPE(ABS64): /* word S + A */ 256 case R_TYPE(GLOB_DAT): /* word S + A */ 257 tmp = (Elf_Addr)defobj->relocbase + def->st_value + 258 rela->r_addend; 259 if (*where != tmp) 260 *where = tmp; 261 rdbg(("ABS64/GLOB_DAT %s in %s --> %p @ %p in %s", 262 obj->strtab + obj->symtab[symnum].st_name, 263 obj->path, (void *)tmp, where, defobj->path)); 264 break; 265 266 case R_TYPE(IRELATIVE): 267 /* IFUNC relocations are handled in _rtld_call_ifunc */ 268 if (obj->ifunc_remaining_nonplt == 0) 269 obj->ifunc_remaining_nonplt = obj->relalim - rela; 270 rdbg(("IRELATIVE in %s, %zx", obj->path, 271 obj->ifunc_remaining_nonplt)); 272 /* FALLTHROUGH */ 273 274 case R_TYPE(RELATIVE): /* word B + A */ 275 *where = (Elf_Addr)(obj->relocbase + rela->r_addend); 276 rdbg(("RELATIVE in %s --> %p", obj->path, 277 (void *)*where)); 278 break; 279 280 case R_TYPE(COPY): 281 /* 282 * These are deferred until all other relocations have 283 * been done. All we do here is make sure that the 284 * COPY relocation is not in a shared library. They 285 * are allowed only in executable files. 286 */ 287 if (obj->isdynamic) { 288 _rtld_error( 289 "%s: Unexpected R_COPY relocation in shared library", 290 obj->path); 291 return -1; 292 } 293 rdbg(("COPY (avoid in main)")); 294 break; 295 296 case R_TYPE(TLSDESC): 297 _rtld_tlsdesc_fill(obj, rela, where, 0); 298 break; 299 300 case R_TLS_TYPE(TLS_DTPREL): 301 *where = (Elf_Addr)(def->st_value + rela->r_addend); 302 303 rdbg(("TLS_DTPREL %s in %s --> %p", 304 obj->strtab + obj->symtab[symnum].st_name, 305 obj->path, (void *)*where)); 306 break; 307 308 case R_TLS_TYPE(TLS_DTPMOD): 309 *where = (Elf_Addr)(defobj->tlsindex); 310 311 rdbg(("TLS_DTPMOD %s in %s --> %p", 312 obj->strtab + obj->symtab[symnum].st_name, 313 obj->path, (void *)*where)); 314 break; 315 316 case R_TLS_TYPE(TLS_TPREL): 317 if (!defobj->tls_static && 318 _rtld_tls_offset_allocate(__UNCONST(defobj))) 319 return -1; 320 321 *where = (Elf_Addr)(def->st_value + defobj->tlsoffset + 322 rela->r_addend + sizeof(struct tls_tcb)); 323 324 rdbg(("TLS_TPREL %s in %s --> %p in %s", 325 obj->strtab + obj->symtab[symnum].st_name, 326 obj->path, (void *)*where, defobj->path)); 327 break; 328 329 default: 330 rdbg(("sym = %lu, type = %lu, offset = %p, " 331 "addend = %p, contents = %p", 332 (u_long)ELF_R_SYM(rela->r_info), 333 (u_long)ELF_R_TYPE(rela->r_info), 334 (void *)rela->r_offset, (void *)rela->r_addend, 335 (void *)*where)); 336 _rtld_error("%s: Unsupported relocation type %ld " 337 "in non-PLT relocations", 338 obj->path, (u_long) ELF_R_TYPE(rela->r_info)); 339 return -1; 340 } 341 } 342 return 0; 343 } 344 345 int 346 _rtld_relocate_plt_lazy(Obj_Entry *obj) 347 { 348 349 if (!obj->relocbase) 350 return 0; 351 352 for (const Elf_Rela *rela = obj->pltrela; rela < obj->pltrelalim; rela++) { 353 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 354 355 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JUMP_SLOT) || 356 ELF_R_TYPE(rela->r_info) == R_TYPE(TLSDESC) || 357 ELF_R_TYPE(rela->r_info) == R_TYPE(IRELATIVE)); 358 359 switch (ELF_R_TYPE(rela->r_info)) { 360 case R_TYPE(JUMP_SLOT): 361 /* Just relocate the GOT slots pointing into the PLT */ 362 *where += (Elf_Addr)obj->relocbase; 363 rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where)); 364 break; 365 case R_TYPE(TLSDESC): 366 _rtld_tlsdesc_fill(obj, rela, where, SYMLOOK_IN_PLT); 367 break; 368 case R_TYPE(IRELATIVE): 369 obj->ifunc_remaining = obj->pltrelalim - rela; 370 break; 371 } 372 } 373 374 return 0; 375 } 376 377 void 378 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen) 379 { 380 const Elf_Rela *rela; 381 Elf_Addr *where, target; 382 383 while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) { 384 rela = obj->pltrelalim - obj->ifunc_remaining; 385 --obj->ifunc_remaining; 386 if (ELF_R_TYPE(rela->r_info) == R_TYPE(IRELATIVE)) { 387 where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 388 target = (Elf_Addr)(obj->relocbase + rela->r_addend); 389 _rtld_exclusive_exit(mask); 390 target = _rtld_resolve_ifunc2(obj, target); 391 _rtld_exclusive_enter(mask); 392 if (*where != target) 393 *where = target; 394 } 395 } 396 } 397 398 static int 399 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela, 400 Elf_Addr *tp) 401 { 402 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 403 Elf_Addr new_value; 404 const Elf_Sym *def; 405 const Obj_Entry *defobj; 406 407 switch (ELF_R_TYPE(rela->r_info)) { 408 case R_TYPE(JUMP_SLOT): 409 def = _rtld_find_plt_symdef(ELF_R_SYM(rela->r_info), obj, 410 &defobj, tp != NULL); 411 if (__predict_false(def == NULL)) 412 return -1; 413 if (__predict_false(def == &_rtld_sym_zero)) 414 return 0; 415 416 if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { 417 if (tp == NULL) 418 return 0; 419 new_value = _rtld_resolve_ifunc(defobj, def); 420 } else { 421 new_value = (Elf_Addr)(defobj->relocbase + 422 def->st_value); 423 } 424 rdbg(("bind now/fixup in %s --> old=%p new=%p", 425 defobj->strtab + def->st_name, (void *)*where, 426 (void *)new_value)); 427 if (*where != new_value) 428 *where = new_value; 429 if (tp) 430 *tp = new_value; 431 break; 432 case R_TYPE(TLSDESC): 433 _rtld_tlsdesc_fill(obj, rela, where, SYMLOOK_IN_PLT); 434 break; 435 } 436 437 return 0; 438 } 439 440 Elf_Addr 441 _rtld_bind(const Obj_Entry *obj, Elf_Word relaidx) 442 { 443 const Elf_Rela *rela = obj->pltrela + relaidx; 444 Elf_Addr new_value = 0; 445 446 _rtld_shared_enter(); 447 int err = _rtld_relocate_plt_object(obj, rela, &new_value); 448 if (err) 449 _rtld_die(); 450 _rtld_shared_exit(); 451 452 return new_value; 453 } 454 int 455 _rtld_relocate_plt_objects(const Obj_Entry *obj) 456 { 457 const Elf_Rela *rela; 458 int err = 0; 459 460 for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) { 461 err = _rtld_relocate_plt_object(obj, rela, NULL); 462 if (err) 463 break; 464 } 465 466 return err; 467 } 468