summaryrefslogblamecommitdiffstats
path: root/cpukit/libdl/rtl-rap.c
blob: 3fd1428bf25c4ced94f05aef6472ae0f04e64b23 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12

                                           









                                          
  
                                                                
  



















                                                                              
   
 
                    




                   
                     







                          
                                   
                    

                                     










                                       
                                        







                                               
                                    


                                                      
                        














                                          
                                                                     











                                                                                             
                                    


                                                            
                        



                  
                            
 




















                                                                                            







































































                                                    
                                                                     











                                                                 



                                               
 
                                             

                                             
                                               





                                                                       
                                                               
















                                                                           




                                   



                                                                              
               















                                                                         
                                                                           















































                                                                              

                                                          

                                         
                              


                                  
                                    




                                                                       
                                                                           


                       
                                                                


                                                    
                                  


































                                                                                   
                                                       













                                                    

                                                                   
                                                      
                                                                      
 





                                                                                












                                                    

                                                               
                                        
                                                                      
 





                                                                               













                                
                                    





                                                                     
                                                                   
 
                          
                        




                                   







                                                                   

                                                                            
 
              





                                                                  
                                           
 




                                              

                                                    
                                                         


                 

                                           



















                                                     
                            


















                                        
                                                         




                                                 

                                                                        

     
                                      











                                                              

                                                                   

























                                                                        
                                                                   
 

                          

                    
                                                                 












                                                                   
                                                              

                                                                            

                                                                    
                 
   


                                                                    



                                




                                                          
                                                                      






                                                 

                                                                     













                                                                              
                                                                      








                                                                    
                                                                      


                               
                                                                                    

















                                                                                   


                                   



























































































                                                                                   
                                                     
 






                                       


















                                                          
                                                    
 



                            




















                                                            

                                                                       







                                             
                                                    





                                                                
                                                                    










                                                             
                                                              










                                                          
                                                        















                                                  
                                                   



















                                                                

                                                          


























                                                                         
                                                                        
 
                                                


























                                                                              
                                                                   
















                                                                 





                                                          



                                                                         
                                                    





                                                   
                                                   




                                                   

                                        


              
    
                                              




              
                        



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

/**
 * @file
 *
 * @ingroup rtems_rtld
 *
 * @brief RTEMS Run-Time Link Editor
 *
 * This is the RAP format loader support..
 */

/*
 *  COPYRIGHT (c) 2012-2013, 2018 Chris Johns <chrisj@rtems.org>
 *
 * 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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

#include <rtems/rtl/rtl.h>
#include "rtl-elf.h"
#include "rtl-error.h"
#include <rtems/rtl/rtl-obj-comp.h>
#include "rtl-rap.h"
#include <rtems/rtl/rtl-trace.h>
#include <rtems/rtl/rtl-unresolved.h>

/**
 * The offsets in the unresolved array.
 */
#define REL_R_OFFSET (0)
#define REL_R_INFO   (1)
#define REL_R_ADDEND (2)

/**
 * The ELF format signature.
 */
static rtems_rtl_loader_format rap_sig =
{
  .label = "RAP",
  .flags = RTEMS_RTL_FMT_COMP
};

/**
 * The section definitions found in a RAP file.
 */
typedef struct rtems_rtl_rap_sectdef
{
  const char*    name;    /**< Name of the section. */
  const uint32_t flags;   /**< Section flags. */
} rtems_rtl_rap_sectdef;

/**
 * The section indexes. These are fixed.
 */
#define RTEMS_RTL_RAP_TEXT_SEC  (0)
#define RTEMS_RTL_RAP_CONST_SEC (1)
#define RTEMS_RTL_RAP_CTOR_SEC  (2)
#define RTEMS_RTL_RAP_DTOR_SEC  (3)
#define RTEMS_RTL_RAP_DATA_SEC  (4)
#define RTEMS_RTL_RAP_BSS_SEC   (5)
#define RTEMS_RTL_RAP_SECS      (6)

/**
 * The sections as loaded from a RAP file.
 */
