+/* Hitachi SH64-specific support for 32-bit ELF
+ Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
+
+ This file is part of BFD, the Binary File Descriptor library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#define SH64_ELF
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "elf-bfd.h"
+#include "elf/sh.h"
+#include "../opcodes/sh64-opc.h"
+
+static boolean sh64_address_in_cranges
+ PARAMS ((asection *cranges, bfd_vma, sh64_elf_crange *));
+
+/* Ordering functions of a crange, for the qsort and bsearch calls and for
+ different endianness. */
+
+int
+_bfd_sh64_crange_qsort_cmpb (p1, p2)
+ const PTR p1;
+ const PTR p2;
+{
+ bfd_vma a1 = bfd_getb32 (p1);
+ bfd_vma a2 = bfd_getb32 (p2);
+
+ /* Preserve order if there's ambiguous contents. */
+ if (a1 == a2)
+ return (char *) p1 - (char *) p2;
+
+ return a1 - a2;
+}
+
+int
+_bfd_sh64_crange_qsort_cmpl (p1, p2)
+ const PTR p1;
+ const PTR p2;
+{
+ bfd_vma a1 = (bfd_vma) bfd_getl32 (p1);
+ bfd_vma a2 = (bfd_vma) bfd_getl32 (p2);
+
+ /* Preserve order if there's ambiguous contents. */
+ if (a1 == a2)
+ return (char *) p1 - (char *) p2;
+
+ return a1 - a2;
+}
+
+int
+_bfd_sh64_crange_bsearch_cmpb (p1, p2)
+ const PTR p1;
+ const PTR p2;
+{
+ bfd_vma a1 = *(bfd_vma *) p1;
+ bfd_vma a2 = (bfd_vma) bfd_getb32 (p2);
+ bfd_size_type size
+ = (bfd_size_type) bfd_getb32 (SH64_CRANGE_CR_SIZE_OFFSET + (char *) p2);
+
+ if (a1 >= a2 + size)
+ return 1;
+ if (a1 < a2)
+ return -1;
+ return 0;
+}
+
+int
+_bfd_sh64_crange_bsearch_cmpl (p1, p2)
+ const PTR p1;
+ const PTR p2;
+{
+ bfd_vma a1 = *(bfd_vma *) p1;
+ bfd_vma a2 = (bfd_vma) bfd_getl32 (p2);
+ bfd_size_type size
+ = (bfd_size_type) bfd_getl32 (SH64_CRANGE_CR_SIZE_OFFSET + (char *) p2);
+
+ if (a1 >= a2 + size)
+ return 1;
+ if (a1 < a2)
+ return -1;
+ return 0;
+}
+
+/* Check whether a specific address is specified within a .cranges
+ section. Return FALSE if not found, and TRUE if found, and the region
+ filled into RANGEP if non-NULL. */
+
+static boolean
+sh64_address_in_cranges (cranges, addr, rangep)
+ asection *cranges;
+ bfd_vma addr;
+ sh64_elf_crange *rangep;
+{
+ bfd_byte *cranges_contents;
+ bfd_byte *found_rangep;
+ bfd_size_type cranges_size = bfd_section_size (cranges->owner, cranges);
+
+ /* If the size is not a multiple of the cranges entry size, then
+ something is badly wrong. */
+ if ((cranges_size % SH64_CRANGE_SIZE) != 0)
+ return false;
+
+ /* If this section has relocations, then we can't do anything sane. */
+ if (bfd_get_section_flags (cranges->owner, cranges) & SEC_RELOC)
+ return false;
+
+ /* Has some kind soul (or previous call) left processed, sorted contents
+ for us? */
+ if ((bfd_get_section_flags (cranges->owner, cranges) & SEC_IN_MEMORY)
+ && elf_section_data (cranges)->this_hdr.sh_type == SHT_SH5_CR_SORTED)
+ cranges_contents = cranges->contents;
+ else
+ {
+ cranges_contents
+ = bfd_malloc (cranges->_cooked_size == 0
+ ? cranges->_cooked_size : cranges->_raw_size);
+ if (cranges_contents == NULL)
+ return false;
+
+ if (! bfd_get_section_contents (cranges->owner, cranges,
+ cranges_contents, (file_ptr) 0,
+ cranges_size))
+ goto error_return;
+
+ /* Is it sorted? */
+ if (elf_section_data (cranges)->this_hdr.sh_type
+ != SHT_SH5_CR_SORTED)
+ /* Nope. Lets sort it. */
+ qsort (cranges_contents, cranges_size / SH64_CRANGE_SIZE,
+ SH64_CRANGE_SIZE,
+ bfd_big_endian (cranges->owner)
+ ? _bfd_sh64_crange_qsort_cmpb : _bfd_sh64_crange_qsort_cmpl);
+
+ /* Let's keep it around. */
+ cranges->contents = cranges_contents;
+ bfd_set_section_flags (cranges->owner, cranges,
+ bfd_get_section_flags (cranges->owner, cranges)
+ | SEC_IN_MEMORY);
+
+ /* It's sorted now. */
+ elf_section_data (cranges)->this_hdr.sh_type = SHT_SH5_CR_SORTED;
+ }
+
+ /* Try and find a matching range. */
+ found_rangep
+ = bsearch (&addr, cranges_contents, cranges_size / SH64_CRANGE_SIZE,
+ SH64_CRANGE_SIZE,
+ bfd_big_endian (cranges->owner)
+ ? _bfd_sh64_crange_bsearch_cmpb
+ : _bfd_sh64_crange_bsearch_cmpl);
+
+ /* Fill in a few return values if we found a matching range. */
+ if (found_rangep)
+ {
+ enum sh64_elf_cr_type cr_type
+ = bfd_get_16 (cranges->owner,
+ SH64_CRANGE_CR_TYPE_OFFSET + found_rangep);
+ bfd_vma cr_addr
+ = bfd_get_32 (cranges->owner,
+ SH64_CRANGE_CR_ADDR_OFFSET
+ + (char *) found_rangep);
+ bfd_size_type cr_size
+ = bfd_get_32 (cranges->owner,
+ SH64_CRANGE_CR_SIZE_OFFSET
+ + (char *) found_rangep);
+
+ rangep->cr_addr = cr_addr;
+ rangep->cr_size = cr_size;
+ rangep->cr_type = cr_type;
+
+ return true;
+ }
+
+ /* There is a .cranges section, but it does not have a descriptor
+ matching this address. */
+ return false;
+
+error_return:
+ free (cranges_contents);
+ return false;
+}
+
+/* Determine what ADDR points to in SEC, and fill in a range descriptor in
+ *RANGEP if it's non-NULL. */
+
+enum sh64_elf_cr_type
+sh64_get_contents_type (sec, addr, rangep)
+ asection *sec;
+ bfd_vma addr;
+ sh64_elf_crange *rangep;
+{
+ asection *cranges;
+
+ /* Fill in the range with the boundaries of the section as a default. */
+ if (bfd_get_flavour (sec->owner) == bfd_target_elf_flavour
+ && elf_elfheader (sec->owner)->e_type == ET_EXEC)
+ {
+ rangep->cr_addr = bfd_get_section_vma (sec->owner, sec);
+ rangep->cr_size = bfd_section_size (sec->owner, sec);
+ rangep->cr_type = CRT_NONE;
+ }
+ else
+ return false;
+
+ /* If none of the pertinent bits are set, then it's a SHcompact (or at
+ least not SHmedia). */
+ if ((elf_section_data (sec)->this_hdr.sh_flags
+ & (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)) == 0)
+ {
+ enum sh64_elf_cr_type cr_type
+ = ((bfd_get_section_flags (sec->owner, sec) & SEC_CODE) != 0
+ ? CRT_SH5_ISA16 : CRT_DATA);
+ rangep->cr_type = cr_type;
+ return cr_type;
+ }
+
+ /* If only the SHF_SH5_ISA32 bit is set, then we have SHmedia. */
+ if ((elf_section_data (sec)->this_hdr.sh_flags
+ & (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)) == SHF_SH5_ISA32)
+ {
+ rangep->cr_type = CRT_SH5_ISA32;
+ return CRT_SH5_ISA32;
+ }
+
+ /* Otherwise, we have to look up the .cranges section. */
+ cranges = bfd_get_section_by_name (sec->owner, SH64_CRANGES_SECTION_NAME);
+
+ if (cranges == NULL)
+ /* A mixed section but there's no .cranges section. This is probably
+ bad input; it does not comply to specs. */
+ return CRT_NONE;
+
+ /* If this call fails, we will still have CRT_NONE in rangep->cr_type
+ and that will be suitable to return. */
+ sh64_address_in_cranges (cranges, addr, rangep);
+
+ return rangep->cr_type;
+}
+
+/* This is a simpler exported interface for the benefit of gdb et al. */
+
+boolean
+sh64_address_is_shmedia (sec, addr)
+ asection *sec;
+ bfd_vma addr;
+{
+ sh64_elf_crange dummy;
+ return sh64_get_contents_type (sec, addr, &dummy) == CRT_SH5_ISA32;
+}