1/* SuperH SH64-specific support for 32-bit ELF
2   Copyright (C) 2000-2017 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
31static 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
37int
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
50int
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
63int
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
78int
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
97static bfd_boolean
98sh64_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
178error_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
187enum sh64_elf_cr_type
188sh64_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
240bfd_boolean
241sh64_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