gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / opcodes / xtensa-dis.c
index 2b2ea297f1190e12944b7617f518935bc733a8e9..6b6baf624e27f3e539d6a268c74134f68712b67f 100644 (file)
@@ -1,24 +1,25 @@
 /* xtensa-dis.c.  Disassembly functions for Xtensa.
-   Copyright 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 2003-2020 Free Software Foundation, Inc.
    Contributed by Bob Wilson at Tensilica, Inc. (bwilson@tensilica.com)
 
-   This file is part of GDB, GAS, and the GNU binutils.
+   This file is part of the GNU opcodes library.
 
-   GDB, GAS, and the GNU binutils are free software; you can redistribute
-   them and/or modify them under the terms of the GNU General Public
-   License as published by the Free Software Foundation; either version 2,
-   or (at your option) any later version.
+   This library 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 3, or (at your option)
+   any later version.
 
-   GDB, GAS, and the GNU binutils are distributed in the hope that they
-   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.
+   It 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 file; see the file COPYING.  If not, write to the Free
-   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
-   USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this file; see the file COPYING.  If not, write to the
+   Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
+#include "sysdep.h"
 #include <stdlib.h>
 #include <stdio.h>
 #include <sys/types.h>
@@ -26,8 +27,9 @@
 #include "xtensa-isa.h"
 #include "ansidecl.h"
 #include "libiberty.h"
-#include "sysdep.h"
-#include "dis-asm.h"
+#include "bfd.h"
+#include "elf/xtensa.h"
+#include "disassemble.h"
 
 #include <setjmp.h>
 
@@ -42,9 +44,120 @@ int show_raw_fields;
 struct dis_private
 {
   bfd_byte *byte_buf;
-  jmp_buf bailout;
+  OPCODES_SIGJMP_BUF bailout;
+  /* Persistent fields, valid for last_section only.  */
+  asection *last_section;
+  property_table_entry *insn_table_entries;
+  int insn_table_entry_count;
+  /* Cached property table search position.  */
+  bfd_vma insn_table_cur_addr;
+  int insn_table_cur_idx;
 };
 
+static void
+xtensa_coalesce_insn_tables (struct dis_private *priv)
+{
+  const int mask = ~(XTENSA_PROP_DATA | XTENSA_PROP_NO_TRANSFORM);
+  int count = priv->insn_table_entry_count;
+  int i, j;
+
+  /* Loop over all entries, combining adjacent ones that differ only in
+     the flag bits XTENSA_PROP_DATA and XTENSA_PROP_NO_TRANSFORM.  */
+
+  for (i = j = 0; j < count; ++i)
+    {
+      property_table_entry *entry = priv->insn_table_entries + i;
+
+      *entry = priv->insn_table_entries[j];
+
+      for (++j; j < count; ++j)
+       {
+         property_table_entry *next = priv->insn_table_entries + j;
+         int fill = xtensa_compute_fill_extra_space (entry);
+         int size = entry->size + fill;
+
+         if (entry->address + size == next->address)
+           {
+             int entry_flags = entry->flags & mask;
+             int next_flags = next->flags & mask;
+
+             if (next_flags == entry_flags)
+               entry->size = next->address - entry->address + next->size;
+             else
+               break;
+           }
+         else
+           {
+             break;
+           }
+       }
+    }
+  priv->insn_table_entry_count = i;
+}
+
+static property_table_entry *
+xtensa_find_table_entry (bfd_vma memaddr, struct disassemble_info *info)
+{
+  struct dis_private *priv = (struct dis_private *) info->private_data;
+  int i;
+
+  if (priv->insn_table_entries == NULL
+      || priv->insn_table_entry_count < 0)
+    return NULL;
+
+  if (memaddr < priv->insn_table_cur_addr)
+    priv->insn_table_cur_idx = 0;
+
+  for (i = priv->insn_table_cur_idx; i < priv->insn_table_entry_count; ++i)
+    {
+      property_table_entry *block = priv->insn_table_entries + i;
+
+      if (block->size != 0)
+       {
+         if ((memaddr >= block->address
+              && memaddr < block->address + block->size)
+             || memaddr < block->address)
+           {
+             priv->insn_table_cur_addr = memaddr;
+             priv->insn_table_cur_idx = i;
+             return block;
+           }
+       }
+    }
+  return NULL;
+}
+
+/* Check whether an instruction crosses an instruction block boundary
+   (according to property tables).
+   If it does, return 0 (doesn't fit), else return 1.  */
+
+static int
+xtensa_instruction_fits (bfd_vma memaddr, int size,
+                        property_table_entry *insn_block)
+{
+  unsigned max_size;
+
+  /* If no property table info, assume it fits.  */
+  if (insn_block == NULL || size <= 0)
+    return 1;
+
+  /* If too high, limit nextstop by the next insn address.  */
+  if (insn_block->address > memaddr)
+    {
+      /* memaddr is not in an instruction block, but is followed by one.  */
+      max_size = insn_block->address - memaddr;
+    }
+  else
+    {
+      /* memaddr is in an instruction block, go no further than the end.  */
+      max_size = insn_block->address + insn_block->size - memaddr;
+    }
+
+  /* Crossing a boundary, doesn't "fit".  */
+  if ((unsigned)size > max_size)
+    return 0;
+  return 1;
+}
 
 static int
 fetch_data (struct disassemble_info *info, bfd_vma memaddr)
