| /* Self tests for disassembler for GDB, the GNU debugger. |
| |
| Copyright (C) 2017-2021 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 <http://www.gnu.org/licenses/>. */ |
| |
| #include "defs.h" |
| #include "disasm.h" |
| #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_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 */ |
| 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, use maint selftest -verbose. */ |
| |
| 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, |
| (run_verbose () ? gdb_stdout : &null_stream), |
| gdb_disassembler_test::read_memory), |
| m_insn (insn), m_len (len) |
| { |
| } |
| |
| int |
| print_insn (CORE_ADDR memaddr) |
| { |
| if (run_verbose ()) |
| { |
| fprintf_unfiltered (stream (), "%s ", |
| gdbarch_bfd_arch_info (arch ())->arch_name); |
| } |
| |
| int len = gdb_disassembler::print_insn (memaddr); |
| |
| if (run_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<gdb_disassembler_test *>(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 |
| |
| void _initialize_disasm_selftests (); |
| void |
| _initialize_disasm_selftests () |
| { |
| selftests::register_test_foreach_arch ("print_one_insn", |
| selftests::print_one_insn_test); |
| selftests::register_test_foreach_arch ("memory_error", |
| selftests::memory_error_test); |
| } |