#define ELFSIZE ARCH_ELFSIZE
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kobj.h>
#include <sys/exec.h>
#include <sys/exec_elf.h>
#define _RF_S 0x80000000
#define _RF_A 0x40000000
#define _RF_P 0x20000000
#define _RF_G 0x10000000
#define _RF_B 0x08000000
#define _RF_U 0x04000000
#define _RF_SZ(s) (((s) & 0xff) << 8)
#define _RF_RS(s) ( (s) & 0xff)
static const int reloc_target_flags[] = {
0,
_RF_S|_RF_A| _RF_SZ(8) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(16) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A|_RF_P| _RF_SZ(8) | _RF_RS(0),
_RF_S|_RF_A|_RF_P| _RF_SZ(16) | _RF_RS(0),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_G| _RF_SZ(32) | _RF_RS(0),
_RF_G| _RF_SZ(32) | _RF_RS(0),
_RF_G| _RF_SZ(32) | _RF_RS(10),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10),
_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2),
_RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0),
_RF_SZ(32) | _RF_RS(0),
_RF_A| _RF_B| _RF_SZ(32) | _RF_RS(0),
_RF_S|_RF_A| _RF_U| _RF_SZ(32) | _RF_RS(0),
};
#ifdef RTLD_DEBUG_RELOC
static const char *reloc_names[] = {
"NONE", "RELOC_8", "RELOC_16", "RELOC_32", "DISP_8",
"DISP_16", "DISP_32", "WDISP_30", "WDISP_22", "HI22",
"22", "13", "LO10", "GOT10", "GOT13",
"GOT22", "PC10", "PC22", "WPLT30", "COPY",
"GLOB_DAT", "JMP_SLOT", "RELATIVE", "UA_32"
};
#endif
#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0)
#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0)
#define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0)
#define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0)
#define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0)
#define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff)
#define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff)
static const int reloc_target_bitmask[] = {
#define _BM(x) (~(-(1ULL << (x))))
0,
_BM(8), _BM(16), _BM(32),
_BM(8), _BM(16), _BM(32),
_BM(30), _BM(22),
_BM(22), _BM(22),
_BM(13), _BM(10),
_BM(10), _BM(13), _BM(22),
_BM(10), _BM(22),
_BM(30), 0,
-1, -1, -1,
_BM(32)
#undef _BM
};
#define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t])
int
kobj_reloc(kobj_t ko, uintptr_t relocbase, const void *data,
bool isrela, bool local)
{
const Elf_Rela *rela;
Elf_Addr *where, addr;
Elf_Word value, mask;
uintptr_t tmp;
u_int symidx, type;
int error;
rela = data;
where = (Elf_Addr *) (relocbase + rela->r_offset);
symidx = ELF_R_SYM(rela->r_info);
type = ELF_R_TYPE(rela->r_info);
value = rela->r_addend;
const Elf_Sym *sym = kobj_symbol(ko, symidx);
if (!local && ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
return 0;
}
if (type == R_TYPE(NONE))
return 0;
if (!isrela) {
printf("kobj_reloc: only support RELA relocations\n");
return -1;
}
if (type == R_TYPE(JMP_SLOT) || type == R_TYPE(COPY) ||
type > R_TYPE(6)) {
printf("kobj_reloc: unexpected reloc type %d\n", type);
return -1;
}
if (type == R_TYPE(RELATIVE)) {
*where += (Elf_Addr)(relocbase + value);
return 0;
}
if (RELOC_RESOLVE_SYMBOL(type)) {
error = kobj_sym_lookup(ko, symidx, &addr);
if (error)
return -1;
value += addr;
}
if (RELOC_PC_RELATIVE(type)) {
value -= (Elf_Word)where;
}
if (RELOC_BASE_RELATIVE(type)) {
value += (Elf_Word)(relocbase + *where);
}
mask = RELOC_VALUE_BITMASK(type);
value >>= RELOC_VALUE_RIGHTSHIFT(type);
value &= mask;
if (RELOC_UNALIGNED(type)) {
char *ptr = (char *)where;
int i, size = RELOC_TARGET_SIZE(type)/8;
for (i = 0, tmp = 0; i < size; i++)
tmp = (tmp << 8) | ptr[i];
tmp &= ~mask;
tmp |= value;
for (i=0; i<size; i++)
ptr[i] = ((tmp >> (8*i)) & 0xff);
} else {
*where &= ~mask;
*where |= value;
}
return 0;
}
int
kobj_machdep(kobj_t ko, void *base, size_t size, bool load)
{
return 0;
}