This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch/ARM] Add support for single-stepping through IF-THEN blocks


Here's the patch I was ultimately working on that produced the rest of
today's cleanups.

The Thumb-2 instruction set has an interesting instruction used for
conditional execution: IT, or If-Then.  Here's an example:

    cmp     r0, r1 @ Compare r0 and r1
    ittee   eq     @ If they are equal
    moveq   r0, #1 @ Then do this
    moveq   r1, #1 @ And this
    movne   r0, #2 @ Otherwise this
    movne   r1, #2 @ And this

There are two complications for single stepping through these blocks
using software-single-step.

1) We use an undefined instruction, rather than the architectural BKPT
instruction, as the GNU/Linux software breakpoint.  If a hardware
debugger is connected to the system, BKPT will trigger it.  It's
a bit anti-social, and makes hybrid kernel/user debugging scenarios
very difficult.  The BKPT instruction has the nice property that
it triggers unconditionally - but undefined instructions don't; the
condition could be "this instruction is available".  So if we
insert a breakpoint at the very next PC and that instruction
turns out not to be executed, we'll skip right past the breakpoint.

2) There's no restriction on changing the flags that IT uses to
determine whether to execute an instruction.  You can't branch into
the middle of an IT block, you can't branch out of the middle, but
you can adjust the flags.  So without simulating every flags-setting
instruction (possible, but there are quite a lot of them) it's
impossible to reliably predict what the next executed instruction
will be!  Fortunately, we can narrow it down to two possibilities
and insert a breakpoint at each.  The exact details are in comments
in the patch.

Point (1) is also why this patch adds a 32-bit breakpoint instruction.
If we skip over the non-executed breakpoint, then we might try to
execute the second half of a 32-bit instruction as an instruction.
It has good odds of being a valid instruction, and who knows what it
might do.

This depends on the Z0 "length" -> "kind" patch.  To work correctly it
also requires either my earlier patch (for SIGILL handling) or a
patched kernel.  So I won't commit it right away, but I plan to commit
it when its dependencies are in.

Tested on arm-none-linux-gnueabi where it fixes a few failures when
compiling for Thumb-2.

-- 
Daniel Jacobowitz
CodeSourcery

2010-01-28  Daniel Jacobowitz  <dan@codesourcery.com>

	gdb/
	* arm-linux-tdep.c (arm_linux_thumb2_be_breakpoint)
	(arm_linux_thumb2_le_breakpoint): New constants.
	(arm_linux_init_abi): Set thumb2_breakpoint and
	thumb2_breakpoint_size.
	* arm-tdep.c (thumb_insn_size, thumb_advance_itstate): New functions.
	(thumb_get_next_pc): Add a comment.  Rename IT to ITSTATE.
	Implement support for single stepping through IT blocks if
	a 32-bit Thumb breakpoint instruction is available.
	(arm_breakpoint_from_pc): If a 32-bit Thumb breakpoint instruction
	is available, use it when needed.
	(arm_remote_breakpoint_from_pc): New function.
	(arm_gdbarch_init): Register arm_remote_breakpoint_from_pc.
	* arm-tdep.h (struct gdbarch_tdep): Correct thumb_breakpoint
	comment.  Add thumb2_breakpoint and thumb2_breakpoint_size.

	gdb/testsuite/
	* gdb.arch/thumb2-it.S, gdb.arch/thumb2-it.exp: New files.

	gdb/gdbserver/
	* linux-arm-low.c (thumb_breakpoint_len): Delete.
	(thumb2_breakpoint): New.
	(arm_breakpoint_at): Check for Thumb-2 breakpoints.

---
 gdb/arm-linux-tdep.c                 |   11 ++
 gdb/arm-tdep.c                       |  170 +++++++++++++++++++++++++++++++++--
 gdb/arm-tdep.h                       |    9 +
 gdb/gdbserver/linux-arm-low.c        |    9 +
 gdb/testsuite/gdb.arch/thumb2-it.S   |  139 ++++++++++++++++++++++++++++
 gdb/testsuite/gdb.arch/thumb2-it.exp |  140 ++++++++++++++++++++++++++++
 6 files changed, 467 insertions(+), 11 deletions(-)

