[rs6000,v2,PR,gdb/27525] displaced stepping across addpcis/lnia.

Message ID 51978a814fe5323522121faf8200474e1a448f94.camel@vnet.ibm.com
State New
Headers show
Series
  • [rs6000,v2,PR,gdb/27525] displaced stepping across addpcis/lnia.
Related show

Commit Message

Kumar N, Bhuvanendra via Gdb-patches March 27, 2021, 12:25 a.m.
[PATCH, rs6000][PR gdb/27525] displaced stepping across addpcis/lnia.
    
    Hi,
      This addresses PR gdb/27525.     The lnia and other variations
    of the addpcis instruction write the value of the NIA into a target register.
    If we are single-stepping across a breakpoint, the instruction is executed
    from a displaced location, and thusly the written value of the PC/NIA
    will be incorrect.   The changes here will measure the displacement
    offset, and adjust the target register value to compensate.
    
    This is written in a way that I believe will make it easier to
    update to handle prefixed (8 byte) instructions in a future patch.
    
    [V2 updates] - Rework the helper functions so they are in-line within
    ppc_displaced_step_fixup().  Cleaned up FIXME comments.  Moved ADDPCIS related
    #define values up near where other defines currently live.
    
    
    YYYY-MM-DD  Will Schmidt  <will_schmidt@vnet.ibm.com>
    
    gdb/ChangeLog:
    
            * gdb/rs6000-tdep.c (ppc_displaced_step_fixup): Update to handle
            the addpcis/lnia instruction.
    
    gdb/testsuite/ChangeLog:
    
            * gdb/testsuite/gdb.arch/powerpc-addpcis.exp: Testcase harness to
            exercise single-stepping over subpcis,lnia,addpcis instructions
            with displacement.
            * gdb/testsuite/gdb.arch/powerpc-addpcis.s: Testcase with stream
            of addpcis/lnia/subpcis instructions.
            * gdb/testsuite/gdb.arch/powerpc-lnia.exp: Testcase harness to
            exercise single-stepping over lnia instructions with displacement.
            * gdb/testsuite/gdb.arch/powerpc-lnia.s: Testcase with stream of
            lnia instructions.

Comments

Kumar N, Bhuvanendra via Gdb-patches March 29, 2021, 3:16 p.m. | #1
On Fri, Mar 26, 2021 at 07:25:07PM -0500, will schmidt wrote:
>     gdb/ChangeLog:

>     

>             * gdb/rs6000-tdep.c (ppc_displaced_step_fixup): Update to handle

>             the addpcis/lnia instruction.


Should omit the leading "gdb/".

>     gdb/testsuite/ChangeLog:

>     

>             * gdb/testsuite/gdb.arch/powerpc-addpcis.exp: Testcase harness to

>             exercise single-stepping over subpcis,lnia,addpcis instructions

>             with displacement.

>             * gdb/testsuite/gdb.arch/powerpc-addpcis.s: Testcase with stream

>             of addpcis/lnia/subpcis instructions.

>             * gdb/testsuite/gdb.arch/powerpc-lnia.exp: Testcase harness to

>             exercise single-stepping over lnia instructions with displacement.

>             * gdb/testsuite/gdb.arch/powerpc-lnia.s: Testcase with stream of

>             lnia instructions.


Should omit the leading "gdb/testsuite/".

> +  /* Handle the addpcis/lnia instruction.  */

> +  if ((insn & ADDPCIS_INSN_MASK) == ADDPCIS_INSN)

> +    {

> +      LONGEST displaced_offset;

> +      ULONGEST current_val;

> +      /* Measure the displacement.  */

> +      displaced_offset = from - to;

> +      displaced_debug_printf ("(ppc) addpcis displaced_offset offset %lx",

> +				displaced_offset );

> +      /* Identify the target register that was updated by the instruction.  */

> +      int regnum = (insn & ADDPCIS_TARGET_REGISTER) >> ADDPCIS_INSN_REGSHIFT;

> +      displaced_debug_printf ("(ppc) addpcis register:%d, insn_size:%ld",

> +				regnum,offset);

> +      /* Read and update the target value.  */

> +      regcache_cooked_read_unsigned (regs, regnum , &current_val);

> +      displaced_debug_printf ("(ppc) target regnum %d was 0x%lx now 0x%lx",

> +			regnum, current_val, current_val + displaced_offset );

> +      regcache_cooked_write_unsigned (regs, regnum,

> +					current_val + displaced_offset);

> +      /* point the PC back at the non-displaced instruction.  */

> +      displaced_debug_printf ("(ppc) addpcis fixup register:%d,insn_size:%ld",

> +				regnum, offset);

> +      regcache_cooked_write_unsigned (regs, gdbarch_pc_regnum (gdbarch),

> +				    from + offset);

> +    }


