forked from Imagelibrary/rtems
- Load symbols before allocation. - Parse reloc records and place any reloc recs in a cache to use while the allocator is locked. - Relocate symbols after section allocation. - Split section loading into allocation/locating and loading. - Update all arch back-ends with a new reloc interface to control tramp handling. - Add `-a` and `-t` to the object list shell command. Closes #3741
548 lines
16 KiB
C
548 lines
16 KiB
C
/*
|
|
* Taken from NetBSD and stripped of the relocations not needed on RTEMS.
|
|
*/
|
|
|
|
/* $NetBSD: ppc_reloc.c,v 1.44 2010/01/13 20:17:22 christos Exp $ */
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <rtems/rtl/rtl.h>
|
|
#include "rtl-bit-alloc.h"
|
|
#include "rtl-elf.h"
|
|
#include "rtl-error.h"
|
|
#include <rtems/rtl/rtl-trace.h>
|
|
#include "rtl-unwind.h"
|
|
#include "rtl-unwind-dw2.h"
|
|
|
|
#define ha(x) ((((u_int32_t)(x) & 0x8000) ? \
|
|
((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16)
|
|
#define l(x) ((u_int32_t)(x) & 0xffff)
|
|
|
|
/*
|
|
* SDATA allocator.
|
|
*/
|
|
static rtems_rtl_bit_alloc* sdata;
|
|
|
|
static Elf_Addr
|
|
get_sda_base (void)
|
|
{
|
|
uint32_t sda_base;
|
|
__asm__ volatile (" mr %0, 13\n" : "=r" (sda_base));
|
|
return sda_base;
|
|
}
|
|
|
|
/*
|
|
* Access the variables via asm statements to avoid any fix up issues
|
|
* generated by the C compiler which thinks they belong in the .sdata
|
|
* section.
|
|
*/
|
|
|
|
#define GET_ADDR(_l, _v) \
|
|
__asm__ volatile (" lis %0, " #_l "@h\n" \
|
|
" ori %0, %0, " #_l "@l\n" : "=r" (_v))
|
|
|
|
static void*
|
|
get_sdata_start (void)
|
|
{
|
|
#if _ARCH_PPC64
|
|
return NULL;
|
|
#else
|
|
Elf_Addr addr;
|
|
GET_ADDR(__SDATA_START__, addr);
|
|
return (void*) addr;
|
|
#endif
|
|
}
|
|
|
|
#if !_ARCH_PPC64
|
|
static size_t
|
|
get_sdata_sbss_size (void)
|
|
{
|
|
Elf_Addr sdata_begin;
|
|
Elf_Addr sbss_end;
|
|
GET_ADDR(bsp_section_sdata_begin, sdata_begin);
|
|
GET_ADDR(bsp_section_sbss_end, sbss_end);
|
|
return sbss_end - sdata_begin;
|
|
}
|
|
|
|
static size_t
|
|
get_sdata_libdl_size (void)
|
|
{
|
|
Elf_Addr begin;
|
|
Elf_Addr end;
|
|
GET_ADDR(bsp_section_sdata_libdl_begin, begin);
|
|
GET_ADDR(bsp_section_sdata_libdl_end, end);
|
|
return end - begin;
|
|
}
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
#if !_ARCH_PPC64
|
|
struct {
|
|
const char* label;
|
|
size_t len;
|
|
} prefix[] = {
|
|
#define SEPARATED_PREFIX(_p) { _p, sizeof (_p) - 1 }
|
|
SEPARATED_PREFIX (".sdata"),
|
|
SEPARATED_PREFIX (".sbss"),
|
|
};
|
|
const size_t prefixes = sizeof (prefix) / sizeof (prefix[0]);
|
|
size_t p;
|
|
for (p = 0; p < prefixes; ++p)
|
|
{
|
|
if (strncmp (name, prefix[p].label, prefix[p].len) == 0)
|
|
return flags | RTEMS_RTL_OBJ_SECT_ARCH_ALLOC;
|
|
}
|
|
#endif
|
|
return flags;
|
|
}
|
|
|
|
bool
|
|
rtems_rtl_elf_arch_section_alloc (const rtems_rtl_obj* obj,
|
|
rtems_rtl_obj_sect* sect)
|
|
{
|
|
#if _ARCH_PPC64
|
|
rtems_rtl_set_error (ENOMEM, ".sdata no supported by ABI");
|
|
return false;
|
|
#else
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
|
|
printf ("rtl: section: arch: alloc: name=%s size=%zu flags=%08" PRIx32 \
|
|
" order=%i link=%d info=%d\n",
|
|
sect->name, sect->size, sect->flags, sect->load_order,
|
|
sect->link, sect->info);
|
|
|
|
if (sdata == NULL)
|
|
{
|
|
sdata = rtems_rtl_bit_alloc_open (get_sdata_start (),
|
|
get_sdata_libdl_size (),
|
|
sizeof (uint32_t),
|
|
get_sdata_sbss_size ());
|
|
if (sdata == NULL)
|
|
{
|
|
rtems_rtl_set_error (ENOMEM, "no memory for sdata allocator");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
sect->base = rtems_rtl_bit_alloc_balloc (sdata, sect->size);
|
|
if (sect->base == NULL)
|
|
{
|
|
rtems_rtl_set_error (ENOMEM, "no .sdata memory: %s", sect->name);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
rtems_rtl_elf_arch_section_free (const rtems_rtl_obj* obj,
|
|
rtems_rtl_obj_sect* sect)
|
|
{
|
|
#if !_ARCH_PPC64
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
|
|
printf ("rtl: section: arch: free: name=%s size=%zu\n", sect->name, sect->size);
|
|
if (sdata != NULL)
|
|
rtems_rtl_bit_alloc_bfree (sdata, sect->base, sect->size);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
rtems_rtl_elf_rel_resolve_sym (Elf_Word type)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
size_t
|
|
rtems_rtl_elf_relocate_tramp_max_size (void)
|
|
{
|
|
/*
|
|
* We have 4 instructions and each instruction is 32bits.
|
|
*/
|
|
return 4 * 4;
|
|
}
|
|
|
|
static void*
|
|
set_veneer (void* tramopline, Elf_Addr target)
|
|
{
|
|
/*
|
|
* http://shell-storm.org/online/Online-Assembler-and-Disassembler/
|
|
*
|
|
* lis 12,0x1234
|
|
* ori 12,12,0x5678
|
|
* mtctr 12
|
|
* bctr
|
|
*/
|
|
#if COMPILE_ASM
|
|
asm volatile (" lis 12,0x1234\n" \
|
|
" ori 12,12,0x5678\n" \
|
|
" mtctr 12\n" \
|
|
" bctr\n");
|
|
#endif
|
|
uint32_t* tramp = (uint32_t*) tramopline;
|
|
*tramp++ = 0x3d800000 | (target >> 16);
|
|
*tramp++ = 0x618c0000 | (target & 0xffff);
|
|
*tramp++ = 0x7d8903a6;
|
|
*tramp++ = 0x4e800420;
|
|
return tramp;
|
|
}
|
|
|
|
static size_t
|
|
get_veneer_size (int type)
|
|
{
|
|
(void) type;
|
|
return rtems_rtl_elf_relocate_tramp_max_size ();
|
|
}
|
|
|
|
/**
|
|
* The offsets in the reloc words.
|
|
*/
|
|
#define REL_R_OFFSET (0)
|
|
#define REL_R_INFO (1)
|
|
#define REL_R_ADDEND (2)
|
|
|
|
static 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)
|
|
{
|
|
Elf_Addr* where;
|
|
Elf_Word tmp;
|
|
uint32_t mask = 0;
|
|
uint32_t bits = 0;
|
|
bool needs_tramp = false;
|
|
|
|
where = (Elf_Addr *)(sect->base + rela->r_offset);
|
|
switch (ELF_R_TYPE(rela->r_info)) {
|
|
case R_TYPE(NONE):
|
|
break;
|
|
|
|
case R_TYPE(32):
|
|
/*
|
|
* value:1; Field: word32; Expression: S + A
|
|
*/
|
|
if (!parsing) {
|
|
*where = symvalue + rela->r_addend;
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
|
|
printf ("rtl: ADDR32 %p @ %p in %s\n",
|
|
(void *)*(where), where, rtems_rtl_obj_oname (obj));
|
|
}
|
|
break;
|
|
|
|
case R_TYPE(14):
|
|
/*
|
|
* value:7; Field: low14*; Expression: (S + A) >> 2
|
|
*/
|
|
case R_TYPE(24):
|
|
/*
|
|
* value:2; Field: low24*; Expression: (S + A) >> 2
|
|
*/
|
|
if (ELF_R_TYPE(rela->r_info) == R_TYPE(14)) {
|
|
bits = 14;
|
|
mask = 0xfffc;
|
|
} else {
|
|
bits = 24;
|
|
mask = 0x3fffffc;
|
|
}
|
|
|
|
if (parsing && sect->base == 0) {
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
|
|
printf ("rtl: ADDR14/ADDR24 tramp cache\n");
|
|
return rtems_rtl_elf_rel_tramp_cache;
|
|
}
|
|
|
|
tmp = (symvalue + rela->r_addend) >> 2;
|
|
if (tmp > ((1<<bits) - 1 )) {
|
|
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: ADDR14/ADDR24 tramp add\n");
|
|
return rtems_rtl_elf_rel_tramp_add;
|
|
}
|
|
if (!rtems_rtl_obj_has_tramp_space (obj, tramp_size)) {
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
|
|
printf ("rtl: ADDR14/ADDR24 no tramp slot: %s\n", rtems_rtl_obj_oname (obj));
|
|
rtems_rtl_set_error (ENOMEM, "%s: tramp: no slot: ADDR14/ADDR24", sect->name);
|
|
return rtems_rtl_elf_rel_failure;
|
|
}
|
|
needs_tramp = true;
|
|
tramp_addr = (Elf_Addr) obj->tramp_brk;
|
|
obj->tramp_brk = set_veneer(obj->tramp_brk,
|
|
symvalue + rela->r_addend);
|
|
tmp = *where;
|
|
tmp &= ~mask;
|
|
tmp |= (tramp_addr + rela->r_addend) & mask;
|
|
}
|
|
else {
|
|
tmp = *where;
|
|
tmp &= ~mask;
|
|
tmp |= (symvalue + rela->r_addend) & mask;
|
|
}
|
|
|
|
if (!parsing) {
|
|
*where = tmp;
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
|
|
printf ("rtl: ADDR14/ADDR24%s %p @ %p in %s\n",
|
|
needs_tramp ? "(tramp)" : "",
|
|
(void *)*where, where, rtems_rtl_obj_oname (obj));
|
|
}
|
|
break;
|
|
|
|
case R_TYPE(16_HA):
|
|
/*
|
|
* value:6; Field:half16; Expression: #ha(S+A)
|
|
*/
|
|
if (!parsing) {
|
|
tmp = symvalue + rela->r_addend;
|
|
*(uint16_t *)where = (((tmp >> 16) + ((tmp & 0x8000) ? 1: 0)) & 0xffff);
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
|
|
printf ("rtl: 16_HA %p @ %p in %s\n",
|
|
(void *)*(where), where, rtems_rtl_obj_oname (obj));
|
|
}
|
|
break;
|
|
|
|
case R_TYPE(16_HI):
|
|
/*
|
|
* value:5; Field:half16; Expression: #hi(S+A)
|
|
*/
|
|
if (!parsing) {
|
|
*(uint16_t *)where = ((symvalue + rela->r_addend) >> 16) & 0xffff;
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
|
|
printf ("rtl: 16_HI %p @ %p in %s\n",
|
|
(void *)*where, where, rtems_rtl_obj_oname (obj));
|
|
}
|
|
break;
|
|
case R_TYPE(16_LO):
|
|
/*
|
|
* value:4; Field:half16; Expression: #lo(S+A)
|
|
*/
|
|
if (!parsing) {
|
|
*(uint16_t *)where = (symvalue + (rela->r_addend)) & 0xffff;
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
|
|
printf ("rtl: 16_LO %p @ %p in %s\n",
|
|
(void *)*where, where, rtems_rtl_obj_oname (obj));
|
|
}
|
|
break;
|
|
|
|
case R_TYPE(REL14):
|
|
/*
|
|
* value:11; Field:low14*; Expression:(S+A-P)>>2
|
|
*/
|
|
case R_TYPE(REL24):
|
|
/*
|
|
* value:10; Field:low24*; Expression:(S+A-P)>>2
|
|
*/
|
|
if (ELF_R_TYPE(rela->r_info) == R_TYPE(REL24)) {
|
|
mask = 0x3fffffc;
|
|
bits = 24;
|
|
}
|
|
else if (ELF_R_TYPE(rela->r_info) == R_TYPE(REL14)) {
|
|
mask = 0xfffc;
|
|
bits = 14;
|
|
}
|
|
|
|
if (parsing && sect->base == 0) {
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
|
|
printf ("rtl: REL24/REL14 tramp cache\n");
|
|
return rtems_rtl_elf_rel_tramp_cache;
|
|
}
|
|
|
|
tmp =((int) (symvalue + rela->r_addend - (Elf_Addr)where)) >> 2;
|
|
if (((Elf_Sword)tmp > ((1<<(bits-1)) - 1)) ||
|
|
((Elf_Sword)tmp < -(1<<(bits-1)))) {
|
|
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: REL24/REL14 tramp add\n");
|
|
return rtems_rtl_elf_rel_tramp_add;
|
|
}
|
|
if (!rtems_rtl_obj_has_tramp_space (obj, tramp_size)) {
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
|
|
printf ("rtl: REL24/REL14 no tramp slot: %s\n", rtems_rtl_obj_oname (obj));
|
|
rtems_rtl_set_error (ENOMEM, "%s: tramp: no slot: REL24/REL14", sect->name);
|
|
return rtems_rtl_elf_rel_failure;
|
|
}
|
|
needs_tramp = true;
|
|
tramp_addr = (Elf_Addr) obj->tramp_brk;
|
|
obj->tramp_brk = set_veneer(obj->tramp_brk,
|
|
symvalue + rela->r_addend);
|
|
tmp = *where;
|
|
tmp &= ~mask;
|
|
tmp |= (tramp_addr + rela->r_addend - (Elf_Addr)where) & mask;
|
|
}
|
|
else
|
|
{
|
|
tmp = *where;
|
|
tmp &= ~mask;
|
|
tmp |= (symvalue + rela->r_addend - (Elf_Addr)where) & mask;
|
|
}
|
|
|
|
if (!parsing) {
|
|
*where = tmp;
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
|
|
printf ("rtl: REL24/REL14%s %p @ %p in %s\n",
|
|
needs_tramp ? "(tramp)" : "",
|
|
(void *)*where, where, rtems_rtl_obj_oname (obj));
|
|
}
|
|
break;
|
|
|
|
case R_TYPE(REL32):
|
|
/*
|
|
* value:26; Field:word32*; Expression:S+A-P
|
|
*/
|
|
if (!parsing) {
|
|
*where = symvalue + rela->r_addend - (Elf_Addr)where;
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
|
|
printf ("rtl: REL32 %p @ %p in %s\n",
|
|
(void *)*where, where, rtems_rtl_obj_oname (obj));
|
|
}
|
|
break;
|
|
|
|
case R_TYPE(SDAREL16):
|
|
/*
|
|
* A sign-extended 16 bit value relative to _SDA_BASE_, for use with
|
|
* small data items.
|
|
*/
|
|
if (!parsing) {
|
|
Elf_Addr sda_base = get_sda_base ();
|
|
mask = 0xffff;
|
|
tmp = *((Elf32_Half*) where);
|
|
tmp &= ~mask;
|
|
tmp |= (symvalue + rela->r_addend - sda_base) & mask;
|
|
*((Elf32_Half*) where) = tmp;
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
|
|
printf ("rtl: SDAREL16 %p @ %p in %s\n",
|
|
(void *) (uintptr_t) *((Elf32_Half*) where),
|
|
where, rtems_rtl_obj_oname (obj));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
printf ("rtl: reloc unknown: sym = %" PRIu32 ", type = %" PRIu32 ", offset = %p, "
|
|
"contents = %p\n",
|
|
ELF_R_SYM(rela->r_info), (uint32_t) ELF_R_TYPE(rela->r_info),
|
|
(void *)rela->r_offset, (void *)*where);
|
|
rtems_rtl_set_error (EINVAL,
|
|
"%s: Unsupported relocation type %" PRId32
|
|
"in non-PLT relocations",
|
|
sect->name, (uint32_t) ELF_R_TYPE(rela->r_info));
|
|
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)
|
|
{
|
|
(void) obj;
|
|
(void) rel;
|
|
(void) sect;
|
|
(void) symname;
|
|
(void) syminfo;
|
|
(void) symvalue;
|
|
rtems_rtl_set_error (EINVAL, "rel 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)
|
|
{
|
|
(void) obj;
|
|
(void) rel;
|
|
(void) sect;
|
|
(void) symname;
|
|
(void) syminfo;
|
|
(void) symvalue;
|
|
rtems_rtl_set_error (EINVAL, "rel 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);
|
|
}
|