static const rtems_rtl_rap_sectdef rap_sections[RTEMS_RTL_RAP_SECS] =
{
  { ".text",  RTEMS_RTL_OBJ_SECT_TEXT  | RTEMS_RTL_OBJ_SECT_LOAD },
  { ".const", RTEMS_RTL_OBJ_SECT_CONST | RTEMS_RTL_OBJ_SECT_LOAD },
  { ".ctor",  RTEMS_RTL_OBJ_SECT_CONST | RTEMS_RTL_OBJ_SECT_LOAD | RTEMS_RTL_OBJ_SECT_CTOR },
  { ".dtor",  RTEMS_RTL_OBJ_SECT_CONST | RTEMS_RTL_OBJ_SECT_LOAD | RTEMS_RTL_OBJ_SECT_DTOR },
  { ".data",  RTEMS_RTL_OBJ_SECT_DATA  | RTEMS_RTL_OBJ_SECT_LOAD },
  { ".bss",   RTEMS_RTL_OBJ_SECT_BSS   | RTEMS_RTL_OBJ_SECT_ZERO }
};

/**
 * The section definitions found in a RAP file.
 */
typedef struct rtems_rtl_rap_section
{
  uint32_t size;       /**< The size of the section. */
  uint32_t alignment;  /**< The alignment of the section. */
} rtems_rtl_rap_section;

/**
 * The RAP loader.
 */
typedef struct rtems_rtl_rap
{
  rtems_rtl_obj_cache*  file;         /**< The file cache for the RAP file. */
  rtems_rtl_obj_comp*   decomp;       /**< The decompression streamer. */
  uint32_t              length;       /**< The file length. */
  uint32_t              version;      /**< The RAP file version. */
  uint32_t              compression;  /**< The type of compression. */
  uint32_t              checksum;     /**< The checksum. */
  uint32_t              machinetype;  /**< The ELF machine type. */
  uint32_t              datatype;     /**< The ELF data type. */
  uint32_t              class;        /**< The ELF class. */
  uint32_t              init;         /**< The initialisation strtab offset. */
  uint32_t              fini;         /**< The finish strtab offset. */
  rtems_rtl_rap_section secs[RTEMS_RTL_RAP_SECS]; /**< The sections. */
  uint32_t              symtab_size;  /**< The symbol table size. */
  char*                 strtab;       /**< The string table. */
  uint32_t              strtab_size;  /**< The string table size. */
  uint32_t              relocs_size;  /**< The relocation table size. */
  uint32_t              symbols;      /**< The number of symbols. */
  uint32_t              strtable_size;/**< The size of section names and obj names. */
  uint32_t              rpathlen;     /**< The length of rpath. */
  char*                 strtable;     /**< The detail string which resides in obj detail. */
} rtems_rtl_rap;

/**
 * Check the machine type.
 */
static bool
rtems_rtl_rap_machine_check (uint32_t machinetype)
{
  /*
   * This code is determined by the machine headers.
   */
  switch (machinetype)
  {
    ELFDEFNNAME (MACHDEP_ID_CASES)
    default:
      return false;
  }
  return true;
}

/**
 * Check the data type.
 */
static bool
rtems_rtl_rap_datatype_check (uint32_t datatype)
{
  /*
   * This code is determined by the machine headers.
   */
  if (datatype != ELFDEFNNAME (MACHDEP_ENDIANNESS))
    return false;
  return true;
}

/**
 * Check the class of executable.
 */
static bool
rtems_rtl_rap_class_check (uint32_t class)
{
  /*
   * This code is determined by the machine headers.
   */
  switch (class)
  {
    case ELFCLASS32:
      if (ARCH_ELFSIZE == 32)
        return true;
      break;
    case ELFCLASS64:
      if (ARCH_ELFSIZE == 64)
        return true;
      break;
    default:
      break;
  }
  return false;
}

static uint32_t
rtems_rtl_rap_get_uint32 (const uint8_t* buffer)
{
  uint32_t value = 0;
  int      b;
  for (b = 0; b < sizeof (uint32_t); ++b)
  {
    value <<= 8;
    value |= buffer[b];
  }
  return value;
}