Index: gdb-mainline/gdb/arm-linux-tdep.c
===================================================================
--- gdb-mainline.orig/gdb/arm-linux-tdep.c	2010-01-28 12:21:54.000000000 -0800
+++ gdb-mainline/gdb/arm-linux-tdep.c	2010-01-28 12:31:27.000000000 -0800
@@ -74,6 +74,14 @@ static const char arm_linux_thumb_be_bre
 
 static const char arm_linux_thumb_le_breakpoint[] = {0x01, 0xde};
 
+/* Because the 16-bit Thumb breakpoint is affected by Thumb-2 IT blocks,
+   we must use a length-appropriate breakpoint for 32-bit Thumb
+   instructions.  See also thumb_get_next_pc.  */
+
+static const char arm_linux_thumb2_be_breakpoint[] = { 0xf7, 0xf0, 0xa0, 0x00 };
+
+static const char arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa0 };
+
 /* Description of the longjmp buffer.  */
 #define ARM_LINUX_JB_ELEMENT_SIZE	INT_REGISTER_SIZE
 #define ARM_LINUX_JB_PC			21
@@ -851,6 +859,7 @@ arm_linux_init_abi (struct gdbarch_info 
       else
 	tdep->arm_breakpoint = arm_linux_arm_be_breakpoint;
       tdep->thumb_breakpoint = arm_linux_thumb_be_breakpoint;
+      tdep->thumb2_breakpoint = arm_linux_thumb2_be_breakpoint;
     }
   else
     {
@@ -859,9 +868,11 @@ arm_linux_init_abi (struct gdbarch_info 
       else
 	tdep->arm_breakpoint = arm_linux_arm_le_breakpoint;
       tdep->thumb_breakpoint = arm_linux_thumb_le_breakpoint;
+      tdep->thumb2_breakpoint = arm_linux_thumb2_le_breakpoint;
     }
   tdep->arm_breakpoint_size = sizeof (arm_linux_arm_le_breakpoint);
   tdep->thumb_breakpoint_size = sizeof (arm_linux_thumb_le_breakpoint);
+  tdep->thumb2_breakpoint_size = sizeof (arm_linux_thumb2_le_breakpoint);
 
   if (tdep->fp_model == ARM_FLOAT_AUTO)
     tdep->fp_model = ARM_FLOAT_FPA;
Index: gdb-mainline/gdb/arm-tdep.c
===================================================================
--- gdb-mainline.orig/gdb/arm-tdep.c	2010-01-28 12:21:54.000000000 -0800
+++ gdb-mainline/gdb/arm-tdep.c	2010-01-28 13:30:18.000000000 -0800
@@ -2256,17 +2256,50 @@ bitcount (unsigned long val)
   return nbits;
 }
 
+/* Return the size in bytes of the complete Thumb instruction whose
+   first halfword is INST1.  */
+
+static int
+thumb_insn_size (unsigned short inst1)
+{
+  if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
+    return 4;
+  else
+    return 2;
+}
+
+static int
+thumb_advance_itstate (unsigned int itstate)
+{
+  /* Preserve IT[7:5], the first three bits of the condition.  Shift
+     the upcoming condition flags left by one bit.  */
+  itstate = (itstate & 0xe0) | ((itstate << 1) & 0x1f);
+
+  /* If we have finished the IT block, clear the state.  */
+  if ((itstate & 0x0f) == 0)
+    itstate = 0;
+
+  return itstate;
+}
+
+/* Find the next PC after the current instruction executes.  In some
+   cases we can not statically determine the answer (see the IT state
+   handling in this function); in that case, a breakpoint may be
+   inserted in addition to the returned PC, which will be used to set
+   another breakpoint by our caller.  */
+
 static CORE_ADDR
 thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
+  struct address_space *aspace = get_frame_address_space (frame);
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
   unsigned long pc_val = ((unsigned long) pc) + 4;	/* PC after prefetch */
   unsigned short inst1;
   CORE_ADDR nextpc = pc + 2;		/* default is next instruction */
   unsigned long offset;
-  ULONGEST status, it;
+  ULONGEST status, itstate;
 
   inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
 
