1 /* SuperH SH64-specific support for 32-bit ELF
2    Copyright (C) 2000-2016 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
_bfd_sh64_crange_qsort_cmpb(const void * p1,const void * p2)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
_bfd_sh64_crange_qsort_cmpl(const void * p1,const void * p2)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
_bfd_sh64_crange_bsearch_cmpb(const void * p1,const void * p2)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
_bfd_sh64_crange_bsearch_cmpl(const void * p1,const void * p2)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
sh64_address_in_cranges(asection * cranges,bfd_vma addr,sh64_elf_crange * rangep)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
sh64_get_contents_type(asection * sec,bfd_vma addr,sh64_elf_crange * rangep)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
sh64_address_is_shmedia(asection * sec,bfd_vma addr)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