1 /* $NetBSD: kobj_machdep.c,v 1.12 2026/09/09 15:17:56 skrll 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.12 2026/09/09 15:17:56 skrll 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 #define RTYPE(type) \ 61 { \ 62 .rtype = R_AARCH64_ ## type, \ 63 .name = "R_AARCH64_" # type, \ 64 } 65 66 static const struct rtypeinfo rtypetbl[] = { 67 RTYPE(ABS64), 68 RTYPE(ADD_ABS_LO12_NC), 69 RTYPE(LDST128_ABS_LO12_NC), 70 RTYPE(LDST64_ABS_LO12_NC), 71 RTYPE(LDST32_ABS_LO12_NC), 72 RTYPE(LDST16_ABS_LO12_NC), 73 RTYPE(LDST8_ABS_LO12_NC), 74 RTYPE(ADR_PREL_PG_HI21_NC), 75 RTYPE(ADR_PREL_PG_HI21), 76 RTYPE(JUMP26), 77 RTYPE(CALL26), 78 RTYPE(PREL32), 79 RTYPE(PREL16), 80 }; 81 82 static const char * 83 strrtype(Elf_Word rtype) 84 { 85 int i; 86 static char buf[64]; 87 88 for (i = 0; i < __arraycount(rtypetbl); i++) { 89 if (rtypetbl[i].rtype == rtype) 90 return rtypetbl[i].name; 91 } 92 snprintf(buf, sizeof(buf), "RELOCATION-TYPE-%d", rtype); 93 return buf; 94 } 95 #endif /* KOBJ_MACHDEP_DEBUG */ 96 97 static inline bool 98 checkalign(Elf_Addr addr, int alignbyte, void *where, Elf64_Addr off) 99 { 100 if ((addr & (alignbyte - 1)) != 0) { 101 printf("kobj_reloc: Relocation 0x%jx unaligned at %p" 102 " (base+0x%jx). must be aligned %d\n", 103 (uintptr_t)addr, where, off, alignbyte); 104 return true; 105 } 106 return false; 107 } 108 109 static inline bool 110 checkoverflow(Elf_Addr addr, int bitwidth, Elf_Addr targetaddr, 111 const char *bitscale, void *where, Elf64_Addr off) 112 { 113 const Elf_Addr mask = ~__BITS(bitwidth - 1, 0); 114 115 if (((addr & mask) != 0) && ((addr & mask) != mask)) { 116 printf("kobj_reloc: Relocation 0x%jx too far from %p" 117 " (base+0x%jx) for %dbit%s\n", 118 (uintptr_t)targetaddr, where, off, bitwidth, bitscale); 119 return true; 120 } 121 return false; 122 } 123 124 #define WIDTHMASK(w) (0xffffffffffffffffUL >> (64 - (w))) 125 126 int 127 kobj_reloc(kobj_t ko, uintptr_t relocbase, const void *data, 128 bool isrela, bool local) 129 { 130 Elf_Addr saddr, addend, raddr, val; 131 Elf64_Addr off, *where; 132 Elf32_Addr *where32; 133 uint16_t *where16; 134 Elf_Word rtype, symidx; 135 const Elf_Rela *rela; 136 int error; 137 uint32_t *insn, immhi, immlo, shift; 138 bool nc = false; 139 #ifdef KOBJ_MACHDEP_DEBUG 140 #ifdef DDB 141 char disasmbuf[256]; 142 #endif 143 Elf_Addr old; 144 #endif /* KOBJ_MACHDEP_DEBUG */ 145 146 147 #ifdef KOBJ_MACHDEP_DEBUG 148 printf("%s:%d: ko=%p, relocbase=0x%jx, data=%p" 149 ", isrela=%d, local=%d\n", __func__, __LINE__, 150 ko, relocbase, data, isrela, local); 151 #endif /* KOBJ_MACHDEP_DEBUG */ 152 153 if (!isrela) { 154 printf("kobj_reloc: REL relocations not supported"); 155 error = 1; 156 goto done; 157 } 158 159 rela = (const Elf_Rela *)data; 160 addend = rela->r_addend; 161 rtype = ELF_R_TYPE(rela->r_info); 162 symidx = ELF_R_SYM(rela->r_info); 163 off = rela->r_offset; 164 where = (Elf_Addr *)(relocbase + off); 165 166 /* pointer to 32bit, 16bit, and instruction */ 167 where32 = (void *)where; 168 where16 = (void *)where; 169 insn = (uint32_t *)where; 170 171 /* no need to lookup any symbols */ 172 switch (rtype) { 173 case R_AARCH64_NONE: 174 case R_AARCH64_NONE2: 175 return 0; 176 } 177 178 const Elf_Sym *sym = kobj_symbol(ko, symidx); 179 180 if (!local && ELF_ST_BIND(sym->st_info) == STB_LOCAL) { 181 return 0; 182 } 183 184 error = kobj_sym_lookup(ko, symidx, &saddr); 185 if (error != 0) { 186 printf("kobj_reloc: symidx %d lookup failure." 187 " relocation type %d at %p (base+0x%jx)\n", 188 symidx, rtype, where, off); 189 goto done; 190 } 191 192 #ifdef KOBJ_MACHDEP_DEBUG 193 printf("%s:%d: symidx=%d, saddr=0x%jx, addend=0x%jx\n", 194 __func__, __LINE__, symidx, (uintptr_t)saddr, (uintptr_t)addend); 195 printf("%s:%d: rtype=%s, where=%p (base+0x%jx)\n", 196 __func__, __LINE__, strrtype(rtype), where, off); 197 old = *where; 198 #ifdef DDB 199 snprintf(disasmbuf, sizeof(disasmbuf), "%08x %s", 200 le32toh(*insn), strdisasm((vaddr_t)insn, 0)); 201 #endif 202 #endif /* KOBJ_MACHDEP_DEBUG */ 203 204 switch (rtype) { 205 case R_AARCH64_ABS64: 206 /* 207 * S + A 208 * e.g.) .quad <sym>+addend 209 */ 210 *where = saddr + addend; 211 break; 212 case R_AARCH64_ABS32: 213 /* 214 * S + A 215 * e.g.) .word <sym>+addend 216 */ 217 *where32 = saddr + addend; 218 break; 219 case R_AARCH64_ABS16: 220 /* 221 * S + A 222 * e.g.) .short <sym>+addend 223 */ 224 *where16 = saddr + addend; 225 break; 226 case R_AARCH64_ADD_ABS_LO12_NC: 227 case R_AARCH64_LDST8_ABS_LO12_NC: 228 case R_AARCH64_LDST16_ABS_LO12_NC: 229 case R_AARCH64_LDST32_ABS_LO12_NC: 230 case R_AARCH64_LDST64_ABS_LO12_NC: 231 case R_AARCH64_LDST128_ABS_LO12_NC: 232 switch (rtype) { 233 case R_AARCH64_ADD_ABS_LO12_NC: 234 case R_AARCH64_LDST8_ABS_LO12_NC: 235 shift = 0; 236 break; 237 case R_AARCH64_LDST16_ABS_LO12_NC: 238 shift = 1; 239 break; 240 case R_AARCH64_LDST32_ABS_LO12_NC: 241 shift = 2; 242 break; 243 case R_AARCH64_LDST64_ABS_LO12_NC: 244 shift = 3; 245 break; 246 case R_AARCH64_LDST128_ABS_LO12_NC: 247 shift = 4; 248 break; 249 default: 250 panic("illegal rtype: %d\n", rtype); 251 } 252 /* 253 * S + A 254 * e.g.) add x0,x0,#:lo12:<sym>+<addend> 255 * ldrb w0,[x0,#:lo12:<sym>+<addend>] 256 * ldrh w0,[x0,#:lo12:<sym>+<addend>] 257 * ldr w0,[x0,#:lo12:<sym>+<addend>] 258 * ldr x0,[x0,#:lo12:<sym>+<addend>] 259 */ 260 val = saddr + addend; 261 if (checkalign(val, 1 << shift, where, off)) { 262 error = 1; 263 break; 264 } 265 val &= WIDTHMASK(12); 266 val >>= shift; 267 *insn = htole32( 268 (le32toh(*insn) & ~__BITS(21,10)) | (val << 10)); 269 break; 270 271 case R_AARCH64_ADR_PREL_PG_HI21_NC: 272 nc = true; 273 /* FALLTHRU */ 274 case R_AARCH64_ADR_PREL_PG_HI21: 275 /* 276 * Page(S + A) - Page(P) 277 * e.g.) adrp x0,<sym>+<addend> 278 */ 279 val = saddr + addend; 280 val = val >> 12; 281 raddr = val << 12; 282 val -= (uintptr_t)where >> 12; 283 if (!nc && checkoverflow(val, 21, raddr, " x 4k", where, off)) { 284 error = 1; 285 break; 286 } 287 immlo = val & WIDTHMASK(2); 288 immhi = (val >> 2) & WIDTHMASK(19); 289 *insn = htole32((le32toh(*insn) & 290 ~(__BITS(30,29) | __BITS(23,5))) | 291 (immlo << 29) | (immhi << 5)); 292 break; 293 294 case R_AARCH64_JUMP26: 295 case R_AARCH64_CALL26: 296 /* 297 * S + A - P 298 * e.g.) b <sym>+<addend> 299 * bl <sym>+<addend> 300 */ 301 raddr = saddr + addend; 302 val = raddr - (uintptr_t)where; 303 if (checkalign(val, 4, where, off)) { 304 error = 1; 305 break; 306 } 307 val = (intptr_t)val >> 2; 308 if (checkoverflow(val, 26, raddr, " word", where, off)) { 309 error = 1; 310 break; 311 } 312 val &= WIDTHMASK(26); 313 *insn = htole32((le32toh(*insn) & ~__BITS(25,0)) | val); 314 break; 315 316 case R_AARCH64_PREL64: 317 /* 318 * S + A - P 319 * e.g.) 1: .quad <sym>+<addend>-1b 320 */ 321 raddr = saddr + addend; 322 val = raddr - (uintptr_t)where; 323 if (checkoverflow(val, 64, raddr, "", where, off)) { 324 error = 1; 325 break; 326 } 327 *where = val; 328 break; 329 case R_AARCH64_PREL32: 330 /* 331 * S + A - P 332 * e.g.) 1: .word <sym>+<addend>-1b 333 */ 334 raddr = saddr + addend; 335 val = raddr - (uintptr_t)where; 336 if (checkoverflow(val, 32, raddr, "", where, off)) { 337 error = 1; 338 break; 339 } 340 *where32 = val; 341 break; 342 case R_AARCH64_PREL16: 343 /* 344 * S + A - P 345 * e.g.) 1: .short <sym>+<addend>-1b 346 */ 347 raddr = saddr + addend; 348 val = raddr - (uintptr_t)where; 349 if (checkoverflow(val, 16, raddr, "", where, off)) { 350 error = 1; 351 break; 352 } 353 *where16 = val; 354 break; 355 default: 356 printf("kobj_reloc: unsupported relocation type %d" 357 " at %p (base+0x%jx) symidx %u\n", 358 rtype, where, off, symidx); 359 error = 1; 360 break; 361 } 362 363 #ifdef KOBJ_MACHDEP_DEBUG 364 printf("%s: reloc\n", __func__); 365 printf("%s: *where %016jx\n", __func__, (uintptr_t)old); 366 printf("%s: -> %016jx\n", __func__, (uintptr_t)*where); 367 #ifdef DDB 368 printf("%s: insn %s\n", __func__, disasmbuf); 369 printf("%s: -> %08x %s\n", __func__, 370 le32toh(*insn), strdisasm((vaddr_t)insn, 0)); 371 #endif 372 printf("\n"); 373 #endif /* KOBJ_MACHDEP_DEBUG */ 374 375 done: 376 if (error != 0) 377 return -1; 378 return 0; 379 } 380 381 static void 382 kobj_idcache_wbinv_all(void) 383 { 384 cpu_idcache_wbinv_all(); 385 } 386 387 int 388 kobj_machdep(kobj_t ko, void *base, size_t size, bool load) 389 { 390 uint64_t where; 391 392 if (load) { 393 if (cold) { 394 kobj_idcache_wbinv_all(); 395 } else { 396 where = xc_broadcast(0, 397 (xcfunc_t)kobj_idcache_wbinv_all, NULL, NULL); 398 xc_wait(where); 399 } 400 } 401 402 return 0; 403 } 404