text: /* SuperH SH64-specific support for 32-bit ELF
     Copyright 2000, 2001, 2002, 2003 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 "elf32-sh64.h"
  #include "../opcodes/sh64-opc.h"
  
  static bfd_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 bfd_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.  */
  
  bfd_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;
  }
  