/* $NetBSD: kobj_machdep.c,v 1.6 2024/01/18 03:36:24 msaitoh Exp $ */ /*- * Copyright (c) 2014,2023 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Matt Thomas of 3am Software Foundry, and by Nick Hudson. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __RCSID("$NetBSD: kobj_machdep.c,v 1.6 2024/01/18 03:36:24 msaitoh Exp $"); #include #include #include #include #include #include #include struct hi20 { void *where; long addend; bool local; } last_hi20; static int kobj_findhi20(kobj_t ko, uintptr_t relocbase, bool local, const Elf_Rela *lo12, Elf_Addr *hi20addr) { const Elf_Rela *relalim; const Elf_Rela *rela; /* * Find the relocation section. */ for (size_t i = 0; i < ko->ko_nrela; i++) { rela = ko->ko_relatab[i].rela; if (rela == NULL) { continue; } relalim = rela + ko->ko_relatab[i].nrela; /* Check that the relocation is in this section */ if (lo12 < rela || relalim < lo12) continue; /* * Now find the HI20 relocation from the LO12 relocation * address (hi20addr) */ for (; rela < relalim; rela++) { Elf_Addr * const where = (Elf_Addr *)(relocbase + rela->r_offset); if (where != hi20addr) { continue; } /* Found the referenced HI20 relocation */ uintptr_t symidx = ELF_R_SYM(rela->r_info); #if 0 if (symidx >= ko->ko_symcnt) { continue; } #endif /* * If this symbol doesn't is global and we're doing * the local symbols at this point then we can't * resolve it yet, so tell our caller. */ const Elf_Sym *sym = kobj_symbol(ko, symidx); if (local && ELF_ST_BIND(sym->st_info) != STB_LOCAL) { last_hi20.local = false; return 1; } last_hi20.local = true; Elf_Addr addr; const int error = kobj_sym_lookup(ko, symidx, &addr); if (error) return -1; long addend = rela->r_addend; /* needs to be signed */ addend -= (intptr_t)where; last_hi20.where = where; last_hi20.addend = addend; addend += addr; return 0; } } return -1; } int kobj_reloc(kobj_t ko, uintptr_t relocbase, const void *data, bool isrela, bool local) { if (!isrela) { panic("kobj_reloc: REL relocations not supported"); } const Elf_Rela * const rela = (const Elf_Rela *)data; Elf_Addr * const where = (Elf_Addr *)(relocbase + rela->r_offset); long addend = rela->r_addend; /* needs to be signed */ uint32_t *wwhere = (uint32_t *)where; uint16_t * const hwhere = (uint16_t *)where; uint8_t * const bwhere = (uint8_t *)where; const u_int rtype = ELF_R_TYPE(rela->r_info); const u_int symidx = ELF_R_SYM(rela->r_info); const Elf_Sym *sym = kobj_symbol(ko, symidx); /* * Check that we need to process this relocation in this pass. * All relocations to local symols can be done in the first pass * apart from PCREL_LO12 that reference a HI20 to a global symbol. */ switch (rtype) { case R_RISCV_PCREL_LO12_I: case R_RISCV_PCREL_LO12_S: if (addend != 0) { printf("oops\n"); } case R_RISCV_PCREL_HI20: case R_RISCV_HI20: break; default: /* Don't do other relocations again */ if (!local && ELF_ST_BIND(sym->st_info) == STB_LOCAL) { return 0; } } /* Relocation calculation */ switch (rtype) { case R_RISCV_NONE: case R_RISCV_RELAX: return 0; case R_RISCV_BRANCH: case R_RISCV_JAL: // XXXNH eh? what's with the symidx test?' if (symidx == 0) break; /* FALLTHROUGH */ case R_RISCV_CALL_PLT: case R_RISCV_CALL: case R_RISCV_PCREL_HI20: case R_RISCV_RVC_BRANCH: case R_RISCV_RVC_JUMP: case R_RISCV_32_PCREL: addend -= (intptr_t)where; /* A -= P */ /* FALLTHROUGH */ #ifdef _LP64 case R_RISCV_64: /* doubleword64 S + A */ case R_RISCV_ADD64: case R_RISCV_SUB64: #endif case R_RISCV_HI20: case R_RISCV_PCREL_LO12_I: case R_RISCV_PCREL_LO12_S: case R_RISCV_LO12_I: case R_RISCV_LO12_S: case R_RISCV_ADD32: case R_RISCV_ADD16: case R_RISCV_ADD8: case R_RISCV_SUB32: case R_RISCV_SUB16: case R_RISCV_SUB8: case R_RISCV_SUB6: case R_RISCV_SET32: case R_RISCV_SET16: case R_RISCV_SET8: case R_RISCV_SET6: { Elf_Addr addr; const int error = kobj_sym_lookup(ko, symidx, &addr); if (error) { /* * This is a fatal error in the 2nd pass, i.e. * local == false */ if (!local) return -1; return 0; } switch (rtype) { case R_RISCV_PCREL_HI20: case R_RISCV_HI20: { // XXXNH why is this without middle? last_hi20.addend = addend + addr; last_hi20.where = where; last_hi20.local = ELF_ST_BIND(sym->st_info) == STB_LOCAL; /* * If this is the 2nd pass then and the symbol is * local then it's already been fixed up. */ if (!local && last_hi20.local) { // last_hi20.global = false; return 0; } const uint32_t lobits = 12; const uint32_t middle = 1U << (lobits - 1); addend += addr + middle; break; } case R_RISCV_PCREL_LO12_I: case R_RISCV_PCREL_LO12_S: { if (last_hi20.where != (void *)addr) { int err = kobj_findhi20(ko, relocbase, local, rela, (Elf_Addr *)addr); if (err < 0) return -1; else if (err > 0) { KASSERT(local); return 0; } } /* * In the second pass if the HI20 symbol was local * it was already processed. */ if (!local && last_hi20.local) { return 0; } addend = addend + (last_hi20.addend & __BITS(11,0)); break; } default: addend += addr; /* A += S */ } break; } default: printf("%s: unexpected relocation type %u\n\n", __func__, rtype); return -1; } /* Relocation memory update */ switch (rtype) { case R_RISCV_64: /* word64 S + A */ *where = addend; break; case R_RISCV_32: /* word32 S + A */ case R_RISCV_SET32: /* word32 S + A */ case R_RISCV_32_PCREL: /* word32 S + A - P */ *wwhere = addend; break; case R_RISCV_SET16: /* word16 S + A */ *hwhere = addend; break; case R_RISCV_SET8: /* word8 S + A */ *bwhere = addend; break; case R_RISCV_SET6: { /* word6 S + A */ const uint8_t mask = __BITS(5, 0); *bwhere = (*bwhere & ~mask) | __SHIFTIN(addend, mask); break; } case R_RISCV_ADD64: /* word64 V + S + A */ *where += addend; break; case R_RISCV_ADD32: /* word32 V + S + A */ *wwhere += addend; break; case R_RISCV_ADD16: /* word16 V + S + A */ *hwhere += addend; break; case R_RISCV_ADD8: /* word8 V + S + A */ *bwhere += addend; break; case R_RISCV_SUB64: /* word64 V - S - A */ *where -= addend; break; case R_RISCV_SUB32: /* word32 V - S - A */ *wwhere -= addend; break; case R_RISCV_SUB16: /* word16 V - S - A */ *hwhere -= addend; break; case R_RISCV_SUB8: /* word8 V - S - A */ *bwhere -= addend; break; case R_RISCV_SUB6: /* word6 V - S - A */ // XXXNH *bwhere -= addend; break; case R_RISCV_BRANCH: { /* * B-type * 31 | 30 25 | ... | 11 8 | 7 | ... * imm[12] | imm[10:5] | ... | imm[4:1] | imm[11] | ... */ const uint32_t immA = __SHIFTOUT(addend, __BIT(12)); const uint32_t immB = __SHIFTOUT(addend, __BITS(10, 5)); const uint32_t immC = __SHIFTOUT(addend, __BITS( 4, 1)); const uint32_t immD = __SHIFTOUT(addend, __BIT(11)); addend = __SHIFTIN(immA, __BIT(31)) | __SHIFTIN(immB, __BITS(30, 25)) | __SHIFTIN(immC, __BITS(11, 8)) | __SHIFTIN(immD, __BIT(7)); const uint32_t mask = __BITS(31, 25) | __BITS(11, 7); *wwhere = (*wwhere & ~mask) | addend; break; } case R_RISCV_JAL: { /* * J-type * 31 | 30 21 | 20 | 19 12 | ... * imm[20] | imm[10:1] | imm[11] | imm[19:12] | ... */ const uint32_t immA = __SHIFTOUT(addend, __BIT(20)); const uint32_t immB = __SHIFTOUT(addend, __BITS(10, 1)); const uint32_t immC = __SHIFTOUT(addend, __BIT(11)); const uint32_t immD = __SHIFTOUT(addend, __BITS(19, 12)); addend = __SHIFTIN(immA, __BIT(31)) | __SHIFTIN(immB, __BITS(30, 21)) | __SHIFTIN(immC, __BIT(20)) | __SHIFTIN(immD, __BITS(19,12)); /* FALLTHROUGH */ } case R_RISCV_HI20: /* LUI/AUIPC (S + A - P) */ case R_RISCV_PCREL_HI20: { /* LUI/AUIPC (S + A - P) */ /* * U-Type * 31 12 | ... * imm[31:12] | ... */ const uint32_t mask = __BITS(31, 12); *wwhere = (addend & mask) | (*wwhere & ~mask); break; } case R_RISCV_PCREL_LO12_I: case R_RISCV_LO12_I: { /* low12 I-type instructions */ /* * I-Type * 31 20 | ... * imm[11:0] | ... */ *wwhere += ((addend) << 20); break; } case R_RISCV_PCREL_LO12_S: case R_RISCV_LO12_S: { /* low12 S-type instructions */ /* * S-type * * 31 25 | ... | 11 7 | ... * imm[11:5] | ... | imm[4:0] | ... * */ const uint32_t immA = __SHIFTOUT(addend, __BITS(11, 5)); const uint32_t immB = __SHIFTOUT(addend, __BITS( 4, 0)); const uint32_t mask = __BITS(31, 25) | __BITS(11, 7); addend = __SHIFTIN(immA, __BITS(31, 25)) | __SHIFTIN(immB, __BITS(11, 7)); *wwhere = (*wwhere & ~mask) | addend; break; } case R_RISCV_CALL: case R_RISCV_CALL_PLT: { /* * U-type * * 31 12 | ... * imm[31:12] | ... * * * I-type * * 31 25 | ... * imm[11:0] | ... */ // XXXNH better name... const int32_t check = (int32_t)addend >> 20; if (check == 0 || check == -1) { // This falls into the range for the JAL instruction. /* * J-type * 31 | 30 21 | 20 | 19 12 | ... * imm[20] | imm[10:1] | imm[11] | imm[19:12] | ... */ const uint32_t immA = __SHIFTOUT(addend, __BIT(20)); const uint32_t immB = __SHIFTOUT(addend, __BITS(10, 1)); const uint32_t immC = __SHIFTOUT(addend, __BIT(11)); const uint32_t immD = __SHIFTOUT(addend, __BITS(19, 12)); addend = __SHIFTIN(immA, __BIT(31)) | __SHIFTIN(immB, __BITS(30, 21)) | __SHIFTIN(immC, __BIT(20)) | __SHIFTIN(immD, __BITS(19,12)); // Convert the auipc/jalr to a jal/nop wwhere[0] = addend | (wwhere[1] & 0xf80) | 0x6f; wwhere[1] = 0x00000013; // nop (addi x0, x0, 0) printf("%s: %s where (%p) [0] -> %x, [1] -> %x\n", __func__, "R_RISCV_CALL", wwhere, wwhere[0], wwhere[1]); break; } wwhere[0] = ((addend + 0x800) & 0xfffff000) | (wwhere[0] & 0xfff); wwhere[1] = (addend << 20) | (wwhere[1] & 0x000fffff); printf("%s: %s where (%p) [0] -> %x, [1] -> %x\n", __func__, "R_RISCV_CALL", wwhere, wwhere[0], wwhere[1]); break; } case R_RISCV_RVC_BRANCH: { /* * CB-type * * ... | 12 10 | ... | 6 5 3 2 | ... * ... | offset[8|4:3] | ... | offset[7:6|2:1|5] | ... */ const uint16_t immA = __SHIFTOUT(addend, __BIT(8)); const uint16_t immB = __SHIFTOUT(addend, __BITS(4, 3)); const uint16_t immC = __SHIFTOUT(addend, __BITS(7, 6)); const uint16_t immD = __SHIFTOUT(addend, __BITS(2, 1)); const uint16_t immE = __SHIFTOUT(addend, __BIT(5)); const uint16_t mask = __BITS(12, 10) | __BITS(6, 2); addend = __SHIFTIN(immA, __BIT(12)) | __SHIFTIN(immB, __BITS(11, 10)) | __SHIFTIN(immC, __BITS( 6, 5)) | __SHIFTIN(immD, __BITS( 4, 3)) | __SHIFTIN(immE, __BIT(2)); *hwhere = (*hwhere & ~mask) | addend; break; } case R_RISCV_RVC_JUMP: { /* * CJ-type * * ... | 12 2 | ... * ... | offset[11|4|9:8|10|6|7|3:1|5] | ... */ const uint16_t immA = __SHIFTOUT(addend, __BIT(11)); const uint16_t immB = __SHIFTOUT(addend, __BIT(4)); const uint16_t immC = __SHIFTOUT(addend, __BITS(9, 8)); const uint16_t immD = __SHIFTOUT(addend, __BIT(10)); const uint16_t immE = __SHIFTOUT(addend, __BIT(6)); const uint16_t immF = __SHIFTOUT(addend, __BIT(7)); const uint16_t immG = __SHIFTOUT(addend, __BITS(3, 1)); const uint16_t immH = __SHIFTOUT(addend, __BIT(5)); const uint16_t mask = __BITS(12, 2); addend = __SHIFTIN(immA, __BIT(12)) | __SHIFTIN(immB, __BIT(11)) | __SHIFTIN(immC, __BITS(10, 9)) | __SHIFTIN(immD, __BIT(8)) | __SHIFTIN(immE, __BIT(7)) | __SHIFTIN(immF, __BIT(6)) | __SHIFTIN(immG, __BITS(5, 3)) | __SHIFTIN(immH, __BIT(2)); *hwhere = (*hwhere & ~mask) | addend; break; } case R_RISCV_RVC_LUI: { /* * CI-type * * ... | 12 | ... | 6 2 | ... * ... | offset[17] | ... | offset[16:12] | ... */ const uint16_t immA = __SHIFTOUT(addend, __BIT(17)); const uint16_t immB = __SHIFTOUT(addend, __BITS(16,12)); const uint16_t mask = __BIT(12) | __BITS(6, 2); addend = __SHIFTIN(immA, __BIT(12)) | __SHIFTIN(immB, __BITS(6, 2)); *hwhere = (*hwhere & ~mask) | addend; break; } default: printf("Unexpected relocation type %d\n", rtype); } return 0; } int kobj_machdep(kobj_t ko, void *base, size_t size, bool load) { if (load) __asm("fence rw,rw; fence.i"); return 0; }