diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c index 43880fa4426..ce98dc2f884 100644 --- a/gdb/rs6000-tdep.c +++ b/gdb/rs6000-tdep.c @@ -6247,6 +6247,69 @@ UNKNOWN_OP: return 0; } +/* Used for matching tw, twi, td and tdi instructions for POWER. */ + +static constexpr uint32_t TX_INSN_MASK = 0xFC0007FF; +static constexpr uint32_t TW_INSN = 0x7C000008; +static constexpr uint32_t TD_INSN = 0x7C000088; + +static constexpr uint32_t TXI_INSN_MASK = 0xFC000000; +static constexpr uint32_t TWI_INSN = 0x0C000000; +static constexpr uint32_t TDI_INSN = 0x08000000; + +static inline bool +is_tw_insn (uint32_t insn) +{ + return (insn & TX_INSN_MASK) == TW_INSN; +} + +static inline bool +is_twi_insn (uint32_t insn) +{ + return (insn & TXI_INSN_MASK) == TWI_INSN; +} + +static inline bool +is_td_insn (uint32_t insn) +{ + return (insn & TX_INSN_MASK) == TD_INSN; +} + +static inline bool +is_tdi_insn (uint32_t insn) +{ + return (insn & TXI_INSN_MASK) == TDI_INSN; +} + +/* Implementation of gdbarch_program_breakpoint_here_p for POWER. */ + +static bool +rs6000_program_breakpoint_here_p (gdbarch *gdbarch, CORE_ADDR address) +{ + gdb_byte target_mem[PPC_INSN_SIZE]; + + /* Enable the automatic memory restoration from breakpoints while + we read the memory. Otherwise we may find temporary breakpoints, ones + inserted by GDB, and flag them as permanent breakpoints. */ + scoped_restore restore_memory + = make_scoped_restore_show_memory_breakpoints (0); + + if (target_read_memory (address, target_mem, PPC_INSN_SIZE) == 0) + { + uint32_t insn = (uint32_t) extract_unsigned_integer + (target_mem, PPC_INSN_SIZE, gdbarch_byte_order_for_code (gdbarch)); + + /* Check if INSN is a TW, TWI, TD or TDI instruction. There + are multiple choices of such instructions with different registers + and / or immediate values but they all cause a break. */ + if (is_tw_insn (insn) || is_twi_insn (insn) || is_td_insn (insn) + || is_tdi_insn (insn)) + return true; + } + + return false; +} + /* Initialize the current architecture based on INFO. If possible, re-use an architecture from ARCHES, which is a list of architectures already created during this debugging session. @@ -7109,6 +7172,8 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) rs6000_breakpoint::kind_from_pc); set_gdbarch_sw_breakpoint_from_kind (gdbarch, rs6000_breakpoint::bp_from_kind); + set_gdbarch_program_breakpoint_here_p (gdbarch, + rs6000_program_breakpoint_here_p); /* The value of symbols of type N_SO and N_FUN maybe null when it shouldn't be. */ diff --git a/gdb/testsuite/gdb.arch/powerpc-trap.exp b/gdb/testsuite/gdb.arch/powerpc-trap.exp new file mode 100644 index 00000000000..57a683d1203 --- /dev/null +++ b/gdb/testsuite/gdb.arch/powerpc-trap.exp @@ -0,0 +1,72 @@ +# Copyright 2021 Free Software Foundation, Inc. +# +# 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 . +# +# This file is part of the gdb testsuite. + +# Test if GDB stops at various trap instructions inserted into +# the code. + +if { [istarget powerpc-*] } { + standard_testfile powerpc-trap.s + # Number of expected SIGTRAPs to get. This needs to be kept in sync + # with the source file powerpc-trap.s. + set expected_traps 3 +} elseif {[istarget powerpc64*] } { + standard_testfile powerpc64-trap.s + # Number of expected SIGTRAPs to get. This needs to be kept in sync + # with the source file powerpc64-trap.s. + set expected_traps 5 +} else { + verbose "Skipping ${gdb_test_file_name}." + return +} + +if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} { + return -1 +} + +if {![runto_main]} { + untested "could not run to main" + return -1 +} + +set keep_going 1 +set count 0 + +while {$keep_going} { + set keep_going 0 + + # Continue to next program breakpoint instruction. + gdb_test_multiple "continue" "trap instruction $count causes SIGTRAP" { + -re "Program received signal SIGTRAP, Trace/breakpoint trap.*$gdb_prompt $" { + pass $gdb_test_name + + # Advance PC to next instruction + gdb_test "set \$pc = \$pc + 4" "" "advance past trap instruction $count" + + incr count + if {$count < $expected_traps} { + set keep_going 1 + } + } + } +} + +# Verify we stopped at the expected number of SIGTRAP's. +gdb_assert {$count == $expected_traps} "all trap instructions triggered" + +# One last continue to reach the end of the test, to make sure we don't get +# another SIGTRAP. +gdb_test "continue" "exited normally.*" "continue to end" diff --git a/gdb/testsuite/gdb.arch/powerpc-trap.s b/gdb/testsuite/gdb.arch/powerpc-trap.s new file mode 100644 index 00000000000..b03176f747e --- /dev/null +++ b/gdb/testsuite/gdb.arch/powerpc-trap.s @@ -0,0 +1,31 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2021 Free Software Foundation, Inc. + + 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 . */ + +/* To test if GDB stops at various trap instructions inserted into + the code. */ + +.global main +.type main,function +main: + ori 0, 0, 0 + trap + tw 12, 2, 2 + twi 31, 3, 3 + ori 0, 0, 0 + li 3, 0 + blr + diff --git a/gdb/testsuite/gdb.arch/powerpc64-trap.s b/gdb/testsuite/gdb.arch/powerpc64-trap.s new file mode 100644 index 00000000000..2272b42edc2 --- /dev/null +++ b/gdb/testsuite/gdb.arch/powerpc64-trap.s @@ -0,0 +1,33 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2021 Free Software Foundation, Inc. + + 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 . */ + +/* To test if GDB stops at various trap instructions inserted into + the code. */ + +.global main +.type main,function +main: + ori 0, 0, 0 + trap + tw 12, 2, 2 + twi 31, 3, 3 + td 12, 2, 2 + tdi 31, 3, 3 + ori 0, 0, 0 + li 3, 0 + blr +