Home | History | Annotate | Download | only in bfd
      1 /* SuperH SH64-specific support for 32-bit ELF
      2    Copyright (C) 2000-2014 Free Software Foundation, Inc.
      3 
      4    This file is part of BFD, the Binary File Descriptor library.
      5 
      6    This program is free software; you can redistribute it and/or modify
      7    it under the terms of the GNU General Public License as published by
      8    the Free Software Foundation; either version 3 of the License, or
      9    (at your option) any later version.
     10 
     11    This program is distributed in the hope that it will be useful,
     12    but WITHOUT ANY WARRANTY; without even the implied warranty of
     13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14    GNU General Public License for more details.
     15 
     16    You should have received a copy of the GNU General Public License
     17    along with this program; if not, write to the Free Software
     18    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
     19    MA 02110-1301, USA.  */
     20 
     21 #define SH64_ELF
     22 
     23 #include "sysdep.h"
     24 #include "bfd.h"
     25 #include "libbfd.h"
     26 #include "elf-bfd.h"
     27 #include "elf/sh.h"
     28 #include "elf32-sh64.h"
     29 #include "../opcodes/sh64-opc.h"
     30 
     31 static bfd_boolean sh64_address_in_cranges
     32   (asection *cranges, bfd_vma, sh64_elf_crange *);
     33 
     34 /* Ordering functions of a crange, for the qsort and bsearch calls and for
     35    different endianness.  */
     36 
     37 int
     38 _bfd_sh64_crange_qsort_cmpb (const void *p1, const void *p2)
     39 {
     40   bfd_vma a1 = bfd_getb32 (p1);
     41   bfd_vma a2 = bfd_getb32 (p2);
     42 
     43   /* Preserve order if there's ambiguous contents.  */
     44   if (a1 == a2)
     45     return (char *) p1 - (char *) p2;
     46 
     47   return a1 - a2;
     48 }
     49 
     50 int
     51 _bfd_sh64_crange_qsort_cmpl (const void *p1, const void *p2)
     52 {
     53   bfd_vma a1 = (bfd_vma) bfd_getl32 (p1);
     54   bfd_vma a2 = (bfd_vma) bfd_getl32 (p2);
     55 
     56   /* Preserve order if there's ambiguous contents.  */
     57   if (a1 == a2)
     58     return (char *) p1 - (char *) p2;
     59 
     60   return a1 - a2;
     61 }
     62 
     63 int
     64 _bfd_sh64_crange_bsearch_cmpb (const void *p1, const void *p2)
     65 {
     66   bfd_vma a1 = *(bfd_vma *) p1;
     67   bfd_vma a2 = (bfd_vma) bfd_getb32 (p2);
     68   bfd_size_type size
     69     = (bfd_size_type) bfd_getb32 (SH64_CRANGE_CR_SIZE_OFFSET + (char *) p2);
     70 
     71   if (a1 >= a2 + size)
     72     return 1;
     73   if (a1 < a2)
     74     return -1;
     75   return 0;
     76 }
     77 
     78 int
     79 _bfd_sh64_crange_bsearch_cmpl (const void *p1, const void *p2)
     80 {
     81   bfd_vma a1 = *(bfd_vma *) p1;
     82   bfd_vma a2 = (bfd_vma) bfd_getl32 (p2);
     83   bfd_size_type size
     84     = (bfd_size_type) bfd_getl32 (SH64_CRANGE_CR_SIZE_OFFSET + (char *) p2);
     85 
     86   if (a1 >= a2 + size)
     87     return 1;
     88   if (a1 < a2)
     89     return -1;
     90   return 0;
     91 }
     92 
     93 /* Check whether a specific address is specified within a .cranges
     94    section.  Return FALSE if not found, and TRUE if found, and the region
     95    filled into RANGEP if non-NULL.  */
     96 
     97 static bfd_boolean
     98 sh64_address_in_cranges (asection *cranges, bfd_vma addr,
     99 			 sh64_elf_crange *rangep)
    100 {
    101   bfd_byte *cranges_contents;
    102   bfd_byte *found_rangep;
    103   bfd_size_type cranges_size = cranges->size;
    104 
    105   /* If the size is not a multiple of the cranges entry size, then
    106      something is badly wrong.  */
    107   if ((cranges_size % SH64_CRANGE_SIZE) != 0)
    108     return FALSE;
    109 
    110   /* If this section has relocations, then we can't do anything sane.  */
    111   if (bfd_get_section_flags (cranges->owner, cranges) & SEC_RELOC)
    112     return FALSE;
    113 
    114   /* Has some kind soul (or previous call) left processed, sorted contents
    115      for us?  */
    116   if ((bfd_get_section_flags (cranges->owner, cranges) & SEC_IN_MEMORY)
    117       && elf_section_data (cranges)->this_hdr.sh_type == SHT_SH5_CR_SORTED)
    118     cranges_contents = cranges->contents;
    119   else
    120     {
    121       if (!bfd_malloc_and_get_section (cranges->owner, cranges,
    122 				       &cranges_contents))
    123 	goto error_return;
    124 
    125       /* Is it sorted?  */
    126       if (elf_section_data (cranges)->this_hdr.sh_type
    127 	  != SHT_SH5_CR_SORTED)
    128 	/* Nope.  Lets sort it.  */
    129 	qsort (cranges_contents, cranges_size / SH64_CRANGE_SIZE,
    130 	       SH64_CRANGE_SIZE,
    131 	       bfd_big_endian (cranges->owner)
    132 	       ? _bfd_sh64_crange_qsort_cmpb : _bfd_sh64_crange_qsort_cmpl);
    133 
    134       /* Let's keep it around.  */
    135       cranges->contents = cranges_contents;
    136       bfd_set_section_flags (cranges->owner, cranges,
    137 			     bfd_get_section_flags (cranges->owner, cranges)
    138 			     | SEC_IN_MEMORY);
    139 
    140       /* It's sorted now.  */
    141       elf_section_data (cranges)->this_hdr.sh_type = SHT_SH5_CR_SORTED;
    142     }
    143 
    144   /* Try and find a matching range.  */
    145   found_rangep
    146     = bsearch (&addr, cranges_contents, cranges_size / SH64_CRANGE_SIZE,
    147 	       SH64_CRANGE_SIZE,
    148 	       bfd_big_endian (cranges->owner)
    149 	       ? _bfd_sh64_crange_bsearch_cmpb
    150 	       : _bfd_sh64_crange_bsearch_cmpl);
    151 
    152   /* Fill in a few return values if we found a matching range.  */
    153   if (found_rangep)
    154     {
    155       enum sh64_elf_cr_type cr_type
    156 	= bfd_get_16 (cranges->owner,
    157 		      SH64_CRANGE_CR_TYPE_OFFSET + found_rangep);
    158       bfd_vma cr_addr
    159 	= bfd_get_32 (cranges->owner,
    160 		      SH64_CRANGE_CR_ADDR_OFFSET
    161 		      + (char *) found_rangep);
    162       bfd_size_type cr_size
    163 	= bfd_get_32 (cranges->owner,
    164 		      SH64_CRANGE_CR_SIZE_OFFSET
    165 		      + (char *) found_rangep);
    166 
    167       rangep->cr_addr = cr_addr;
    168       rangep->cr_size = cr_size;
    169       rangep->cr_type = cr_type;
    170 
    171       return TRUE;
    172     }
    173 
    174   /* There is a .cranges section, but it does not have a descriptor
    175      matching this address.  */
    176   return FALSE;
    177 
    178 error_return:
    179   if (cranges_contents != NULL)
    180     free (cranges_contents);
    181   return FALSE;
    182 }
    183 
    184 /* Determine what ADDR points to in SEC, and fill in a range descriptor in
    185    *RANGEP if it's non-NULL.  */
    186 
    187 enum sh64_elf_cr_type
    188 sh64_get_contents_type (asection *sec, bfd_vma addr, sh64_elf_crange *rangep)
    189 {
    190   asection *cranges;
    191 
    192   /* Fill in the range with the boundaries of the section as a default.  */
    193   if (bfd_get_flavour (sec->owner) == bfd_target_elf_flavour
    194       && elf_elfheader (sec->owner)->e_type == ET_EXEC)
    195     {
    196       rangep->cr_addr = bfd_get_section_vma (sec->owner, sec);
    197       rangep->cr_size = sec->size;
    198       rangep->cr_type = CRT_NONE;
    199     }
    200   else
    201     return FALSE;
    202 
    203   /* If none of the pertinent bits are set, then it's a SHcompact (or at
    204      least not SHmedia).  */
    205   if ((elf_section_data (sec)->this_hdr.sh_flags
    206        & (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)) == 0)
    207     {
    208       enum sh64_elf_cr_type cr_type
    209 	= ((bfd_get_section_flags (sec->owner, sec) & SEC_CODE) != 0
    210 	   ? CRT_SH5_ISA16 : CRT_DATA);
    211       rangep->cr_type = cr_type;
    212       return cr_type;
    213     }
    214 
    215   /* If only the SHF_SH5_ISA32 bit is set, then we have SHmedia.  */
    216   if ((elf_section_data (sec)->this_hdr.sh_flags
    217        & (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)) == SHF_SH5_ISA32)
    218     {
    219       rangep->cr_type = CRT_SH5_ISA32;
    220       return CRT_SH5_ISA32;
    221     }
    222 
    223   /* Otherwise, we have to look up the .cranges section.  */
    224   cranges = bfd_get_section_by_name (sec->owner, SH64_CRANGES_SECTION_NAME);
    225 
    226   if (cranges == NULL)
    227     /* A mixed section but there's no .cranges section.  This is probably
    228        bad input; it does not comply to specs.  */
    229     return CRT_NONE;
    230 
    231   /* If this call fails, we will still have CRT_NONE in rangep->cr_type
    232      and that will be suitable to return.  */
    233   sh64_address_in_cranges (cranges, addr, rangep);
    234 
    235   return rangep->cr_type;
    236 }
    237 
    238 /* This is a simpler exported interface for the benefit of gdb et al.  */
    239 
    240 bfd_boolean
    241 sh64_address_is_shmedia (asection *sec, bfd_vma addr)
    242 {
    243   sh64_elf_crange dummy;
    244   return sh64_get_contents_type (sec, addr, &dummy) == CRT_SH5_ISA32;
    245 }
    246