arch/arm64: support relocate for aarch64

Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
This commit is contained in:
dongjiuzhu1 2023-10-16 20:27:15 +08:00 committed by Xiang Xiao
parent 0c805ca0a9
commit 489bd15271
4 changed files with 1020 additions and 0 deletions

217
arch/arm64/include/elf.h Normal file
View File

@ -0,0 +1,217 @@
/****************************************************************************
* arch/arm64/include/elf.h
*
* 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.
*
****************************************************************************/
/* Reference: "ELF for the ARM 64-bit Architecture," ARM IHI 0056B, current
* through AArch64 ABI release 1.0, May 22, 2013, ARM Limited.
*/
#ifndef __ARCH_ARM64_INCLUDE_ELF_H
#define __ARCH_ARM64_INCLUDE_ELF_H
/****************************************************************************
* Pre-processor Prototypes
****************************************************************************/
/* 4.2.1 ELF Identification. Should have:
*
* e_machine = EM_AARCH64
* e_ident[EI_CLASS] = ELFCLASS64
* e_ident[EI_DATA] = ELFDATA2LSB (little endian) or
* ELFDATA2MSB (big endian)
*/
#define EM_ARCH EM_AARCH64
/* e_flags: there are no processor-specific flags so this field
* shall contain zero.
*/
/* Table 4-2, Processor specific section types */
#define SHT_AARCH64_ATTRIBUTES 0x70000003 /* Object file compatibility attributes */
/* 4.6.3 Relocation codes
*
* S (when used on its own) is the address of the symbol.
* A is the addend for the relocation.
* P is the address of the place being relocated (derived from r_offset).
* X is the result of a relocation operation, before any masking or
* bit-selection operation is applied
* Page(expr) is the page address of the expression expr, defined as
* (expr & ~0xFFF). (This applies even if the machine page size supported
* by the platform has a different value.)
* GOT is the address of the Global Offset Table, the table of code and data
* addresses to be resolved at dynamic link time. The GOT and each entry in
* it must be 64-bit aligned.
* GDAT(S+A) represents a 64-bit entry in the GOT for address S+A. The entry
* will be relocated at run time with relocation R_AARCH64_GLOB_DAT(S+A).
* G(expr) is the address of the GOT entry for the expression expr.
* Delta(S) if S is a normal symbol, resolves to the difference between the
* static link address of S and the execution address of S. If S is the
* null symbol (ELF symbol index 0), resolves to the difference between
* the static link address of P and the execution address of P.
* Indirect(expr) represents the result of calling expr as a function. The
* result is the return value from the function that is returned in r0.
* The arguments passed to the function are defined by the platform ABI.
* [msb:lsb] is a bit-mask operation representing the selection of bits in
* a value. The bits selected range from lsb up to msb inclusive. For
* example, bits [3:0] represents the bits under the mask 0x0000000F.
* When range checking is applied to a value, it is applied before the
* masking operation is performed.
*/
#define R_AARCH64_NONE 0 /* Miscellaneous No relocation */
#define R_AARCH64_ABS64 257 /* Direct 64 bit. */
#define R_AARCH64_ABS32 258 /* Direct 32 bit. */
#define R_AARCH64_ABS16 259 /* Direct 16-bit. */
#define R_AARCH64_PREL64 260 /* PC-relative 64-bit. */
#define R_AARCH64_PREL32 261 /* PC-relative 32-bit. */
#define R_AARCH64_PREL16 262 /* PC-relative 16-bit. */
#define R_AARCH64_MOVW_UABS_G0 263 /* Dir. MOVZ imm. from bits 15:0. */
#define R_AARCH64_MOVW_UABS_G0_NC 264 /* Likewise for MOVK; no check. */
#define R_AARCH64_MOVW_UABS_G1 265 /* Dir. MOVZ imm. from bits 31:16. */
#define R_AARCH64_MOVW_UABS_G1_NC 266 /* Likewise for MOVK; no check. */
#define R_AARCH64_MOVW_UABS_G2 267 /* Dir. MOVZ imm. from bits 47:32. */
#define R_AARCH64_MOVW_UABS_G2_NC 268 /* Likewise for MOVK; no check. */
#define R_AARCH64_MOVW_UABS_G3 269 /* Dir. MOV{K,Z} imm. from 63:48. */
#define R_AARCH64_MOVW_SABS_G0 270 /* Dir. MOV{N,Z} imm. from 15:0. */
#define R_AARCH64_MOVW_SABS_G1 271 /* Dir. MOV{N,Z} imm. from 31:16. */
#define R_AARCH64_MOVW_SABS_G2 272 /* Dir. MOV{N,Z} imm. from 47:32. */
#define R_AARCH64_LD_PREL_LO19 273 /* PC-rel. LD imm. from bits 20:2. */
#define R_AARCH64_ADR_PREL_LO21 274 /* PC-rel. ADR imm. from bits 20:0. */
#define R_AARCH64_ADR_PREL_PG_HI21 275 /* Page-rel. ADRP imm. from 32:12. */
#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 /* Likewise; no overflow check. */
#define R_AARCH64_ADD_ABS_LO12_NC 277 /* Dir. ADD imm. from bits 11:0. */
#define R_AARCH64_LDST8_ABS_LO12_NC 278 /* Likewise for LD/ST; no check. */
#define R_AARCH64_TSTBR14 279 /* PC-rel. TBZ/TBNZ imm. from 15:2. */
#define R_AARCH64_CONDBR19 280 /* PC-rel. cond. br. imm. from 20:2. */
#define R_AARCH64_JUMP26 282 /* PC-rel. B imm. from bits 27:2. */
#define R_AARCH64_CALL26 283 /* Likewise for CALL. */
#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* Dir. ADD imm. from bits 11:1. */
#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* Likewise for bits 11:2. */
#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* Likewise for bits 11:3. */
#define R_AARCH64_MOVW_PREL_G0 287 /* PC-rel. MOV{N,Z} imm. from 15:0. */
#define R_AARCH64_MOVW_PREL_G0_NC 288 /* Likewise for MOVK; no check. */
#define R_AARCH64_MOVW_PREL_G1 289 /* PC-rel. MOV{N,Z} imm. from 31:16. */
#define R_AARCH64_MOVW_PREL_G1_NC 290 /* Likewise for MOVK; no check. */
#define R_AARCH64_MOVW_PREL_G2 291 /* PC-rel. MOV{N,Z} imm. from 47:32. */
#define R_AARCH64_MOVW_PREL_G2_NC 292 /* Likewise for MOVK; no check. */
#define R_AARCH64_MOVW_PREL_G3 293 /* PC-rel. MOV{N,Z} imm. from 63:48. */
#define R_AARCH64_LDST128_ABS_LO12_NC 299 /* Dir. ADD imm. from bits 11:4. */
#define R_AARCH64_MOVW_GOTOFF_G0 300 /* GOT-rel. off. MOV{N,Z} imm. 15:0. */
#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 /* Likewise for MOVK; no check. */
#define R_AARCH64_MOVW_GOTOFF_G1 302 /* GOT-rel. o. MOV{N,Z} imm. 31:16. */
#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 /* Likewise for MOVK; no check. */
#define R_AARCH64_MOVW_GOTOFF_G2 304 /* GOT-rel. o. MOV{N,Z} imm. 47:32. */
#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 /* Likewise for MOVK; no check. */
#define R_AARCH64_MOVW_GOTOFF_G3 306 /* GOT-rel. o. MOV{N,Z} imm. 63:48. */
#define R_AARCH64_GOTREL64 307 /* GOT-relative 64-bit. */
#define R_AARCH64_GOTREL32 308 /* GOT-relative 32-bit. */
#define R_AARCH64_GOT_LD_PREL19 309 /* PC-rel. GOT off. load imm. 20:2. */
#define R_AARCH64_LD64_GOTOFF_LO15 310 /* GOT-rel. off. LD/ST imm. 14:3. */
#define R_AARCH64_ADR_GOT_PAGE 311 /* P-page-rel. GOT off. ADRP 32:12. */
#define R_AARCH64_LD64_GOT_LO12_NC 312 /* Dir. GOT off. LD/ST imm. 11:3. */
#define R_AARCH64_LD64_GOTPAGE_LO15 313 /* GOT-page-rel. GOT off. LD/ST 14:3 */
#define R_AARCH64_TLSGD_ADR_PREL21 512 /* PC-relative ADR imm. 20:0. */
#define R_AARCH64_TLSGD_ADR_PAGE21 513 /* page-rel. ADRP imm. 32:12. */
#define R_AARCH64_TLSGD_ADD_LO12_NC 514 /* direct ADD imm. from 11:0. */
#define R_AARCH64_TLSGD_MOVW_G1 515 /* GOT-rel. MOV{N,Z} 31:16. */
#define R_AARCH64_TLSGD_MOVW_G0_NC 516 /* GOT-rel. MOVK imm. 15:0. */
#define R_AARCH64_TLSLD_ADR_PREL21 517 /* Like 512; local dynamic model. */
#define R_AARCH64_TLSLD_ADR_PAGE21 518 /* Like 513; local dynamic model. */
#define R_AARCH64_TLSLD_ADD_LO12_NC 519 /* Like 514; local dynamic model. */
#define R_AARCH64_TLSLD_MOVW_G1 520 /* Like 515; local dynamic model. */
#define R_AARCH64_TLSLD_MOVW_G0_NC 521 /* Like 516; local dynamic model. */
#define R_AARCH64_TLSLD_LD_PREL19 522 /* TLS PC-rel. load imm. 20:2. */
#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 /* TLS DTP-rel. MOV{N,Z} 47:32. */
#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 /* TLS DTP-rel. MOV{N,Z} 31:16. */
#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 /* Likewise; MOVK; no check. */
#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 /* TLS DTP-rel. MOV{N,Z} 15:0. */
#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 /* Likewise; MOVK; no check. */
#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 /* DTP-rel. ADD imm. from 23:12. */
#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 /* DTP-rel. ADD imm. from 11:0. */
#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 /* Likewise; no ovfl. check. */
#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 /* DTP-rel. LD/ST imm. 11:0. */
#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 /* Likewise; no check. */
#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 /* DTP-rel. LD/ST imm. 11:1. */
#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 /* Likewise; no check. */
#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 /* DTP-rel. LD/ST imm. 11:2. */
#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 /* Likewise; no check. */
#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 /* DTP-rel. LD/ST imm. 11:3. */
#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 /* Likewise; no check. */
#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 /* GOT-rel. MOV{N,Z} 31:16. */
#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 /* GOT-rel. MOVK 15:0. */
#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 /* Page-rel. ADRP 32:12. */
#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* Direct LD off. 11:3. */
#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 /* PC-rel. load imm. 20:2. */
#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 /* TLS TP-rel. MOV{N,Z} 47:32. */
#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 /* TLS TP-rel. MOV{N,Z} 31:16. */
#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 /* Likewise; MOVK; no check. */
#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 /* TLS TP-rel. MOV{N,Z} 15:0. */
#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 /* Likewise; MOVK; no check. */
#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 /* TP-rel. ADD imm. 23:12. */
#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 /* TP-rel. ADD imm. 11:0. */
#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 /* Likewise; no ovfl. check. */
#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 /* TP-rel. LD/ST off. 11:0. */
#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 /* Likewise; no ovfl. check. */
#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 /* TP-rel. LD/ST off. 11:1. */
#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 /* Likewise; no check. */
#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 /* TP-rel. LD/ST off. 11:2. */
#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 /* Likewise; no check. */
#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 /* TP-rel. LD/ST off. 11:3. */
#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 /* Likewise; no check. */
#define R_AARCH64_TLSDESC_LD_PREL19 560 /* PC-rel. load immediate 20:2. */
#define R_AARCH64_TLSDESC_ADR_PREL21 561 /* PC-rel. ADR immediate 20:0. */
#define R_AARCH64_TLSDESC_ADR_PAGE21 562 /* Page-rel. ADRP imm. 32:12. */
#define R_AARCH64_TLSDESC_LD64_LO12 563 /* Direct LD off. from 11:3. */
#define R_AARCH64_TLSDESC_ADD_LO12 564 /* Direct ADD imm. from 11:0. */
#define R_AARCH64_TLSDESC_OFF_G1 565 /* GOT-rel. MOV{N,Z} imm. 31:16. */
#define R_AARCH64_TLSDESC_OFF_G0_NC 566 /* GOT-rel. MOVK imm. 15:0; no ck. */
#define R_AARCH64_TLSDESC_LDR 567 /* Relax LDR. */
#define R_AARCH64_TLSDESC_ADD 568 /* Relax ADD. */
#define R_AARCH64_TLSDESC_CALL 569 /* Relax BLR. */
#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 /* TP-rel. LD/ST off. 11:4. */
#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 /* Likewise; no check. */
#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 /* DTP-rel. LD/ST imm. 11:4. */
#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 /* Likewise; no check. */
#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */
#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */
#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */
#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */
#define R_AARCH64_TLS_DTPMOD 1028 /* Module number, 64 bit. */
#define R_AARCH64_TLS_DTPREL 1029 /* Module-relative offset, 64 bit. */
#define R_AARCH64_TLS_TPREL 1030 /* TP-relative offset, 64 bit. */
#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */
#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */
/* 5.1 Program Header */
#define PT_AARCH64_ARCHEXT 0x70000000 /* Reserved for architecture compatibility information */
#define PT_AARCH64_UNWIND 0x70000001 /* Reserved for exception unwinding tables */
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Data
****************************************************************************/
#endif /* __ARCH_ARM64_INCLUDE_ELF_H */

View File

@ -82,6 +82,7 @@
#define EM_V850 87 /* NEC v850 */
#define EM_M32R 88 /* Renesas M32R */
#define EM_XTENSA 94 /* Tensilica Xtensa */
#define EM_AARCH64 183 /* ARM-64 Architecture */
#define EM_RISCV 243 /* RISC-V */
#define EM_ALPHA 0x9026
#define EM_CYGNUS_V850 0x9080

View File

@ -18,6 +18,10 @@
#
############################################################################
ifeq ($(CONFIG_LIBC_ARCH_ELF_64BIT),y)
CSRCS += arch_elf.c
endif
ifeq ($(CONFIG_ARM64_MEMCHR),y)
ASRCS += arch_memchr.S
endif

View File

@ -0,0 +1,798 @@
/****************************************************************************
* 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)
{
berr("ERROR: REL relocation not supported\n");
return -ENOSYS;
}
int up_relocateadd(const Elf64_Rela *rel, const Elf64_Sym *sym,
uintptr_t addr)
{
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;
}