1 /* $NetBSD: mdreloc.c,v 1.48 2024/08/03 21:59:58 riastradh Exp $ */ 2 3 #include <sys/cdefs.h> 4 #ifndef lint 5 __RCSID("$NetBSD: mdreloc.c,v 1.48 2024/08/03 21:59:58 riastradh Exp $"); 6 #endif /* not lint */ 7 8 /* 9 * Arm (32-bit) ELF relocations. 10 * 11 * Reference: 12 * 13 * [AAELF32] ELF for the Arm Architecture, 2022Q3. Arm Ltd. 14 * https://github.com/ARM-software/abi-aa/blob/2982a9f3b512a5bfdc9e3fea5d3b298f9165c36b/aaelf32/aaelf32.rst 15 */ 16 17 #include <sys/types.h> 18 #include <string.h> 19 20 #include "debug.h" 21 #include "rtld.h" 22 23 void _rtld_bind_start(void); 24 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 25 caddr_t _rtld_bind(const Obj_Entry *, Elf_Word); 26 27 void 28 _rtld_setup_pltgot(const Obj_Entry *obj) 29 { 30 obj->pltgot[1] = (Elf_Addr) obj; 31 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; 32 } 33 34 void 35 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 36 { 37 const Elf_Rel *rel = 0, *rellim; 38 Elf_Addr relsz = 0; 39 Elf_Addr *where; 40 41 for (; dynp->d_tag != DT_NULL; dynp++) { 42 switch (dynp->d_tag) { 43 case DT_REL: 44 rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); 45 break; 46 case DT_RELSZ: 47 relsz = dynp->d_un.d_val; 48 break; 49 } 50 } 51 rellim = (const Elf_Rel *)((const uint8_t *)rel + relsz); 52 for (; rel < rellim; rel++) { 53 where = (Elf_Addr *)(relocbase + rel->r_offset); 54 *where += (Elf_Addr)relocbase; 55 } 56 } 57 58 /* 59 * It is possible for the compiler to emit relocations for unaligned data. 60 * We handle this situation with these inlines. 61 */ 62 #define RELOC_ALIGNED_P(x) \ 63 (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) 64 65 static inline Elf_Addr 66 load_ptr(void *where) 67 { 68 Elf_Addr res; 69 70 memcpy(&res, where, sizeof(res)); 71 72 return (res); 73 } 74 75 static inline void 76 store_ptr(void *where, Elf_Addr val) 77 { 78 79 memcpy(where, &val, sizeof(val)); 80 } 81 82 int 83 _rtld_relocate_nonplt_objects(Obj_Entry *obj) 84 { 85 const Elf_Rel *rel; 86 const Elf_Sym *def = NULL; 87 const Obj_Entry *defobj = NULL; 88 unsigned long last_symnum = ULONG_MAX; 89 90 for (rel = obj->rel; rel < obj->rellim; rel++) { 91 Elf_Addr *where; 92 Elf_Addr tmp; 93 unsigned long symnum; 94 95 where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 96 97 switch (ELF_R_TYPE(rel->r_info)) { 98 case R_TYPE(PC24): /* word32 S - P + A */ 99 case R_TYPE(ABS32): /* word32 B + S + A */ 100 case R_TYPE(GLOB_DAT): /* word32 B + S */ 101 case R_TYPE(TLS_DTPOFF32): 102 case R_TYPE(TLS_DTPMOD32): 103 case R_TYPE(TLS_TPOFF32): 104 symnum = ELF_R_SYM(rel->r_info); 105 if (last_symnum != symnum) { 106 last_symnum = symnum; 107 def = _rtld_find_symdef(symnum, obj, &defobj, 108 false); 109 if (def == NULL) 110 return -1; 111 } 112 break; 113 114 default: 115 break; 116 } 117 118 switch (ELF_R_TYPE(rel->r_info)) { 119 case R_TYPE(NONE): 120 break; 121 122 #if 1 /* XXX should not occur */ 123 case R_TYPE(PC24): { /* word32 S - P + A */ 124 Elf32_Sword addend; 125 126 /* 127 * Extract addend and sign-extend if needed. 128 */ 129 addend = *where; 130 if (addend & 0x00800000) 131 addend |= 0xff000000; 132 tmp = (Elf_Addr)obj->relocbase + def->st_value 133 - (Elf_Addr)where + (addend << 2); 134 if ((tmp & 0xfe000000) != 0xfe000000 && 135 (tmp & 0xfe000000) != 0) { 136 _rtld_error( 137 "%s: R_ARM_PC24 relocation @ %p to %s failed " 138 "(displacement %ld (%#lx) out of range)", 139 obj->path, where, 140 obj->strtab + obj->symtab[ 141 ELF_R_SYM(rel->r_info)].st_name, 142 (long) tmp, (long) tmp); 143 return -1; 144 } 145 tmp >>= 2; 146 *where = (*where & 0xff000000) | (tmp & 0x00ffffff); 147 rdbg(("PC24 %s in %s --> %p @ %p in %s", 148 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)] 149 .st_name, obj->path, (void *)*where, where, 150 defobj->path)); 151 break; 152 } 153 #endif 154 155 case R_TYPE(ABS32): /* word32 B + S + A */ 156 case R_TYPE(GLOB_DAT): /* word32 B + S */ 157 if (__predict_true(RELOC_ALIGNED_P(where))) { 158 tmp = *where + (Elf_Addr)defobj->relocbase + 159 def->st_value; 160 /* Set the Thumb bit, if needed. */ 161 if (ELF_ST_TYPE(def->st_info) == STT_ARM_TFUNC) 162 tmp |= 1; 163 *where = tmp; 164 } else { 165 tmp = load_ptr(where) + 166 (Elf_Addr)defobj->relocbase + 167 def->st_value; 168 /* Set the Thumb bit, if needed. */ 169 if (ELF_ST_TYPE(def->st_info) == STT_ARM_TFUNC) 170 tmp |= 1; 171 store_ptr(where, tmp); 172 } 173 rdbg(("ABS32/GLOB_DAT %s in %s --> %p @ %p in %s", 174 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)] 175 .st_name, obj->path, (void *)tmp, where, 176 defobj->path)); 177 break; 178 179 case R_TYPE(IRELATIVE): 180 /* IFUNC relocations are handled in _rtld_call_ifunc */ 181 if (obj->ifunc_remaining_nonplt == 0) 182 obj->ifunc_remaining_nonplt = obj->rellim - rel; 183 /* FALL-THROUGH */ 184 185 case R_TYPE(RELATIVE): /* word32 B + A */ 186 if (__predict_true(RELOC_ALIGNED_P(where))) { 187 tmp = *where + (Elf_Addr)obj->relocbase; 188 *where = tmp; 189 } else { 190 tmp = load_ptr(where) + 191 (Elf_Addr)obj->relocbase; 192 store_ptr(where, tmp); 193 } 194 rdbg(("RELATIVE in %s --> %p", obj->path, 195 (void *)tmp)); 196 break; 197 198 case R_TYPE(COPY): 199 /* 200 * These are deferred until all other relocations have 201 * been done. All we do here is make sure that the 202 * COPY relocation is not in a shared library. They 203 * are allowed only in executable files. 204 */ 205 if (obj->isdynamic) { 206 _rtld_error( 207 "%s: Unexpected R_COPY relocation in shared library", 208 obj->path); 209 return -1; 210 } 211 rdbg(("COPY (avoid in main)")); 212 break; 213 214 case R_TYPE(TLS_DTPOFF32): 215 tmp = (Elf_Addr)(def->st_value); 216 if (__predict_true(RELOC_ALIGNED_P(where))) 217 *where = tmp; 218 else 219 store_ptr(where, tmp); 220 221 rdbg(("TLS_DTPOFF32 %s in %s --> %p", 222 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)] 223 .st_name, obj->path, (void *)tmp)); 224 225 break; 226 case R_TYPE(TLS_DTPMOD32): 227 tmp = (Elf_Addr)(defobj->tlsindex); 228 if (__predict_true(RELOC_ALIGNED_P(where))) 229 *where = tmp; 230 else 231 store_ptr(where, tmp); 232 233 rdbg(("TLS_DTPMOD32 %s in %s --> %p", 234 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)] 235 .st_name, obj->path, (void *)tmp)); 236 237 break; 238 239 case R_TYPE(TLS_TPOFF32): 240 if (!defobj->tls_static && 241 _rtld_tls_offset_allocate(__UNCONST(defobj))) 242 return -1; 243 244 if (__predict_true(RELOC_ALIGNED_P(where))) 245 tmp = *where; 246 else 247 tmp = load_ptr(where); 248 tmp += (Elf_Addr)def->st_value + defobj->tlsoffset + sizeof(struct tls_tcb); 249 if (__predict_true(RELOC_ALIGNED_P(where))) 250 *where = tmp; 251 else 252 store_ptr(where, tmp); 253 rdbg(("TLS_TPOFF32 %s in %s --> %p", 254 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)] 255 .st_name, obj->path, (void *)tmp)); 256 break; 257 258 default: 259 rdbg(("sym = %lu, type = %lu, offset = %p, " 260 "contents = %p", 261 (u_long)ELF_R_SYM(rel->r_info), 262 (u_long)ELF_R_TYPE(rel->r_info), 263 (void *)rel->r_offset, (void *)load_ptr(where))); 264 _rtld_error("%s: Unsupported relocation type %ld " 265 "in non-PLT relocations", 266 obj->path, (u_long) ELF_R_TYPE(rel->r_info)); 267 return -1; 268 } 269 } 270 return 0; 271 } 272 273 int 274 _rtld_relocate_plt_lazy(Obj_Entry *obj) 275 { 276 const Elf_Rel *rel; 277 278 for (rel = obj->pltrellim; rel-- > obj->pltrel; ) { 279 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 280 281 assert(ELF_R_TYPE(rel->r_info) == R_TYPE(JUMP_SLOT) || 282 ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)); 283 284 if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) 285 obj->ifunc_remaining = obj->pltrellim - rel; 286 287 /* Just relocate the GOT slots pointing into the PLT */ 288 *where += (Elf_Addr)obj->relocbase; 289 rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where)); 290 } 291 292 return 0; 293 } 294 295 static int 296 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rel *rel, 297 Elf_Addr *tp) 298 { 299 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 300 Elf_Addr new_value; 301 const Elf_Sym *def; 302 const Obj_Entry *defobj; 303 unsigned long info = rel->r_info; 304 305 assert(ELF_R_TYPE(info) == R_TYPE(JUMP_SLOT)); 306 307 def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, tp != NULL); 308 if (__predict_false(def == NULL)) 309 return -1; 310 if (__predict_false(def == &_rtld_sym_zero)) 311 return 0; 312 313 if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { 314 if (tp == NULL) 315 return 0; 316 new_value = _rtld_resolve_ifunc(defobj, def); 317 } else { 318 new_value = (Elf_Addr)(defobj->relocbase + def->st_value); 319 } 320 /* Set the Thumb bit, if needed. */ 321 if (ELF_ST_TYPE(def->st_info) == STT_ARM_TFUNC) 322 new_value |= 1; 323 rdbg(("bind now/fixup in %s --> old=%p new=%p", 324 defobj->strtab + def->st_name, (void *)*where, (void *)new_value)); 325 if (*where != new_value) 326 *where = new_value; 327 if (tp) 328 *tp = new_value; 329 330 return 0; 331 } 332 333 caddr_t 334 _rtld_bind(const Obj_Entry *obj, Elf_Word reloff) 335 { 336 const Elf_Rel *rel = (const Elf_Rel *)((const uint8_t *)obj->pltrel + reloff); 337 Elf_Addr new_value = 0; /* XXX gcc */ 338 int err; 339 340 _rtld_shared_enter(); 341 err = _rtld_relocate_plt_object(obj, rel, &new_value); 342 if (err) 343 _rtld_die(); 344 _rtld_shared_exit(); 345 346 return (caddr_t)new_value; 347 } 348 int 349 _rtld_relocate_plt_objects(const Obj_Entry *obj) 350 { 351 const Elf_Rel *rel; 352 int err = 0; 353 354 for (rel = obj->pltrel; rel < obj->pltrellim; rel++) { 355 err = _rtld_relocate_plt_object(obj, rel, NULL); 356 if (err) 357 break; 358 } 359 360 return err; 361 } 362