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