#include <KernelExport.h>
#include <elf_priv.h>
#include <boot/elf.h>
#include <arch/elf.h>
#ifdef TRACE_ARCH_ELF
# define TRACE(x) dprintf x
# define CHATTY 1
#else
# define TRACE(x) ;
# define CHATTY 0
#endif
#ifndef _BOOT_MODE
static bool
is_in_image(struct elf_image_info *image, addr_t address)
{
return (address >= image->text_region.start
&& address < image->text_region.start + image->text_region.size)
|| (address >= image->data_region.start
&& address < image->data_region.start + image->data_region.size);
}
#endif
#ifdef _BOOT_MODE
status_t
boot_arch_elf_relocate_rel(struct preloaded_elf64_image *image, Elf64_Rel *rel,
int rel_len)
#else
int
arch_elf_relocate_rel(struct elf_image_info *image,
struct elf_image_info *resolve_image, Elf64_Rel *rel, int rel_len)
#endif
{
return B_NO_ERROR;
}
static inline void
write_word32(addr_t P, Elf64_Word value)
{
*(Elf64_Word*)P = value;
}
static inline void
write_word64(addr_t P, Elf64_Xword value)
{
*(Elf64_Xword*)P = value;
}
static inline void
write_hi30(addr_t P, Elf64_Word value)
{
*(Elf64_Word*)P |= value >> 2;
}
static inline void
write_hi22(addr_t P, Elf64_Word value)
{
*(Elf64_Word*)P |= value >> 10;
}
static inline void
write_lo10(addr_t P, Elf64_Word value)
{
*(Elf64_Word*)P |= value & 0x3ff;
}
static inline void
write_hh22(addr_t P, Elf64_Xword value)
{
*(Elf64_Word*)P |= value >> 42;
}
static inline void
write_hm10(addr_t P, Elf64_Xword value)
{
*(Elf64_Word*)P |= (value >> 32) & 0x3ff;
}
#ifdef _BOOT_MODE
status_t
boot_arch_elf_relocate_rela(struct preloaded_elf64_image *image,
Elf64_Rela *rel, int rel_len)
#else
int
arch_elf_relocate_rela(struct elf_image_info *image,
struct elf_image_info *resolve_image, Elf64_Rela *rel, int rel_len)
#endif
{
int i;
Elf64_Sym *sym;
int vlErr;
Elf64_Addr S = 0;
#define P ((addr_t)(image->text_region.delta + rel[i].r_offset))
#define A ((addr_t)rel[i].r_addend)
#define B (image->text_region.delta)
#define REQUIRE_GOT \
if (G == 0) { \
dprintf("arch_elf_relocate_rela(): Failed to get GOT address!\n"); \
return B_ERROR; \
}
#define REQUIRE_PLT \
if (L == 0) { \
dprintf("arch_elf_relocate_rela(): Failed to get PLT address!\n"); \
return B_ERROR; \
}
for (i = 0; i * (int)sizeof(Elf64_Rela) < rel_len; i++) {
#if CHATTY
dprintf("looking at rel type %" PRIu64 ", offset 0x%lx, sym 0x%lx, "
"addend 0x%lx\n", ELF64_R_TYPE(rel[i].r_info), rel[i].r_offset,
ELF64_R_SYM(rel[i].r_info), rel[i].r_addend);
#endif
switch (ELF64_R_TYPE(rel[i].r_info)) {
case R_SPARC_WDISP30:
case R_SPARC_HI22:
case R_SPARC_LO10:
case R_SPARC_HH22:
case R_SPARC_LM22:
case R_SPARC_HM10:
case R_SPARC_GLOB_DAT:
case R_SPARC_JMP_SLOT:
case R_SPARC_64:
sym = SYMBOL(image, ELF64_R_SYM(rel[i].r_info));
#ifdef _BOOT_MODE
vlErr = boot_elf_resolve_symbol(image, sym, &S);
#else
vlErr = elf_resolve_symbol(image, sym, resolve_image, &S);
#endif
if (vlErr < 0) {
dprintf("%s(): Failed to relocate "
"entry index %d, rel type %" PRIu64 ", offset 0x%lx, "
"sym 0x%lx, addend 0x%lx\n", __FUNCTION__, i,
ELF64_R_TYPE(rel[i].r_info),
rel[i].r_offset, ELF64_R_SYM(rel[i].r_info),
rel[i].r_addend);
return vlErr;
}
break;
}
switch (ELF64_R_TYPE(rel[i].r_info)) {
case R_SPARC_WDISP30:
{
write_hi30(P, S + A - P);
}
case R_SPARC_HI22:
case R_SPARC_LM22:
{
write_hi22(P, S + A);
break;
}
case R_SPARC_LO10:
{
write_lo10(P, S + A);
break;
}
case R_SPARC_HH22:
{
write_hh22(P, S + A);
break;
}
case R_SPARC_HM10:
{
write_hm10(P, S + A);
break;
}
case R_SPARC_GLOB_DAT:
{
write_word64(P, S + A);
break;
}
case R_SPARC_JMP_SLOT:
{
addr_t jumpOffset = S - (P + 8);
if ((jumpOffset & 0xc0000000) != 0
&& (~jumpOffset & 0xc0000000) != 0) {
dprintf("arch_elf_relocate_rela(): R_SPARC_JMP_SLOT: "
"Offsets > 30 bit currently not supported!\n");
dprintf("jumpOffset: %p\n", (void*)jumpOffset);
return B_ERROR;
} else {
uint32* instructions = (uint32*)P;
instructions[0] = 0x01000000;
instructions[1] = 0x8210000f;
instructions[2] = 0x40000000 | ((jumpOffset >> 2) & 0x3fffffff);
instructions[3] = 0x9e100001;
}
break;
}
case R_SPARC_RELATIVE:
{
write_word32(P, B + A);
break;
}
case R_SPARC_64:
{
write_word64(P, S + A);
break;
}
default:
dprintf("arch_elf_relocate_rela: unhandled relocation type %"
PRIu64 "\n", ELF64_R_TYPE(rel[i].r_info));
return B_ERROR;
}
}
return B_OK;
}