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