[PATCH 2/2] ppc: recognize all program traps

Lancelot SIX lsix@lancelotsix.com
Wed Nov 24 10:43:51 GMT 2021


Hi,

I do not know PPC so I will not comment on what actually done, but I
have minor remarks noted below.

On Tue, Nov 23, 2021 at 03:42:37PM +0000, Jan Vrany via Gdb-patches wrote:
> Permanent program breakpoints (ones inserted into the code) other than
> the one GDB uses for POWER (0x7fe00008) did not result in stop but
> caused GDB to loop infinitely.
> 
> This was because GDB did not recognize trap instructions other than
> "trap". For example, "tw 12, 4, 4" was not be recognized, causing GDB
> to loop forever.
> 
> This commit fixes this by providing POWER specific hook
> (gdbarch_program_breakpoint_here_p) recognizing all tw, twi, td and tdi
> instructions.
> 
> Tested on Linux on PowerPC e500 and on QEMU PPC64le.
> ---
>  gdb/rs6000-tdep.c                       | 66 +++++++++++++++++++++++
>  gdb/testsuite/gdb.arch/powerpc-trap.exp | 72 +++++++++++++++++++++++++
>  gdb/testsuite/gdb.arch/powerpc-trap.s   | 30 +++++++++++
>  gdb/testsuite/gdb.arch/ppc64-trap.exp   | 72 +++++++++++++++++++++++++
>  gdb/testsuite/gdb.arch/ppc64-trap.s     | 32 +++++++++++
>  5 files changed, 272 insertions(+)
>  create mode 100644 gdb/testsuite/gdb.arch/powerpc-trap.exp
>  create mode 100644 gdb/testsuite/gdb.arch/powerpc-trap.s
>  create mode 100644 gdb/testsuite/gdb.arch/ppc64-trap.exp
>  create mode 100644 gdb/testsuite/gdb.arch/ppc64-trap.s
> 
> diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
> index 43880fa4426..eeafca1ff99 100644
> --- a/gdb/rs6000-tdep.c
> +++ b/gdb/rs6000-tdep.c
> @@ -6247,6 +6247,70 @@ ppc_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
>    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)
> +{
> +  const uint32_t insn_len = 4;
> +  gdb_byte target_mem[4];

You could probably use 'gdb_byte target_mem[insn_len]' here.

> +
> +  /* 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, insn_len) == 0)
> +    {
> +      uint32_t insn = (uint32_t)extract_unsigned_integer (
> +          target_mem, insn_len, 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 +7173,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..4e86bd4b9a1
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/powerpc-trap.exp
> @@ -0,0 +1,72 @@
> +# Copyright 2020-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 <http://www.gnu.org/licenses/>.
> +#
> +# This file is part of the gdb testsuite.
> +
> +# Test if GDB stops at various trap instructions inserted into
> +# the code.
> +
> +if { ![istarget powerpc-*] } {
> +    verbose "Skipping ${gdb_test_file_name}."
> +    return
> +}
> +
> +standard_testfile .s
> +if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} {
> +    return -1
> +}
> +
> +if {![runto_main]} {
> +    untested "could not run to main"
> +    return -1
> +}
> +
> +# Number of expected SIGTRAP's to get.  This needs to be kept in sync
> +# with the source file.
> +set expected_traps 3
> +set keep_going 1
> +set count 0
> +
> +# Make sure we have a lower timeout in case GDB doesn't support a particular
> +# instruction.  Such instruction will cause GDB to loop infinitely.
> +while {$keep_going} {
> +    # 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 nex instruction
> +	    gdb_test "set \$pc = \$pc + 4" "" "advance past trap instruction $count"
> +
> +	 #    # Insert a breakpoint at the program breakpoint instruction so
> +	 #    # GDB can step over it.
> +	 #    gdb_test "break" \
> +		# "Breakpoint $decimal at $hex: file .*$srcfile, line $decimal.*" \
> +		# "insert breakpoint at trap instruction $count"

The part above is commented.  Is this something you forgot to remove
before creating your patch?

> +	    incr count
> +	}
> +	# We've reached the end of the test.
> +	-re "exited with code 01.*$gdb_prompt $" {
> +	    set keep_going 0
> +	}
> +	timeout {
> +	    fail $gdb_test_name
> +	    set keep_going 0
> +	}
> +    }
> +}
> +
> +# Verify we stopped at the expected number of SIGTRAP's.
> +gdb_assert {$count == $expected_traps} "all trap instructions triggered"
> diff --git a/gdb/testsuite/gdb.arch/powerpc-trap.s b/gdb/testsuite/gdb.arch/powerpc-trap.s
> new file mode 100644
> index 00000000000..c503cb1e33e
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/powerpc-trap.s
> @@ -0,0 +1,30 @@
> +/* 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 <http://www.gnu.org/licenses/>. */
> +
> +/* 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
> +  blr
> +
> diff --git a/gdb/testsuite/gdb.arch/ppc64-trap.exp b/gdb/testsuite/gdb.arch/ppc64-trap.exp
> new file mode 100644
> index 00000000000..1341808b9fd
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/ppc64-trap.exp
> @@ -0,0 +1,72 @@
> +# Copyright 2020-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 <http://www.gnu.org/licenses/>.
> +#
> +# This file is part of the gdb testsuite.
> +
> +# Test if GDB stops at various trap instructions inserted into
> +# the code.
> +
> +if { ![istarget powerpc64-*] } {
> +    verbose "Skipping ${gdb_test_file_name}."
> +    return
> +}
> +
> +standard_testfile .s
> +if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} {
> +    return -1
> +}
> +
> +if {![runto_main]} {
> +    untested "could not run to main"
> +    return -1
> +}
> +
> +# Number of expected SIGTRAP's to get.  This needs to be kept in sync
> +# with the source file.
> +set expected_traps 5
> +set keep_going 1
> +set count 0
> +
> +# Make sure we have a lower timeout in case GDB doesn't support a particular
> +# instruction.  Such instruction will cause GDB to loop infinitely.
> +while {$keep_going} {
> +    # 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 nex instruction
> +	    gdb_test "set \$pc = \$pc + 4" "" "advance past trap instruction $count"
> +
> +	 #    # Insert a breakpoint at the program breakpoint instruction so
> +	 #    # GDB can step over it.
> +	 #    gdb_test "break" \
> +		# "Breakpoint $decimal at $hex: file .*$srcfile, line $decimal.*" \
> +		# "insert breakpoint at trap instruction $count"

Similar remark here, did you forget to remove this?

Best,
Lancelot.

> +	    incr count
> +	}
> +	# We've reached the end of the test.
> +	-re "exited with code 01.*$gdb_prompt $" {
> +	    set keep_going 0
> +	}
> +	timeout {
> +	    fail $gdb_test_name
> +	    set keep_going 0
> +	}
> +    }
> +}
> +
> +# Verify we stopped at the expected number of SIGTRAP's.
> +gdb_assert {$count == $expected_traps} "all trap instructions triggered"
> diff --git a/gdb/testsuite/gdb.arch/ppc64-trap.s b/gdb/testsuite/gdb.arch/ppc64-trap.s
> new file mode 100644
> index 00000000000..b307018f3ec
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/ppc64-trap.s
> @@ -0,0 +1,32 @@
> +/* 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 <http://www.gnu.org/licenses/>. */
> +
> +/* 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
> +  blr
> +
> -- 
> 2.30.2
> 


More information about the Gdb-patches mailing list