I don't think we need all these displaced_debug_printf statements here,
they look quite redundant.  I think a single statement that just shows
how the returned value was updated should be enough.

Otherwise this looks good to me.

Thanks,
Ulrich

-- 
  Dr. Ulrich Weigand
  GNU/Linux compilers and toolchain
  Ulrich.Weigand@de.ibm.com
Kumar N, Bhuvanendra via Gdb-patches March 29, 2021, 3:35 p.m. | #2
On 2021-03-29 11:16 a.m., Ulrich Weigand via Gdb-patches wrote:
> On Fri, Mar 26, 2021 at 07:25:07PM -0500, will schmidt wrote:

>>     gdb/ChangeLog:

>>     

>>             * gdb/rs6000-tdep.c (ppc_displaced_step_fixup): Update to handle

>>             the addpcis/lnia instruction.

> 

> Should omit the leading "gdb/".

> 

>>     gdb/testsuite/ChangeLog:

>>     

>>             * gdb/testsuite/gdb.arch/powerpc-addpcis.exp: Testcase harness to

>>             exercise single-stepping over subpcis,lnia,addpcis instructions

>>             with displacement.

>>             * gdb/testsuite/gdb.arch/powerpc-addpcis.s: Testcase with stream

>>             of addpcis/lnia/subpcis instructions.

>>             * gdb/testsuite/gdb.arch/powerpc-lnia.exp: Testcase harness to

>>             exercise single-stepping over lnia instructions with displacement.

>>             * gdb/testsuite/gdb.arch/powerpc-lnia.s: Testcase with stream of

>>             lnia instructions.

> 

> Should omit the leading "gdb/testsuite/".

> 

>> +  /* Handle the addpcis/lnia instruction.  */

>> +  if ((insn & ADDPCIS_INSN_MASK) == ADDPCIS_INSN)

>> +    {

>> +      LONGEST displaced_offset;

>> +      ULONGEST current_val;

>> +      /* Measure the displacement.  */

>> +      displaced_offset = from - to;

>> +      displaced_debug_printf ("(ppc) addpcis displaced_offset offset %lx",

>> +				displaced_offset );

>> +      /* Identify the target register that was updated by the instruction.  */

>> +      int regnum = (insn & ADDPCIS_TARGET_REGISTER) >> ADDPCIS_INSN_REGSHIFT;

>> +      displaced_debug_printf ("(ppc) addpcis register:%d, insn_size:%ld",

>> +				regnum,offset);

>> +      /* Read and update the target value.  */

>> +      regcache_cooked_read_unsigned (regs, regnum , &current_val);

>> +      displaced_debug_printf ("(ppc) target regnum %d was 0x%lx now 0x%lx",

>> +			regnum, current_val, current_val + displaced_offset );

>> +      regcache_cooked_write_unsigned (regs, regnum,

>> +					current_val + displaced_offset);

>> +      /* point the PC back at the non-displaced instruction.  */

>> +      displaced_debug_printf ("(ppc) addpcis fixup register:%d,insn_size:%ld",

>> +				regnum, offset);

>> +      regcache_cooked_write_unsigned (regs, gdbarch_pc_regnum (gdbarch),

>> +				    from + offset);

>> +    }

> 

> I don't think we need all these displaced_debug_printf statements here,

> they look quite redundant.  I think a single statement that just shows

> how the returned value was updated should be enough.


You can also omit the "(ppc)" prefix in the messages, as the function
name printed will already contain "ppc" (ppc_displaced_step_fixup).