@@ -2279,18 +2312,100 @@ thumb_get_next_pc (struct frame_info *fr
      block is active.  These bits read as zero on earlier
      processors.  */
   status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
-  it = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
+  itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
+
+  /* If-Then handling.  On GNU/Linux, where this routine is used, we
+     use an undefined instruction as a breakpoint.  Unlike BKPT, IT
+     can disable execution of the undefined instruction.  So we might
+     miss the breakpoint if we set it on a skipped conditional
+     instruction.  Because conditional instructions can change the
+     flags, affecting the execution of further instructions, we may
+     need to set two breakpoints.  */
+
+  if (gdbarch_tdep (gdbarch)->thumb2_breakpoint != NULL)
+    {
+      if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
+	{
+	  /* An IT instruction.  Because this instruction does not
+	     modify the flags, we can accurately predict the next
+	     executed instruction.  */
+	  itstate = inst1 & 0x00ff;
+	  pc += thumb_insn_size (inst1);
+
+	  while (itstate != 0 && ! condition_true (itstate >> 4, status))
+	    {
+	      inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
+	      pc += thumb_insn_size (inst1);
+	      itstate = thumb_advance_itstate (itstate);
+	    }
+
+	  return pc;
+	}
+      else if (itstate != 0)
+	{
+	  /* We are in a conditional block.  Check the condition.  */
+	  if (! condition_true (itstate >> 4, status))
+	    {
+	      /* Advance to the next executed instruction.  */
+	      pc += thumb_insn_size (inst1);
+	      itstate = thumb_advance_itstate (itstate);
+
+	      while (itstate != 0 && ! condition_true (itstate >> 4, status))
+		{
+		  inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
+		  pc += thumb_insn_size (inst1);
+		  itstate = thumb_advance_itstate (itstate);
+		}
 
-  /* On GNU/Linux, where this routine is used, we use an undefined
-     instruction as a breakpoint.  Unlike BKPT, IT can disable execution
-     of the undefined instruction.  So we might miss the breakpoint!  */
-  if (((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0) || (it & 0x0f))
-    error (_("Stepping through Thumb-2 IT blocks is not yet supported"));
+	      return pc;
+	    }
+	  else if ((itstate & 0x0f) == 0x08)
+	    {
+	      /* This is the last instruction of the conditional
+		 block, and it is executed.  We can handle it normally
+		 because the following instruction is not conditional,
+		 and we must handle it normally because it is
+		 permitted to branch.  Fall through.  */
+	    }
+	  else
+	    {
+	      int cond_negated;
+
+	      /* There are conditional instructions after this one.
+		 If this instruction modifies the flags, then we can
+		 not predict what the next executed instruction will
+		 be.  Fortunately, this instruction is architecturally
+		 forbidden to branch; we know it will fall through.
+		 Start by skipping past it.  */
+	      pc += thumb_insn_size (inst1);
+	      itstate = thumb_advance_itstate (itstate);
+
+	      /* Set a breakpoint on the following instruction.  */
+	      gdb_assert ((itstate & 0x0f) != 0);
+	      insert_single_step_breakpoint (gdbarch, aspace, pc);
+	      cond_negated = (itstate >> 4) & 1;
+
+	      /* Skip all following instructions with the same
+		 condition.  If there is a later instruction in the IT
+		 block with the opposite condition, set the other
+		 breakpoint there.  If not, then set a breakpoint on
+		 the instruction after the IT block.  */
+	      do
+		{
+		  inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
+		  pc += thumb_insn_size (inst1);
+		  itstate = thumb_advance_itstate (itstate);
+		}
+	      while (itstate != 0 && ((itstate >> 4) & 1) == cond_negated);
 
-  if (it & 0x0f)
+	      return pc;
+	    }
+	}
+    }
+  else if (itstate & 0x0f)
     {
       /* We are in a conditional block.  Check the condition.  */
-      int cond = it >> 4;
+      int cond = itstate >> 4;
 
       if (! condition_true (cond, status))
 	{
@@ -2301,6 +2416,8 @@ thumb_get_next_pc (struct frame_info *fr
 	  else
 	    return pc + 2;
 	}
+
+      /* Otherwise, handle the instruction normally.  */
     }
 
   if ((inst1 & 0xff00) == 0xbd00)	/* pop {rlist, pc} */
@@ -4749,10 +4866,29 @@ static const unsigned char *
 arm_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr)
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
 
   if (arm_pc_is_thumb (*pcptr))
     {
       *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
+
+      /* If we have a separate 32-bit breakpoint instruction for Thumb-2,
+	 check whether we are replacing a 32-bit instruction.  */
+      if (tdep->thumb2_breakpoint != NULL)
+	{
+	  gdb_byte buf[2];
+	  if (target_read_memory (*pcptr, buf, 2) == 0)
+	    {
+	      unsigned short inst1;
+	      inst1 = extract_unsigned_integer (buf, 2, byte_order_for_code);
+	      if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
+		{
+		  *lenptr = tdep->thumb2_breakpoint_size;
+		  return tdep->thumb2_breakpoint;
+		}
+	    }
+	}
+
       *lenptr = tdep->thumb_breakpoint_size;
       return tdep->thumb_breakpoint;
     }
@@ -4763,6 +4899,20 @@ arm_breakpoint_from_pc (struct gdbarch *
     }
 }
 
+static void
+arm_remote_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr,
+			       int *kindptr)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  arm_breakpoint_from_pc (gdbarch, pcptr, kindptr);
+
+  if (arm_pc_is_thumb (*pcptr) && *kindptr == 4)
+    /* The documented magic value for a 32-bit Thumb-2 breakpoint, so
+       that this is not confused with a 32-bit ARM breakpoint.  */
+    *kindptr = 3;
+}
+
 /* Extract from an array REGBUF containing the (raw) register state a
    function return value of type TYPE, and copy that, in virtual
    format, into VALBUF.  */