static bool
rtems_rtl_rap_read_uint32 (rtems_rtl_obj_comp* comp, uint32_t* value)
{
  uint8_t buffer[sizeof (uint32_t)];

  if (!rtems_rtl_obj_comp_read (comp, buffer, sizeof (uint32_t)))
    return false;

  *value = rtems_rtl_rap_get_uint32 (buffer);

  return true;
}

static bool
rtems_rtl_rap_loader (rtems_rtl_obj*      obj,
                      int                 fd,
                      rtems_rtl_obj_sect* sect,
                      void*               data)
{
  rtems_rtl_rap* rap = (rtems_rtl_rap*) data;

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD))
    printf ("rtl: rap: input %s=%" PRIu32 "\n",
            sect->name, rtems_rtl_obj_comp_input (rap->decomp));

  return rtems_rtl_obj_comp_read (rap->decomp, sect->base, sect->size);
}

static bool
rtems_rtl_rap_relocate (rtems_rtl_rap* rap, rtems_rtl_obj* obj)
{
  #define SYMNAME_BUFFER_SIZE (1024)
  char*    symname_buffer = NULL;
  int      section;

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
    printf ("rtl: relocation\n");

  symname_buffer = malloc (SYMNAME_BUFFER_SIZE);
  if (!symname_buffer)
  {
    rtems_rtl_set_error (ENOMEM, "no memory for local symbol name buffer");
    return false;
  }

  for (section = 0; section < RTEMS_RTL_RAP_SECS; ++section)
  {
    rtems_rtl_obj_sect* targetsect;
    uint32_t            header = 0;
    int                 relocs;
    bool                is_rela;
    int                 r;

    targetsect = rtems_rtl_obj_find_section (obj, rap_sections[section].name);

    if (!targetsect)
      continue;

    if (!rtems_rtl_rap_read_uint32 (rap->decomp, &header))
    {
      free (symname_buffer);
      return false;
    }

    /*
     * Bit 31 of the header indicates if the relocations for this section
     * have a valid addend field.
     */

    is_rela = (header & (1 << 31)) != 0 ? true : false;
    relocs = header & ~(1 << 31);

    if (relocs && rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
      printf ("rtl: relocation: %s: header: %08" PRIx32 " relocs: %d %s\n",
              rap_sections[section].name,
              header, relocs, is_rela ? "rela" : "rel");

    for (r = 0; r < relocs; ++r)
    {
      uint32_t    info = 0;
      uint32_t    offset = 0;
      uint32_t    addend = 0;
      Elf_Word    type;
      const char* symname = NULL;
      uint32_t    symname_size;
      Elf_Word    symtype = 0;
      Elf_Word    symvalue = 0;

      if (!rtems_rtl_rap_read_uint32 (rap->decomp, &info))
      {
        free (symname_buffer);
        return false;
      }

      if (!rtems_rtl_rap_read_uint32 (rap->decomp, &offset))
      {
        free (symname_buffer);
        return false;
      }

      /*
       * The types are:
       *
       *  0  Section symbol offset in addend.
       *  1  Symbol appended to the relocation record.
       *  2  Symbol is in the strtabl.
       *
       * If type 2 bits 30:8 is the offset in the strtab. If type 1 the bits
       * are the size of the string. The lower 8 bits of the info field if the
       * ELF relocation type field.
       */

      if (((info & (1 << 31)) == 0) || is_rela)
      {
        if (!rtems_rtl_rap_read_uint32 (rap->decomp, &addend))
        {
          free (symname_buffer);
          return false;
        }
      }

      if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
        printf (" %2d: info=%08" PRIx32 " offset=%" PRIu32
                " addend=%" PRIu32 "\n",
                r, info, offset, addend);

      type = ELF_R_TYPE(info);

      if ((info & (1 << 31)) == 0)
      {
        rtems_rtl_obj_sect* symsect;

        symsect = rtems_rtl_obj_find_section_by_index (obj, info >> 8);
        if (!symsect)
        {
          free (symname_buffer);
          rtems_rtl_set_error (EINVAL, "symsect not found: %d", info >> 8);
          return false;
        }

        symvalue = (Elf_Addr)(uintptr_t) symsect->base + addend;
      }
      else if (rtems_rtl_elf_rel_resolve_sym (type))
      {
        rtems_rtl_obj_sym* symbol;

        symname_size = (info & ~(3 << 30)) >> 8;

        if ((info & (1 << 30)) != 0)
        {
          symname = rap->strtab + symname_size;
        }
        else
        {
          if (symname_size > (SYMNAME_BUFFER_SIZE - 1))
          {
            free (symname_buffer);
            rtems_rtl_set_error (EINVAL, "reloc symbol too big");
            return false;
          }

          if (!rtems_rtl_obj_comp_read (rap->decomp, symname_buffer, symname_size))
          {
            free (symname_buffer);
            return false;
          }

          symname_buffer[symname_size] = '\0';
          symname = symname_buffer;
        }

        symbol = rtems_rtl_symbol_obj_find (obj, symname);

        if (!symbol)
        {
          rtems_rtl_set_error (EINVAL, "global symbol not found: %s", symname);
          free (symname_buffer);
          return false;
        }

        symvalue = (Elf_Addr)(uintptr_t) symbol->value;
      }

      if (is_rela)
      {
        Elf_Rela rela;

        rela.r_offset = offset;
        rela.r_info = type;

        if ((info & (1 << 31)) == 0)
          rela.r_addend = 0;
        else rela.r_addend = addend;

        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
          printf (" %2d: rela: type:%-2d off:%" PRIu32 " addend:%d"
                  " symname=%s symtype=%ju symvalue=0x%08jx\n",
                  r, (int) type, offset, (int) addend,
                  symname, (uintmax_t) symtype, (uintmax_t) symvalue);

        if (rtems_rtl_elf_relocate_rela (obj,
                                         &rela,
                                         targetsect,
                                         symname,
                                         symtype,
                                         symvalue) == rtems_rtl_elf_rel_failure)
        {
          free (symname_buffer);
          return false;
        }
      }
      else
      {
        Elf_Rel rel;

        rel.r_offset = offset;
        rel.r_info = type;

        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
          printf (" %2d: rel: type:%-2d off:%" PRIu32
                  " symname=%s symtype=%ju symvalue=0x%08jx\n",
                  r, (int) type, offset,
                  symname, (uintmax_t) symtype, (uintmax_t) symvalue);

        if (rtems_rtl_elf_relocate_rel (obj,
                                        &rel,
                                        targetsect,
                                        symname,
                                        symtype,
                                        symvalue) == rtems_rtl_elf_rel_failure)
        {
          free (symname_buffer);
          return false;
        }
      }
    }
  }

  free (symname_buffer);

  return true;
}

/**
 * The structure of obj->linkmap is:
 *
 * |object_detail(0..obj_num)|section_detail(0..sec_num[0..obj_num])|
 * obj_name(0..obj_num)|section_name(0..sec_num[0..obj_num])
 *
 */
static bool
rtems_rtl_rap_load_linkmap (rtems_rtl_rap* rap, rtems_rtl_obj* obj)
{
  void*            detail;
  struct link_map* tmp1;
  section_detail*  tmp2;
  uint32_t         obj_detail_size;
  uint32_t         pos = 0;
  int              i;
  int              j;

  obj_detail_size = sizeof (struct link_map) * obj->obj_num;

  for (i = 0; i < obj->obj_num; ++i)
  {
    obj_detail_size += (obj->sec_num[i] * sizeof (section_detail));
  }

  detail = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT,
                                obj_detail_size + rap->strtable_size, true);

  if (!detail)
  {
    rap->strtable_size = 0;
    rtems_rtl_set_error (ENOMEM, "no memory for obj global syms");
    return false;
  }

  rap->strtable = detail + obj_detail_size;

  /*
   *  Read the obj names and section names
   */
  if (!rtems_rtl_obj_comp_read (rap->decomp,
                                rap->strtable,
                                rap->strtable_size))
  {
    rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, detail);
    return false;
  }

  obj->linkmap = (struct link_map*) detail;

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
  {
    if (rap->rpathlen > 0)
      printf ("File rpath:\n");
  }

  while (pos < rap->rpathlen)
  {
    if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
    {
      printf ("          %s\n", rap->strtable + pos);
    }
    pos = pos + strlen (rap->strtable + pos) + 1;
  }

  if (rap->rpathlen > 0)
    pos = rap->rpathlen;

  for (i = 0; i < obj->obj_num; ++i)
  {
    tmp1 = obj->linkmap + i;
    tmp1->name = rap->strtable + pos;
    tmp1->sec_num = obj->sec_num[i];
    tmp1->rpathlen = rap->rpathlen;
    tmp1->rpath = (char*) rap->strtable;
    pos += strlen (tmp1->name) + 1;

    if (!i)
    {
      tmp1->l_next = NULL;
      tmp1->l_prev = NULL;
    }
    else
    {
      (tmp1 - 1)->l_next = tmp1;
      tmp1->l_prev = tmp1 - 1;
      tmp1->l_next = NULL;
    }
  }

  tmp2 = (section_detail*) (obj->linkmap + obj->obj_num);

  for (i = 0; i < obj->obj_num; ++i)
  {
    if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
    {
      printf ("File %d: %s\n", i, (obj->linkmap + i)->name);
      printf ("Section: %d sections\n", (unsigned int) obj->sec_num[i]);
    }

    obj->linkmap[i].sec_detail = tmp2;

    for (j = 0; j < obj->sec_num[i]; ++j)
    {
      uint32_t name;
      uint32_t rap_id;
      uint32_t offset;
      uint32_t size;

      if (!rtems_rtl_rap_read_uint32 (rap->decomp, &name) ||
          !rtems_rtl_rap_read_uint32 (rap->decomp, &offset) ||
          !rtems_rtl_rap_read_uint32 (rap->decomp, &size))
      {
        rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, obj->linkmap);
        obj->linkmap = NULL;
        return false;
      }

      rap_id = offset >> 28;
      offset = offset & 0xfffffff;

      tmp2->name = rap->strtable + name;
      tmp2->offset = offset;
      tmp2->rap_id = rap_id;
      tmp2->size = size;
      pos += strlen (tmp2->name) + 1;

      if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
      {
        printf ("name:%16s offset:0x%08x rap_id:%d size:0x%x\n",
                tmp2->name, (unsigned int) tmp2->offset,
                (unsigned int) tmp2->rap_id, (unsigned int) tmp2->size);
      }

      tmp2 += 1;
    }
  }
  return true;
}