Simon
Kumar N, Bhuvanendra via Gdb-patches March 29, 2021, 4:53 p.m. | #3
On Mon, 2021-03-29 at 11:35 -0400, Simon Marchi wrote:
> 

> On 2021-03-29 11:16 a.m., Ulrich Weigand via Gdb-patches wrote:

> > On Fri, Mar 26, 2021 at 07:25:07PM -0500, will schmidt wrote:

> > >     gdb/ChangeLog:

> > >     

> > >             * gdb/rs6000-tdep.c (ppc_displaced_step_fixup):

> > > Update to handle

> > >             the addpcis/lnia instruction.

> > 

> > Should omit the leading "gdb/".

> > 

> > >     gdb/testsuite/ChangeLog:

> > >     

> > >             * gdb/testsuite/gdb.arch/powerpc-addpcis.exp:

> > > Testcase harness to

> > >             exercise single-stepping over subpcis,lnia,addpcis

> > > instructions

> > >             with displacement.

> > >             * gdb/testsuite/gdb.arch/powerpc-addpcis.s: Testcase

> > > with stream

> > >             of addpcis/lnia/subpcis instructions.

> > >             * gdb/testsuite/gdb.arch/powerpc-lnia.exp: Testcase

> > > harness to

> > >             exercise single-stepping over lnia instructions with

> > > displacement.

> > >             * gdb/testsuite/gdb.arch/powerpc-lnia.s: Testcase

> > > with stream of

> > >             lnia instructions.

> > 

> > Should omit the leading "gdb/testsuite/".

> > 

> > > +  /* Handle the addpcis/lnia instruction.  */

> > > +  if ((insn & ADDPCIS_INSN_MASK) == ADDPCIS_INSN)

> > > +    {

> > > +      LONGEST displaced_offset;

> > > +      ULONGEST current_val;

> > > +      /* Measure the displacement.  */

> > > +      displaced_offset = from - to;

> > > +      displaced_debug_printf ("(ppc) addpcis displaced_offset

> > > offset %lx",

> > > +				displaced_offset );

> > > +      /* Identify the target register that was updated by the

> > > instruction.  */

> > > +      int regnum = (insn & ADDPCIS_TARGET_REGISTER) >>

> > > ADDPCIS_INSN_REGSHIFT;

> > > +      displaced_debug_printf ("(ppc) addpcis register:%d,

> > > insn_size:%ld",

> > > +				regnum,offset);

> > > +      /* Read and update the target value.  */

> > > +      regcache_cooked_read_unsigned (regs, regnum ,

> > > &current_val);

> > > +      displaced_debug_printf ("(ppc) target regnum %d was 0x%lx

> > > now 0x%lx",

> > > +			regnum, current_val, current_val +

> > > displaced_offset );

> > > +      regcache_cooked_write_unsigned (regs, regnum,

> > > +					current_val +

> > > displaced_offset);

> > > +      /* point the PC back at the non-displaced instruction.  */

> > > +      displaced_debug_printf ("(ppc) addpcis fixup

> > > register:%d,insn_size:%ld",

> > > +				regnum, offset);

> > > +      regcache_cooked_write_unsigned (regs, gdbarch_pc_regnum

> > > (gdbarch),

> > > +				    from + offset);

> > > +    }

> > 

> > I don't think we need all these displaced_debug_printf statements

> > here,

> > they look quite redundant.  I think a single statement that just

> > shows

> > how the returned value was updated should be enough.


Ok.


> 

> You can also omit the "(ppc)" prefix in the messages, as the function

> name printed will already contain "ppc" (ppc_displaced_step_fixup).


Ok.
Should I also remove the "(ppc)" from the existing nearby
displaced_debug_printf calls?   If not, i can leave myself a todo and
we/someone can clean those up at a later time..

i.e.

  displaced_debug_printf ("(ppc) fixup (%s, %s)",
			  paddress (gdbarch, from), paddress (gdbarch, to));


	      /* PC-relative addressing is being used in the branch.  */
	      displaced_debug_printf ("(ppc) branch instruction: %s",
				      paddress (gdbarch, insn));
	      displaced_debug_printf ("(ppc) adjusted PC from %s to %s",
				      paddress (gdbarch, current_pc),
				      paddress (gdbarch, from + offset));

