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