1 /* $NetBSD: mdreloc.c,v 1.48 2025/04/16 17:37:48 riastradh Exp $ */ 2 3 /* 4 * Copyright 1996 John D. Polstra. 5 * Copyright 1996 Matt Thomas <matt (at) 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 * i386 ELF relocations. 36 * 37 * References: 38 * 39 * [ABI386-4] System V Application Binary Interface: Intel386 40 * Architecture Processor Supplement, Fourth Edition, 1997-03-19, 41 * The Santa Cruz Operation, Inc. 42 * https://www.sco.com/developers/devspecs/abi386-4.pdf 43 * https://web.archive.org/web/20250329184450/https://www.sco.com/developers/devspecs/abi386-4.pdf 44 * 45 * Note: Intel and SuSE have published an update to the i386 ELF 46 * supplement, but it is not entirely compatible (e.g., it requires 47 * 16-byte alignment for the stack pointer, not just 4-byte alignment), 48 * so it is not reliable as a normative reference: 49 * 50 * [ABI386-2015] System V Application Binary Interface: Intel386 51 * Architecture Processor Supplement, Version 1.0, 2015-02-03. 52 * https://uclibc.org/docs/psABI-i386.pdf 53 * https://web.archive.org/web/20250118211449/https://uclibc.org/docs/psABI-i386.pdf 54 * https://gitlab.com/x86-psABIs/i386-ABI 55 */ 56 57 #include <sys/cdefs.h> 58 #ifndef lint 59 __RCSID("$NetBSD: mdreloc.c,v 1.48 2025/04/16 17:37:48 riastradh Exp $"); 60 #endif /* not lint */ 61 62 #include <sys/types.h> 63 #include <machine/lwp_private.h> 64 65 #include "debug.h" 66 #include "rtld.h" 67 68 void _rtld_bind_start(void); 69 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 70 caddr_t _rtld_bind(const Obj_Entry *, Elf_Word); 71 72 #define rdbg_symname(obj, rela) \ 73 ((obj)->strtab + (obj)->symtab[ELF_R_SYM((rela)->r_info)].st_name) 74 75 void 76 _rtld_setup_pltgot(const Obj_Entry *obj) 77 { 78 obj->pltgot[1] = (Elf_Addr) obj; 79 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; 80 } 81 82 void 83 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 84 { 85 const Elf_Rel *rel = 0, *rellim; 86 Elf_Addr relsz = 0; 87 Elf_Addr *where; 88 89 for (; dynp->d_tag != DT_NULL; dynp++) { 90 switch (dynp->d_tag) { 91 case DT_REL: 92 rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); 93 break; 94 case DT_RELSZ: 95 relsz = dynp->d_un.d_val; 96 break; 97 } 98 } 99 if (rel == 0 || relsz == 0) 100 return; 101 rellim = (const Elf_Rel *)((const uint8_t *)rel + relsz); 102 for (; rel < rellim; rel++) { 103 where = (Elf_Addr *)(relocbase + rel->r_offset); 104 *where += (Elf_Addr)relocbase; 105 } 106 } 107 108 int 109 _rtld_relocate_nonplt_objects(Obj_Entry *obj) 110 { 111 const Elf_Rel *rel; 112 Elf_Addr target = 0; 113 const Elf_Sym *def = NULL; 114 const Obj_Entry *defobj = NULL; 115 unsigned long last_symnum = ULONG_MAX; 116 117 for (rel = obj->rel; rel < obj->rellim; rel++) { 118 Elf_Addr *where; 119 Elf_Addr tmp; 120 unsigned long symnum; 121 122 where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 123 124 switch (ELF_R_TYPE(rel->r_info)) { 125 case R_TYPE(PC32): 126 case R_TYPE(GOT32): 127 case R_TYPE(32): 128 case R_TYPE(GLOB_DAT): 129 case R_TYPE(TLS_TPOFF): 130 case R_TYPE(TLS_TPOFF32): 131 case R_TYPE(TLS_DTPMOD32): 132 case R_TYPE(TLS_DTPOFF32): 133 symnum = ELF_R_SYM(rel->r_info); 134 if (symnum != last_symnum) { 135 last_symnum = symnum; 136 def = _rtld_find_symdef(symnum, obj, &defobj, 137 false); 138 if (def == NULL) 139 return -1; 140 } 141 break; 142 default: 143 break; 144 } 145 146 147 switch (ELF_R_TYPE(rel->r_info)) { 148 case R_TYPE(NONE): 149 break; 150 151 #if 1 /* XXX should not occur */ 152 case R_TYPE(PC32): 153 target = (Elf_Addr)(defobj->relocbase + def->st_value); 154 155 *where += target - (Elf_Addr)where; 156 rdbg(("PC32 %s in %s --> %p in %s", 157 rdbg_symname(obj, rel), 158 obj->path, (void *)*where, defobj->path)); 159 break; 160 161 case R_TYPE(GOT32): 162 #endif 163 case R_TYPE(32): 164 case R_TYPE(GLOB_DAT): 165 target = (Elf_Addr)(defobj->relocbase + def->st_value); 166 167 tmp = target + *where; 168 if (*where != tmp) 169 *where = tmp; 170 rdbg(("32/GLOB_DAT %s in %s --> %p in %s", 171 rdbg_symname(obj, rel), 172 obj->path, (void *)*where, defobj->path)); 173 break; 174 175 176 case R_TYPE(IRELATIVE): 177 /* IFUNC relocations are handled in _rtld_call_ifunc */ 178 if (obj->ifunc_remaining_nonplt == 0) { 179 obj->ifunc_remaining_nonplt = 180 obj->rellim - rel; 181 } 182 /* FALL-THROUGH */ 183 184 case R_TYPE(RELATIVE): 185 *where += (Elf_Addr)obj->relocbase; 186 rdbg(("RELATIVE in %s --> %p", obj->path, 187 (void *)*where)); 188 break; 189 190 case R_TYPE(COPY): 191 /* 192 * These are deferred until all other relocations have 193 * been done. All we do here is make sure that the 194 * COPY relocation is not in a shared library. They 195 * are allowed only in executable files. 196 */ 197 if (obj->isdynamic) { 198 _rtld_error( 199 "%s: Unexpected R_COPY relocation in shared library", 200 obj->path); 201 return -1; 202 } 203 rdbg(("COPY (avoid in main)")); 204 break; 205 206 case R_TYPE(TLS_TPOFF): 207 if (!defobj->tls_static && 208 _rtld_tls_offset_allocate(__UNCONST(defobj))) 209 return -1; 210 211 *where += (Elf_Addr)(def->st_value - defobj->tlsoffset); 212 213 rdbg(("TLS_TPOFF %s in %s --> %p", 214 rdbg_symname(obj, rel), 215 obj->path, (void *)*where)); 216 break; 217 218 case R_TYPE(TLS_TPOFF32): 219 if (!defobj->tls_static && 220 _rtld_tls_offset_allocate(__UNCONST(defobj))) 221 return -1; 222 223 *where += (Elf_Addr)(defobj->tlsoffset - def->st_value); 224 rdbg(("TLS_TPOFF32 %s in %s --> %p", 225 rdbg_symname(obj, rel), 226 obj->path, (void *)*where)); 227 break; 228 229 case R_TYPE(TLS_DTPMOD32): 230 *where = (Elf_Addr)(defobj->tlsindex); 231 232 rdbg(("TLS_DTPMOD32 %s in %s --> %p", 233 rdbg_symname(obj, rel), 234 obj->path, (void *)*where)); 235 break; 236 237 case R_TYPE(TLS_DTPOFF32): 238 *where = (Elf_Addr)(def->st_value); 239 240 rdbg(("TLS_DTPOFF32 %s in %s --> %p", 241 rdbg_symname(obj, rel), 242 obj->path, (void *)*where)); 243 244 break; 245 246 default: 247 rdbg(("sym = %lu, type = %lu, offset = %p, " 248 "contents = %p, symbol = %s", 249 (u_long)ELF_R_SYM(rel->r_info), 250 (u_long)ELF_R_TYPE(rel->r_info), 251 (void *)rel->r_offset, (void *)*where, 252 rdbg_symname(obj, rel))); 253 _rtld_error("%s: Unsupported relocation type %ld " 254 "in non-PLT relocations", 255 obj->path, (u_long) ELF_R_TYPE(rel->r_info)); 256 return -1; 257 } 258 } 259 return 0; 260 } 261 262 int 263 _rtld_relocate_plt_lazy(Obj_Entry *obj) 264 { 265 const Elf_Rel *rel; 266 267 for (rel = obj->pltrellim; rel-- > obj->pltrel; ) { 268 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 269 270 assert(ELF_R_TYPE(rel->r_info) == R_TYPE(JMP_SLOT) || 271 ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)); 272 273 if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) 274 obj->ifunc_remaining = obj->pltrellim - rel; 275 276 /* Just relocate the GOT slots pointing into the PLT */ 277 *where += (Elf_Addr)obj->relocbase; 278 rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where)); 279 } 280 281 return 0; 282 } 283 284 static inline int 285 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rel *rel, 286 Elf_Addr *tp) 287 { 288 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 289 Elf_Addr target; 290 const Elf_Sym *def; 291 const Obj_Entry *defobj; 292 unsigned long info = rel->r_info; 293 294 if (ELF_R_TYPE(info) == R_TYPE(IRELATIVE)) 295 return 0; 296 297 assert(ELF_R_TYPE(info) == R_TYPE(JMP_SLOT)); 298 299 def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, tp != NULL); 300 if (__predict_false(def == NULL)) 301 return -1; 302 if (__predict_false(def == &_rtld_sym_zero)) 303 return 0; 304 305 if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { 306 if (tp == NULL) 307 return 0; 308 target = _rtld_resolve_ifunc(defobj, def); 309 } else { 310 target = (Elf_Addr)(defobj->relocbase + def->st_value); 311 } 312 313 rdbg(("bind now/fixup in %s --> old=%p new=%p", 314 defobj->strtab + def->st_name, (void *)*where, 315 (void *)target)); 316 if (*where != target) 317 *where = target; 318 if (tp) 319 *tp = target; 320 return 0; 321 } 322 323 caddr_t 324 _rtld_bind(const Obj_Entry *obj, Elf_Word reloff) 325 { 326 const Elf_Rel *rel = (const Elf_Rel *)((const uint8_t *)obj->pltrel 327 + reloff); 328 Elf_Addr new_value; 329 int err; 330 331 new_value = 0; /* XXX gcc */ 332 333 _rtld_shared_enter(); 334 err = _rtld_relocate_plt_object(obj, rel, &new_value); 335 if (err) 336 _rtld_die(); 337 _rtld_shared_exit(); 338 339 return (caddr_t)new_value; 340 } 341 342 int 343 _rtld_relocate_plt_objects(const Obj_Entry *obj) 344 { 345 const Elf_Rel *rel; 346 int err = 0; 347 348 for (rel = obj->pltrel; rel < obj->pltrellim; rel++) { 349 err = _rtld_relocate_plt_object(obj, rel, NULL); 350 if (err) 351 break; 352 } 353 return err; 354 } 355 356 /* 357 * i386 specific GNU variant of __tls_get_addr using register based 358 * argument passing. 359 */ 360 #define DTV_MAX_INDEX(dtv) ((size_t)((dtv)[-1])) 361 362 __dso_public __attribute__((__regparm__(1))) void * 363 ___tls_get_addr(void *arg_) 364 { 365 size_t *arg = (size_t *)arg_; 366 void **dtv; 367 struct tls_tcb *tcb = __lwp_getprivate_fast(); 368 size_t idx = arg[0], offset = arg[1]; 369 370 dtv = tcb->tcb_dtv; 371 372 if (__predict_true(idx < DTV_MAX_INDEX(dtv) && dtv[idx] != NULL)) 373 return (uint8_t *)dtv[idx] + offset; 374 375 return _rtld_tls_get_addr(tcb, idx, offset); 376 } 377