Thanks
-Will

> 

> Simon
Kumar N, Bhuvanendra via Gdb-patches March 30, 2021, 8:10 p.m. | #4
On 3/26/21 5:25 PM, will schmidt via Gdb-patches wrote:
>     

>     gdb/ChangeLog:

>     

>             * gdb/rs6000-tdep.c (ppc_displaced_step_fixup): Update to handle

>             the addpcis/lnia instruction.

>     

>     gdb/testsuite/ChangeLog:

>     

>             * gdb/testsuite/gdb.arch/powerpc-addpcis.exp: Testcase harness to

>             exercise single-stepping over subpcis,lnia,addpcis instructions

>             with displacement.

>             * gdb/testsuite/gdb.arch/powerpc-addpcis.s: Testcase with stream

>             of addpcis/lnia/subpcis instructions.

>             * gdb/testsuite/gdb.arch/powerpc-lnia.exp: Testcase harness to

>             exercise single-stepping over lnia instructions with displacement.

>             * gdb/testsuite/gdb.arch/powerpc-lnia.s: Testcase with stream of

>             lnia instructions.

> 


I know I'm a bit late to this game, but if there is still time...

Please also mention the bug in the ChangeLogs, e.g.,

gdb/ChangeLog:

   PR gdb/27525
   * rs6000-tdep.c (ppc_displaced_step_fixup): ...

Keith
Kumar N, Bhuvanendra via Gdb-patches March 31, 2021, 2:47 p.m. | #5
On Tue, 2021-03-30 at 13:10 -0700, Keith Seitz wrote:
> On 3/26/21 5:25 PM, will schmidt via Gdb-patches wrote:

> >     

> >     gdb/ChangeLog:

> >     

> >             * gdb/rs6000-tdep.c (ppc_displaced_step_fixup): Update

> > to handle

> >             the addpcis/lnia instruction.

> >     

> >     gdb/testsuite/ChangeLog:

> >     

> >             * gdb/testsuite/gdb.arch/powerpc-addpcis.exp: Testcase

> > harness to

> >             exercise single-stepping over subpcis,lnia,addpcis

> > instructions

> >             with displacement.

> >             * gdb/testsuite/gdb.arch/powerpc-addpcis.s: Testcase

> > with stream

> >             of addpcis/lnia/subpcis instructions.

> >             * gdb/testsuite/gdb.arch/powerpc-lnia.exp: Testcase

> > harness to

> >             exercise single-stepping over lnia instructions with

> > displacement.

> >             * gdb/testsuite/gdb.arch/powerpc-lnia.s: Testcase with

> > stream of

> >             lnia instructions.

> > 

> 

> I know I'm a bit late to this game, but if there is still time...

Plenty of time, i think.  I'm figuring out the process of requesting
write access now.  :-) 


> Please also mention the bug in the ChangeLogs, e.g.,


Will do, 
thanks
-WIll

> 

> gdb/ChangeLog:

> 

>    PR gdb/27525

>    * rs6000-tdep.c (ppc_displaced_step_fixup): ...

> 

> Keith

>

Patch

diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index b09f63137d..1db9663b50 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -861,10 +861,16 @@  typedef BP_MANIPULATION_ENDIAN (little_breakpoint, big_breakpoint)
 #define STDCX_INSTRUCTION 0x7c0001ad
 #define STBCX_INSTRUCTION 0x7c00056d
 #define STHCX_INSTRUCTION 0x7c0005ad
 #define STQCX_INSTRUCTION 0x7c00016d
 
+/* Instruction masks for single-stepping of addpcis/lnia.  */
+#define ADDPCIS_INSN            0x4c000004
+#define ADDPCIS_INSN_MASK       0xfc00003e
+#define ADDPCIS_TARGET_REGISTER 0x03F00000
+#define ADDPCIS_INSN_REGSHIFT   21
+
 /* Check if insn is one of the Load And Reserve instructions used for atomic
    sequences.  */
 #define IS_LOAD_AND_RESERVE_INSN(insn)	((insn & LOAD_AND_RESERVE_MASK) == LWARX_INSTRUCTION \
 					 || (insn & LOAD_AND_RESERVE_MASK) == LDARX_INSTRUCTION \
 					 || (insn & LOAD_AND_RESERVE_MASK) == LBARX_INSTRUCTION \
