summaryrefslogblamecommitdiffstats
path: root/cpukit/libdl/rtl-mdreloc-microblaze.c
blob: 0956e22bc71d3cd2be2ce35a21f005e3ba6c076a (plain) (tree)














































































































































                                                                              

                                       
                         
                                                  









                                      

























                                                                              
                                                                            


                                                                            
                                   
                                                                         
                                         



                                                    
                                 















                                                                              
                   

                                                                       
                                                                  

                                                                           
                                                                         















































































                                                                            
/* SPDX-License-Identifier: BSD-2-Clause */

/*
 * Copyright (C) 2023 On-Line Applications Research Corporation (OAR)
 *
 * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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>

#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <rtems/rtl/rtl.h>
#include "rtl-elf.h"
#include "rtl-error.h"
#include <rtems/rtl/rtl-trace.h>
#include "rtl-unwind.h"
#include "rtl-unwind-dw2.h"

uint32_t
rtems_rtl_elf_section_flags (const rtems_rtl_obj* obj,
                             const Elf_Shdr*      shdr) {
  (void) obj;
  (void) 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 type != 0;
}

uint32_t rtems_rtl_obj_tramp_alignment (const rtems_rtl_obj* obj)
{
  (void) obj;
  return sizeof(uint32_t);
}

size_t
rtems_rtl_elf_relocate_tramp_max_size (void) {
  /*
   * Disable by returning 0.
   */
  return 0;
}

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;
  return rtems_rtl_elf_rel_no_error;
}

static void write16le(void *loc, uint16_t val) {
  *((uint16_t *) loc) = val;
}

static void write32le(void *loc, uint32_t val) {
  *((uint32_t *) loc) = val;
}

static uint16_t read16le(void *loc) {
  return *((uint16_t *) loc);
}

static uint32_t read32le(void *loc) {
  return *((uint32_t *) loc);
}

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_Word *where;
  Elf_Word addend = (Elf_Word)rela->r_addend;
  Elf_Addr target;

  where = (Elf_Addr *)(sect->base + rela->r_offset);

  /* Target value */
  target = (Elf_Addr)symvalue + addend;
  /* Final PCREL value */
  Elf_Word pcrel_val = target - ((Elf_Word)where);

  if (parsing) {
    return rtems_rtl_elf_rel_no_error;
  }

  switch (ELF_R_TYPE(rela->r_info)) {
  case R_TYPE(NONE):
    break;

  case R_TYPE(64):
    /* Set the lower 16 bits of where to the upper 16 bits of target */
    write16le(where,
              (read16le(where) & 0xFFFF0000) | (target >> 16));
    /* Set the lower 16 bits of where + 4 to the lower 16 bits of target */
    write16le(where + 1,
              (read16le(where + 1) & 0xFFFF0000) | (target & 0xFFFF));
    break;

  case R_TYPE(32):
    {
      uintptr_t addr = (uintptr_t)where;
      if ((uintptr_t)where & 3) {
        /* `where` is not aligned to a 4-byte boundary. */
        uintptr_t addr_down = addr & ~3;
        uintptr_t addr_up = (addr + 3) & ~3;

        uint32_t value_down = read32le((void*)addr_down);
        uint32_t value_up = read32le((void*)addr_up);

        /*
         * Calculate how many bytes `where` is offset from the previous 4-byte
         * boundary.
         */
        unsigned offset = addr & 3;

        /*
         * Combine `target` with `value_down` and `value_up` to form the new
         * values to write.
         */
        uint32_t new_value_down = (value_down & ((1 << (offset * 8)) - 1)) |
          (target << (offset * 8));
        uint32_t new_value_up = (value_up & ~((1 << (offset * 8)) - 1)) |
          (target >> ((4 - offset) * 8));

        write32le((void*)addr_down, new_value_down);
        write32le((void*)addr_up, new_value_up);
      } else {
        write32le(where, target);
      }
    }
    break;

  case R_TYPE(32_PCREL):
    write32le(where, pcrel_val);

    if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
      printf ("rtl: R_MICROBLAZE_32_PCREL %p @ %p in %s\n",
              (void *) * (where), where, rtems_rtl_obj_oname (obj));
    break;

  case R_TYPE(64_PCREL):
    /*
     * Compensate for the fact that the PC is 4 bytes ahead of the instruction
     */
    pcrel_val -= 4;
    /* Set the lower 16 bits of where to the upper 16 bits of target */
    write16le(where,
              (read16le(where) & 0xFFFF0000) | (pcrel_val >> 16));
    /* Set the lower 16 bits of where + 4 to the lower 16 bits of target */
    write16le(where + 1,
              (read16le(where + 1) & 0xFFFF0000) | (pcrel_val & 0xFFFF));

    if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
      printf ("rtl: R_MICROBLAZE_64_PCREL %p @ %p in %s\n",
              (void *) * (where), where, rtems_rtl_obj_oname (obj));
    break;

  case R_TYPE(32_PCREL_LO):
    write16le(where, (read16le(where) & 0xFFFF0000) | (pcrel_val & 0xFFFF));
    break;

  default:
    rtems_rtl_set_error (EINVAL,
                         "%s: Unsupported relocation type %d "
                         "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 (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_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_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, "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);
}