@@ -53,6 +166,8 @@ fetch_data (struct disassemble_info *info, bfd_vma memaddr)
   struct dis_private *priv = (struct dis_private *) info->private_data;
   int insn_size = xtensa_isa_maxlength (xtensa_default_isa);
 
+  insn_size = MAX (insn_size, 4);
+
   /* Read the maximum instruction size, padding with zeros if we go past
      the end of the text section.  This code will automatically adjust
      length when we hit the end of the buffer.  */
@@ -66,7 +181,7 @@ fetch_data (struct disassemble_info *info, bfd_vma memaddr)
        return length;
     }
   (*info->memory_error_func) (status, memaddr, info);
-  longjmp (priv->bailout, 1);
+  OPCODES_SIGLONGJMP (priv->bailout, 1);
   /*NOTREACHED*/
 }
 
@@ -80,7 +195,7 @@ print_xtensa_operand (bfd_vma memaddr,
 {
   xtensa_isa isa = xtensa_default_isa;
   int signed_operand_val;
-    
+
   if (show_raw_fields)
     {
       if (operand_val < 0xa)
@@ -124,7 +239,7 @@ print_xtensa_operand (bfd_vma memaddr,
                                 xtensa_regfile_shortname (isa, opnd_rf),
                                 operand_val);
          i++;
-       } 
+       }
     }
 }
 
@@ -140,11 +255,12 @@ print_insn_xtensa (bfd_vma memaddr, struct disassemble_info *info)
   xtensa_isa isa;
   xtensa_opcode opc;
   xtensa_format fmt;
-  struct dis_private priv;
+  static struct dis_private priv;
   static bfd_byte *byte_buf = NULL;
   static xtensa_insnbuf insn_buffer = NULL;
   static xtensa_insnbuf slot_buffer = NULL;
   int first, first_slot, valid_insn;
+  property_table_entry *insn_block;
 
   if (!xtensa_default_isa)
     xtensa_default_isa = xtensa_isa_init (0, 0);
@@ -175,48 +291,103 @@ print_insn_xtensa (bfd_vma memaddr, struct disassemble_info *info)
   priv.byte_buf = byte_buf;
 
   info->private_data = (void *) &priv;
-  if (setjmp (priv.bailout) != 0)
+
+  /* Prepare instruction tables.  */
+
+  if (info->section != NULL)
+    {
+      asection *section = info->section;
+
+      if (priv.last_section != section)
+       {
+         bfd *abfd = section->owner;
+
+         if (priv.last_section != NULL)
+           {
+             /* Reset insn_table_entries.  */
+             priv.insn_table_entry_count = 0;
+             free (priv.insn_table_entries);
+             priv.insn_table_entries = NULL;
+           }
+         priv.last_section = section;
+
+         /* Read insn_table_entries.  */
+         priv.insn_table_entry_count =
+           xtensa_read_table_entries (abfd, section,
+                                      &priv.insn_table_entries,
+                                      XTENSA_PROP_SEC_NAME, FALSE);
+         if (priv.insn_table_entry_count == 0)
+           {
+             free (priv.insn_table_entries);
+             priv.insn_table_entries = NULL;
+             /* Backwards compatibility support.  */
+             priv.insn_table_entry_count =
+               xtensa_read_table_entries (abfd, section,
+                                          &priv.insn_table_entries,
+                                          XTENSA_INSN_SEC_NAME, FALSE);
+           }
+         priv.insn_table_cur_idx = 0;
+         xtensa_coalesce_insn_tables (&priv);
+       }
+      /* Else nothing to do, same section as last time.  */
+    }
+
+  if (OPCODES_SIGSETJMP (priv.bailout) != 0)
       /* Error return.  */
       return -1;
 
