9b583cc539
Add support for the relocation type R_ARM_ARM_THM_CALL which is produced for the ARM Thumb BL and BLX (branch immediate) instructions. These instructions are used for non-static functions like void test1(void) { } void main(void) { test1(); } Without support for this relocation, test1() has to be static. Signed-off-by: Bjarki Arge Andreasen <bjarki@arge-andreasen.me>
90 lines
2.6 KiB
C
90 lines
2.6 KiB
C
/*
|
|
* 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;
|
|
}
|
|
}
|