summaryrefslogtreecommitdiffstats
path: root/cpukit/libdl/rtl-mdreloc-aarch64.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libdl/rtl-mdreloc-aarch64.c')
-rw-r--r--cpukit/libdl/rtl-mdreloc-aarch64.c592
1 files changed, 592 insertions, 0 deletions
diff --git a/cpukit/libdl/rtl-mdreloc-aarch64.c b/cpukit/libdl/rtl-mdreloc-aarch64.c
new file mode 100644
index 0000000000..1eb1d1e87d
--- /dev/null
+++ b/cpukit/libdl/rtl-mdreloc-aarch64.c
@@ -0,0 +1,592 @@
+/*
+ * Taken from NetBSD and stripped of the relocations not needed on RTEMS.
+ */
+
+/* $NetBSD: mdreloc.c,v 1.14 2020/06/16 21:01:30 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2014 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas of 3am Software Foundry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2014-2015 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Andrew Turner
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: mdreloc.c,v 1.14 2020/06/16 21:01:30 joerg Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/endian.h>
+
+#include <rtems/rtl/rtl.h>
+#include "rtl-elf.h"
+#include "rtl-error.h"
+#include <rtems/rtl/rtl-trace.h>
+#include <rtems/score/tls.h>
+
+#include "rtl-unwind.h"
+#include "rtl-unwind-dw2.h"
+
+struct tls_data {
+ size_t td_tlsindex;
+ Elf_Addr td_tlsoffs;
+};
+
+rtems_rtl_elf_rel_status
+rtems_rtl_elf_reloc_rela (
+ rtems_rtl_obj* obj,
+ const Elf_Rela* rela,
+ const rtems_rtl_obj_sect* sect,
+ const char* symname,
+ const Elf_Byte syminfo,
+ const Elf_Word symvalue,
+ const bool parsing
+);
+
+#define __BITS(hi,lo) ((~((~(Elf_Addr)0)<<((hi)+1)))&((~(Elf_Addr)0)<<(lo)))
+#define WIDTHMASK(w) (0xffffffffffffffffUL >> (64 - (w)))
+
+static inline bool
+checkoverflow(Elf_Addr addr, int bitwidth, Elf_Addr targetaddr,
+ const char *bitscale, void *where, Elf64_Addr off)
+{
+ const Elf_Addr mask = ~__BITS(bitwidth - 1, 0);
+
+ if (((addr & mask) != 0) && ((addr & mask) != mask)) {
+ printf("kobj_reloc: Relocation 0x%" PRIxPTR " too far from %p"
+ " (base+0x%jx) for %dbit%s\n",
+ (uintptr_t)targetaddr, where, off, bitwidth, bitscale);
+ return true;
+ }
+
+ return false;
+}
+
+static inline bool
+checkalign(Elf_Addr addr, int alignbyte, void *where, Elf64_Addr off)
+{
+ if ((addr & (alignbyte - 1)) != 0) {
+ printf("kobj_reloc: Relocation 0x%" PRIxPTR " unaligned at %p"
+ " (base+0x%jx). must be aligned %d\n",
+ (uintptr_t)addr, where, off, alignbyte);
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Set to 1 to allow untested relocations. If you tested one and it
+ * works or you fixed the relocation please remove the guard.
+ */
+#define ALLOW_UNTESTED_RELOCS 1
+
+static void*
+set_veneer(void* tramopline, Elf_Addr target)
+{
+ /*
+ * http://shell-storm.org/online/Online-Assembler-and-Disassembler/
+ *
+ * ldr x9, #8
+ * br x9
+ *
+ */
+ uint64_t* tramp = (uint64_t*) tramopline;
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ *tramp++ = 0xd61f012058000049;
+#else
+ *tramp++ = 0xd61f012058000049; /* not tested */
+#endif
+ *tramp++ = (uint64_t) target;
+ return tramp;
+}
+
+static size_t
+get_veneer_size(int type)
+{
+ (void) type;
+ return 16;
+}
+
+uint32_t rtems_rtl_obj_tramp_alignment (const rtems_rtl_obj* obj)
+{
+ (void) obj;
+ return sizeof(uint64_t);
+}
+
+size_t
+rtems_rtl_elf_relocate_tramp_max_size (void)
+{
+ return 16;
+}
+
+uint32_t
+rtems_rtl_elf_section_flags (const rtems_rtl_obj* obj,
+ const Elf_Shdr* shdr)
+{
+ return 0;
+}
+
+uint32_t
+rtems_rtl_elf_arch_parse_section (const rtems_rtl_obj* obj,
+ int section,
+ const char* name,
+ const Elf_Shdr* shdr,
+ const uint32_t flags)
+{
+ (void) obj;
+ (void) section;
+ (void) name;
+ (void) shdr;
+ return flags;
+}
+
+bool
+rtems_rtl_elf_arch_section_alloc (const rtems_rtl_obj* obj,
+ rtems_rtl_obj_sect* sect)
+{
+ (void) obj;
+ (void) sect;
+ return false;
+}
+
+bool
+rtems_rtl_elf_arch_section_free (const rtems_rtl_obj* obj,
+ rtems_rtl_obj_sect* sect)
+{
+ (void) obj;
+ (void) sect;
+ return false;
+}
+
+bool
+rtems_rtl_elf_rel_resolve_sym (Elf_Word type)
+{
+ return true;
+}
+
+rtems_rtl_elf_rel_status
+rtems_rtl_elf_reloc_rela (rtems_rtl_obj* obj,
+ const Elf_Rela* rela,
+ const rtems_rtl_obj_sect* sect,
+ const char* symname,
+ const Elf_Byte syminfo,
+ const Elf_Word symvalue,
+ const bool parsing)
+{
+ Elf64_Addr *where;
+ Elf32_Addr *where32;
+ Elf_Addr off = rela->r_offset;
+ Elf_Addr target;
+ Elf_Addr raddr;
+ uint32_t *insn, immhi, immlo, shift;
+
+ where = (Elf_Addr *)(sect->base + rela->r_offset);
+ where32 = (void *)where;
+
+ insn = (uint32_t *)where;
+
+ /*
+ * S - the address of the symbol
+ * A - the addend of the reolcation
+ * P - the address of the place being relocated (derived from r_offset)
+ * Page(expr) - the page address of the expression expr, defined as (expr & ~0xFFF).
+ * TPREL(expr) - the thread pointer offset of the expression expr
+ */
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_TYPE(NONE):
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) {
+ printf ("rtl: NONE %p in %s\n", where, rtems_rtl_obj_oname (obj));
+ }
+ break;
+
+ case R_TYPE(ABS64): /* word S + A */
+ case R_TYPE(GLOB_DAT): /* word S + A */
+ if (!parsing) {
+ target = (Elf_Addr)symvalue + rela->r_addend;
+
+ if (*where != target)
+ *where = target;
+
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: reloc 64/GLOB_DAT in %s --> %p in %s\n",
+ sect->name, (void *)(uintptr_t)*where,
+ rtems_rtl_obj_oname (obj));
+ }
+ break;
+
+ /*
+ * 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.
+ */
+ case R_TYPE(RELATIVE): /* Delta(S) + A */
+ if (!parsing) {
+ *where = (Elf_Addr)(uintptr_t)(sect->base + rela->r_addend);
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: reloc RELATIVE in %s --> %p in %s\n",
+ sect->name, (void *)(uintptr_t)*where,
+ rtems_rtl_obj_oname (obj));
+ }
+ break;
+
+ case R_TYPE(COPY):
+ /*
+ * These are deferred until all other relocations have
+ * been done. All we do here is make sure that the
+ * COPY relocation is not in a shared library. They
+ * are allowed only in executable files.
+ */
+ printf("rtl: reloc COPY (please report)\n");
+ break;
+
+ case R_AARCH64_ADD_TPREL_HI12: /* TPREL(S + A) */
+ case R_AARCH64_ADD_TPREL_LO12:
+ case R_AARCH64_ADD_TPREL_LO12_NC:
+ uint32_t of_check = 0;
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_AARCH64_ADD_TPREL_LO12:
+ of_check = 212;
+ /* fallthrough */
+ case R_AARCH64_ADD_TPREL_LO12_NC:
+ shift = 0;
+ break;
+ case R_AARCH64_ADD_TPREL_HI12:
+ of_check = 224;
+ shift = 12;
+ break;
+ default:
+ printf("illegal rtype: %" PRIu64 "\n", ELF_R_TYPE(rela->r_info));
+ break;
+ }
+
+ if (!parsing) {
+ target = (Elf_Addr)symvalue;
+ target >>= shift;
+ target &= WIDTHMASK(12);
+ if (of_check && target >= of_check) {
+ return rtems_rtl_elf_rel_failure;
+ }
+ *insn = htole32(
+ (le32toh(*insn) & ~__BITS(21,10)) | (target << 10));
+ }
+ break;
+
+ case R_AARCH64_ADD_ABS_LO12_NC: /* S + A */
+ case R_AARCH64_LDST8_ABS_LO12_NC:
+ case R_AARCH_LDST16_ABS_LO12_NC:
+ case R_AARCH_LDST32_ABS_LO12_NC:
+ case R_AARCH_LDST64_ABS_LO12_NC:
+ case R_AARCH64_LDST128_ABS_LO12_NC:
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_AARCH64_ADD_ABS_LO12_NC:
+ case R_AARCH64_LDST8_ABS_LO12_NC:
+ shift = 0;
+ break;
+ case R_AARCH_LDST16_ABS_LO12_NC:
+ shift = 1;
+ break;
+ case R_AARCH_LDST32_ABS_LO12_NC:
+ shift = 2;
+ break;
+ case R_AARCH_LDST64_ABS_LO12_NC:
+ shift = 3;
+ break;
+ case R_AARCH64_LDST128_ABS_LO12_NC:
+ shift = 4;
+ break;
+ default:
+ printf("illegal rtype: %" PRIu64 "\n", ELF_R_TYPE(rela->r_info));
+ break;
+ }
+
+ /*
+ * S + A
+ * e.g.) add x0,x0,#:lo12:<sym>+<addend>
+ * ldrb w0,[x0,#:lo12:<sym>+<addend>]
+ * ldrh w0,[x0,#:lo12:<sym>+<addend>]
+ * ldr w0,[x0,#:lo12:<sym>+<addend>]
+ * ldr x0,[x0,#:lo12:<sym>+<addend>]
+ */
+ if (!parsing) {
+ target = (Elf_Addr)symvalue + rela->r_addend;
+ if (checkalign(target, 1 << shift, where, off)) {
+ printf ("rtl: reloc checkalign failed in %s --> %p in %s\n",
+ sect->name, (void *)(uintptr_t)*where,
+ rtems_rtl_obj_oname (obj));
+ printf("ELF_R_TYPE is : %" PRIu64 "\n", ELF_R_TYPE(rela->r_info));
+ break;
+ }
+ target &= WIDTHMASK(12);
+ target >>= shift;
+ *insn = htole32(
+ (le32toh(*insn) & ~__BITS(21,10)) | (target << 10));
+ }
+ break;
+
+ case R_AARCH64_ADR_PREL_PG_HI21:
+ /*
+ * Page(S + A) - Page(P)
+ * e.g.) adrp x0,<sym>+<addend>
+ */
+ if (!parsing) {
+ target = (Elf_Addr)symvalue + rela->r_addend;
+ target = target >> 12;
+ raddr = target << 12;
+ target -= (uintptr_t)where >> 12;
+
+ if (checkoverflow(target, 21, raddr, " x 4k", where, off)) {
+ printf("]] %d\n", __LINE__);
+ return rtems_rtl_elf_rel_failure;
+ }
+
+ immlo = target & WIDTHMASK(2);
+ immhi = (target >> 2) & WIDTHMASK(19);
+ *insn = htole32((le32toh(*insn) &
+ ~(__BITS(30,29) | __BITS(23,5))) |
+ (immlo << 29) | (immhi << 5));
+ }
+ break;
+
+ case R_AARCH_JUMP26:
+ case R_AARCH_CALL26:
+ /*
+ * S + A - P
+ * e.g.) b <sym>+<addend>
+ * bl <sym>+<addend>
+ */
+ if (parsing && sect->base == 0) {
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: JUMP26/PC26/CALL tramp cache\n");
+ return rtems_rtl_elf_rel_tramp_cache;
+ }
+
+ raddr = (Elf_Addr)symvalue + rela->r_addend;
+ target = raddr - (uintptr_t)where;
+ if (checkalign(target, 4, where, off)) {
+ return rtems_rtl_elf_rel_failure;
+ }
+
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf (
+ "rtl: JUMP26/PC26/CALL: insn=%p where=%p target=%p raddr=%p parsing=%d\n",
+ insn, (void*) where, (void*)(uintptr_t) target, (void*)(uintptr_t) raddr,
+ parsing
+ );
+
+ target = (intptr_t)target >> 2;
+
+ if (((Elf_Sword)target > 0x1FFFFFF) || ((Elf_Sword)target < -0x2000000)) {
+ Elf_Word tramp_addr;
+ size_t tramp_size = get_veneer_size(ELF_R_TYPE(rela->r_info));
+
+ if (parsing) {
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: JUMP26/PC26/CALL tramp add\n");
+ return rtems_rtl_elf_rel_tramp_add;
+ }
+
+ if (!rtems_rtl_obj_has_tramp_space (obj, tramp_size)) {
+ rtems_rtl_set_error (EINVAL,
+ "%s: CALL/JUMP26: overflow: no tramp memory",
+ sect->name);
+ return rtems_rtl_elf_rel_failure;
+ }
+
+ tramp_addr = ((Elf_Addr)(uintptr_t) obj->tramp_brk) | (symvalue & 1);
+ obj->tramp_brk = set_veneer(obj->tramp_brk, symvalue);
+ target = tramp_addr + rela->r_addend - (uintptr_t)where;
+ target = (uintptr_t)target >> 2;
+ }
+
+ if (checkoverflow(target, 26, raddr, " word", where, off)) {
+ return rtems_rtl_elf_rel_failure;
+ }
+
+ if (!parsing) {
+ target &= WIDTHMASK(26);
+ *insn = htole32((le32toh(*insn) & ~__BITS(25,0)) | target);
+ }
+
+ break;
+
+ case R_AARCH64_PREL32:
+ /*
+ * S + A - P
+ * e.g.) 1: .word <sym>+<addend>-1b
+ */
+ if (!parsing) {
+ raddr = (Elf_Addr)symvalue + rela->r_addend;
+ target = raddr - (uintptr_t)where;
+ if (checkoverflow(target, 32, raddr, "", where, off)) {
+ printf("]] %d\n", __LINE__);
+ return rtems_rtl_elf_rel_failure;
+ }
+ *where32 = target;
+ }
+ break;
+
+ case R_TYPE(TLSDESC):
+ printf ("rtl: reloc TLSDESC in %s --> %p in %s\n",
+ sect->name, (void *)(uintptr_t)*where,
+ rtems_rtl_obj_oname (obj));
+ break;
+
+ case R_TLS_TYPE(TLS_DTPREL):
+ printf ("rtl: reloc TLS_DTPREL in %s --> %p in %s\n",
+ sect->name, (void *)(uintptr_t)*where,
+ rtems_rtl_obj_oname (obj));
+ break;
+ case R_TLS_TYPE(TLS_DTPMOD):
+ printf ("rtl: reloc TLS_DTPMOD in %s --> %p in %s\n",
+ sect->name, (void *)(uintptr_t)*where,
+ rtems_rtl_obj_oname (obj));
+ break;
+
+ case R_TLS_TYPE(TLS_TPREL):
+ printf ("rtl: reloc TLS_TPREL in %s --> %p in %s\n",
+ sect->name, (void *)(uintptr_t)*where,
+ rtems_rtl_obj_oname (obj));
+ break;
+
+ default:
+ printf ("rtl: Unsupported relocation type (%" PRIu64
+ ") in %s --> %p in %s\n",
+ ELF_R_TYPE(rela->r_info), sect->name, (void *)where,
+ rtems_rtl_obj_oname (obj));
+ return rtems_rtl_elf_rel_failure;
+ }
+
+ return rtems_rtl_elf_rel_no_error;
+}
+
+rtems_rtl_elf_rel_status
+rtems_rtl_elf_relocate_rela_tramp (rtems_rtl_obj* obj,
+ const Elf_Rela* rela,
+ const rtems_rtl_obj_sect* sect,
+ const char* symname,
+ const Elf_Byte syminfo,
+ const Elf_Word symvalue)
+{
+ return rtems_rtl_elf_reloc_rela (obj,
+ rela,
+ sect,
+ symname,
+ syminfo,
+ symvalue,
+ true);
+}
+
+rtems_rtl_elf_rel_status
+rtems_rtl_elf_relocate_rela (rtems_rtl_obj* obj,
+ const Elf_Rela* rela,
+ const rtems_rtl_obj_sect* sect,
+ const char* symname,
+ const Elf_Byte syminfo,
+ const Elf_Word symvalue)
+{
+ return rtems_rtl_elf_reloc_rela (obj,
+ rela,
+ sect,
+ symname,
+ syminfo,
+ symvalue,
+ false);
+}
+
+rtems_rtl_elf_rel_status
+rtems_rtl_elf_relocate_rel_tramp (rtems_rtl_obj* obj,
+ const Elf_Rel* rel,
+ const rtems_rtl_obj_sect* sect,
+ const char* symname,
+ const Elf_Byte syminfo,
+ const Elf_Word symvalue)
+{
+ rtems_rtl_set_error (EINVAL, "rela type record not supported");
+ return rtems_rtl_elf_rel_failure;
+}
+
+rtems_rtl_elf_rel_status
+rtems_rtl_elf_relocate_rel (rtems_rtl_obj* obj,
+ const Elf_Rel* rel,
+ const rtems_rtl_obj_sect* sect,
+ const char* symname,
+ const Elf_Byte syminfo,
+ const Elf_Word symvalue)
+{
+ rtems_rtl_set_error (EINVAL, "rela type record not supported");
+ return rtems_rtl_elf_rel_failure;
+}
+
+bool
+rtems_rtl_elf_unwind_parse (const rtems_rtl_obj* obj,
+ const char* name,
+ uint32_t flags) {
+ return rtems_rtl_elf_unwind_dw2_parse (obj, name, flags);
+}
+
+bool
+rtems_rtl_elf_unwind_register (rtems_rtl_obj* obj) {
+ return rtems_rtl_elf_unwind_dw2_register (obj);
+}
+
+bool
+rtems_rtl_elf_unwind_deregister (rtems_rtl_obj* obj) {
+ return rtems_rtl_elf_unwind_dw2_deregister (obj);
+}