* Copyright 2010, Ithamar R. Adema <ithamar.adema@team-embedded.nl>
* All rights reserved. Distributed under the terms of the MIT License.
*
* Copyright 2009, Johannes Wischert, johanneswi@gmail.com.
* All rights reserved. Distributed under the terms of the MIT License.
*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*
* Copyright 2002, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#ifdef _BOOT_MODE
#include <boot/arch.h>
#endif
#include <KernelExport.h>
#include <elf_priv.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
#ifdef TRACE_ARCH_ELF
static const char *kRelocations[] = {
"R_ARM_NONE",
"R_ARM_PC24",
"R_ARM_ABS32",
"R_ARM_REL32",
"R_ARM_LDR_PC_G0",
"R_ARM_ABS16",
"R_ARM_ABS12",
"R_ARM_THM_ABS5",
"R_ARM_ABS8",
"R_ARM_SBREL32",
"R_ARM_THM_CALL",
"R_ARM_THM_PC8",
"R_ARM_BREL_ADJ",
"R_ARM_TLS_DESC",
"R_ARM_THM_SWI8",
"R_ARM_XPC25",
"R_ARM_THM_XPC22",
"R_ARM_TLS_DTPMOD32",
"R_ARM_TLS_DTPOFF32",
"R_ARM_TLS_TPOFF32",
"R_ARM_COPY",
"R_ARM_GLOB_DAT",
"R_ARM_JUMP_SLOT",
"R_ARM_RELATIVE",
"R_ARM_GOTOFF32",
"R_ARM_BASE_PREL",
"R_ARM_GOT_BREL",
"R_ARM_PLT32",
"R_ARM_CALL",
"R_ARM_JUMP24",
"R_ARM_THM_JUMP24",
"R_ARM_BASE_ABS",
"R_ARM_ALU_PCREL_7_0",
"R_ARM_ALU_PCREL_15_8",
"R_ARM_ALU_PCREL_23_15",
"R_ARM_LDR_SBREL_11_0_NC",
"R_ARM_ALU_SBREL_19_12_NC",
"R_ARM_ALU_SBREL_27_20_CK",
"R_ARM_TARGET1",
"R_ARM_SBREL31",
"R_ARM_V4BX",
"R_ARM_TARGET2",
"R_ARM_PREL31",
"R_ARM_MOVW_ABS_NC",
"R_ARM_MOVT_ABS",
"R_ARM_MOVW_PREL_NC",
"R_ARM_MOVT_PREL",
"R_ARM_THM_MOVW_ABS_NC",
"R_ARM_THM_MOVT_ABS",
"R_ARM_THM_MOVW_PREL_NC",
"R_ARM_THM_MOVT_PREL",
"R_ARM_THM_JUMP19",
"R_ARM_THM_JUMP6",
"R_ARM_THM_ALU_PREL_11_0",
"R_ARM_THM_PC12",
"R_ARM_ABS32_NOI",
"R_ARM_REL32_NOI",
"R_ARM_ALU_PC_G0_NC",
"R_ARM_ALU_PC_G0",
"R_ARM_ALU_PC_G1_NC",
"R_ARM_ALU_PC_G1",
"R_ARM_ALU_PC_G2",
"R_ARM_LDR_PC_G1",
"R_ARM_LDR_PC_G2",
"R_ARM_LDRS_PC_G0",
"R_ARM_LDRS_PC_G1",
"R_ARM_LDRS_PC_G2",
"R_ARM_LDC_PC_G0",
"R_ARM_LDC_PC_G1",
"R_ARM_LDC_PC_G2",
"R_ARM_ALU_SB_G0_NC",
"R_ARM_ALU_SB_G0",
"R_ARM_ALU_SB_G1_NC",
"R_ARM_ALU_SB_G1",
"R_ARM_ALU_SB_G2",
"R_ARM_LDR_SB_G0",
"R_ARM_LDR_SB_G1",
"R_ARM_LDR_SB_G2",
"R_ARM_LDRS_SB_G0",
"R_ARM_LDRS_SB_G1",
"R_ARM_LDRS_SB_G2",
"R_ARM_LDC_SB_G0",
"R_ARM_LDC_SB_G1",
"R_ARM_LDC_SB_G2",
"R_ARM_MOVW_BREL_NC",
"R_ARM_MOVT_BREL",
"R_ARM_MOVW_BREL",
"R_ARM_THM_MOVW_BREL_NC",
"R_ARM_THM_MOVT_BREL",
"R_ARM_THM_MOVW_BREL",
"R_ARM_TLS_GOTDESC",
"R_ARM_TLS_CALL",
"R_ARM_TLS_DESCSEQ",
"R_ARM_THM_TLS_CALL",
"R_ARM_PLT32_ABS",
"R_ARM_GOT_ABS",
"R_ARM_GOT_PREL",
"R_ARM_GOT_BREL12",
"R_ARM_GOTOFF12",
"R_ARM_GOTRELAX",
"R_ARM_GNU_VTENTRY",
"R_ARM_GNU_VTINHERIT",
"R_ARM_THM_JUMP11",
"R_ARM_THM_JUMP8",
"R_ARM_TLS_GD32",
"R_ARM_TLS_LDM32",
"R_ARM_TLS_LDO32",
"R_ARM_TLS_IE32",
"R_ARM_TLS_LE32",
"R_ARM_TLS_LDO12",
"R_ARM_TLS_LE12",
"R_ARM_TLS_IE12GP",
};
#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_elf32_image *image, Elf32_Rel *rel,
int relLength)
#else
int
arch_elf_relocate_rel(struct elf_image_info *image,
struct elf_image_info *resolveImage, Elf32_Rel *rel, int relLength)
#endif
{
elf_addr S;
addr_t A;
addr_t P;
addr_t finalAddress;
addr_t *resolveAddress;
int i;
S = A = P = 0;
for (i = 0; i * (int)sizeof(Elf32_Rel) < relLength; i++) {
TRACE(("looking at rel type %s, offset 0x%lx\n",
kRelocations[ELF32_R_TYPE(rel[i].r_info)], rel[i].r_offset));
switch (ELF32_R_TYPE(rel[i].r_info)) {
case R_ARM_JMP_SLOT:
case R_ARM_GLOB_DAT:
case R_ARM_ABS32:
{
Elf32_Sym *symbol;
status_t status;
symbol = SYMBOL(image, ELF32_R_SYM(rel[i].r_info));
#ifdef _BOOT_MODE
status = boot_elf_resolve_symbol(image, symbol, &S);
#else
status = elf_resolve_symbol(image, symbol, resolveImage, &S);
#endif
if (status < B_OK) {
#ifndef _BOOT_MODE
TRACE(("failed relocating %s\n", SYMNAME(image, symbol)));
#endif
return B_OK;
}
#ifndef _BOOT_MODE
TRACE(("S %p (%s)\n", (void *)S, SYMNAME(image, symbol)));
#endif
}
}
switch (ELF32_R_TYPE(rel[i].r_info)) {
case R_ARM_ABS32:
case R_ARM_RELATIVE:
#ifndef _BOOT_MODE
A = *(addr_t *)(image->text_region.delta + rel[i].r_offset);
#else
A = boot_elf32_get_relocation(image->text_region.delta + rel[i].r_offset);
#endif
TRACE(("A %p\n", (void *)A));
break;
}
switch (ELF32_R_TYPE(rel[i].r_info)) {
case R_ARM_NONE:
continue;
case R_ARM_RELATIVE:
finalAddress = image->text_region.delta + A;
break;
case R_ARM_JMP_SLOT:
case R_ARM_GLOB_DAT:
finalAddress = S;
break;
case R_ARM_ABS32:
finalAddress = S + A;
break;
default:
dprintf("arch_elf_relocate_rel: unhandled relocation type %d\n",
ELF32_R_TYPE(rel[i].r_info));
return B_BAD_DATA;
}
resolveAddress = (addr_t *)(image->text_region.delta + rel[i].r_offset);
#ifndef _BOOT_MODE
if (!is_in_image(image, (addr_t)resolveAddress)) {
dprintf("arch_elf_relocate_rel: invalid offset %#lx\n",
rel[i].r_offset);
return B_BAD_ADDRESS;
}
*resolveAddress = finalAddress;
#else
boot_elf32_set_relocation((Elf32_Addr)resolveAddress, finalAddress);
#endif
TRACE(("-> offset %#lx = %#lx\n",
(image->text_region.delta + rel[i].r_offset), finalAddress));
}
return B_NO_ERROR;
}
static inline void
write_32(addr_t P, Elf32_Word value)
{
*(Elf32_Word*)P = value;
}
static inline void
write_16(addr_t P, Elf32_Word value)
{
*(Elf32_Half*)P = (Elf32_Half)value;
}
static inline bool
write_16_check(addr_t P, Elf32_Word value)
{
if ((value & 0xffff0000) && (~value & 0xffff8000))
return false;
*(Elf32_Half*)P = (Elf32_Half)value;
return true;
}
static inline bool
write_8(addr_t P, Elf32_Word value)
{
*(uint8 *)P = (uint8)value;
return true;
}
static inline bool
write_8_check(addr_t P, Elf32_Word value)
{
if ((value & 0xffffff00) && (~value & 0xffffff80))
return false;
*(uint8 *)P = (uint8)value;
return true;
}
#ifdef _BOOT_MODE
status_t
boot_arch_elf_relocate_rela(struct preloaded_elf32_image *image,
Elf32_Rela *rel, int rel_len)
#else
int
arch_elf_relocate_rela(struct elf_image_info *image,
struct elf_image_info *resolve_image, Elf32_Rela *rel, int rel_len)
#endif
{
int i;
Elf32_Sym *sym;
int vlErr;
elf_addr S = 0;
addr_t R = 0;
addr_t G = 0;
addr_t L = 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)
#warning ARM:define T correctly for thumb!!!
#define T 0
#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(Elf32_Rela) < rel_len; i++) {
#if CHATTY
dprintf("looking at rel type %d, offset 0x%lx, "
"sym 0x%lx, addend 0x%lx\n", ELF32_R_TYPE(rel[i].r_info),
rel[i].r_offset, ELF32_R_SYM(rel[i].r_info), rel[i].r_addend);
#endif
switch (ELF32_R_TYPE(rel[i].r_info)) {
#warning ARM:ADDOTHERREL
case R_ARM_GLOB_DAT:
sym = SYMBOL(image, ELF32_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 %d, offset 0x%lx, sym 0x%lx, "
"addend 0x%lx\n", __FUNCTION__, i,
ELF32_R_TYPE(rel[i].r_info), rel[i].r_offset,
ELF32_R_SYM(rel[i].r_info), rel[i].r_addend);
return vlErr;
}
break;
}
#warning ARM:ADDOTHERREL
switch (ELF32_R_TYPE(rel[i].r_info)) {
case R_ARM_GLOB_DAT:
write_32(P, (S + A) | T);
break;
case R_ARM_NONE:
break;
default:
dprintf("arch_elf_relocate_rela(): unhandled "
"relocation type %d!\n", ELF32_R_TYPE(rel[i].r_info));
return B_ERROR;
}
}
#warning ARM: FIXME!!!!!!!
return B_NO_ERROR;
}