@@ -6091,6 +6241,8 @@ arm_gdbarch_init (struct gdbarch_info in
 
   /* Breakpoint manipulation.  */
   set_gdbarch_breakpoint_from_pc (gdbarch, arm_breakpoint_from_pc);
+  set_gdbarch_remote_breakpoint_from_pc (gdbarch,
+					 arm_remote_breakpoint_from_pc);
 
   /* Information about registers, etc.  */
   set_gdbarch_deprecated_fp_regnum (gdbarch, ARM_FP_REGNUM);	/* ??? */
Index: gdb-mainline/gdb/arm-tdep.h
===================================================================
--- gdb-mainline.orig/gdb/arm-tdep.h	2010-01-28 12:21:54.000000000 -0800
+++ gdb-mainline/gdb/arm-tdep.h	2010-01-28 12:31:27.000000000 -0800
@@ -167,9 +167,16 @@ struct gdbarch_tdep
 
   const char *arm_breakpoint;	/* Breakpoint pattern for an ARM insn.  */
   int arm_breakpoint_size;	/* And its size.  */
-  const char *thumb_breakpoint;	/* Breakpoint pattern for an ARM insn.  */
+  const char *thumb_breakpoint;	/* Breakpoint pattern for a Thumb insn.  */
   int thumb_breakpoint_size;	/* And its size.  */
 
+  /* If the Thumb breakpoint is an undefined instruction (which is
+     affected by IT blocks) rather than a BKPT instruction (which is
+     not), then we need a 32-bit Thumb breakpoint to preserve the
+     instruction count in IT blocks.  */
+  const char *thumb2_breakpoint;
+  int thumb2_breakpoint_size;
+
   int jb_pc;			/* Offset to PC value in jump buffer. 
 				   If this is negative, longjmp support
 				   will be disabled.  */
Index: gdb-mainline/gdb/testsuite/gdb.arch/thumb2-it.S
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdb-mainline/gdb/testsuite/gdb.arch/thumb2-it.S	2010-01-28 13:30:18.000000000 -0800
@@ -0,0 +1,139 @@
+/* Thumb-2 IT blocks test program.
+
+   Copyright 2010 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/>.  */
+
+	.syntax unified
+	.text
+	.p2align 2
+	.code 16
+
+#ifndef __thumb2__
+
+	.type main,%function
+	.thumb_func
+	.globl main
+main:
+	mov	r0, #0
+	bx	lr	@ No Thumb-2
+
+#else
+
+	.type main,%function
+	.thumb_func
+	.globl main
+main:
+	mov	r0, #0
+	bx	lr	@ Thumb-2 OK
+
+	@ One conditional instruction, executed.
+	.type it_1,%function
+	.thumb_func
+it_1:
+	mov	r0, #0	@ Setup
+	cmp	r0, #0	@ Setup
+	it	eq	@ IT instruction, Expected == 1
+	addeq	r0, #1	@ Reached
+	bx	lr	@ Done
+
+	@ One conditional instruction, skipped.
+	.type it_2,%function
+	.thumb_func
+it_2:
+	mov	r0, #0	@ Setup
+	cmp	r0, #0	@ Setup
+	it	ne	@ IT instruction, Expected == 0
+	addne	r0, #1	@ Not reached
+	bx	lr	@ Done, Check $r0 == 0
+
+	@ Block of four, alternating, starting with executed.
+	.type it_3,%function
+	.thumb_func
+it_3:
+	mov	r0, #0	@ Setup
+	cmp	r0, #0	@ Setup
+	itete	ge	@ IT instruction, Expected == 2
+	addge	r0, #1	@ Reached
+	addlt	r0, #2	@ Not reached
+	addge	r0, #4	@ Reached
+	addlt	r0, #8	@ Not reached
+	bx	lr	@ Done, Check $r0 == 5
+
+	@ Block of four, changing flags.
+	.type it_4,%function
+	.thumb_func
+it_4:
+	mov	r0, #0	@ Setup
+	cmp	r0, #0	@ Setup
+	itttt	ge	@ IT instruction, Expected == 2
+	addge	r0, #1	@ Reached
+	cmpge	r0, #10	@ Reached
+	addge	r0, #4	@ Not reached
+	addge	r0, #8	@ Not reached
+	bx	lr	@ Done, Check $r0 == 1
+
+	@ Block of two, ending with taken branch.
+	.type it_5,%function
+	.thumb_func
+it_5:
+	mov	r0, #0	@ Setup
+	cmp	r0, #0	@ Setup
+	itt	ge	@ IT instruction, Expected == 2
+	addge	r0, #1	@ Reached
+	bge	.L5	@ Reached
+	add	r0, #2	@ Never reached
+.L5:	bx	lr	@ Done, Check $r0 == 1
+
+	@ Block of two, ending with untaken branch.
+	.type it_6,%function
+	.thumb_func
+it_6:
+	mov	r0, #0	@ Setup
+	cmp	r0, #0	@ Setup
+	ite	ge	@ IT instruction, Expected == 2
+	addge	r0, #1	@ Reached
+	blt	.L6	@ Not reached
+	add	r0, #2	@ Reached
+.L6:	bx	lr	@ Done, Check $r0 == 3
+
+	@ Block of four, taken, of different sizes
+	.type it_7,%function
+	.thumb_func
+it_7:
+	mov	r0, #0	@ Setup
+	cmp	r0, #0	@ Setup
+	itttt	ge	@ IT instruction, Expected == 4
+	addge.n	r0, #1	@ Reached
+	addge.w	r0, #2	@ Reached
+	addge.n	r0, #4	@ Reached
+	addge.w	r0, #8	@ Reached
+	bx	lr	@ Done, Check $r0 == 15
+
+	@ Block of four, only first executed.
+	.type it_3,%function
+	.thumb_func
+it_8:
+	mov	r0, #0	@ Setup
+	cmp	r0, #0	@ Setup
+	iteee	ge	@ IT instruction, Expected == 1
+	addge	r0, #1	@ Reached
+	addlt	r0, #2	@ Not reached
+	addlt	r0, #4	@ Not reached
+	addlt	r0, #8	@ Not reached
+	bx	lr	@ Done, Check $r0 == 1
+
+#endif /* __thumb2__ */
Index: gdb-mainline/gdb/testsuite/gdb.arch/thumb2-it.exp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdb-mainline/gdb/testsuite/gdb.arch/thumb2-it.exp	2010-01-28 13:30:18.000000000 -0800
@@ -0,0 +1,140 @@
+# Copyright 2010 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 single stepping over Thumb-2 IT blocks.
+
+if {![istarget arm*-*eabi*]} then {
+    verbose "Skipping Thumb-2 tests."
+    return
+}
+
+set testfile "thumb2-it"
+set srcfile ${testfile}.S
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable debug] != "" } {
+    untested thumb2-it.exp
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if ![runto_main] then {
+    untested thumb2-it.exp
+    return -1
+}
+
+# Make sure that the compiler options allow Thumb-2.
+gdb_test_multiple "list" "list main" {
+    -re ".*@ No Thumb-2.*$gdb_prompt $" {
+	pass "list main"
+	untested thumb2-it.exp
+	return -1
+    }
+    -re ".*@ Thumb-2 OK.*$gdb_prompt $" {
+	pass "list main"
+    }
+}
+
+proc test_it_block { func } {
+    global gdb_prompt
+    global software_step
+
+    if { ! [gdb_breakpoint "*${func}"] } {
+	unresolved "$func, IT block tests"
+	return
+    }
+
+    gdb_test "call ${func}()" "Breakpoint.*@ Setup.*" "$func, call"
+
+    set expected 0
+    set reached 0
+    set steps 0
+    set ok 1
+    while { $ok } {
+	set ok 0
+	set msg "$func, stepi $steps"
+	gdb_test_multiple "stepi" "$msg" {
+	    -re ".*@ Setup.*$gdb_prompt $" {
+		pass "$msg"
+		set ok 1
+	    }
+	    -re ".*@ IT instruction, Expected == (\[0-9\]*)\r\n$gdb_prompt $" {
+		set expected $expect_out(1,string)
+		pass "$msg"
+		set ok 1
+	    }
+	    -re ".*@ Reached.*$gdb_prompt $" {
+		incr reached
+		pass "$msg"
+		set ok 1
+		if { [regexp {@ Reached, Set ([^\r\n]*)\r\n} $expect_out(0,string) dummy change] } {
+		    gdb_test "set $change" "" "$func, set $change"
+		}
+	    }
+	    -re ".*@ Not reached.*$gdb_prompt $" {
+		# An instruction in an IT block whose predicate is false when
+		# we reach it.  If using software single step, we should not
+		# stop here.
+		if { $software_step } {
+		    fail "$msg"
+		} else {
+		    pass "$msg"
+		    set ok 1
+		}
+	    }
+	    -re ".*@ Never reached.*$gdb_prompt $" {
+		# An instruction that should be branched over.
+		fail "$msg"
+	    }
+	    -re ".*@ Done.*$gdb_prompt $" {
+		pass "$msg"
+		if { $reached == $expected } {
+		    pass "$func, correct instructions reached"
+		} else {
+		    fail "$func, correct instructions reached"
+		}
+		if { [regexp {@ Done, Check ([^\r\n]*)\r\n} $expect_out(0,string) dummy check] } {
+		    gdb_test "print $check" ".* = 1" "$func, $check"
+		}
+	    }
+	}
+	if { ! $ok } {
+	    break
+	}
+	incr steps
+	continue
+    }
+
+    gdb_test "continue" "" "$func, continue"
+    return
+}
+
+# If we are using software single-stepping in GDB, then GDB will not
+# stop at conditional instructions with a false predicate during stepi.
+# If we are using a simulator or debug interface with hardware single
+# step, then GDB will stop at such instructions.
+if { [istarget arm*-linux*] } {
+    set software_step 1
+} else {
+    set software_step 0
+}
+
+for { set i 1 } { $i <= 8 } { incr i } {
+    test_it_block it_${i}
+}
Index: gdb-mainline/gdb/gdbserver/linux-arm-low.c
===================================================================
--- gdb-mainline.orig/gdb/gdbserver/linux-arm-low.c	2010-01-28 13:30:17.000000000 -0800
+++ gdb-mainline/gdb/gdbserver/linux-arm-low.c	2010-01-28 13:30:32.000000000 -0800
@@ -199,7 +199,7 @@ arm_set_pc (CORE_ADDR pc)
 static const unsigned long arm_breakpoint = 0xef9f0001;
 #define arm_breakpoint_len 4
 static const unsigned short thumb_breakpoint = 0xde01;
-#define thumb_breakpoint_len 2
+static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
 
 /* For new EABI binaries.  We recognize it regardless of which ABI
    is used for gdbserver, so single threaded debugging should work
@@ -222,6 +222,13 @@ arm_breakpoint_at (CORE_ADDR where)
       (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
       if (insn == thumb_breakpoint)
 	return 1;
+
+      if (insn == thumb2_breakpoint[0])
+	{
+	  (*the_target->read_memory) (where + 2, (unsigned char *) &insn, 2);
+	  if (insn == thumb2_breakpoint[1])
+	    return 1;
+	}
     }
   else
     {


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]