static bool
rtems_rtl_rap_load_symbols (rtems_rtl_rap* rap, rtems_rtl_obj* obj)
{
  rtems_rtl_obj_sym* gsym;
  int                sym;

  obj->global_size =
    rap->symbols * sizeof (rtems_rtl_obj_sym) + rap->strtab_size;

  obj->global_table = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_SYMBOL,
                                           obj->global_size, true);
  if (!obj->global_table)
  {
    obj->global_size = 0;
    rtems_rtl_set_error (ENOMEM, "no memory for obj global syms");
    return false;
  }

  obj->global_syms = rap->symbols;

  rap->strtab = (((char*) obj->global_table) +
                 (rap->symbols * sizeof (rtems_rtl_obj_sym)));

  if (!rtems_rtl_obj_comp_read (rap->decomp, rap->strtab, rap->strtab_size))
  {
    rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, obj->global_table);
    return false;
  }

  for (sym = 0, gsym = obj->global_table; sym < rap->symbols; ++sym)
  {
    rtems_rtl_obj_sect* symsect;
    uint32_t            data;
    uint32_t            name;
    uint32_t            value;

    if (!rtems_rtl_rap_read_uint32 (rap->decomp, &data) ||
        !rtems_rtl_rap_read_uint32 (rap->decomp, &name) ||
        !rtems_rtl_rap_read_uint32 (rap->decomp, &value))
    {
      rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, obj->global_table);
      obj->global_table = NULL;
      obj->global_syms = 0;
      obj->global_size = 0;
      return false;
    }

    if (rtems_rtl_trace (RTEMS_RTL_TRACE_SYMBOL))
      printf ("rtl: sym:load: data=0x%08" PRIx32 " name=0x%08" PRIx32
              " value=0x%08" PRIx32 "\n",
              data, name, value);

    /*
     * If there is a globally exported symbol already present and this
     * symbol is not weak raise an error. If the symbol is weak and present
     * globally ignore this symbol and use the global one and if it is not
     * present take this symbol global or weak. We accept the first weak
     * symbol we find and make it globally exported.
     */
    if (rtems_rtl_symbol_global_find (rap->strtab + name) &&
        (ELF_ST_BIND (data & 0xffff) != STB_WEAK))
    {
      rtems_rtl_set_error (EINVAL,
                           "duplicate global symbol: %s", rap->strtab + name);
      rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, obj->global_table);
      obj->global_table = NULL;
      obj->global_syms = 0;
      obj->global_size = 0;
      return false;
    }

    symsect = rtems_rtl_obj_find_section_by_index (obj, data >> 16);
    if (!symsect)
    {
      rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, obj->global_table);
      obj->global_table = NULL;
      obj->global_syms = 0;
      obj->global_size = 0;
      rtems_rtl_set_error (EINVAL, "section index not found: %" PRIu32, data >> 16);
      return false;
    }

    rtems_chain_set_off_chain (&gsym->node);
    gsym->name = rap->strtab + name;
    gsym->value = (uint8_t*) (value + symsect->base);
    gsym->data = data & 0xffff;

    if (rtems_rtl_trace (RTEMS_RTL_TRACE_SYMBOL))
      printf ("rtl: sym:add:%-2d name:%-20s bind:%-2d type:%-2d val:%8p sect:%d\n",
              sym, gsym->name,
              (int) ELF_ST_BIND (data & 0xffff),
              (int) ELF_ST_TYPE (data & 0xffff),
              gsym->value, (int) (data >> 16));

    ++gsym;
  }

  if (obj->global_syms)
    rtems_rtl_symbol_obj_add (obj);

  return true;
}

