/* Self tests for disassembler for GDB, the GNU debugger. Copyright (C) 2017-2020 Free Software Foundation, Inc. This file is part of GDB. 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 3 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, see . */ #include "defs.h" #include "disasm.h" #if GDB_SELF_TEST #include "gdbsupport/selftest.h" #include "selftest-arch.h" #include "gdbarch.h" namespace selftests { /* Test disassembly of one instruction. */ static void print_one_insn_test (struct gdbarch *gdbarch) { size_t len = 0; const gdb_byte *insn = NULL; switch (gdbarch_bfd_arch_info (gdbarch)->arch) { case bfd_arch_bfin: /* M3.L = 0xe117 */ static const gdb_byte bfin_insn[] = {0x17, 0xe1, 0xff, 0xff}; insn = 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); break; case bfd_arch_ia64: case bfd_arch_mep: case bfd_arch_mips: case bfd_arch_tic6x: case bfd_arch_xtensa: return; case bfd_arch_s390: /* nopr %r7 */ static const gdb_byte s390_insn[] = {0x07, 0x07}; insn = 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); 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: 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; default: { /* Test disassemble breakpoint instruction. */ CORE_ADDR pc = 0; int kind = gdbarch_breakpoint_kind_from_pc (gdbarch, &pc); int bplen; insn = gdbarch_sw_breakpoint_from_kind (gdbarch, kind, &bplen); len = bplen; break; } } SELF_CHECK (len > 0); /* 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. */ static const bool verbose = false; class gdb_disassembler_test : public gdb_disassembler { public: explicit gdb_disassembler_test (struct gdbarch *gdbarch, const gdb_byte *insn, size_t len) : gdb_disassembler (gdbarch, (verbose ? gdb_stdout : &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"); return len; } private: /* A buffer contain one instruction. */ const gdb_byte *m_insn; /* Length of the buffer. */ size_t m_len; static int read_memory (bfd_vma memaddr, gdb_byte *myaddr, unsigned int len, struct disassemble_info *info) { gdb_disassembler_test *self = static_cast(info->application_data); /* The disassembler in opcodes may read more data than one instruction. Supply infinite consecutive copies of the same instruction. */ for (size_t i = 0; i < len; i++) myaddr[i] = self->m_insn[(memaddr + i) % self->m_len]; return 0; } }; gdb_disassembler_test di (gdbarch, insn, len); SELF_CHECK (di.print_insn (0) == len); } /* Test disassembly on memory error. */ static void memory_error_test (struct gdbarch *gdbarch) { class gdb_disassembler_test : public gdb_disassembler { public: gdb_disassembler_test (struct gdbarch *gdbarch) : gdb_disassembler (gdbarch, &null_stream, gdb_disassembler_test::read_memory) { } static int read_memory (bfd_vma memaddr, gdb_byte *myaddr, unsigned int len, struct disassemble_info *info) { /* Always return an error. */ return -1; } }; gdb_disassembler_test di (gdbarch); bool saw_memory_error = false; try { di.print_insn (0); } catch (const gdb_exception_error &ex) { if (ex.error == MEMORY_ERROR) saw_memory_error = true; } /* Expect MEMORY_ERROR. */ SELF_CHECK (saw_memory_error); } } // namespace selftests #endif /* GDB_SELF_TEST */ void _initialize_disasm_selftests (void) { #if GDB_SELF_TEST selftests::register_test_foreach_arch ("print_one_insn", selftests::print_one_insn_test); selftests::register_test_foreach_arch ("memory_error", selftests::memory_error_test); #endif }