#include <sys/cdefs.h>
__RCSID("$NetBSD: kobj_machdep.c,v 1.6 2024/01/18 03:36:24 msaitoh Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kobj_impl.h>
#include <sys/kobj.h>
#include <sys/exec.h>
#include <sys/exec_elf.h>
#include <riscv/locore.h>
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;
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;
if (lo12 < rela || relalim < lo12)
continue;
for (; rela < relalim; rela++) {
Elf_Addr * const where =
(Elf_Addr *)(relocbase + rela->r_offset);
if (where != hi20addr) {
continue;
}
uintptr_t symidx = ELF_R_SYM(rela->r_info);
#if 0
if (symidx >= ko->ko_symcnt) {
continue;
}
#endif
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;
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;
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);
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:
if (!local && ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
return 0;
}
}
switch (rtype) {
case R_RISCV_NONE:
case R_RISCV_RELAX:
return 0;
case R_RISCV_BRANCH:
case R_RISCV_JAL:
if (symidx == 0)
break;
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;
#ifdef _LP64
case R_RISCV_64:
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) {
if (!local)
return -1;
return 0;
}
switch (rtype) {
case R_RISCV_PCREL_HI20:
case R_RISCV_HI20: {
last_hi20.addend = addend + addr;
last_hi20.where = where;
last_hi20.local = ELF_ST_BIND(sym->st_info) == STB_LOCAL;
if (!local && last_hi20.local) {
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;
}
}
if (!local && last_hi20.local) {
return 0;
}
addend = addend + (last_hi20.addend & __BITS(11,0));
break;
}
default:
addend += addr;
}
break;
}
default:
printf("%s: unexpected relocation type %u\n\n", __func__, rtype);
return -1;
}
switch (rtype) {
case R_RISCV_64:
*where = addend;
break;
case R_RISCV_32:
case R_RISCV_SET32:
case R_RISCV_32_PCREL:
*wwhere = addend;
break;
case R_RISCV_SET16:
*hwhere = addend;
break;
case R_RISCV_SET8:
*bwhere = addend;
break;
case R_RISCV_SET6: {
const uint8_t mask = __BITS(5, 0);
*bwhere = (*bwhere & ~mask) | __SHIFTIN(addend, mask);
break;
}
case R_RISCV_ADD64:
*where += addend;
break;
case R_RISCV_ADD32:
*wwhere += addend;
break;
case R_RISCV_ADD16:
*hwhere += addend;
break;
case R_RISCV_ADD8:
*bwhere += addend;
break;
case R_RISCV_SUB64:
*where -= addend;
break;
case R_RISCV_SUB32:
*wwhere -= addend;
break;
case R_RISCV_SUB16:
*hwhere -= addend;
break;
case R_RISCV_SUB8:
*bwhere -= addend;
break;
case R_RISCV_SUB6:
*bwhere -= addend;
break;
case R_RISCV_BRANCH: {
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: {
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));
}
case R_RISCV_HI20:
case R_RISCV_PCREL_HI20: {
const uint32_t mask = __BITS(31, 12);
*wwhere = (addend & mask) | (*wwhere & ~mask);
break;
}
case R_RISCV_PCREL_LO12_I:
case R_RISCV_LO12_I: {
*wwhere += ((addend) << 20);
break;
}
case R_RISCV_PCREL_LO12_S:
case R_RISCV_LO12_S: {
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: {
const int32_t check = (int32_t)addend >> 20;
if (check == 0 || check == -1) {
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));
wwhere[0] = addend | (wwhere[1] & 0xf80) | 0x6f;
wwhere[1] = 0x00000013;
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: {
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: {
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: {
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;
}