static bool
rtems_rtl_rap_parse_header (uint8_t*  rhdr,
                            size_t*   rhdr_len,
                            uint32_t* length,
                            uint32_t* version,
                            uint32_t* compression,
                            uint32_t* checksum)
{
  char* sptr = (char*) rhdr;
  char* eptr;

  *rhdr_len = 0;

  /*
   * "RAP," = 4 bytes, total 4
   */

  if ((rhdr[0] != 'R') || (rhdr[1] != 'A') || (rhdr[2] != 'P') || (rhdr[3] != ','))
    return false;

  sptr = sptr + 4;

  /*
   * "00000000," = 9 bytes, total 13
   */

  *length = strtoul (sptr, &eptr, 10);

  if (*eptr != ',')
    return false;

  sptr = eptr + 1;

  /*
   * "0000," = 5 bytes, total 18
   */

  *version = strtoul (sptr, &eptr, 10);

  if (*eptr != ',')
    return false;

  sptr = eptr + 1;

  /*
   * "NONE," and "LZ77," = 5 bytes, total 23
   */

  if ((sptr[0] == 'N') &&
      (sptr[1] == 'O') &&
      (sptr[2] == 'N') &&
      (sptr[3] == 'E'))
  {
    *compression = RTEMS_RTL_COMP_NONE;
    eptr = sptr + 4;
  }
  else if ((sptr[0] == 'L') &&
           (sptr[1] == 'Z') &&
           (sptr[2] == '7') &&
           (sptr[3] == '7'))
  {
    *compression = RTEMS_RTL_COMP_LZ77;
    eptr = sptr + 4;
  }
  else
    return false;

  if (*eptr != ',')
    return false;

  sptr = eptr + 1;

  /*
   * "00000000," = 9 bytes, total 32
   */
  *checksum = strtoul (sptr, &eptr, 16);

  /*
   * "\n" = 1 byte, total 33
   */
  if (*eptr != '\n')
    return false;

  *rhdr_len = ((uint8_t*) eptr) - rhdr + 1;

  return true;
}