@@ -939,12 +945,37 @@  ppc_displaced_step_fixup (struct gdbarch *gdbarch,
   opcode = insn & BRANCH_MASK;
 
   displaced_debug_printf ("(ppc) fixup (%s, %s)",
 			  paddress (gdbarch, from), paddress (gdbarch, to));
 
+  /* Handle the addpcis/lnia instruction.  */
+  if ((insn & ADDPCIS_INSN_MASK) == ADDPCIS_INSN)
+    {
+      LONGEST displaced_offset;
+      ULONGEST current_val;
+      /* Measure the displacement.  */
+      displaced_offset = from - to;
+      displaced_debug_printf ("(ppc) addpcis displaced_offset offset %lx",
+				displaced_offset );
+      /* Identify the target register that was updated by the instruction.  */
+      int regnum = (insn & ADDPCIS_TARGET_REGISTER) >> ADDPCIS_INSN_REGSHIFT;
+      displaced_debug_printf ("(ppc) addpcis register:%d, insn_size:%ld",
+				regnum,offset);
+      /* Read and update the target value.  */
+      regcache_cooked_read_unsigned (regs, regnum , &current_val);
+      displaced_debug_printf ("(ppc) target regnum %d was 0x%lx now 0x%lx",
+			regnum, current_val, current_val + displaced_offset );
+      regcache_cooked_write_unsigned (regs, regnum,
+					current_val + displaced_offset);
+      /* point the PC back at the non-displaced instruction.  */
+      displaced_debug_printf ("(ppc) addpcis fixup register:%d,insn_size:%ld",
+				regnum, offset);
+      regcache_cooked_write_unsigned (regs, gdbarch_pc_regnum (gdbarch),
+				    from + offset);
+    }
   /* Handle PC-relative branch instructions.  */
-  if (opcode == B_INSN || opcode == BC_INSN || opcode == BXL_INSN)
+  else if (opcode == B_INSN || opcode == BC_INSN || opcode == BXL_INSN)
     {
       ULONGEST current_pc;
 
       /* Read the current PC value after the instruction has been executed
 	 in a displaced location.  Calculate the offset to be applied to the
diff --git a/gdb/testsuite/gdb.arch/powerpc-addpcis.exp b/gdb/testsuite/gdb.arch/powerpc-addpcis.exp
new file mode 100644
index 0000000000..4475ab70d9
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/powerpc-addpcis.exp
@@ -0,0 +1,111 @@ 
+# 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/>.
+
+
+# Test to confirm that gdb is properly single stepping over the
+# displaced addpcis instruction.
+# The addpcis instruction (and it's extended mnemonics lnia and subpcis
+# apply an immediate shifted value (X || 0x0000) to the current PC/NIA
+# value, and store that value into the instructions target register.
+# When the instruction is displaced, it needs special handling.
+
+# lnia Rx == addpcis Rx,0
+# subcis Rx,value == addpcis Rx,-value
+
+if { ![istarget powerpc*-*] } {
+    verbose "Skipping powerpc addpcis test."
+    return
+}
+
+set retval 0
+
+set testfile "powerpc-addpcis"
+set srcfile ${testfile}.s
+set binfile [standard_output_file ${testfile}]
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {}] != "" } {
+    untested "PowerPC addpcis test"
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_load ${binfile}
+
+if ![runto_main] then {
+      return
+}
+
+set check_pc [get_hexadecimal_valueof "\$pc" "default0"]
+set bp1 *$check_pc+4
+set bp2 *$check_pc+12
+set bp3 *$check_pc+16
+gdb_breakpoint $bp1
+gdb_breakpoint $bp2
+gdb_breakpoint $bp3
+
+gdb_test "stepi" "" "set r3 "
+set check_r3 [get_hexadecimal_valueof "\$r3" "default0"]
+gdb_test "stepi" "" "set r4"
+set check_r4 [get_hexadecimal_valueof "\$r4" "default0"]
+gdb_test "stepi" "" "set r5"
+set check_r5 [get_hexadecimal_valueof "\$r5" "default0"]
+gdb_test "stepi" "" "set r6"
+set check_r6 [get_hexadecimal_valueof "\$r6" "default0"]
+gdb_test "stepi" "" "set r7"
+set check_r7 [get_hexadecimal_valueof "\$r7" "default0"]
+gdb_test "stepi" "" "set r8"
+set check_r8 [get_hexadecimal_valueof "\$r8" "default0"]
+gdb_test "stepi" "" "set r9"
+set check_r9 [get_hexadecimal_valueof "\$r9" "default0"]
+
+# R6 will contain the reference value.  All other
+# instructions in this test will be storing values
+# relative to what is stored in R6.
+
+#	subpcis 3,+0x100 	# /* set r3 */
+#	subpcis 4,+0x10	# /* set r4 */
+#	subpcis 5,+0x1	# /* set r5 */
+#	lnia    6		# /* set r6 */
+#	addpcis 7,+0x1	# /* set r7 */
+#	addpcis 8,+0x10	# /* set r8 */
+#	addpcis 9,+0x100	# /* set r9 */
+
+if [expr $check_r3 + 0x1000000   != $check_r6 - 0xc ] {
+    fail "unexpected value r3 + 0x1,000,000 != r6 + 0xc ; r3: $check_r3  r6: $check_r6 "
+}
+if [expr $check_r4 + 0x100000  != $check_r6 - 0x8 ] {
+    fail "unexpected value r4 + 0x100,000 != r6 - 0x8 ; r4: $check_r4  r6: $check_r6 "
+}
+if [expr $check_r5 + 0x10000  != $check_r6 - 0x4 ] {
+    fail "unexpected value r5 + 0x10,000 != r6 , r5: $check_r5  r6: $check_r6 "
+}
+if [expr $check_r6 != $check_r6] {
+    fail "unexpected value r6 != r6 , r6: $check_r6  r6: $check_r6 "
+}
+if [expr $check_r7 - 0x10000  != $check_r6 + 0x4] {
+    fail "unexpected value r7 - 0x10,000 != r6 + 0x4 , r7: $check_r7  r7: $check_r6 "
+}
+if [expr $check_r8 - 0x100000  != $check_r6 + 0x8 ] {
+    fail "unexpected value r8 - 0x100,000 != r6 , r8: $check_r8  r8: $check_r6 "
+}
+if [expr $check_r9 - 0x1000000  != $check_r6 + 0xc ] {
+    fail "unexpected value r9 - 0x1,000,000 != r6 + 0xc , r9: $check_r9  r6: $check_r6 "
+}
+
+gdb_test "info break"
+gdb_test "info register r3 r4 r5 r6 r7 r8 r9"
+gdb_test "disas main"
+
diff --git a/gdb/testsuite/gdb.arch/powerpc-addpcis.s b/gdb/testsuite/gdb.arch/powerpc-addpcis.s
new file mode 100644
index 0000000000..4a31fefcee
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/powerpc-addpcis.s
@@ -0,0 +1,15 @@ 
+
+.global main
+.type main,function
+# addpcis: the sum of NIA + ( D || 0x0000) is placed in RT.
+main:
+	subpcis 3,+0x100  	# /* set r3 */
+	subpcis 4,+0x10  	# /* set r4 */
+	subpcis 5,+0x1  	# /* set r5 */
+	lnia    6  		# /* set r6 */
+	addpcis 7,+0x1  	# /* set r7 */
+	addpcis 8,+0x10  	# /* set r8 */
+	addpcis 9,+0x100  	# /* set r9 */
+	blr
+
+
diff --git a/gdb/testsuite/gdb.arch/powerpc-lnia.exp b/gdb/testsuite/gdb.arch/powerpc-lnia.exp
new file mode 100644
index 0000000000..17109ef0f6
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/powerpc-lnia.exp
@@ -0,0 +1,105 @@ 
+# 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/>.
+
+# Test to see if gdb is properly single stepping over the
+# displaced lnia instruction.
+# lnia is an extended mnemonic for the addpcis instruction, which
+# stores the $NIA plus an immediate value into a register.
+#
+#		lnia Rx == addpcis Rx,0 == lnia Rx
+#		subcis Rx,value == addpcis Rx,-value
+
+if { ![istarget powerpc*-*] } {
+    verbose "Skipping powerpc lnia test."
+    return
+}
+
+set retval 0
+
+set testfile "powerpc-lnia"
+set srcfile ${testfile}.s
+set binfile [standard_output_file ${testfile}]
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {}] != "" } {
+    untested "PowerPC lnia test"
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_load ${binfile}
+
+if ![runto_main] then {
+      return
+}
+
+set before_pc 0
+set check_pc [get_hexadecimal_valueof "\$pc" "default0"]
+
+# set some breakpoints on the instructions below main().
+set bp1 *$check_pc+4
+set bp2 *$check_pc+12
+set bp3 *$check_pc+16
+gdb_breakpoint $bp1
+gdb_breakpoint $bp2
+gdb_breakpoint $bp3
+
+# single-step through the lnia instructions, and retrieve the
+# register values as we proceed.
+gdb_test "stepi" "" "set r3"
+set check_r3 [get_hexadecimal_valueof "\$r3" "default0"]
+gdb_test "stepi" "" "set r4"
+set check_r4 [get_hexadecimal_valueof "\$r4" "default0"]
+gdb_test "stepi" "" "set r5"
+set check_r5 [get_hexadecimal_valueof "\$r5" "default0"]
+gdb_test "stepi" "" "set r6"
+set check_r6 [get_hexadecimal_valueof "\$r6" "default0"]
+gdb_test "stepi" "" "set r7"
+set check_r7 [get_hexadecimal_valueof "\$r7" "default0"]
+gdb_test "stepi" "" "set r8"
+set check_r8 [get_hexadecimal_valueof "\$r8" "default0"]
+gdb_test "stepi" "" "set r9"
+set check_r9 [get_hexadecimal_valueof "\$r9" "default0"]
+
+# Ensure that our register values are as expected.
+# Specifically that the values loaded by the lnia instruction
+# reflect the value of the PC as if the instruction was
+# not displaced.
+if [expr $check_r3 + 4 != $check_r4] {
+    fail "unexpected value r3+4 != r4 , r3: $check_r3  r4: $check_r4 "
+}
+if [expr $check_r4 + 4 != $check_r5] {
+    fail "unexpected value r4+4 != r5 , r4: $check_r4  r5: $check_r5 "
+}
+if [expr $check_r5 + 4 != $check_r6] {
+    fail "unexpected value r5+4 != r6 , r5: $check_r5  r6: $check_r6 "
+}
+if [expr $check_r6 + 4 != $check_r7] {
+    fail "unexpected value r6+4 != r7 , r6: $check_r6  r7: $check_r7 "
+}
+if [expr $check_r7 + 4 != $check_r8] {
+    fail "unexpected value r7+4 != r8 , r7: $check_r7  r8: $check_r8 "
+}
+if [expr $check_r8 + 4 != $check_r9] {
+    fail "unexpected value r8+4 != r9 , r8: $check_r8  r9: $check_r9 "
+}
+
+gdb_test "info break"
+gdb_test "info register r3 r4 r5 r6 r7 r8 r9"
+gdb_test "disas main"
+
+# Let the inferior store all vector registers in a buffer, then dump
+# the buffer and check it.
+
diff --git a/gdb/testsuite/gdb.arch/powerpc-lnia.s b/gdb/testsuite/gdb.arch/powerpc-lnia.s
new file mode 100644
index 0000000000..a09caceca6
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/powerpc-lnia.s
@@ -0,0 +1,13 @@ 
+
+.global main
+.type main,function
+main:
+	lnia 3  # /* set r3 */
+	lnia 4  # /* set r4 */
+	lnia 5  # /* set r5 */
+	lnia 6  # /* set r6 */
+	lnia 7  # /* set r7 */
+	lnia 8  # /* set r8 */
+	lnia 9  # /* set r9 */
+	blr
+