gdb: LoongArch: Handle special struct in dummy call
[binutils-gdb.git] / gdb / disasm-selftests.c
index 7d0b006d3972eba5041fd4de205599c924fee8ca..0cf766a48b76317eb33bb45caa57d1743a71b4f5 100644 (file)
@@ -1,6 +1,6 @@
 /* Self tests for disassembler for GDB, the GNU debugger.
 
-   Copyright (C) 2017 Free Software Foundation, Inc.
+   Copyright (C) 2017-2023 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 
 #include "defs.h"
 #include "disasm.h"
-
-#if GDB_SELF_TEST
-#include "selftest.h"
+#include "gdbsupport/selftest.h"
 #include "selftest-arch.h"
+#include "gdbarch.h"
 
 namespace selftests {
 
-/* Test disassembly of one instruction.  */
+/* Return a pointer to a buffer containing an instruction that can be
+   disassembled for architecture GDBARCH.  *LEN will be set to the length
+   of the returned buffer.
 
-static void
-print_one_insn_test (struct gdbarch *gdbarch)
+   If there's no known instruction to disassemble for GDBARCH (because we
+   haven't figured on out, not because no instructions exist) then nullptr
+   is returned, and *LEN is set to 0.  */
+
+static const gdb_byte *
+get_test_insn (struct gdbarch *gdbarch, size_t *len)
 {
-  size_t len = 0;
-  const gdb_byte *insn = NULL;
+  *len = 0;
+  const gdb_byte *insn = nullptr;
 
   switch (gdbarch_bfd_arch_info (gdbarch)->arch)
     {
@@ -41,79 +46,170 @@ print_one_insn_test (struct gdbarch *gdbarch)
       static const gdb_byte bfin_insn[] = {0x17, 0xe1, 0xff, 0xff};
 
       insn = bfin_insn;
-      len = sizeof (bfin_insn);
+      *len = sizeof (bfin_insn);
       break;
     case bfd_arch_arm:
       /* mov     r0, #0 */
       static const gdb_byte arm_insn[] = {0x0, 0x0, 0xa0, 0xe3};
 
       insn = arm_insn;
-      len = sizeof (arm_insn);
+      *len = sizeof (arm_insn);
       break;
     case bfd_arch_ia64:
+      /* We get:
+        internal-error: gdbarch_sw_breakpoint_from_kind:
+        Assertion `gdbarch->sw_breakpoint_from_kind != NULL' failed.  */
+      return insn;
     case bfd_arch_mep:
+      /* Disassembles as '*unknown*' insn, then len self-check fails.  */
+      return insn;
     case bfd_arch_mips:
+      if (gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_mips16)
+       /* Disassembles insn, but len self-check fails.  */
+       return insn;
+      goto generic_case;
     case bfd_arch_tic6x:
+      /* Disassembles as '<undefined instruction 0x56454314>' insn, but len
+        self-check passes, so let's allow it.  */
+      goto generic_case;
     case bfd_arch_xtensa:
-      return;
+      /* Disassembles insn, but len self-check fails.  */
+      return insn;
+    case bfd_arch_or1k:
+      /* Disassembles as '*unknown*' insn, but len self-check passes, so let's
+        allow it.  */
+      goto generic_case;
     case bfd_arch_s390:
       /* nopr %r7 */
       static const gdb_byte s390_insn[] = {0x07, 0x07};
 
       insn = s390_insn;
-      len = sizeof (s390_insn);
+      *len = sizeof (s390_insn);
       break;
     case bfd_arch_xstormy16:
       /* nop */
       static const gdb_byte xstormy16_insn[] = {0x0, 0x0};
 
       insn = xstormy16_insn;
-      len = sizeof (xstormy16_insn);
+      *len = sizeof (xstormy16_insn);
+      break;
+    case bfd_arch_nios2:
+    case bfd_arch_score:
+    case bfd_arch_riscv:
+      /* nios2, riscv, and score need to know the current instruction
+        to select breakpoint instruction.  Give the breakpoint
+        instruction kind explicitly.  */
+      {
+       int bplen;
+       insn = gdbarch_sw_breakpoint_from_kind (gdbarch, 4, &bplen);
+       *len = bplen;
+      }
       break;
     case bfd_arch_arc:
       /* PR 21003 */
       if (gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_arc_arc601)
-       return;
-      /* fall through */
-    case bfd_arch_nios2:
-    case bfd_arch_score:
-      /* nios2 and score need to know the current instruction to select
-        breakpoint instruction.  Give the breakpoint instruction kind
-        explicitly.  */
-      int bplen;
-      insn = gdbarch_sw_breakpoint_from_kind (gdbarch, 4, &bplen);
-      len = bplen;
+       return insn;
+      goto generic_case;
+    case bfd_arch_z80:
+      {
+       int bplen;
+       insn = gdbarch_sw_breakpoint_from_kind (gdbarch, 0x0008, &bplen);
+       *len = bplen;
+      }
       break;
+    case bfd_arch_i386:
+      {
+       const struct bfd_arch_info *info = gdbarch_bfd_arch_info (gdbarch);
+       /* The disassembly tests will fail on x86-linux because
+          opcodes rejects an attempt to disassemble for an arch with
+          a 64-bit address size when bfd_vma is 32-bit.  */
+       if (info->bits_per_address > sizeof (bfd_vma) * CHAR_BIT)
+         return insn;
+      }
+      /* fall through */
     default:
+    generic_case:
       {
        /* Test disassemble breakpoint instruction.  */
        CORE_ADDR pc = 0;
-       int kind = gdbarch_breakpoint_kind_from_pc (gdbarch, &pc);
+       int kind;
        int bplen;
 
-       insn = gdbarch_sw_breakpoint_from_kind (gdbarch, kind, &bplen);
-       len = bplen;
-
+       struct gdbarch_info info;
+       info.bfd_arch_info = gdbarch_bfd_arch_info (gdbarch);
+
+       enum gdb_osabi it;
+       bool found = false;
+       for (it = GDB_OSABI_UNKNOWN; it != GDB_OSABI_INVALID;
+            it = static_cast<enum gdb_osabi>(static_cast<int>(it) + 1))
+         {
+           if (it == GDB_OSABI_UNKNOWN)
+             continue;
+
+           info.osabi = it;
+
+           if (it != GDB_OSABI_NONE)
+             {
+               if (!has_gdb_osabi_handler (info))
+                 /* Unsupported.  Skip to prevent warnings like:
+                    A handler for the OS ABI <x> is not built into this
+                    configuration of GDB.  Attempting to continue with the
+                    default <y> settings.  */
+                 continue;
+             }
+
+           gdbarch = gdbarch_find_by_info (info);
+           SELF_CHECK (gdbarch != NULL);
+
+           try
+             {
+               kind = gdbarch_breakpoint_kind_from_pc (gdbarch, &pc);
+               insn = gdbarch_sw_breakpoint_from_kind (gdbarch, kind, &bplen);
+             }
+           catch (...)
+             {
+               continue;
+             }
+           found = true;
+           break;
+         }
+
+       /* Assert that we have found an instruction to disassemble.  */
+       SELF_CHECK (found);
+
+       *len = bplen;
        break;
       }
     }
-  SELF_CHECK (len > 0);
+  SELF_CHECK (*len > 0);
+
+  return insn;
+}
+
+/* Test disassembly of one instruction.  */
+
+static void
+print_one_insn_test (struct gdbarch *gdbarch)
+{
+  size_t len;
+  const gdb_byte *insn = get_test_insn (gdbarch, &len);
+
+  if (insn == nullptr)
+    return;
 
   /* Test gdb_disassembler for a given gdbarch by reading data from a
      pre-allocated buffer.  If you want to see the disassembled
-     instruction printed to gdb_stdout, set verbose to true.  */
+     instruction printed to gdb_stdout, use maint selftest -verbose.  */
 
   class gdb_disassembler_test : public gdb_disassembler
   {
   public:
 
-    const bool verbose = false;
-
     explicit gdb_disassembler_test (struct gdbarch *gdbarch,
                                    const gdb_byte *insn,
                                    size_t len)
       : gdb_disassembler (gdbarch,
-                         (verbose ? gdb_stdout : &null_stream),
+                         (run_verbose () ? gdb_stdlog : &null_stream),
                          gdb_disassembler_test::read_memory),
        m_insn (insn), m_len (len)
     {
@@ -122,16 +218,10 @@ print_one_insn_test (struct gdbarch *gdbarch)
     int
     print_insn (CORE_ADDR memaddr)
     {
-      if (verbose)
-       {
-         fprintf_unfiltered (stream (), "%s ",
-                             gdbarch_bfd_arch_info (arch ())->arch_name);
-       }
-
       int len = gdb_disassembler::print_insn (memaddr);
 
-      if (verbose)
-       fprintf_unfiltered (stream (), "\n");
+      if (run_verbose ())
+       debug_printf ("\n");
 
       return len;
     }
@@ -144,7 +234,8 @@ print_one_insn_test (struct gdbarch *gdbarch)
     size_t m_len;
 
     static int read_memory (bfd_vma memaddr, gdb_byte *myaddr,
-                           unsigned int len, struct disassemble_info *info)
+                           unsigned int len,
+                           struct disassemble_info *info) noexcept
     {
       gdb_disassembler_test *self
        = static_cast<gdb_disassembler_test *>(info->application_data);
@@ -164,6 +255,32 @@ print_one_insn_test (struct gdbarch *gdbarch)
   SELF_CHECK (di.print_insn (0) == len);
 }
 
+/* Test the gdb_buffered_insn_length function.  */
+
+static void
+buffered_insn_length_test (struct gdbarch *gdbarch)
+{
+  size_t buf_len;
+  const gdb_byte *insn = get_test_insn (gdbarch, &buf_len);
+
+  if (insn == nullptr)
+    return;
+
+  /* The tic6x architecture is VLIW.  Disassembling requires that the
+     entire instruction bundle be available.  However, the buffer we got
+     back from get_test_insn only contains a single instruction, which is
+     just part of an instruction bundle.  As a result, the disassemble will
+     fail.  To avoid this, skip tic6x tests now.  */
+  if (gdbarch_bfd_arch_info (gdbarch)->arch == bfd_arch_tic6x)
+    return;
+
+  CORE_ADDR insn_address = 0;
+  int calculated_len = gdb_buffered_insn_length (gdbarch, insn, buf_len,
+                                                insn_address);
+
+  SELF_CHECK (calculated_len == buf_len);
+}
+
 /* Test disassembly on memory error.  */
 
 static void
@@ -180,42 +297,50 @@ memory_error_test (struct gdbarch *gdbarch)
 
     static int read_memory (bfd_vma memaddr, gdb_byte *myaddr,
                            unsigned int len,
-                           struct disassemble_info *info)
+                           struct disassemble_info *info) noexcept
     {
       /* Always return an error.  */
       return -1;
     }
   };
 