bool
rtems_rtl_rap_file_check (rtems_rtl_obj* obj, int fd)
{
  rtems_rtl_obj_cache* header;
  uint8_t*             rhdr = NULL;
  size_t               rlen = 64;
  uint32_t             length = 0;
  uint32_t             version = 0;
  uint32_t             compression = 0;
  uint32_t             checksum = 0;

  rtems_rtl_obj_caches (&header, NULL, NULL);

  if (!rtems_rtl_obj_cache_read (header, fd, obj->ooffset,
                                 (void**) &rhdr, &rlen))
    return false;

  if (!rtems_rtl_rap_parse_header (rhdr,
                                   &rlen,
                                   &length,
                                   &version,
                                   &compression,
                                   &checksum))
    return false;

  return true;
}

bool
rtems_rtl_rap_file_load (rtems_rtl_obj* obj, int fd)
{
  rtems_rtl_rap rap = { 0 };
  uint8_t*      rhdr = NULL;
  size_t        rlen = 64;
  int           section;

  rtems_rtl_obj_caches (&rap.file, NULL, NULL);

  if (!rtems_rtl_obj_cache_read (rap.file, fd, obj->ooffset,
                                 (void**) &rhdr, &rlen))
    return false;

  if (!rtems_rtl_rap_parse_header (rhdr,
                                   &rlen,
                                   &rap.length,
                                   &rap.version,
                                   &rap.compression,
                                   &rap.checksum))
  {
    rtems_rtl_set_error (EINVAL, "invalid RAP file format");
    return false;
  }

  /*
   * Set up the decompressor.
   */
  rtems_rtl_obj_decompress (&rap.decomp, rap.file, fd, rap.compression,
                            rlen + obj->ooffset);

  /*
   * uint32_t: machinetype
   * uint32_t: datatype
   * uint32_t: class
   */

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD))
    printf ("rtl: rap: input machine=%" PRIu32 "\n",
            rtems_rtl_obj_comp_input (rap.decomp));

  if (!rtems_rtl_rap_read_uint32 (rap.decomp, &rap.machinetype))
    return false;

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD))
    printf ("rtl: rap: machinetype=%" PRIu32 "\n", rap.machinetype);

  if (!rtems_rtl_rap_machine_check (rap.machinetype))
  {
    rtems_rtl_set_error (EINVAL, "invalid machinetype");
    return false;
  }

  if (!rtems_rtl_rap_read_uint32 (rap.decomp, &rap.datatype))
    return false;

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD))
    printf ("rtl: rap: datatype=%" PRIu32 "\n", rap.datatype);

  if (!rtems_rtl_rap_datatype_check (rap.datatype))
  {
    rtems_rtl_set_error (EINVAL, "invalid datatype");
    return false;
  }

  if (!rtems_rtl_rap_read_uint32 (rap.decomp, &rap.class))
    return false;

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD))
    printf ("rtl: rap: class=%" PRIu32 "\n", rap.class);

  if (!rtems_rtl_rap_class_check (rap.class))
  {
    rtems_rtl_set_error (EINVAL, "invalid class");
    return false;
  }

  /*
   * uint32_t: init
   * uint32_t: fini
   * uint32_t: symtab_size
   * uint32_t: strtab_size
   * uint32_t: relocs_size
   */

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD))
    printf ("rtl: rap: input header=%" PRIu32 "\n",
            rtems_rtl_obj_comp_input (rap.decomp));

  if (!rtems_rtl_rap_read_uint32 (rap.decomp, &rap.init))
    return false;

  if (!rtems_rtl_rap_read_uint32 (rap.decomp, &rap.fini))
    return false;

  if (!rtems_rtl_rap_read_uint32 (rap.decomp, &rap.symtab_size))
    return false;

  if (!rtems_rtl_rap_read_uint32 (rap.decomp, &rap.strtab_size))
    return false;

  if (!rtems_rtl_rap_read_uint32 (rap.decomp, &rap.relocs_size))
    return false;

  rap.symbols = rap.symtab_size / (3 * sizeof (uint32_t));

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD))
    printf ("rtl: rap: load: symtab=%" PRIu32 " (%" PRIu32
            ") strtab=%" PRIu32 " relocs=%" PRIu32 "\n",
            rap.symtab_size, rap.symbols,
            rap.strtab_size, rap.relocs_size);

  /*
   * Load the details
   */
  if (!rtems_rtl_rap_read_uint32 (rap.decomp, &obj->obj_num))
    return false;

  if (obj->obj_num > 0)
  {
    obj->sec_num = (uint32_t*) malloc (sizeof (uint32_t) * obj->obj_num);

    if (!rtems_rtl_rap_read_uint32 (rap.decomp, &rap.rpathlen))
      return false;

    uint32_t i;
    for (i = 0; i < obj->obj_num; ++i)
    {
      if (!rtems_rtl_rap_read_uint32 (rap.decomp, &(obj->sec_num[i])))
        return false;
    }

    if (!rtems_rtl_rap_read_uint32 (rap.decomp, &rap.strtable_size))
      return false;

    if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
      printf ("rtl: rap: details: obj_num=%" PRIu32 "\n", obj->obj_num);

    if (!rtems_rtl_rap_load_linkmap (&rap, obj))
      return false;
  }

  /*
   * uint32_t: text_size
   * uint32_t: text_alignment
   * uint32_t: const_size
   * uint32_t: const_alignment
   * uint32_t: ctor_size
   * uint32_t: ctor_alignment
   * uint32_t: dtor_size
   * uint32_t: dtor_alignment
   * uint32_t: data_size
   * uint32_t: data_alignment
   * uint32_t: bss_size
   * uint32_t: bss_alignment
   */

  for (section = 0; section < RTEMS_RTL_RAP_SECS; ++section)
  {
    if (!rtems_rtl_rap_read_uint32 (rap.decomp, &rap.secs[section].size))
      return false;

    if (!rtems_rtl_rap_read_uint32 (rap.decomp, &rap.secs[section].alignment))
      return false;

    if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD_SECT))
      printf ("rtl: rap: %s: size=%" PRIu32 " align=%" PRIu32 "\n",
              rap_sections[section].name,
              rap.secs[section].size,
              rap.secs[section].alignment);

    if (!rtems_rtl_obj_add_section (obj,
                                    section,
                                    rap_sections[section].name,
                                    rap.secs[section].size,
                                    0,
                                    rap.secs[section].alignment,
                                    0, 0,
                                    rap_sections[section].flags))
      return false;
  }

  /** obj->entry = (void*)(uintptr_t) ehdr.e_entry; */

  /*
   * Allocate the sections.
   */
  if (!rtems_rtl_obj_alloc_sections (obj, fd, NULL, &rap))
    return false;

  if (!rtems_rtl_obj_load_sections (obj, fd, rtems_rtl_rap_loader, &rap))
    return false;

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD))
    printf ("rtl: rap: input symbols=%" PRIu32 "\n",
            rtems_rtl_obj_comp_input (rap.decomp));

  if (!rtems_rtl_rap_load_symbols (&rap, obj))
    return false;

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD))
    printf ("rtl: rap: input relocs=%" PRIu32 "\n",
            rtems_rtl_obj_comp_input (rap.decomp));

  if (!rtems_rtl_rap_relocate (&rap, obj))
    return false;

  rtems_rtl_obj_synchronize_cache (obj);

  return true;
}

bool
rtems_rtl_rap_file_unload (rtems_rtl_obj* obj)
{
  (void) obj;
  return true;
}

rtems_rtl_loader_format*
rtems_rtl_rap_file_sig (void)
{
  return &rap_sig;
}