/* * 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 #ifndef lint __RCSID("$NetBSD: mdreloc.c,v 1.14 2020/06/16 21:01:30 joerg Exp $"); #endif /* not lint */ #include #include #include #include #include #include #include #include #include "rtl-elf.h" #include "rtl-error.h" #include #include "rtl-unwind.h" typedef unsigned _Unwind_Word __attribute__((__mode__(__word__))); typedef _Unwind_Word _uw; 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%jx 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%jx 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; } 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). */ 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 *)*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)(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 *)*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_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: 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; default: printf("illegal rtype: %ld\n", ELF_R_TYPE(rela->r_info)); break; } /* * S + A * e.g.) add x0,x0,#:lo12:+ * ldrb w0,[x0,#:lo12:+] * ldrh w0,[x0,#:lo12:+] * ldr w0,[x0,#:lo12:+] * ldr x0,[x0,#:lo12:+] */ 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 *)*where, rtems_rtl_obj_oname (obj)); printf("ELF_R_TYPE is : %ld\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,+ */ 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)) { 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 + * bl + */ 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; } 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) 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 +-1b */ if (!parsing) { raddr = (Elf_Addr)symvalue + rela->r_addend; target = raddr - (uintptr_t)where; if (checkoverflow(target, 32, raddr, "", where, off)) { 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 *)*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 *)*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 *)*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 *)*where, rtems_rtl_obj_oname (obj)); break; default: printf ("rtl: Unsupported relocation type in %s --> %p in %s\n", 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) { /* * We location the EH sections in section flags. */ return false; } bool rtems_rtl_elf_unwind_register (rtems_rtl_obj* obj) { return true; } bool rtems_rtl_elf_unwind_deregister (rtems_rtl_obj* obj) { obj->loader = NULL; return true; } /* An exception index table entry. */ typedef struct __EIT_entry { _uw fnoffset; _uw content; } __EIT_entry; /* The exception index table location in the base module */ extern __EIT_entry __exidx_start; extern __EIT_entry __exidx_end; /* * A weak reference is in libgcc, provide a real version and provide a way to * manage loaded modules. * * Passed in the return address and a reference to the number of records * found. We set the start of the exidx data and the number of records. */ _Unwind_Ptr __gnu_Unwind_Find_exidx (_Unwind_Ptr return_address, int* nrec) __attribute__ ((__noinline__, __used__, __noclone__)); _Unwind_Ptr __gnu_Unwind_Find_exidx (_Unwind_Ptr return_address, int* nrec) { rtems_rtl_data* rtl; rtems_chain_node* node; __EIT_entry* exidx_start = &__exidx_start; __EIT_entry* exidx_end = &__exidx_end; rtl = rtems_rtl_lock (); node = rtems_chain_first (&rtl->objects); while (!rtems_chain_is_tail (&rtl->objects, node)) { rtems_rtl_obj* obj = (rtems_rtl_obj*) node; if (rtems_rtl_obj_text_inside (obj, (void*) return_address)) { exidx_start = (__EIT_entry*) obj->eh_base; exidx_end = (__EIT_entry*) (obj->eh_base + obj->eh_size); break; } node = rtems_chain_next (node); } rtems_rtl_unlock (); *nrec = exidx_end - exidx_start; return (_Unwind_Ptr) exidx_start; }