996625ec58
There is a problem with the current elf loader for risc-v: when a pair of PCREL_HI20 / LO12 relocations are encountered, it is assumed that these will follow each other immediately, as follows: label: auipc a0, %pcrel_hi(symbol) // R_RISCV_PCREL_HI20 load/store a0, %pcrel_lo(label)(a0) // R_RISCV_PCREL_LO12_I/S With this assumption, the hi/lo relocations are both done when a hi20 relocation entry is encountered, first to the current instruction (addr) and to the next instruction (addr + 4). However, this assumption is wrong. There is nothing in the elf relocation specification[1] that mandates this. Thus, the hi/lo relocation always needs to first fixup the hi-part, and when the lo-part is encountered, it needs to find the corresponding hi relocation entry, via the given "label". This necessitates (re-)visiting the relocation entries for the current section as well as looking for "label" in the symbol table. The NuttX elf loader does not allow such operations to be done in the machine specific part, so this patch fixes the relocation issue by introducing an architecture specific cache for the hi20 relocation and symbol table entries. When a lo12 relocation is encountered, the cache can be consulted to find the hi20 part. [1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc
800 lines
20 KiB
C
800 lines
20 KiB
C
/****************************************************************************
|
|
* libs/libc/machine/arm64/arch_elf.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
#include <endian.h>
|
|
|
|
#include <nuttx/compiler.h>
|
|
#include <nuttx/bits.h>
|
|
#include <nuttx/elf.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* For triggering a fault on purpose (reserved) */
|
|
|
|
#define FAULT_BRK_IMM 0x100
|
|
|
|
/* BRK instruction encoding
|
|
* The #imm16 value should be placed at bits[20:5] within BRK ins
|
|
*/
|
|
|
|
#define AARCH64_BREAK_MON 0xd4200000
|
|
|
|
/* BRK instruction for provoking a fault on purpose
|
|
* Unlike kgdb, #imm16 value with unallocated handler is used for faulting.
|
|
*/
|
|
|
|
#define AARCH64_BREAK_FAULT (AARCH64_BREAK_MON | (FAULT_BRK_IMM << 5))
|
|
|
|
#define ADR_IMM_HILOSPLIT 2
|
|
#define ADR_IMM_SIZE (2 * 1024 * 1024)
|
|
#define ADR_IMM_LOMASK ((1 << ADR_IMM_HILOSPLIT) - 1)
|
|
#define ADR_IMM_HIMASK ((ADR_IMM_SIZE >> ADR_IMM_HILOSPLIT) - 1)
|
|
#define ADR_IMM_LOSHIFT 29
|
|
#define ADR_IMM_HISHIFT 5
|
|
|
|
#define INSN_SF_BIT BIT(31)
|
|
#define INSN_N_BIT BIT(22)
|
|
#define INSN_LSL_12 BIT(22)
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
enum reloc_op_e
|
|
{
|
|
RELOC_OP_NONE,
|
|
RELOC_OP_ABS,
|
|
RELOC_OP_PREL,
|
|
RELOC_OP_PAGE,
|
|
};
|
|
|
|
enum insn_movw_imm_type_e
|
|
{
|
|
INSN_IMM_MOVNZ,
|
|
INSN_IMM_MOVKZ,
|
|
};
|
|
|
|
enum insn_imm_type_e
|
|
{
|
|
INSN_IMM_ADR,
|
|
INSN_IMM_26,
|
|
INSN_IMM_19,
|
|
INSN_IMM_16,
|
|
INSN_IMM_14,
|
|
INSN_IMM_12,
|
|
INSN_IMM_N,
|
|
INSN_IMM_MAX
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
static uint32_t
|
|
aarch64_insn_encode_immediate(enum insn_imm_type_e type,
|
|
uint32_t insn, uint64_t imm)
|
|
{
|
|
uint32_t immlo;
|
|
uint32_t immhi;
|
|
uint32_t mask;
|
|
int shift;
|
|
|
|
if (insn == AARCH64_BREAK_FAULT)
|
|
{
|
|
return AARCH64_BREAK_FAULT;
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case INSN_IMM_ADR:
|
|
{
|
|
shift = 0;
|
|
immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT;
|
|
imm >>= ADR_IMM_HILOSPLIT;
|
|
immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT;
|
|
imm = immlo | immhi;
|
|
mask = (ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) |
|
|
(ADR_IMM_HIMASK << ADR_IMM_HISHIFT);
|
|
}
|
|
break;
|
|
|
|
case INSN_IMM_26:
|
|
{
|
|
mask = BIT(26) - 1;
|
|
shift = 0;
|
|
}
|
|
break;
|
|
|
|
case INSN_IMM_19:
|
|
{
|
|
mask = BIT(19) - 1;
|
|
shift = 5;
|
|
}
|
|
break;
|
|
|
|
case INSN_IMM_16:
|
|
{
|
|
mask = BIT(16) - 1;
|
|
shift = 5;
|
|
}
|
|
break;
|
|
|
|
case INSN_IMM_14:
|
|
{
|
|
mask = BIT(14) - 1;
|
|
shift = 5;
|
|
}
|
|
break;
|
|
|
|
case INSN_IMM_12:
|
|
{
|
|
mask = BIT(12) - 1;
|
|
shift = 10;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
berr("unknown immediate encoding %d\n", type);
|
|
|
|
return AARCH64_BREAK_FAULT;
|
|
}
|
|
}
|
|
|
|
/* Update the immediate field. */
|
|
|
|
insn &= ~(mask << shift);
|
|
insn |= (imm & mask) << shift;
|
|
|
|
return insn;
|
|
}
|
|
|
|
static uint64_t do_reloc(enum reloc_op_e op,
|
|
uintptr_t place, uint64_t val)
|
|
{
|
|
switch (op)
|
|
{
|
|
case RELOC_OP_ABS:
|
|
return val;
|
|
case RELOC_OP_PREL:
|
|
return val - (uint64_t)place;
|
|
case RELOC_OP_PAGE:
|
|
return (val & ~0xfff) - ((uint64_t)place & ~0xfff);
|
|
case RELOC_OP_NONE:
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int reloc_data(enum reloc_op_e op, uintptr_t place,
|
|
uint64_t val, int len)
|
|
{
|
|
int64_t sval = do_reloc(op, place, val);
|
|
|
|
/* The ELF psABI for AArch64 documents the 16-bit and 32-bit place
|
|
* relative and absolute relocations as having a range of [-2^15, 2^16)
|
|
* or [-2^31, 2^32), respectively. However, in order to be able to
|
|
* detect overflows reliably, we have to choose whether we interpret
|
|
* such quantities as signed or as unsigned, and stick with it.
|
|
* The way we organize our address space requires a signed
|
|
* interpretation of 32-bit relative references, so let's use that
|
|
* for all R_AARCH64_PRELxx relocations. This means our upper
|
|
* bound for overflow detection should be Sxx_MAX rather than Uxx_MAX.
|
|
*/
|
|
|
|
switch (len)
|
|
{
|
|
case 16:
|
|
{
|
|
*(int16_t *)place = sval;
|
|
switch (op)
|
|
{
|
|
case RELOC_OP_ABS:
|
|
{
|
|
if (sval < 0 || sval > UINT16_MAX)
|
|
{
|
|
return -ERANGE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case RELOC_OP_PREL:
|
|
{
|
|
if (sval < INT16_MIN || sval > INT16_MAX)
|
|
{
|
|
return -ERANGE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
berr("Invalid 16-bit data relocation (%d)\n", op);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 32:
|
|
{
|
|
*(int32_t *)place = sval;
|
|
switch (op)
|
|
{
|
|
case RELOC_OP_ABS:
|
|
{
|
|
if (sval < 0 || sval > UINT32_MAX)
|
|
{
|
|
return -ERANGE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case RELOC_OP_PREL:
|
|
{
|
|
if (sval < INT32_MIN || sval > INT32_MAX)
|
|
{
|
|
return -ERANGE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
berr("Invalid 32-bit data relocation (%d)\n", op);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 64:
|
|
{
|
|
*(int64_t *)place = sval;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
berr("Invalid length (%d) for data relocation\n", len);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int reloc_insn_movw(enum reloc_op_e op, uintptr_t place,
|
|
uint64_t val, int lsb,
|
|
enum insn_movw_imm_type_e imm_type)
|
|
{
|
|
uint32_t insn = htole32(*(uint32_t *)place);
|
|
uint64_t imm;
|
|
int64_t sval;
|
|
|
|
sval = do_reloc(op, place, val);
|
|
imm = sval >> lsb;
|
|
|
|
if (imm_type == INSN_IMM_MOVNZ)
|
|
{
|
|
/* For signed MOVW relocations, we have to manipulate the
|
|
* instruction encoding depending on whether or not the
|
|
* immediate is less than zero.
|
|
*/
|
|
|
|
insn &= ~(3 << 29);
|
|
if (sval >= 0)
|
|
{
|
|
/* >=0: Set the instruction to MOVZ (opcode 10b). */
|
|
|
|
insn |= 2 << 29;
|
|
}
|
|
else
|
|
{
|
|
/* <0: Set the instruction to MOVN (opcode 00b).
|
|
* Since we've masked the opcode already, we
|
|
* don't need to do anything other than
|
|
* inverting the new immediate field.
|
|
*/
|
|
|
|
imm = ~imm;
|
|
}
|
|
}
|
|
|
|
/* Update the instruction with the new encoding. */
|
|
|
|
insn = aarch64_insn_encode_immediate(INSN_IMM_16, insn, imm);
|
|
*(uint32_t *)place = le32toh(insn);
|
|
|
|
if (imm > UINT16_MAX)
|
|
{
|
|
return -ERANGE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int reloc_insn_imm(enum reloc_op_e op, uintptr_t place,
|
|
uint64_t val, int lsb, int len,
|
|
enum insn_imm_type_e imm_type)
|
|
{
|
|
int64_t sval;
|
|
uint64_t imm;
|
|
uint64_t imm_mask;
|
|
uint32_t insn = le32toh(*(uint32_t *)place);
|
|
|
|
/* Calculate the relocation value. */
|
|
|
|
sval = do_reloc(op, place, val);
|
|
sval >>= lsb;
|
|
|
|
/* Extract the value bits and shift them to bit 0. */
|
|
|
|
imm_mask = (BIT(lsb + len) - 1) >> lsb;
|
|
imm = sval & imm_mask;
|
|
|
|
/* Update the instruction's immediate field. */
|
|
|
|
insn = aarch64_insn_encode_immediate(imm_type, insn, imm);
|
|
*(uint32_t *)place = htole32(insn);
|
|
|
|
/* Extract the upper value bits (including the sign bit) and
|
|
* shift them to bit 0.
|
|
*/
|
|
|
|
sval = (int64_t)(sval & ~(imm_mask >> 1)) >> (len - 1);
|
|
|
|
/* Overflow has occurred if the upper bits are not all equal to
|
|
* the sign bit of the value.
|
|
*/
|
|
|
|
if ((uint64_t)(sval + 1) >= 2)
|
|
{
|
|
return -ERANGE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: up_checkarch
|
|
*
|
|
* Description:
|
|
* Given the ELF header in 'hdr', verify that the ELF file is appropriate
|
|
* for the current, configured architecture. Every architecture that uses
|
|
* the ELF loader must provide this function.
|
|
*
|
|
* Input Parameters:
|
|
* hdr - The ELF header read from the ELF file.
|
|
*
|
|
* Returned Value:
|
|
* True if the architecture supports this ELF file.
|
|
*
|
|
****************************************************************************/
|
|
|
|
bool up_checkarch(const Elf64_Ehdr *ehdr)
|
|
{
|
|
/* Make sure it's an ARM executable */
|
|
|
|
if (ehdr->e_machine != EM_AARCH64)
|
|
{
|
|
berr("ERROR: Not for AARCH64: e_machine=%04x\n", ehdr->e_machine);
|
|
return false;
|
|
}
|
|
|
|
/* Make sure that 64-bit objects are supported */
|
|
|
|
if (ehdr->e_ident[EI_CLASS] != ELFCLASS64)
|
|
{
|
|
berr("ERROR: Need 64-bit objects: e_ident[EI_CLASS]=%02x\n",
|
|
ehdr->e_ident[EI_CLASS]);
|
|
return false;
|
|
}
|
|
|
|
/* Verify endian-ness */
|
|
|
|
#ifdef CONFIG_ENDIAN_BIG
|
|
if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB)
|
|
#else
|
|
if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
|
|
#endif
|
|
{
|
|
berr("ERROR: Wrong endian-ness: e_ident[EI_DATA]=%02x\n",
|
|
ehdr->e_ident[EI_DATA]);
|
|
return false;
|
|
}
|
|
|
|
/* TODO: Check ABI here. */
|
|
|
|
return true;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: up_relocate and up_relocateadd
|
|
*
|
|
* Description:
|
|
* Perform an architecture-specific ELF relocation. Every architecture
|
|
* that uses the ELF loader must provide this function.
|
|
*
|
|
* Input Parameters:
|
|
* rel - The relocation type
|
|
* sym - The ELF symbol structure containing the fully resolved value.
|
|
* There are a few relocation types for a few architectures that do
|
|
* not require symbol information. For those, this value will be
|
|
* NULL. Implementations of these functions must be able to handle
|
|
* that case.
|
|
* addr - The address that requires the relocation.
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) if the relocation was successful. Otherwise, a negated errno
|
|
* value indicating the cause of the relocation failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int up_relocate(const Elf64_Rel *rel, const Elf64_Sym *sym, uintptr_t addr,
|
|
void *arch_data)
|
|
{
|
|
berr("ERROR: REL relocation not supported\n");
|
|
return -ENOSYS;
|
|
}
|
|
|
|
int up_relocateadd(const Elf64_Rela *rel, const Elf64_Sym *sym,
|
|
uintptr_t addr, void *arch_data)
|
|
{
|
|
bool overflow_check = true;
|
|
uint64_t val;
|
|
int ret = 0;
|
|
|
|
/* addr corresponds to P in the AArch64 ELF document. */
|
|
|
|
/* val corresponds to (S + A) in the AArch64 ELF document. */
|
|
|
|
val = sym->st_value + rel->r_addend;
|
|
|
|
/* Handle the relocation by relocation type */
|
|
|
|
switch (ELF64_R_TYPE(rel->r_info))
|
|
{
|
|
case R_AARCH64_NONE:
|
|
{
|
|
/* No relocation */
|
|
}
|
|
break;
|
|
|
|
/* Data relocations */
|
|
|
|
case R_AARCH64_ABS64:
|
|
{
|
|
overflow_check = false;
|
|
ret = reloc_data(RELOC_OP_ABS, addr, val, 64);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_ABS32:
|
|
{
|
|
ret = reloc_data(RELOC_OP_ABS, addr, val, 32);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_ABS16:
|
|
{
|
|
ret = reloc_data(RELOC_OP_ABS, addr, val, 16);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_PREL64:
|
|
{
|
|
overflow_check = false;
|
|
ret = reloc_data(RELOC_OP_PREL, addr, val, 64);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_PREL32:
|
|
{
|
|
ret = reloc_data(RELOC_OP_PREL, addr, val, 32);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_PREL16:
|
|
{
|
|
ret = reloc_data(RELOC_OP_PREL, addr, val, 16);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_UABS_G0_NC:
|
|
{
|
|
overflow_check = false;
|
|
}
|
|
|
|
/* fallthrough */
|
|
|
|
case R_AARCH64_MOVW_UABS_G0:
|
|
{
|
|
ret = reloc_insn_movw(RELOC_OP_ABS, addr, val, 0,
|
|
INSN_IMM_MOVKZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_UABS_G1_NC:
|
|
{
|
|
overflow_check = false;
|
|
}
|
|
|
|
/* fallthrough */
|
|
|
|
case R_AARCH64_MOVW_UABS_G1:
|
|
{
|
|
ret = reloc_insn_movw(RELOC_OP_ABS, addr, val, 16,
|
|
INSN_IMM_MOVKZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_UABS_G2_NC:
|
|
{
|
|
overflow_check = false;
|
|
}
|
|
|
|
/* fallthrough */
|
|
|
|
case R_AARCH64_MOVW_UABS_G2:
|
|
{
|
|
ret = reloc_insn_movw(RELOC_OP_ABS, addr, val, 32,
|
|
INSN_IMM_MOVKZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_UABS_G3:
|
|
{
|
|
/* We're using the top bits so we can't overflow. */
|
|
|
|
overflow_check = false;
|
|
ret = reloc_insn_movw(RELOC_OP_ABS, addr, val, 48,
|
|
INSN_IMM_MOVKZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_SABS_G0:
|
|
{
|
|
ret = reloc_insn_movw(RELOC_OP_ABS, addr, val, 0,
|
|
INSN_IMM_MOVNZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_SABS_G1:
|
|
{
|
|
ret = reloc_insn_movw(RELOC_OP_ABS, addr, val, 16,
|
|
INSN_IMM_MOVNZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_SABS_G2:
|
|
{
|
|
ret = reloc_insn_movw(RELOC_OP_ABS, addr, val, 32,
|
|
INSN_IMM_MOVNZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_PREL_G0_NC:
|
|
{
|
|
overflow_check = false;
|
|
ret = reloc_insn_movw(RELOC_OP_PREL, addr, val, 0,
|
|
INSN_IMM_MOVKZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_PREL_G0:
|
|
{
|
|
ret = reloc_insn_movw(RELOC_OP_PREL, addr, val, 0,
|
|
INSN_IMM_MOVNZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_PREL_G1_NC:
|
|
{
|
|
overflow_check = false;
|
|
ret = reloc_insn_movw(RELOC_OP_PREL, addr, val, 16,
|
|
INSN_IMM_MOVKZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_PREL_G1:
|
|
{
|
|
ret = reloc_insn_movw(RELOC_OP_PREL, addr, val, 16,
|
|
INSN_IMM_MOVNZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_PREL_G2_NC:
|
|
{
|
|
overflow_check = false;
|
|
ret = reloc_insn_movw(RELOC_OP_PREL, addr, val, 32,
|
|
INSN_IMM_MOVKZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_PREL_G2:
|
|
{
|
|
ret = reloc_insn_movw(RELOC_OP_PREL, addr, val, 32,
|
|
INSN_IMM_MOVNZ);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_MOVW_PREL_G3:
|
|
{
|
|
/* We're using the top bits so we can't overflow. */
|
|
|
|
overflow_check = false;
|
|
ret = reloc_insn_movw(RELOC_OP_PREL, addr, val, 48,
|
|
INSN_IMM_MOVNZ);
|
|
}
|
|
break;
|
|
|
|
/* Immediate instruction relocations. */
|
|
|
|
case R_AARCH64_LD_PREL_LO19:
|
|
{
|
|
ret = reloc_insn_imm(RELOC_OP_PREL, addr, val, 2, 19,
|
|
INSN_IMM_19);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_ADR_PREL_LO21:
|
|
{
|
|
ret = reloc_insn_imm(RELOC_OP_PREL, addr, val, 0, 21,
|
|
INSN_IMM_ADR);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_ADR_PREL_PG_HI21_NC:
|
|
{
|
|
overflow_check = false;
|
|
}
|
|
|
|
/* fallthrough */
|
|
|
|
case R_AARCH64_ADR_PREL_PG_HI21:
|
|
{
|
|
if (((uint64_t)addr & 0xfff) < 0xff8)
|
|
{
|
|
ret = reloc_insn_imm(RELOC_OP_PAGE, addr, val, 12, 21,
|
|
INSN_IMM_ADR);
|
|
}
|
|
else
|
|
{
|
|
uint32_t insn;
|
|
|
|
/* patch ADRP to ADR if it is in range */
|
|
|
|
ret = reloc_insn_imm(RELOC_OP_PREL, addr, val & ~0xfff, 0, 21,
|
|
INSN_IMM_ADR);
|
|
if (ret == 0)
|
|
{
|
|
insn = le32toh(*(uint32_t *)addr);
|
|
insn &= ~BIT(31);
|
|
*(uint32_t *)addr = htole32(insn);
|
|
}
|
|
else
|
|
{
|
|
berr("Out of range for ADR\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_ADD_ABS_LO12_NC:
|
|
case R_AARCH64_LDST8_ABS_LO12_NC:
|
|
{
|
|
overflow_check = false;
|
|
ret = reloc_insn_imm(RELOC_OP_ABS, addr, val, 0, 12,
|
|
INSN_IMM_12);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_LDST16_ABS_LO12_NC:
|
|
{
|
|
overflow_check = false;
|
|
ret = reloc_insn_imm(RELOC_OP_ABS, addr, val, 1, 11,
|
|
INSN_IMM_12);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_LDST32_ABS_LO12_NC:
|
|
{
|
|
overflow_check = false;
|
|
ret = reloc_insn_imm(RELOC_OP_ABS, addr, val, 2, 10,
|
|
INSN_IMM_12);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_LDST64_ABS_LO12_NC:
|
|
{
|
|
overflow_check = false;
|
|
ret = reloc_insn_imm(RELOC_OP_ABS, addr, val, 3, 9,
|
|
INSN_IMM_12);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_LDST128_ABS_LO12_NC:
|
|
{
|
|
overflow_check = false;
|
|
ret = reloc_insn_imm(RELOC_OP_ABS, addr, val, 4, 8,
|
|
INSN_IMM_12);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_TSTBR14:
|
|
{
|
|
ret = reloc_insn_imm(RELOC_OP_PREL, addr, val, 2, 14,
|
|
INSN_IMM_14);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_CONDBR19:
|
|
{
|
|
ret = reloc_insn_imm(RELOC_OP_PREL, addr, val, 2, 19,
|
|
INSN_IMM_19);
|
|
}
|
|
break;
|
|
|
|
case R_AARCH64_JUMP26:
|
|
case R_AARCH64_CALL26:
|
|
{
|
|
ret = reloc_insn_imm(RELOC_OP_PREL, addr, val, 2, 26,
|
|
INSN_IMM_26);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
berr("ERROR: Unsupported relocation: %"PRIu64"\n",
|
|
ELF64_R_TYPE(rel->r_info));
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (overflow_check && ret == -ERANGE)
|
|
{
|
|
goto overflow;
|
|
}
|
|
|
|
return OK;
|
|
|
|
overflow:
|
|
berr("ERROR: overflow in relocation type %"PRIu64" val %"PRIu64"\n",
|
|
ELF64_R_TYPE(rel->r_info), val);
|
|
return -ENOEXEC;
|
|
}
|