1/* $NetBSD: reloc.c,v 1.118 2023/07/30 09:20:14 riastradh Exp $ */ 2 3/* 4 * Copyright 1996 John D. Polstra. 5 * Copyright 1996 Matt Thomas <matt@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@polstra.com>. 38 */ 39 40#include <sys/cdefs.h> 41#ifndef lint 42__RCSID("$NetBSD: reloc.c,v 1.118 2023/07/30 09:20:14 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 63static int _rtld_do_copy_relocation(const Obj_Entry *, const Elf_Rela *); 64 65static 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 */ 125int 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 if (_rtld_relro(dstobj, true) == -1) 158 return -1; 159#endif 160#endif /* RTLD_INHIBIT_COPY_RELOCS */ 161 162 return (0); 163} 164 165/* 166 * Relocate newly-loaded shared objects. The argument is a pointer to 167 * the Obj_Entry for the first such object. All objects from the first 168 * to the end of the list of objects are relocated. Returns 0 on success, 169 * or -1 on failure. 170 */ 171int 172_rtld_relocate_objects(Obj_Entry *first, bool bind_now) 173{ 174 Obj_Entry *obj; 175 int ok = 1; 176 177 for (obj = first; obj != NULL; obj = obj->next) { 178 if ((!obj->sysv_hash && !obj->gnu_hash) || 179 obj->symtab == NULL || obj->strtab == NULL) { 180 _rtld_error("%s: Shared object has no run-time" 181 " symbol table", obj->path); 182 return -1; 183 } 184 if (obj->nbuckets == UINT32_MAX) { 185 _rtld_error("%s: Symbol table too large", obj->path); 186 return -1; 187 } 188 rdbg((" relocating %s (%ld/%ld rel/rela, %ld/%ld plt rel/rela)", 189 obj->path, 190 (long)(obj->rellim - obj->rel), 191 (long)(obj->relalim - obj->rela), 192 (long)(obj->pltrellim - obj->pltrel), 193 (long)(obj->pltrelalim - obj->pltrela))); 194 195 if (obj->textrel) { 196 xwarnx("%s: text relocations", obj->path); 197 /* 198 * There are relocations to the write-protected text 199 * segment. 200 */ 201 if (mprotect(obj->mapbase, obj->textsize, 202 PROT_READ | PROT_WRITE) == -1) { 203 _rtld_error("%s: Cannot write-enable text " 204 "segment: %s", obj->path, xstrerror(errno)); 205 return -1; 206 } 207 } 208 dbg(("doing non-PLT relocations")); 209 if (_rtld_relocate_nonplt_objects(obj) < 0) 210 ok = 0; 211 if (obj->textrel) { /* Re-protected the text segment. */ 212 if (mprotect(obj->mapbase, obj->textsize, 213 PROT_READ | PROT_EXEC) == -1) { 214 _rtld_error("%s: Cannot write-protect text " 215 "segment: %s", obj->path, xstrerror(errno)); 216 return -1; 217 } 218 } 219 dbg(("doing lazy PLT binding")); 220 if (_rtld_relocate_plt_lazy(obj) < 0) 221 ok = 0; 222 if (obj->z_now || bind_now) { 223 dbg(("doing immediate PLT binding")); 224 if (_rtld_relocate_plt_objects(obj) < 0) 225 ok = 0; 226 } 227 if (!ok) 228 return -1; 229 230 dbg(("fixing up PLTGOT")); 231 /* Set the special PLTGOT entries. */ 232 if (obj->pltgot != NULL) 233 _rtld_setup_pltgot(obj); 234#ifdef GNU_RELRO 235 if (_rtld_relro(obj, false) == -1) 236 return -1; 237#endif 238 } 239 return 0; 240} 241 242Elf_Addr 243_rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def) 244{ 245 Elf_Addr target; 246 247 _rtld_shared_exit(); 248 target = _rtld_resolve_ifunc2(obj, 249 (Elf_Addr)obj->relocbase + def->st_value); 250 _rtld_shared_enter(); 251 return target; 252} 253 254Elf_Addr 255_rtld_resolve_ifunc2(const Obj_Entry *obj, Elf_Addr addr) 256{ 257 Elf_Addr target; 258 259 target = _rtld_call_function_addr(obj, addr); 260 261 return target; 262} 263 264#if \ 265 !defined(RTLD_COMMON_CALL_IFUNC_RELA) && \ 266 !defined(RTLD_COMMON_CALL_IFUNC_REL) && \ 267 !defined(RTLD_ARCH_CALL_IFUNC) 268void 269_rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen) 270{ 271} 272#endif 273 274#ifdef RTLD_COMMON_CALL_IFUNC_RELA 275# ifdef __sparc__ 276# include <machine/elf_support.h> 277# endif 278 279void 280_rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen) 281{ 282 const Elf_Rela *rela; 283 Elf_Addr *where; 284#ifdef __sparc__ 285 Elf_Word *where2; 286#endif 287 Elf_Addr target; 288 289 while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) { 290 rela = obj->pltrelalim - obj->ifunc_remaining--; 291#ifdef __sparc__ 292#define PLT_IRELATIVE R_TYPE(JMP_IREL) 293#else 294#define PLT_IRELATIVE R_TYPE(IRELATIVE) 295#endif 296 if (ELF_R_TYPE(rela->r_info) != PLT_IRELATIVE) 297 continue; 298#ifdef __sparc__ 299 where2 = (Elf_Word *)(obj->relocbase + rela->r_offset); 300#else 301 where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 302#endif 303 target = (Elf_Addr)(obj->relocbase + rela->r_addend); 304 _rtld_exclusive_exit(mask); 305 target = _rtld_resolve_ifunc2(obj, target); 306 _rtld_exclusive_enter(mask); 307#ifdef __sparc__ 308 sparc_write_branch(where2 + 1, (void *)target); 309#else 310 if (*where != target) 311 *where = target; 312#endif 313 } 314 315 while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) { 316 rela = obj->relalim - obj->ifunc_remaining_nonplt--; 317 if (ELF_R_TYPE(rela->r_info) != R_TYPE(IRELATIVE)) 318 continue; 319 where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 320 target = (Elf_Addr)(obj->relocbase + rela->r_addend); 321 _rtld_exclusive_exit(mask); 322 target = _rtld_resolve_ifunc2(obj, target); 323 _rtld_exclusive_enter(mask); 324 if (*where != target) 325 *where = target; 326 } 327} 328#endif 329 330#ifdef RTLD_COMMON_CALL_IFUNC_REL 331void 332_rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen) 333{ 334 const Elf_Rel *rel; 335 Elf_Addr *where, target; 336 337 while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) { 338 rel = obj->pltrellim - obj->ifunc_remaining; 339 --obj->ifunc_remaining; 340 if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) { 341 where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 342 _rtld_exclusive_exit(mask); 343 target = _rtld_resolve_ifunc2(obj, *where); 344 _rtld_exclusive_enter(mask); 345 if (*where != target) 346 *where = target; 347 } 348 } 349 350 while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) { 351 rel = obj->rellim - obj->ifunc_remaining_nonplt--; 352 if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) { 353 where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 354 _rtld_exclusive_exit(mask); 355 target = _rtld_resolve_ifunc2(obj, *where); 356 _rtld_exclusive_enter(mask); 357 if (*where != target) 358 *where = target; 359 } 360 } 361} 362#endif 363