blob: e3c137e49f2af7f202c331bf31036a39891f9f48 [file] [log] [blame]
/*
* Copyright (c) 2023 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/llext/elf.h>
#include <zephyr/llext/llext.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
LOG_MODULE_REGISTER(elf, CONFIG_LLEXT_LOG_LEVEL);
#define ARM_BL_BLX_UPPER_S_BIT BIT(10)
#define ARM_BL_BLX_ADDEND_OFFSET 0
#define ARM_BL_BLX_ADDEND_SIZE 11
#define ARM_BL_BLX_ADDEND_MASK 0x7FF
#define ARM_BL_BLX_HDR_MASK 0xF800
#define ARM_BL_BLX_LOWER_T1T2_BIT BIT(12)
static int32_t arm_bl_blx_decode_addend(uintptr_t opaddr)
{
uint16_t upper = *((uint16_t *)opaddr);
uint16_t lower = *(((uint16_t *)opaddr) + 1);
int32_t addend = upper & ARM_BL_BLX_UPPER_S_BIT ? UINT32_MAX : 0;
addend <<= ARM_BL_BLX_ADDEND_SIZE;
addend |= upper & ARM_BL_BLX_ADDEND_MASK;
addend <<= ARM_BL_BLX_ADDEND_SIZE;
addend |= lower & ARM_BL_BLX_ADDEND_MASK;
return lower & ARM_BL_BLX_LOWER_T1T2_BIT ? addend << 1 : addend << 2;
}
static void arm_bl_blx_encode_addend(uintptr_t opaddr, int32_t addend)
{
uint16_t upper = *((uint16_t *)opaddr);
uint16_t lower = *(((uint16_t *)opaddr) + 1);
addend = upper & ARM_BL_BLX_UPPER_S_BIT ? addend >> 1 : addend >> 2;
upper &= ARM_BL_BLX_HDR_MASK;
lower &= ARM_BL_BLX_HDR_MASK;
upper |= (addend >> ARM_BL_BLX_ADDEND_SIZE) & ARM_BL_BLX_ADDEND_MASK;
lower |= addend & ARM_BL_BLX_ADDEND_MASK;
*((uint16_t *)opaddr) = upper;
*(((uint16_t *)opaddr) + 1) = lower;
}
/**
* @brief Architecture specific function for relocating partially linked (static) elf
*
* Elf files contain a series of relocations described in a section. These relocation
* instructions are architecture specific and each architecture supporting extensions
* must implement this.
*
* The relocation codes for arm are well documented
* https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst#relocation
*/
void arch_elf_relocate(elf_rela_t *rel, uintptr_t opaddr, uintptr_t opval)
{
elf_word reloc_type = ELF32_R_TYPE(rel->r_info);
switch (reloc_type) {
case R_ARM_ABS32:
/* Add the addend stored at opaddr to opval */
opval += *((uint32_t *)opaddr);
/* Update the absolute address of a load/store instruction */
*((uint32_t *)opaddr) = (uint32_t)opval;
break;
case R_ARM_THM_CALL:
/* Decode the initial addend */
int32_t addend = arm_bl_blx_decode_addend(opaddr);
/* Calculate and add the branch offset (addend) */
addend += ((int32_t)opval) - ((int32_t)opaddr);
/* Encode the addend */
arm_bl_blx_encode_addend(opaddr, addend);
break;
default:
LOG_DBG("Unsupported ARM elf relocation type %d at address %lx",
reloc_type, opaddr);
break;
}
}