+  if (gdbarch_bfd_arch_info (gdbarch)->arch == bfd_arch_i386)
+    {
+      const struct bfd_arch_info *info = gdbarch_bfd_arch_info (gdbarch);
+      /* This test will fail on x86-linux because opcodes rejects an
+        attempt to disassemble for an arch with a 64-bit address size
+        when bfd_vma is 32-bit.  */
+      if (info->bits_per_address > sizeof (bfd_vma) * CHAR_BIT)
+       return;
+    }
+
   gdb_disassembler_test di (gdbarch);
   bool saw_memory_error = false;
 
-  TRY
+  try
     {
       di.print_insn (0);
     }
-  CATCH (ex, RETURN_MASK_ERROR)
+  catch (const gdb_exception_error &ex)
     {
       if (ex.error == MEMORY_ERROR)
        saw_memory_error = true;
     }
-  END_CATCH
 
   /* Expect MEMORY_ERROR.  */
   SELF_CHECK (saw_memory_error);
 }
 
 } // namespace selftests
-#endif /* GDB_SELF_TEST */
-
-/* Suppress warning from -Wmissing-prototypes.  */
-extern initialize_file_ftype _initialize_disasm_selftests;
 
+void _initialize_disasm_selftests ();
 void
-_initialize_disasm_selftests (void)
+_initialize_disasm_selftests ()
 {
-#if GDB_SELF_TEST
-  register_self_test_foreach_arch (selftests::print_one_insn_test);
-  register_self_test_foreach_arch (selftests::memory_error_test);
-#endif
+  selftests::register_test_foreach_arch ("print_one_insn",
+                                        selftests::print_one_insn_test);
+  selftests::register_test_foreach_arch ("memory_error",
+                                        selftests::memory_error_test);
+  selftests::register_test_foreach_arch ("buffered_insn_length",
+                                        selftests::buffered_insn_length_test);
 }