-  /* Don't set "isa" before the setjmp to keep the compiler from griping.  */
-  isa = xtensa_default_isa;
-  size = 0;
-  nslots = 0;
-
   /* Fetch the maximum size instruction.  */
   bytes_fetched = fetch_data (info, memaddr);
 
-  /* Copy the bytes into the decode buffer.  */
-  memset (insn_buffer, 0, (xtensa_insnbuf_size (isa) *
-                          sizeof (xtensa_insnbuf_word)));
-  xtensa_insnbuf_from_chars (isa, insn_buffer, priv.byte_buf, bytes_fetched);
+  insn_block = xtensa_find_table_entry (memaddr, info);
 
-  fmt = xtensa_format_decode (isa, insn_buffer);
-  if (fmt == XTENSA_UNDEFINED
-      || ((size = xtensa_format_length (isa, fmt)) > bytes_fetched))
-    valid_insn = 0;
-  else
+  /* Don't set "isa" before the setjmp to keep the compiler from griping.  */
+  isa = xtensa_default_isa;
+  size = 0;
+  nslots = 0;
+  valid_insn = 0;
+  fmt = 0;
+  if (!insn_block || (insn_block->flags & XTENSA_PROP_INSN))
     {
-      /* Make sure all the opcodes are valid.  */
-      valid_insn = 1;
-      nslots = xtensa_format_num_slots (isa, fmt);
-      for (n = 0; n < nslots; n++)
+      /* Copy the bytes into the decode buffer.  */
+      memset (insn_buffer, 0, (xtensa_insnbuf_size (isa) *
+                              sizeof (xtensa_insnbuf_word)));
+      xtensa_insnbuf_from_chars (isa, insn_buffer, priv.byte_buf,
+                                bytes_fetched);
+
+      fmt = xtensa_format_decode (isa, insn_buffer);
+      if (fmt != XTENSA_UNDEFINED
+         && ((size = xtensa_format_length (isa, fmt)) <= bytes_fetched)
+         && xtensa_instruction_fits (memaddr, size, insn_block))
        {
-         xtensa_format_get_slot (isa, fmt, n, insn_buffer, slot_buffer);
-         if (xtensa_opcode_decode (isa, fmt, n, slot_buffer)
-             == XTENSA_UNDEFINED)
+         /* Make sure all the opcodes are valid.  */
+         valid_insn = 1;
+         nslots = xtensa_format_num_slots (isa, fmt);
+         for (n = 0; n < nslots; n++)
            {
-             valid_insn = 0;
-             break;
+             xtensa_format_get_slot (isa, fmt, n, insn_buffer, slot_buffer);
+             if (xtensa_opcode_decode (isa, fmt, n, slot_buffer)
+                 == XTENSA_UNDEFINED)
+               {
+                 valid_insn = 0;
+                 break;
+               }
            }
        }
     }
 
   if (!valid_insn)
     {
-      (*info->fprintf_func) (info->stream, ".byte %#02x", priv.byte_buf[0]);
-      return 1;
+      if (insn_block && (insn_block->flags & XTENSA_PROP_LITERAL)
+         && (memaddr & 3) == 0 && bytes_fetched >= 4)
+       {
+         return 4;
+       }
+      else
+       {
+         (*info->fprintf_func) (info->stream, ".byte %#02x", priv.byte_buf[0]);
+         return 1;
+       }
     }
 
   if (nslots > 1)
This page took 0.0274 seconds and 4 git commands to generate.