/* 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)
{
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)
{
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;
}
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);
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
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);
}