binutils-gdb/gdb/disasm-selftests.c
John Baldwin f37bc8b13c Use the existing instruction to determine the RISC-V breakpoint kind.
RISC-V supports instructions of varying lengths.  Standard existing
instructions in the base ISA are 4 bytes in length, but the 'C'
extension adds support for compressed, 2 byte instructions.  RISC-V
supports two different breakpoint instructions: EBREAK is a 4 byte
instruction in the base ISA, and C.EBREAK is a 2 byte instruction only
available on processors implementing the 'C' extension.  Using EBREAK
to set breakpoints on compressed instructions causes problems as the
second half of EBREAK will overwrite the first 2 bytes of the
following instruction breaking other threads in the process if their
PC is the following instruction.  Thus, breakpoints on compressed
instructions need to use C.EBREAK instead of EBREAK.

Previously, the riscv architecture checked the MISA register to
determine if the 'C' extension was available.  If so, it used C.EBREAK
for all breakpoints.  However, the MISA register is not necessarily
available to supervisor mode operating systems.  While native targets
could provide a fake MISA register value, this patch instead examines
the existing instruction at a breakpoint target to determine which
breakpoint instruction to use.  If the existing instruction is a
compressed instruction, C.EBREAK is used, otherwise EBREAK is used.

gdb/ChangeLog:

	* disasm-selftests.c (print_one_insn_test): Add bfd_arch_riscv to
	case with explicit breakpoint kind.
	* riscv-tdep.c (show_use_compressed_breakpoints): Remove
	'additional_info' and related logic.
	(riscv_debug_breakpoints): New variable.
	(riscv_breakpoint_kind_from_pc): Use the length of the existing
	instruction to determine the breakpoint kind.
	(_initialize_riscv_tdep): Add 'set/show debug riscv breakpoints'
	flag.  Update description of 'set/show riscv
	use-compressed-breakpoints' flag.
2018-09-28 14:15:54 -07:00

221 lines
5.4 KiB
C

/* Self tests for disassembler for GDB, the GNU debugger.
Copyright (C) 2017-2018 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"
#if GDB_SELF_TEST
#include "selftest.h"
#include "selftest-arch.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<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 (ex, RETURN_MASK_ERROR)
{
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 */
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
}