mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-27 04:52:05 +08:00
6f2643dbd2
I noticed that the ARC print_one_insn selftest was failing. The problem is that in print_one_insn_test the arc case falls through into the special case that handles nios2, score, and riscv. The special case for these targets hard codes the breakpoint kind to '4'. This is find for bare metal arc (see arc-tdep.c, arc_sw_breakpoint_from_kind), however, for arc/linux only breakpoint kind '2' is supported (see arc-linux-tdep.c, arc_linux_sw_breakpoint_from_kind). So the code in print_one_insn_test as it is currently written passed in an invalid breakpoint kind, this leads to GDB trying to disassemble unexpected memory. The fix is to reorder the code in print_one_insn_test so that the arc case falls through into the default case. In the default we no longer hard code the breakpoint kind, and everything should be good. Additionally, given the arc code only expects specific breakpoint kind values, I thought it would be nice to add some gdb_assert to validate things. This assert would have triggered in this case and made it easier to find the error. After this commit, configure GDB with --enable-targets=all, then run gdb.gdb/unittest.exp, there should no longer be any failures. gdb/ChangeLog: * arc-linux-tdep.c (arc_linux_sw_breakpoint_from_kind): Add an assert. * arc-tdep.c (arc_breakpoint_kind_from_pc): Likewise. * disasm-selftests.c (print_one_insn_test): Fall throough from ARC case to the default.
219 lines
5.4 KiB
C
219 lines
5.4 KiB
C
/* 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 <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, 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 (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);
|
|
}
|