1 /* $NetBSD: kobj_machdep.c,v 1.9 2024/02/16 17:18:19 andvar Exp $ */ 2 3 /* 4 * Copyright (c) 2018 Ryo Shimizu 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: kobj_machdep.c,v 1.9 2024/02/16 17:18:19 andvar Exp $"); 31 32 #define ELFSIZE ARCH_ELFSIZE 33 34 #include "opt_ddb.h" 35 36 #include <sys/param.h> 37 #include <sys/kernel.h> 38 #include <sys/kobj.h> 39 #include <sys/exec.h> 40 #include <sys/exec_elf.h> 41 #include <sys/errno.h> 42 #include <sys/queue.h> 43 #include <sys/tree.h> 44 #include <sys/xcall.h> 45 46 #include <arm/cpufunc.h> 47 48 /* #define KOBJ_MACHDEP_DEBUG */ 49 50 #ifdef KOBJ_MACHDEP_DEBUG 51 #ifdef DDB 52 #include <aarch64/db_machdep.h> /* for strdisasm() */ 53 #endif 54 55 struct rtypeinfo { 56 Elf_Word rtype; 57 const char *name; 58 }; 59 60 static const struct rtypeinfo rtypetbl[] = { 61 { R_AARCH64_ABS64, "R_AARCH64_ABS64" }, 62 { R_AARCH64_ADD_ABS_LO12_NC, "R_AARCH64_ADD_ABS_LO12_NC" }, 63 { R_AARCH_LDST64_ABS_LO12_NC, "R_AARCH64_LDST64_ABS_LO12_NC" }, 64 { R_AARCH_LDST32_ABS_LO12_NC, "R_AARCH64_LDST32_ABS_LO12_NC" }, 65 { R_AARCH_LDST16_ABS_LO12_NC, "R_AARCH64_LDST16_ABS_LO12_NC" }, 66 { R_AARCH64_LDST8_ABS_LO12_NC, "R_AARCH64_LDST8_ABS_LO12_NC" }, 67 { R_AARCH64_ADR_PREL_PG_HI21_NC, "R_AARCH64_ADR_PREL_PG_HI21_NC"}, 68 { R_AARCH64_ADR_PREL_PG_HI21, "R_AARCH64_ADR_PREL_PG_HI21" }, 69 { R_AARCH_JUMP26, "R_AARCH64_JUMP26" }, 70 { R_AARCH_CALL26, "R_AARCH64_CALL26" }, 71 { R_AARCH64_PREL32, "R_AARCH64_PREL32" }, 72 { R_AARCH64_PREL16, "R_AARCH64_PREL16" } 73 }; 74 75 static const char * 76 strrtype(Elf_Word rtype) 77 { 78 int i; 79 static char buf[64]; 80 81 for (i = 0; i < __arraycount(rtypetbl); i++) { 82 if (rtypetbl[i].rtype == rtype) 83 return rtypetbl[i].name; 84 } 85 snprintf(buf, sizeof(buf), "RELOCATION-TYPE-%d", rtype); 86 return buf; 87 } 88 #endif /* KOBJ_MACHDEP_DEBUG */ 89 90 static inline bool 91 checkalign(Elf_Addr addr, int alignbyte, void *where, Elf64_Addr off) 92 { 93 if ((addr & (alignbyte - 1)) != 0) { 94 printf("kobj_reloc: Relocation 0x%jx unaligned at %p" 95 " (base+0x%jx). must be aligned %d\n", 96 (uintptr_t)addr, where, off, alignbyte); 97 return true; 98 } 99 return false; 100 } 101 102 static inline bool 103 checkoverflow(Elf_Addr addr, int bitwidth, Elf_Addr targetaddr, 104 const char *bitscale, void *where, Elf64_Addr off) 105 { 106 const Elf_Addr mask = ~__BITS(bitwidth - 1, 0); 107 108 if (((addr & mask) != 0) && ((addr & mask) != mask)) { 109 printf("kobj_reloc: Relocation 0x%jx too far from %p" 110 " (base+0x%jx) for %dbit%s\n", 111 (uintptr_t)targetaddr, where, off, bitwidth, bitscale); 112 return true; 113 } 114 return false; 115 } 116 117 #define WIDTHMASK(w) (0xffffffffffffffffUL >> (64 - (w))) 118 119 int 120 kobj_reloc(kobj_t ko, uintptr_t relocbase, const void *data, 121 bool isrela, bool local) 122 { 123 Elf_Addr saddr, addend, raddr, val; 124 Elf64_Addr off, *where; 125 Elf32_Addr *where32; 126 uint16_t *where16; 127 Elf_Word rtype, symidx; 128 const Elf_Rela *rela; 129 int error; 130 uint32_t *insn, immhi, immlo, shift; 131 bool nc = false; 132 #ifdef KOBJ_MACHDEP_DEBUG 133 #ifdef DDB 134 char disasmbuf[256]; 135 #endif 136 Elf_Addr old; 137 #endif /* KOBJ_MACHDEP_DEBUG */ 138 139 140 #ifdef KOBJ_MACHDEP_DEBUG 141 printf("%s:%d: ko=%p, relocbase=0x%jx, data=%p" 142 ", isrela=%d, local=%d\n", __func__, __LINE__, 143 ko, relocbase, data, isrela, local); 144 #endif /* KOBJ_MACHDEP_DEBUG */ 145 146 if (!isrela) { 147 printf("kobj_reloc: REL relocations not supported"); 148 error = 1; 149 goto done; 150 } 151 152 rela = (const Elf_Rela *)data; 153 addend = rela->r_addend; 154 rtype = ELF_R_TYPE(rela->r_info); 155 symidx = ELF_R_SYM(rela->r_info); 156 off = rela->r_offset; 157 where = (Elf_Addr *)(relocbase + off); 158 159 /* pointer to 32bit, 16bit, and instruction */ 160 where32 = (void *)where; 161 where16 = (void *)where; 162 insn = (uint32_t *)where; 163 164 /* no need to lookup any symbols */ 165 switch (rtype) { 166 case R_AARCH64_NONE: 167 case R_AARCH64_NONE2: 168 return 0; 169 } 170 171 const Elf_Sym *sym = kobj_symbol(ko, symidx); 172 173 if (!local && ELF_ST_BIND(sym->st_info) == STB_LOCAL) { 174 return 0; 175 } 176 177 error = kobj_sym_lookup(ko, symidx, &saddr); 178 if (error != 0) { 179 printf("kobj_reloc: symidx %d lookup failure." 180 " relocation type %d at %p (base+0x%jx)\n", 181 symidx, rtype, where, off); 182 goto done; 183 } 184 185 #ifdef KOBJ_MACHDEP_DEBUG 186 printf("%s:%d: symidx=%d, saddr=0x%jx, addend=0x%jx\n", 187 __func__, __LINE__, symidx, (uintptr_t)saddr, (uintptr_t)addend); 188 printf("%s:%d: rtype=%s, where=%p (base+0x%jx)\n", 189 __func__, __LINE__, strrtype(rtype), where, off); 190 old = *where; 191 #ifdef DDB 192 snprintf(disasmbuf, sizeof(disasmbuf), "%08x %s", 193 le32toh(*insn), strdisasm((vaddr_t)insn, 0)); 194 #endif 195 #endif /* KOBJ_MACHDEP_DEBUG */ 196 197 switch (rtype) { 198 case R_AARCH64_ABS64: 199 /* 200 * S + A 201 * e.g.) .quad <sym>+addend 202 */ 203 *where = saddr + addend; 204 break; 205 case R_AARCH64_ABS32: 206 /* 207 * S + A 208 * e.g.) .word <sym>+addend 209 */ 210 *where32 = saddr + addend; 211 break; 212 case R_AARCH64_ABS16: 213 /* 214 * S + A 215 * e.g.) .short <sym>+addend 216 */ 217 *where16 = saddr + addend; 218 break; 219 case R_AARCH64_ADD_ABS_LO12_NC: 220 case R_AARCH64_LDST8_ABS_LO12_NC: 221 case R_AARCH_LDST16_ABS_LO12_NC: 222 case R_AARCH_LDST32_ABS_LO12_NC: 223 case R_AARCH_LDST64_ABS_LO12_NC: 224 switch (rtype) { 225 case R_AARCH64_ADD_ABS_LO12_NC: 226 case R_AARCH64_LDST8_ABS_LO12_NC: 227 shift = 0; 228 break; 229 case R_AARCH_LDST16_ABS_LO12_NC: 230 shift = 1; 231 break; 232 case R_AARCH_LDST32_ABS_LO12_NC: 233 shift = 2; 234 break; 235 case R_AARCH_LDST64_ABS_LO12_NC: 236 shift = 3; 237 break; 238 default: 239 panic("illegal rtype: %d\n", rtype); 240 } 241 /* 242 * S + A 243 * e.g.) add x0,x0,#:lo12:<sym>+<addend> 244 * ldrb w0,[x0,#:lo12:<sym>+<addend>] 245 * ldrh w0,[x0,#:lo12:<sym>+<addend>] 246 * ldr w0,[x0,#:lo12:<sym>+<addend>] 247 * ldr x0,[x0,#:lo12:<sym>+<addend>] 248 */ 249 val = saddr + addend; 250 if (checkalign(val, 1 << shift, where, off)) { 251 error = 1; 252 break; 253 } 254 val &= WIDTHMASK(12); 255 val >>= shift; 256 *insn = htole32( 257 (le32toh(*insn) & ~__BITS(21,10)) | (val << 10)); 258 break; 259 260 case R_AARCH64_ADR_PREL_PG_HI21_NC: 261 nc = true; 262 /* FALLTHRU */ 263 case R_AARCH64_ADR_PREL_PG_HI21: 264 /* 265 * Page(S + A) - Page(P) 266 * e.g.) adrp x0,<sym>+<addend> 267 */ 268 val = saddr + addend; 269 val = val >> 12; 270 raddr = val << 12; 271 val -= (uintptr_t)where >> 12; 272 if (!nc && checkoverflow(val, 21, raddr, " x 4k", where, off)) { 273 error = 1; 274 break; 275 } 276 immlo = val & WIDTHMASK(2); 277 immhi = (val >> 2) & WIDTHMASK(19); 278 *insn = htole32((le32toh(*insn) & 279 ~(__BITS(30,29) | __BITS(23,5))) | 280 (immlo << 29) | (immhi << 5)); 281 break; 282 283 case R_AARCH_JUMP26: 284 case R_AARCH_CALL26: 285 /* 286 * S + A - P 287 * e.g.) b <sym>+<addend> 288 * bl <sym>+<addend> 289 */ 290 raddr = saddr + addend; 291 val = raddr - (uintptr_t)where; 292 if (checkalign(val, 4, where, off)) { 293 error = 1; 294 break; 295 } 296 val = (intptr_t)val >> 2; 297 if (checkoverflow(val, 26, raddr, " word", where, off)) { 298 error = 1; 299 break; 300 } 301 val &= WIDTHMASK(26); 302 *insn = htole32((le32toh(*insn) & ~__BITS(25,0)) | val); 303 break; 304 305 case R_AARCH64_PREL64: 306 /* 307 * S + A - P 308 * e.g.) 1: .quad <sym>+<addend>-1b 309 */ 310 raddr = saddr + addend; 311 val = raddr - (uintptr_t)where; 312 if (checkoverflow(val, 64, raddr, "", where, off)) { 313 error = 1; 314 break; 315 } 316 *where = val; 317 break; 318 case R_AARCH64_PREL32: 319 /* 320 * S + A - P 321 * e.g.) 1: .word <sym>+<addend>-1b 322 */ 323 raddr = saddr + addend; 324 val = raddr - (uintptr_t)where; 325 if (checkoverflow(val, 32, raddr, "", where, off)) { 326 error = 1; 327 break; 328 } 329 *where32 = val; 330 break; 331 case R_AARCH64_PREL16: 332 /* 333 * S + A - P 334 * e.g.) 1: .short <sym>+<addend>-1b 335 */ 336 raddr = saddr + addend; 337 val = raddr - (uintptr_t)where; 338 if (checkoverflow(val, 16, raddr, "", where, off)) { 339 error = 1; 340 break; 341 } 342 *where16 = val; 343 break; 344 default: 345 printf("kobj_reloc: unsupported relocation type %d" 346 " at %p (base+0x%jx) symidx %u\n", 347 rtype, where, off, symidx); 348 error = 1; 349 break; 350 } 351 352 #ifdef KOBJ_MACHDEP_DEBUG 353 printf("%s: reloc\n", __func__); 354 printf("%s: *where %016jx\n", __func__, (uintptr_t)old); 355 printf("%s: -> %016jx\n", __func__, (uintptr_t)*where); 356 #ifdef DDB 357 printf("%s: insn %s\n", __func__, disasmbuf); 358 printf("%s: -> %08x %s\n", __func__, 359 le32toh(*insn), strdisasm((vaddr_t)insn, 0)); 360 #endif 361 printf("\n"); 362 #endif /* KOBJ_MACHDEP_DEBUG */ 363 364 done: 365 if (error != 0) 366 return -1; 367 return 0; 368 } 369 370 static void 371 kobj_idcache_wbinv_all(void) 372 { 373 cpu_idcache_wbinv_all(); 374 } 375 376 int 377 kobj_machdep(kobj_t ko, void *base, size_t size, bool load) 378 { 379 uint64_t where; 380 381 if (load) { 382 if (cold) { 383 kobj_idcache_wbinv_all(); 384 } else { 385 where = xc_broadcast(0, 386 (xcfunc_t)kobj_idcache_wbinv_all, NULL, NULL); 387 xc_wait(where); 388 } 389 } 390 391 return 0; 392 } 393