public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v6 4/7] Share regcache function regcache_raw_read_unsigned.
  2015-12-04 18:46 [PATCH v6 0/6] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
  2015-12-04 18:46 ` [PATCH v6 3/7] Refactor arm_software_single_step to use regcache Antoine Tremblay
  2015-12-04 18:46 ` [PATCH v6 2/7] Share some ARM target dependent code from GDB with GDBServer Antoine Tremblay
@ 2015-12-04 18:46 ` Antoine Tremblay
  2015-12-07 14:40   ` Yao Qi
  2015-12-04 18:46 ` [PATCH v6 1/7] Replace breakpoint_reinsert_addr by get_next_pcs operation in GDBServer Antoine Tremblay
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-04 18:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Antoine Tremblay

This patch is in preparation for software single step support on ARM in
GDBServer. It adds a new shared function regcache_raw_read_unsigned and
regcache_raw_get_unsigned so that GDB and GDBServer can use the same call
to fetch a raw register into an integer.

No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }

gdb/ChangeLog:
	* Makefile.in: Add common-regcache.c/.o.
	* common/common-regcache.h (register_status) New enum.
	(regcache_raw_read_unsigned): New declaration.
	* common/common-regcache.c: New file.
	* regcache.h (enum register_status): Move to common-regcache.h.
	(regcache_raw_read_unsigned): Likewise.
	(regcache_raw_get_unsigned): Likewise.

gdb/gdbserver/ChangeLog:

	* Makefile.in: Add common-regcache.c/.o.
	* regcache.c (init_register_cache): Initialize cache to
	REG_UNAVAILABLE.
	(regcache_raw_read_unsigned): New function.
	* regcache.h (REG_UNAVAILABLE, REG_VALID): Replaced by shared
	register_status enum.
---
 gdb/Makefile.in              |  8 +++++++-
 gdb/common/common-regcache.c | 36 ++++++++++++++++++++++++++++++++++++
 gdb/common/common-regcache.h | 24 ++++++++++++++++++++++++
 gdb/gdbserver/Makefile.in    |  7 +++++--
 gdb/gdbserver/regcache.c     | 26 ++++++++++++++++++++++++--
 gdb/gdbserver/regcache.h     |  7 -------
 gdb/regcache.c               | 15 ---------------
 gdb/regcache.h               | 24 ------------------------
 8 files changed, 96 insertions(+), 51 deletions(-)
 create mode 100644 gdb/common/common-regcache.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 14ad405..0306770 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -894,7 +894,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	common/format.c common/filestuff.c btrace.c record-btrace.c ctf.c \
 	target/waitstatus.c common/print-utils.c common/rsp-low.c \
 	common/errors.c common/common-debug.c common/common-exceptions.c \
-	common/btrace-common.c common/fileio.c \
+	common/btrace-common.c common/fileio.c common/common-regcache.c \
 	$(SUBDIR_GCC_COMPILE_SRCS)
 
 LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -1085,6 +1085,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	format.o registry.o btrace.o record-btrace.o waitstatus.o \
 	print-utils.o rsp-low.o errors.o common-debug.o debug.o \
 	common-exceptions.o btrace-common.o fileio.o \
+	common-regcache.o \
 	$(SUBDIR_GCC_COMPILE_OBS)
 
 TSOBS = inflow.o
@@ -2266,6 +2267,11 @@ btrace-common.o: ${srcdir}/common/btrace-common.c
 fileio.o: ${srcdir}/common/fileio.c
 	$(COMPILE) $(srcdir)/common/fileio.c
 	$(POSTCOMPILE)
+
+common-regcache.o: ${srcdir}/common/common-regcache.c
+	$(COMPILE) $(srcdir)/common/common-regcache.c
+	$(POSTCOMPILE)
+
 #
 # gdb/target/ dependencies
 #
diff --git a/gdb/common/common-regcache.c b/gdb/common/common-regcache.c
new file mode 100644
index 0000000..9ee1102
--- /dev/null
+++ b/gdb/common/common-regcache.c
@@ -0,0 +1,36 @@
+/* Cache and manage the values of registers for GDB, the GNU debugger.
+
+   Copyright (C) 2015 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 "common-defs.h"
+#include "common-regcache.h"
+
+/* Return the register's value or throw if it's not available.  */
+
+ULONGEST
+regcache_raw_get_unsigned (struct regcache *regcache, int regnum)
+{
+  ULONGEST value;
+  enum register_status status;
+
+  status = regcache_raw_read_unsigned (regcache, regnum, &value);
+  if (status == REG_UNAVAILABLE)
+    throw_error (NOT_AVAILABLE_ERROR,
+		 _("Register %d is not available"), regnum);
+  return value;
+}
diff --git a/gdb/common/common-regcache.h b/gdb/common/common-regcache.h
index c470603..97c186c 100644
--- a/gdb/common/common-regcache.h
+++ b/gdb/common/common-regcache.h
@@ -22,6 +22,24 @@
 
 /* This header is a stopgap until we have an independent regcache.  */
 
+enum register_status
+  {
+    /* The register value is not in the cache, and we don't know yet
+       whether it's available in the target (or traceframe).  */
+    REG_UNKNOWN = 0,
+
+    /* The register value is valid and cached.  */
+    REG_VALID = 1,
+
+    /* The register value is unavailable.  E.g., we're inspecting a
+       traceframe, and this register wasn't collected.  Note that this
+       is different a different "unavailable" from saying the register
+       does not exist in the target's architecture --- in that case,
+       the target should have given us a target description that does
+       not include the register in the first place.  */
+    REG_UNAVAILABLE = -1
+  };
+
 /* Return a pointer to the register cache associated with the
    thread specified by PTID.  This function must be provided by
    the client.  */
@@ -38,4 +56,10 @@ extern int regcache_register_size (const struct regcache *regcache, int n);
 
 extern CORE_ADDR regcache_read_pc (struct regcache *regcache);
 
+/* Read a raw register into a unsigned integer.  */
+extern enum register_status regcache_raw_read_unsigned
+  (struct regcache *regcache, int regnum, ULONGEST *val);
+
+ULONGEST regcache_raw_get_unsigned (struct regcache *regcache, int regnum);
+
 #endif /* COMMON_REGCACHE_H */
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index f18243b..82af44c 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -181,7 +181,7 @@ SFILES=	$(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
 	$(srcdir)/common/common-exceptions.c $(srcdir)/symbol.c \
 	$(srcdir)/common/btrace-common.c \
 	$(srcdir)/common/fileio.c $(srcdir)/nat/linux-namespaces.c \
-	$(srcdir)/arch/arm.c
+	$(srcdir)/arch/arm.c $(srcdir)/common/common-regcache.c
 
 DEPFILES = @GDBSERVER_DEPFILES@
 
@@ -195,7 +195,7 @@ OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o \
       mem-break.o hostio.o event-loop.o tracepoint.o xml-utils.o \
       common-utils.o ptid.o buffer.o format.o filestuff.o dll.o notif.o \
       tdesc.o print-utils.o rsp-low.o errors.o common-debug.o cleanups.o \
-      common-exceptions.o symbol.o btrace-common.o fileio.o \
+      common-exceptions.o symbol.o btrace-common.o fileio.o common-regcache.o \
       $(XML_BUILTIN) $(DEPFILES) $(LIBOBJS)
 GDBREPLAY_OBS = gdbreplay.o version.o
 GDBSERVER_LIBS = @GDBSERVER_LIBS@
@@ -583,6 +583,9 @@ waitstatus.o: ../target/waitstatus.c
 fileio.o: ../common/fileio.c
 	$(COMPILE) $<
 	$(POSTCOMPILE)
+common-regcache.o: ../common/common-regcache.c
+	$(COMPILE) $<
+	$(POSTCOMPILE)
 
 # Arch object files rules form ../arch
 
diff --git a/gdb/gdbserver/regcache.c b/gdb/gdbserver/regcache.c
index b9311fe..c608bf3 100644
--- a/gdb/gdbserver/regcache.c
+++ b/gdb/gdbserver/regcache.c
@@ -145,8 +145,9 @@ init_register_cache (struct regcache *regcache,
 	= (unsigned char *) xcalloc (1, tdesc->registers_size);
       regcache->registers_owned = 1;
       regcache->register_status
-	= (unsigned char *) xcalloc (1, tdesc->num_registers);
-      gdb_assert (REG_UNAVAILABLE == 0);
+	= (unsigned char *) xmalloc (tdesc->num_registers);
+      memset ((void *) regcache->register_status, REG_UNAVAILABLE,
+	      tdesc->num_registers);
 #else
       gdb_assert_not_reached ("can't allocate memory from the heap");
 #endif
@@ -435,6 +436,27 @@ collect_register (struct regcache *regcache, int n, void *buf)
 	  register_size (regcache->tdesc, n));
 }
 
+enum register_status
+regcache_raw_read_unsigned (struct regcache *regcache, int regnum,
+			    ULONGEST *val)
+{
+  int size;
+
+  gdb_assert (regcache != NULL);
+  gdb_assert (regnum >= 0 && regnum < regcache->tdesc->num_registers);
+
+  size = register_size (regcache->tdesc, regnum);
+
+  if (size > (int) sizeof (ULONGEST))
+    error (_("That operation is not available on integers of more than"
+            "%d bytes."),
+          (int) sizeof (ULONGEST));
+
+  collect_register (regcache, regnum, val);
+
+  return REG_VALID;
+}
+
 #ifndef IN_PROCESS_AGENT
 
 void
diff --git a/gdb/gdbserver/regcache.h b/gdb/gdbserver/regcache.h
index a0be95e..f4b798b 100644
--- a/gdb/gdbserver/regcache.h
+++ b/gdb/gdbserver/regcache.h
@@ -24,13 +24,6 @@
 struct thread_info;
 struct target_desc;
 
-/* The register exists, it has a value, but we don't know what it is.
-   Used when inspecting traceframes.  */
-#define REG_UNAVAILABLE 0
-
-/* We know the register's value (and we have it cached).  */
-#define REG_VALID 1
-
 /* The data for the register cache.  Note that we have one per
    inferior; this is primarily for simplicity, as the performance
    benefit is minimal.  */
diff --git a/gdb/regcache.c b/gdb/regcache.c
index 7fb9d18..5ee31fb 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -715,21 +715,6 @@ regcache_raw_read_unsigned (struct regcache *regcache, int regnum,
   return status;
 }
 
-/* Return the register's value or throw if it's not available.  */
-
-ULONGEST
-regcache_raw_get_unsigned (struct regcache *regcache, int regnum)
-{
-  ULONGEST value;
-  enum register_status status;
-
-  status = regcache_raw_read_unsigned (regcache, regnum, &value);
-  if (status == REG_UNAVAILABLE)
-    throw_error (NOT_AVAILABLE_ERROR,
-		 _("Register %d is not available"), regnum);
-  return value;
-}
-
 void
 regcache_raw_write_signed (struct regcache *regcache, int regnum, LONGEST val)
 {
diff --git a/gdb/regcache.h b/gdb/regcache.h
index c9d9a7d..36bfef8 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -47,24 +47,6 @@ extern struct gdbarch *get_regcache_arch (const struct regcache *regcache);
 
 extern struct address_space *get_regcache_aspace (const struct regcache *);
 
-enum register_status
-  {
-    /* The register value is not in the cache, and we don't know yet
-       whether it's available in the target (or traceframe).  */
-    REG_UNKNOWN = 0,
-
-    /* The register value is valid and cached.  */
-    REG_VALID = 1,
-
-    /* The register value is unavailable.  E.g., we're inspecting a
-       traceframe, and this register wasn't collected.  Note that this
-       is different a different "unavailable" from saying the register
-       does not exist in the target's architecture --- in that case,
-       the target should have given us a target description that does
-       not include the register in the first place.  */
-    REG_UNAVAILABLE = -1
-  };
-
 enum register_status regcache_register_status (const struct regcache *regcache,
 					       int regnum);
 
@@ -78,12 +60,6 @@ void regcache_raw_write (struct regcache *regcache, int rawnum,
 extern enum register_status
   regcache_raw_read_signed (struct regcache *regcache,
 			    int regnum, LONGEST *val);
-extern enum register_status
-  regcache_raw_read_unsigned (struct regcache *regcache,
-			      int regnum, ULONGEST *val);
-
-ULONGEST regcache_raw_get_unsigned (struct regcache *regcache,
-				    int regnum);
 
 extern void regcache_raw_write_signed (struct regcache *regcache,
 				       int regnum, LONGEST val);
-- 
2.6.3

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v6 1/7] Replace breakpoint_reinsert_addr by get_next_pcs operation in GDBServer.
  2015-12-04 18:46 [PATCH v6 0/6] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
                   ` (2 preceding siblings ...)
  2015-12-04 18:46 ` [PATCH v6 4/7] Share regcache function regcache_raw_read_unsigned Antoine Tremblay
@ 2015-12-04 18:46 ` Antoine Tremblay
  2015-12-04 18:47 ` [PATCH v6 5/7] Support software single step on ARM " Antoine Tremblay
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-04 18:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Antoine Tremblay

This patch in preparation for software single step support on ARM. It refactors
breakpoint_reinsert_addr into get_next_pcs so that multiple location can be
returned.

When software single stepping there can be multiple possible next addresses
because we're stepping over a conditional branch instruction, for example.

The operation get_next_pcs handles that by returning a vector of all the
possible next addresses.

Software breakpoints are installed at each location returned.

No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }

gdb/gdbserver/ChangeLog:
	* linux-aarch64-low.c (the_low_targets): Rename
	breakpoint_reinsert_addr to get_next_pcs.
	* linux-arm-low.c (the_low_targets): Likewise.
	* linux-bfin-low.c (the_low_targets): Likewise.
	* linux-cris-low.c (the_low_targets): Likewise.
	* linux-crisv32-low.c (the_low_targets): Likewise.
	* linux-low.c (can_software_single_step): Likewise.
	(install_software_single_step_breakpoints): New function.
	(start_step_over): Use install_software_single_step_breakpoints.
	* linux-low.h: New CORE_ADDR vector.
	(struct linux_target_ops) Rename breakpoint_reinsert_addr to
	get_next_pcs.
	* linux-mips-low.c (the_low_targets): Likewise.
	* linux-nios2-low.c (the_low_targets): Likewise.
	* linux-sparc-low.c (the_low_targets): Likewise.
---
 gdb/gdbserver/linux-aarch64-low.c |  2 +-
 gdb/gdbserver/linux-arm-low.c     |  2 +-
 gdb/gdbserver/linux-bfin-low.c    |  2 +-
 gdb/gdbserver/linux-cris-low.c    |  2 +-
 gdb/gdbserver/linux-crisv32-low.c |  2 +-
 gdb/gdbserver/linux-low.c         | 27 +++++++++++++++++++++++----
 gdb/gdbserver/linux-low.h         |  5 ++++-
 gdb/gdbserver/linux-mips-low.c    |  2 +-
 gdb/gdbserver/linux-nios2-low.c   |  2 +-
 gdb/gdbserver/linux-sparc-low.c   |  2 +-
 10 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 17798ff..3b8a9ad 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -2963,7 +2963,7 @@ struct linux_target_ops the_low_target =
   aarch64_set_pc,
   NULL, /* breakpoint_kind_from_pc */
   aarch64_sw_breakpoint_from_kind,
-  NULL, /* breakpoint_reinsert_addr */
+  NULL, /* get_next_pcs */
   0,    /* decr_pc_after_break */
   aarch64_breakpoint_at,
   aarch64_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 7703008..f7de2b1 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -1030,7 +1030,7 @@ struct linux_target_ops the_low_target = {
   arm_set_pc,
   arm_breakpoint_kind_from_pc,
   arm_sw_breakpoint_from_kind,
-  NULL, /* breakpoint_reinsert_addr */
+  NULL, /* get_next_pcs */
   0,
   arm_breakpoint_at,
   arm_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-bfin-low.c b/gdb/gdbserver/linux-bfin-low.c
index 912d253..1de2f78 100644
--- a/gdb/gdbserver/linux-bfin-low.c
+++ b/gdb/gdbserver/linux-bfin-low.c
@@ -141,7 +141,7 @@ struct linux_target_ops the_low_target = {
   bfin_set_pc,
   NULL, /* breakpoint_kind_from_pc */
   bfin_sw_breakpoint_from_kind,
-  NULL, /* breakpoint_reinsert_addr */
+  NULL, /* get_next_pcs */
   2,
   bfin_breakpoint_at,
   NULL, /* supports_z_point_type */
diff --git a/gdb/gdbserver/linux-cris-low.c b/gdb/gdbserver/linux-cris-low.c
index 9f4519c..6902a45 100644
--- a/gdb/gdbserver/linux-cris-low.c
+++ b/gdb/gdbserver/linux-cris-low.c
@@ -139,7 +139,7 @@ struct linux_target_ops the_low_target = {
   cris_set_pc,
   NULL, /* breakpoint_kind_from_pc */
   cris_sw_breakpoint_from_kind,
-  NULL, /* breakpoint_reinsert_addr */
+  NULL, /* get_next_pcs */
   0,
   cris_breakpoint_at,
 };
diff --git a/gdb/gdbserver/linux-crisv32-low.c b/gdb/gdbserver/linux-crisv32-low.c
index 2404d0e..28c1981 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -422,7 +422,7 @@ struct linux_target_ops the_low_target = {
   cris_set_pc,
   NULL, /* breakpoint_kind_from_pc */
   cris_sw_breakpoint_from_kind,
-  NULL, /* breakpoint_reinsert_addr */
+  NULL, /* get_next_pcs */
   0,
   cris_breakpoint_at,
   cris_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index ec5337a..8a3c438 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -281,12 +281,12 @@ can_hardware_single_step (void)
 }
 
 /* True if the low target can software single-step.  Such targets
-   implement the BREAKPOINT_REINSERT_ADDR callback.  */
+   implement the GET_NEXT_PCS callback.  */
 
 static int
 can_software_single_step (void)
 {
-  return (the_low_target.breakpoint_reinsert_addr != NULL);
+  return (the_low_target.get_next_pcs != NULL);
 }
 
 /* True if the low target supports memory breakpoints.  If so, we'll
@@ -3957,6 +3957,26 @@ enqueue_pending_signal (struct lwp_info *lwp, int signal, siginfo_t *info)
   lwp->pending_signals = p_sig;
 }
 
+/* Install breakpoints for software single stepping.  */
+
+static void
+install_software_single_step_breakpoints (struct lwp_info *lwp)
+{
+  int i;
+  CORE_ADDR pc;
+  struct regcache *regcache = get_thread_regcache (current_thread, 1);
+  VEC (CORE_ADDR) *next_pcs = NULL;
+  struct cleanup *old_chain = make_cleanup (VEC_cleanup (CORE_ADDR), &next_pcs);
+
+  pc = get_pc (lwp);
+  next_pcs = (*the_low_target.get_next_pcs) (pc, regcache);
+
+  for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); ++i)
+    set_reinsert_breakpoint (pc);
+
+  do_cleanups (old_chain);
+}
+
 /* Resume execution of LWP.  If STEP is nonzero, single-step it.  If
    SIGNAL is nonzero, give it that signal.  */
 
@@ -4522,8 +4542,7 @@ start_step_over (struct lwp_info *lwp)
     }
   else if (can_software_single_step ())
     {
-      CORE_ADDR raddr = (*the_low_target.breakpoint_reinsert_addr) ();
-      set_reinsert_breakpoint (raddr);
+      install_software_single_step_breakpoints (lwp);
       step = 0;
     }
   else
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index c211a37..cd455a8 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -124,6 +124,8 @@ struct process_info_private
 
 struct lwp_info;
 
+DEF_VEC_I (CORE_ADDR);
+
 struct linux_target_ops
 {
   /* Architecture-specific setup.  */
@@ -153,7 +155,8 @@ struct linux_target_ops
   /* See target.h for details.  */
   const gdb_byte *(*sw_breakpoint_from_kind) (int kind, int *size);
 
-  CORE_ADDR (*breakpoint_reinsert_addr) (void);
+  /* Find the next possible PCs after the current instruction executes.  */
+  VEC (CORE_ADDR) *(*get_next_pcs) (CORE_ADDR pc, struct regcache *regcache);
 
   int decr_pc_after_break;
   int (*breakpoint_at) (CORE_ADDR pc);
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index b8f8805..9da4610 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -880,7 +880,7 @@ struct linux_target_ops the_low_target = {
   mips_set_pc,
   NULL, /* breakpoint_kind_from_pc */
   mips_sw_breakpoint_from_kind,
-  NULL, /* breakpoint_reinsert_addr */
+  NULL, /* get_next_pcs */
   0,
   mips_breakpoint_at,
   mips_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-nios2-low.c b/gdb/gdbserver/linux-nios2-low.c
index 9380c3b..1accc03 100644
--- a/gdb/gdbserver/linux-nios2-low.c
+++ b/gdb/gdbserver/linux-nios2-low.c
@@ -267,7 +267,7 @@ struct linux_target_ops the_low_target =
   nios2_set_pc,
   NULL, /* breakpoint_kind_from_pc */
   nios2_sw_breakpoint_from_kind,
-  NULL, /* breakpoint_reinsert_addr */
+  NULL, /* get_next_pcs */
   0,
   nios2_breakpoint_at,
 };
diff --git a/gdb/gdbserver/linux-sparc-low.c b/gdb/gdbserver/linux-sparc-low.c
index 54a849c..bfdd8c1 100644
--- a/gdb/gdbserver/linux-sparc-low.c
+++ b/gdb/gdbserver/linux-sparc-low.c
@@ -320,7 +320,7 @@ struct linux_target_ops the_low_target = {
   NULL,
   NULL, /* breakpoint_kind_from_pc */
   sparc_sw_breakpoint_from_kind,
-  NULL, /* breakpoint_reinsert_addr */
+  NULL, /* get_next_pcs */
   0,
   sparc_breakpoint_at,
   NULL,  /* supports_z_point_type */
-- 
2.6.3

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v6 0/6] Support software single step and conditional breakpoints on ARM in GDBServer.
@ 2015-12-04 18:46 Antoine Tremblay
  2015-12-04 18:46 ` [PATCH v6 3/7] Refactor arm_software_single_step to use regcache Antoine Tremblay
                   ` (6 more replies)
  0 siblings, 7 replies; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-04 18:46 UTC (permalink / raw)
  To: gdb-patches

In this v6:

Patch 3 was changed to add a regcache_get_read_unsigned function that
throws if the register is not available.

The subsequent patches are updated with that change.

Patch 1: is OK.
Patch 2: is OK.
Patch 3: Needs review.
Patch 4: Needs review.
Patch 5: Needs review.
Patch 6: is OK.
Patch 7: is OK.

---

This patch series adds support for software single step and conditional
breakpoints on ARM in GDBServer.

Patches 1,2,3,4 prepares for sharing of software single step code for ARM in
GDB with GDBServer.

Patch 5 Implements the support for ARM software single step.

Patch 6 Adds support for while-stepping actions.

Patch 7 Adds support for conditional breakpoints in GDBServer.

This patch set has no observed regressions, tested on Ubuntu 14.04 ARMv7
and x86.  With gdbserver-{native,extended} / { -marm -mthumb }

Note also that while I could not test thumbv1 instructions directly with
gcc -marmv4t, manual testing of the software single step was done for
thumv1 instructions.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v6 3/7] Refactor arm_software_single_step to use regcache.
  2015-12-04 18:46 [PATCH v6 0/6] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
@ 2015-12-04 18:46 ` Antoine Tremblay
  2015-12-07 14:32   ` Yao Qi
  2015-12-07 14:51   ` Yao Qi
  2015-12-04 18:46 ` [PATCH v6 2/7] Share some ARM target dependent code from GDB with GDBServer Antoine Tremblay
                   ` (5 subsequent siblings)
  6 siblings, 2 replies; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-04 18:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Antoine Tremblay

This patch is in preparation for software single step support on ARM in
GDBServer. It refactors arm_*_software_single_step and sub-functions to
use regcache instead of frame to access registers so that the code can be
shared more easily between GDB and GDBServer.

Note also that since the intention is at some point to get rid of frame
completely in that function, memory reads have also been replaced by
read_memory_unsigned_integer rather than get_frame_memory_unsigned.

No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }

gdb/ChangeLog:

	* arm-linux-tdep.c (arm_linux_sigreturn_next_pc_offset): New function.
	(arm_linux_sigreturn_next_pc): Likewise.
	(arm_linux_syscall_next_pc): Use regcache instead of frame.
	(arm_linux_software_single_step): Likewise.
	* arm-tdep.c (arm_is_thumb): New function.
	(shifted_reg_va): Use regcache instead of frame.
	(thumb_get_next_pc_raw): Likewise.
	(arm_get_next_pc_raw): Likewise.
	(arm_get_next_pc): Likewise.
	(thumb_deal_with_atomic_sequence_raw): Likewise.
	(arm_deal_with_atomic_sequence_raw): Likewise.
	(arm_deal_with_atomic_sequence): Likewise.
	(arm_software_single_step): Likewise.
	* arm-tdep.h (struct gdbarch_tdep): Use regcache for syscall_next_pc.
	(arm_get_next_pc): Use regcache.
	(arm_deal_with_atomic_sequence): Likewise.
	(arm_is_thumb): New declaration.
	* regcache.c (regcache_raw_get_unsigned): New function.
	* regcache.h (regcache_raw_get_unsigned): New function declaration.
---
 gdb/arm-linux-tdep.c |  97 ++++++++++++++++++++++++++++++-------
 gdb/arm-tdep.c       | 132 +++++++++++++++++++++++++++++----------------------
 gdb/arm-tdep.h       |   9 ++--
 gdb/regcache.c       |  15 ++++++
 gdb/regcache.h       |   4 ++
 5 files changed, 180 insertions(+), 77 deletions(-)

diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c
index 73e1271..da8498f 100644
--- a/gdb/arm-linux-tdep.c
+++ b/gdb/arm-linux-tdep.c
@@ -805,6 +805,68 @@ arm_linux_sigreturn_return_addr (struct frame_info *frame,
   return 0;
 }
 
+/* Calculate the offset from stack pointer of the pc register on the stack
+   in the case of a sigreturn or sigreturn_rt syscall.  */
+static int
+arm_linux_sigreturn_next_pc_offset (unsigned long sp,
+				    unsigned long sp_data,
+				    unsigned long svc_number)
+{
+  /* Offset of R0 register.  */
+  int r0_offset = 0;
+  /* Offset of PC register.  */
+  int pc_offset = 0;
+
+  gdb_assert (svc_number == 119 || svc_number == 173);
+
+  /* sigreturn.  */
+  if (svc_number == 119)
+    {
+      if (sp_data == ARM_NEW_SIGFRAME_MAGIC)
+	r0_offset = ARM_UCONTEXT_SIGCONTEXT + ARM_SIGCONTEXT_R0;
+      else
+	r0_offset = ARM_SIGCONTEXT_R0;
+    }
+  /* rt_sigreturn. */
+  else if (svc_number == 173)
+    {
+      if (sp_data == sp + ARM_OLD_RT_SIGFRAME_SIGINFO)
+	r0_offset = ARM_OLD_RT_SIGFRAME_UCONTEXT + ARM_UCONTEXT_SIGCONTEXT
+	  + ARM_SIGCONTEXT_R0;
+      else
+	r0_offset = ARM_NEW_RT_SIGFRAME_UCONTEXT + ARM_UCONTEXT_SIGCONTEXT
+	  + ARM_SIGCONTEXT_R0;
+    }
+
+  pc_offset = r0_offset + 4 * 15;
+
+  return pc_offset;
+}
+
+/* Find the value of the next PC after a sigreturn or rt_sigreturn syscall
+   based on current processor state.  */
+static CORE_ADDR
+arm_linux_sigreturn_next_pc (struct regcache *regcache,
+			     unsigned long svc_number)
+{
+  ULONGEST sp;
+  unsigned long sp_data;
+  CORE_ADDR next_pc = 0;
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int pc_offset = 0;
+
+  gdb_assert (svc_number == 119 || svc_number == 173);
+
+  regcache_cooked_read_unsigned (regcache, ARM_SP_REGNUM, &sp);
+  sp_data = read_memory_unsigned_integer (sp, 4, byte_order);
+
+  pc_offset = arm_linux_sigreturn_next_pc_offset (sp, sp_data, svc_number);
+  next_pc = read_memory_unsigned_integer (sp + pc_offset, 4, byte_order);
+
+  return next_pc;
+}
+
 /* At a ptrace syscall-stop, return the syscall number.  This either
    comes from the SWI instruction (OABI) or from r7 (EABI).
 
@@ -862,21 +924,21 @@ arm_linux_get_syscall_number (struct gdbarch *gdbarch,
    instruction to be executed.  */
 
 static CORE_ADDR
-arm_linux_syscall_next_pc (struct frame_info *frame)
+arm_linux_syscall_next_pc (struct regcache *regcache)
 {
-  CORE_ADDR pc = get_frame_pc (frame);
-  CORE_ADDR return_addr = 0;
-  int is_thumb = arm_frame_is_thumb (frame);
+  CORE_ADDR pc = regcache_read_pc (regcache);
+  CORE_ADDR next_pc = 0;
+  int is_thumb = arm_is_thumb (regcache);
   ULONGEST svc_number = 0;
 
   if (is_thumb)
     {
-      svc_number = get_frame_register_unsigned (frame, 7);
-      return_addr = pc + 2;
+      svc_number = regcache_raw_get_unsigned (regcache, 7);
+      next_pc = pc + 2;
     }
   else
     {
-      struct gdbarch *gdbarch = get_frame_arch (frame);
+      struct gdbarch *gdbarch = get_regcache_arch (regcache);
       enum bfd_endian byte_order_for_code = 
 	gdbarch_byte_order_for_code (gdbarch);
       unsigned long this_instr = 
@@ -889,19 +951,20 @@ arm_linux_syscall_next_pc (struct frame_info *frame)
 	}
       else /* EABI.  */
 	{
-	  svc_number = get_frame_register_unsigned (frame, 7);
+	  svc_number = regcache_raw_get_unsigned (regcache, 7);
 	}
 
-      return_addr = pc + 4;
+      next_pc = pc + 4;
     }
 
-  arm_linux_sigreturn_return_addr (frame, svc_number, &return_addr, &is_thumb);
+  if (svc_number == 119 || svc_number == 173)
+    next_pc = arm_linux_sigreturn_next_pc (regcache, svc_number);
 
   /* Addresses for calling Thumb functions have the bit 0 set.  */
   if (is_thumb)
-    return_addr |= 1;
+    next_pc |= 1;
 
-  return return_addr;
+  return next_pc;
 }
 
 
@@ -910,11 +973,13 @@ arm_linux_syscall_next_pc (struct frame_info *frame)
 static int
 arm_linux_software_single_step (struct frame_info *frame)
 {
-  struct gdbarch *gdbarch = get_frame_arch (frame);
-  struct address_space *aspace = get_frame_address_space (frame);
+  struct regcache *regcache = get_current_regcache ();
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct address_space *aspace = get_regcache_aspace (regcache);
+
   CORE_ADDR next_pc;
 
-  if (arm_deal_with_atomic_sequence (frame))
+  if (arm_deal_with_atomic_sequence (regcache))
     return 1;
 
   /* If the target does have hardware single step, GDB doesn't have
@@ -922,7 +987,7 @@ arm_linux_software_single_step (struct frame_info *frame)
   if (target_can_do_single_step () == 1)
     return 0;
 
-  next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
+  next_pc = arm_get_next_pc (regcache, regcache_read_pc (regcache));
 
   /* The Linux kernel offers some user-mode helpers in a high page.  We can
      not read this page (as of 2.6.23), and even if we could then we couldn't
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 848af97..ebe4c40 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -281,6 +281,19 @@ arm_psr_thumb_bit (struct gdbarch *gdbarch)
     return CPSR_T;
 }
 
+/* Determine if the processor is currently executing in Thumb mode.  */
+
+int
+arm_is_thumb (struct regcache *regcache)
+{
+  ULONGEST cpsr;
+  ULONGEST t_bit = arm_psr_thumb_bit (get_regcache_arch (regcache));
+
+  cpsr = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM);
+
+  return (cpsr & t_bit) != 0;
+}
+
 /* Determine if FRAME is executing in Thumb mode.  */
 
 int
@@ -4302,7 +4315,7 @@ convert_to_extended (const struct floatformat *fmt, void *dbl, const void *ptr,
 }
 
 static unsigned long
-shifted_reg_val (struct frame_info *frame, unsigned long inst, int carry,
+shifted_reg_val (struct regcache *regcache, unsigned long inst, int carry,
 		 unsigned long pc_val, unsigned long status_reg)
 {
   unsigned long res, shift;
@@ -4313,14 +4326,14 @@ shifted_reg_val (struct frame_info *frame, unsigned long inst, int carry,
     {
       int rs = bits (inst, 8, 11);
       shift = (rs == 15 ? pc_val + 8
-			: get_frame_register_unsigned (frame, rs)) & 0xFF;
+	       : regcache_raw_get_unsigned (regcache, rs)) & 0xFF;
     }
   else
     shift = bits (inst, 7, 11);
 
   res = (rm == ARM_PC_REGNUM
 	 ? (pc_val + (bit (inst, 4) ? 12 : 8))
-	 : get_frame_register_unsigned (frame, rm));
+	 : regcache_raw_get_unsigned (regcache, rm));
 
   switch (shifttype)
     {
@@ -4372,10 +4385,10 @@ thumb_advance_itstate (unsigned int itstate)
    another breakpoint by our caller.  */
 
 static CORE_ADDR
-thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
+thumb_get_next_pc_raw (struct regcache *regcache, CORE_ADDR pc)
 {
-  struct gdbarch *gdbarch = get_frame_arch (frame);
-  struct address_space *aspace = get_frame_address_space (frame);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct address_space *aspace = get_regcache_aspace (regcache);
   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 */
@@ -4397,7 +4410,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
      base condition.  One of the low four bits will be set if an IT
      block is active.  These bits read as zero on earlier
      processors.  */
-  status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
+  status = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM);
   itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
 
   /* If-Then handling.  On GNU/Linux, where this routine is used, we
@@ -4512,7 +4525,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
       /* Fetch the saved PC from the stack.  It's stored above
          all of the other registers.  */
       offset = bitcount (bits (inst1, 0, 7)) * INT_REGISTER_SIZE;
-      sp = get_frame_register_unsigned (frame, ARM_SP_REGNUM);
+      sp = regcache_raw_get_unsigned (regcache, ARM_SP_REGNUM);
       nextpc = read_memory_unsigned_integer (sp + offset, 4, byte_order);
     }
   else if ((inst1 & 0xf000) == 0xd000)	/* conditional branch */
@@ -4524,7 +4537,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 	  tdep = gdbarch_tdep (gdbarch);
 
 	  if (tdep->syscall_next_pc != NULL)
-	    nextpc = tdep->syscall_next_pc (frame);
+	    nextpc = tdep->syscall_next_pc (regcache);
 
 	}
       else if (cond != 0x0f && condition_true (cond, status))
@@ -4568,7 +4581,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 	  else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
 	    {
 	      /* SUBS PC, LR, #imm8.  */
-	      nextpc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
+	      nextpc = regcache_raw_get_unsigned (regcache, ARM_LR_REGNUM);
 	      nextpc -= inst2 & 0x00ff;
 	    }
 	  else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380)
@@ -4626,14 +4639,15 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 
 	  if (load_pc)
 	    {
-	      CORE_ADDR addr = get_frame_register_unsigned (frame, rn);
-	      nextpc = get_frame_memory_unsigned (frame, addr + offset, 4);
+	      CORE_ADDR addr = regcache_raw_get_unsigned (regcache, rn);
+	      nextpc = read_memory_unsigned_integer (addr + offset, 4,
+						     byte_order);
 	    }
 	}
       else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
 	{
 	  /* MOV PC or MOVS PC.  */
-	  nextpc = get_frame_register_unsigned (frame, bits (inst2, 0, 3));
+	  nextpc = regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3));
 	  nextpc = MAKE_THUMB_ADDR (nextpc);
 	}
       else if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000)
@@ -4643,7 +4657,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 	  int rn, load_pc = 1;
 
 	  rn = bits (inst1, 0, 3);
-	  base = get_frame_register_unsigned (frame, rn);
+	  base = regcache_raw_get_unsigned (regcache, rn);
 	  if (rn == ARM_PC_REGNUM)
 	    {
 	      base = (base + 4) & ~(CORE_ADDR) 0x3;
@@ -4667,14 +4681,14 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 	  else if ((inst2 & 0x0fc0) == 0x0000)
 	    {
 	      int shift = bits (inst2, 4, 5), rm = bits (inst2, 0, 3);
-	      base += get_frame_register_unsigned (frame, rm) << shift;
+	      base += regcache_raw_get_unsigned (regcache, rm) << shift;
 	    }
 	  else
 	    /* Reserved.  */
 	    load_pc = 0;
 
 	  if (load_pc)
-	    nextpc = get_frame_memory_unsigned (frame, base, 4);
+	    nextpc = read_memory_unsigned_integer (base, 4, byte_order);
 	}
       else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
 	{
@@ -4685,10 +4699,11 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 	  if (tbl_reg == 0x0f)
 	    table = pc + 4;  /* Regcache copy of PC isn't right yet.  */
 	  else
-	    table = get_frame_register_unsigned (frame, tbl_reg);
+	    table = regcache_raw_get_unsigned (regcache, tbl_reg);
 
-	  offset = get_frame_register_unsigned (frame, bits (inst2, 0, 3));
-	  length = 2 * get_frame_memory_unsigned (frame, table + offset, 1);
+	  offset = regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3));
+	  length = 2 * read_memory_unsigned_integer (table + offset, 1,
+						     byte_order);
 	  nextpc = pc_val + length;
 	}
       else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
@@ -4700,10 +4715,11 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 	  if (tbl_reg == 0x0f)
 	    table = pc + 4;  /* Regcache copy of PC isn't right yet.  */
 	  else
-	    table = get_frame_register_unsigned (frame, tbl_reg);
+	    table = regcache_raw_get_unsigned (regcache, tbl_reg);
 
-	  offset = 2 * get_frame_register_unsigned (frame, bits (inst2, 0, 3));
-	  length = 2 * get_frame_memory_unsigned (frame, table + offset, 2);
+	  offset = 2 * regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3));
+	  length = 2 * read_memory_unsigned_integer (table + offset, 2,
+						     byte_order);
 	  nextpc = pc_val + length;
 	}
     }
@@ -4712,14 +4728,14 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
       if (bits (inst1, 3, 6) == 0x0f)
 	nextpc = UNMAKE_THUMB_ADDR (pc_val);
       else
-	nextpc = get_frame_register_unsigned (frame, bits (inst1, 3, 6));
+	nextpc = regcache_raw_get_unsigned (regcache, bits (inst1, 3, 6));
     }
   else if ((inst1 & 0xff87) == 0x4687)	/* mov pc, REG */
     {
       if (bits (inst1, 3, 6) == 0x0f)
 	nextpc = pc_val;
       else
-	nextpc = get_frame_register_unsigned (frame, bits (inst1, 3, 6));
+	nextpc = regcache_raw_get_unsigned (regcache, bits (inst1, 3, 6));
 
       nextpc = MAKE_THUMB_ADDR (nextpc);
     }
@@ -4727,7 +4743,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
     {
       /* CBNZ or CBZ.  */
       int imm = (bit (inst1, 9) << 6) + (bits (inst1, 3, 7) << 1);
-      ULONGEST reg = get_frame_register_unsigned (frame, bits (inst1, 0, 2));
+      ULONGEST reg = regcache_raw_get_unsigned (regcache, bits (inst1, 0, 2));
 
       if (bit (inst1, 11) && reg != 0)
 	nextpc = pc_val + imm;
@@ -4746,9 +4762,9 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
    address.  */
 
 static CORE_ADDR
-arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
+arm_get_next_pc_raw (struct regcache *regcache, CORE_ADDR pc)
 {
-  struct gdbarch *gdbarch = get_frame_arch (frame);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
   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;
@@ -4759,7 +4775,7 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
   pc_val = (unsigned long) pc;
   this_instr = read_memory_unsigned_integer (pc, 4, byte_order_for_code);
 
-  status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
+  status = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM);
   nextpc = (CORE_ADDR) (pc_val + 4);	/* Default case */
 
   if (bits (this_instr, 28, 31) == INST_NV)
@@ -4809,7 +4825,7 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 		rn = bits (this_instr, 0, 3);
 		nextpc = ((rn == ARM_PC_REGNUM)
 			  ? (pc_val + 8)
-			  : get_frame_register_unsigned (frame, rn));
+			  : regcache_raw_get_unsigned (regcache, rn));
 
 		return nextpc;
 	      }
@@ -4819,7 +4835,7 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 	    rn = bits (this_instr, 16, 19);
 	    operand1 = ((rn == ARM_PC_REGNUM)
 			? (pc_val + 8)
-			: get_frame_register_unsigned (frame, rn));
+			: regcache_raw_get_unsigned (regcache, rn));
 
 	    if (bit (this_instr, 25))
 	      {
@@ -4829,7 +4845,7 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 		  & 0xffffffff;
 	      }
 	    else		/* operand 2 is a shifted register.  */
-	      operand2 = shifted_reg_val (frame, this_instr, c,
+	      operand2 = shifted_reg_val (regcache, this_instr, c,
 					  pc_val, status);
 
 	    switch (bits (this_instr, 21, 24))
@@ -4928,7 +4944,7 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 		  rn = bits (this_instr, 16, 19);
 		  base = ((rn == ARM_PC_REGNUM)
 			  ? (pc_val + 8)
-			  : get_frame_register_unsigned (frame, rn));
+			  : regcache_raw_get_unsigned (regcache, rn));
 
 		  if (bit (this_instr, 24))
 		    {
@@ -4936,7 +4952,8 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 		      int c = (status & FLAG_C) ? 1 : 0;
 		      unsigned long offset =
 		      (bit (this_instr, 25)
-		       ? shifted_reg_val (frame, this_instr, c, pc_val, status)
+		       ? shifted_reg_val (regcache, this_instr, c, pc_val,
+					  status)
 		       : bits (this_instr, 0, 11));
 
 		      if (bit (this_instr, 23))
@@ -4961,8 +4978,8 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 		  /* loading pc */
 		  int offset = 0;
 		  unsigned long rn_val
-		    = get_frame_register_unsigned (frame,
-						   bits (this_instr, 16, 19));
+		    = regcache_raw_get_unsigned (regcache,
+						 bits (this_instr, 16, 19));
 
 		  if (bit (this_instr, 23))
 		    {
@@ -5000,7 +5017,7 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
 	    tdep = gdbarch_tdep (gdbarch);
 
 	    if (tdep->syscall_next_pc != NULL)
-	      nextpc = tdep->syscall_next_pc (frame);
+	      nextpc = tdep->syscall_next_pc (regcache);
 
 	  }
 	  break;
@@ -5019,14 +5036,14 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
    loop is detected.  */
 
 CORE_ADDR
-arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
+arm_get_next_pc (struct regcache *regcache, CORE_ADDR pc)
 {
   CORE_ADDR nextpc;
 
-  if (arm_frame_is_thumb (frame))
-    nextpc = thumb_get_next_pc_raw (frame, pc);
+  if (arm_is_thumb (regcache))
+    nextpc = thumb_get_next_pc_raw (regcache, pc);
   else
-    nextpc = arm_get_next_pc_raw (frame, pc);
+    nextpc = arm_get_next_pc_raw (regcache, pc);
 
   return nextpc;
 }
@@ -5057,12 +5074,12 @@ arm_insert_single_step_breakpoint (struct gdbarch *gdbarch,
    the sequence.  */
 
 static int
-thumb_deal_with_atomic_sequence_raw (struct frame_info *frame)
+thumb_deal_with_atomic_sequence_raw (struct regcache *regcache)
 {
-  struct gdbarch *gdbarch = get_frame_arch (frame);
-  struct address_space *aspace = get_frame_address_space (frame);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct address_space *aspace = get_regcache_aspace (regcache);
   enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
-  CORE_ADDR pc = get_frame_pc (frame);
+  CORE_ADDR pc = regcache_read_pc (regcache);
   CORE_ADDR breaks[2] = {-1, -1};
   CORE_ADDR loc = pc;
   unsigned short insn1, insn2;
@@ -5073,7 +5090,7 @@ thumb_deal_with_atomic_sequence_raw (struct frame_info *frame)
   ULONGEST status, itstate;
 
   /* We currently do not support atomic sequences within an IT block.  */
-  status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
+  status = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM);
   itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
   if (itstate & 0x0f)
     return 0;
@@ -5188,12 +5205,12 @@ thumb_deal_with_atomic_sequence_raw (struct frame_info *frame)
 }
 
 static int
-arm_deal_with_atomic_sequence_raw (struct frame_info *frame)
+arm_deal_with_atomic_sequence_raw (struct regcache *regcache)
 {
-  struct gdbarch *gdbarch = get_frame_arch (frame);
-  struct address_space *aspace = get_frame_address_space (frame);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct address_space *aspace = get_regcache_aspace (regcache);
   enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
-  CORE_ADDR pc = get_frame_pc (frame);
+  CORE_ADDR pc = regcache_read_pc (regcache);
   CORE_ADDR breaks[2] = {-1, -1};
   CORE_ADDR loc = pc;
   unsigned int insn;
@@ -5263,12 +5280,12 @@ arm_deal_with_atomic_sequence_raw (struct frame_info *frame)
 }
 
 int
-arm_deal_with_atomic_sequence (struct frame_info *frame)
+arm_deal_with_atomic_sequence (struct regcache *regcache)
 {
-  if (arm_frame_is_thumb (frame))
-    return thumb_deal_with_atomic_sequence_raw (frame);
+  if (arm_is_thumb (regcache))
+    return thumb_deal_with_atomic_sequence_raw (regcache);
   else
-    return arm_deal_with_atomic_sequence_raw (frame);
+    return arm_deal_with_atomic_sequence_raw (regcache);
 }
 
 /* single_step() is called just before we want to resume the inferior,
@@ -5279,14 +5296,15 @@ arm_deal_with_atomic_sequence (struct frame_info *frame)
 int
 arm_software_single_step (struct frame_info *frame)
 {
-  struct gdbarch *gdbarch = get_frame_arch (frame);
-  struct address_space *aspace = get_frame_address_space (frame);
+  struct regcache *regcache = get_current_regcache ();
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct address_space *aspace = get_regcache_aspace (regcache);
   CORE_ADDR next_pc;
 
-  if (arm_deal_with_atomic_sequence (frame))
+  if (arm_deal_with_atomic_sequence (regcache))
     return 1;
 
-  next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
+  next_pc = arm_get_next_pc (regcache, regcache_read_pc (regcache));
   arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
 
   return 1;
diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h
index 9b8447b..0063fef 100644
--- a/gdb/arm-tdep.h
+++ b/gdb/arm-tdep.h
@@ -135,9 +135,9 @@ struct gdbarch_tdep
   struct type *neon_double_type;
   struct type *neon_quad_type;
 
-  /* Return the expected next PC if FRAME is stopped at a syscall
+  /* Return the expected next PC if the program is stopped at a syscall
      instruction.  */
-  CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
+  CORE_ADDR (*syscall_next_pc) (struct regcache *regcache);
 
    /* syscall record.  */
   int (*arm_syscall_record) (struct regcache *regcache, unsigned long svc_number);
@@ -250,11 +250,12 @@ extern void
 		       ULONGEST val, enum pc_write_style write_pc);
 
 CORE_ADDR arm_skip_stub (struct frame_info *, CORE_ADDR);
-CORE_ADDR arm_get_next_pc (struct frame_info *, CORE_ADDR);
+CORE_ADDR arm_get_next_pc (struct regcache *regcache, CORE_ADDR pc);
 void arm_insert_single_step_breakpoint (struct gdbarch *,
 					struct address_space *, CORE_ADDR);
-int arm_deal_with_atomic_sequence (struct frame_info *);
+int arm_deal_with_atomic_sequence (struct regcache *);
 int arm_software_single_step (struct frame_info *);
+int arm_is_thumb (struct regcache *regcache);
 int arm_frame_is_thumb (struct frame_info *frame);
 
 extern struct displaced_step_closure *
diff --git a/gdb/regcache.c b/gdb/regcache.c
index 5ee31fb..7fb9d18 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -715,6 +715,21 @@ regcache_raw_read_unsigned (struct regcache *regcache, int regnum,
   return status;
 }
 
+/* Return the register's value or throw if it's not available.  */
+
+ULONGEST
+regcache_raw_get_unsigned (struct regcache *regcache, int regnum)
+{
+  ULONGEST value;
+  enum register_status status;
+
+  status = regcache_raw_read_unsigned (regcache, regnum, &value);
+  if (status == REG_UNAVAILABLE)
+    throw_error (NOT_AVAILABLE_ERROR,
+		 _("Register %d is not available"), regnum);
+  return value;
+}
+
 void
 regcache_raw_write_signed (struct regcache *regcache, int regnum, LONGEST val)
 {
diff --git a/gdb/regcache.h b/gdb/regcache.h
index a9fb44b..c9d9a7d 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -81,6 +81,10 @@ extern enum register_status
 extern enum register_status
   regcache_raw_read_unsigned (struct regcache *regcache,
 			      int regnum, ULONGEST *val);
+
+ULONGEST regcache_raw_get_unsigned (struct regcache *regcache,
+				    int regnum);
+
 extern void regcache_raw_write_signed (struct regcache *regcache,
 				       int regnum, LONGEST val);
 extern void regcache_raw_write_unsigned (struct regcache *regcache,
-- 
2.6.3

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v6 2/7] Share some ARM target dependent code from GDB with GDBServer.
  2015-12-04 18:46 [PATCH v6 0/6] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
  2015-12-04 18:46 ` [PATCH v6 3/7] Refactor arm_software_single_step to use regcache Antoine Tremblay
@ 2015-12-04 18:46 ` Antoine Tremblay
  2015-12-04 18:46 ` [PATCH v6 4/7] Share regcache function regcache_raw_read_unsigned Antoine Tremblay
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-04 18:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Antoine Tremblay

This patch is in preparation for software single stepping support on ARM
it shares some functions and definitions that will be needed.

No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }

Not tested: wince/bsd build.

gdb/ChangeLog:

	* arch/arm.c (bitcount): Move from arm-tdep.c.
	(condition_true): Likewise.
	* arch/arm.h (Instruction Definitions): Move form arm-tdep.h.
	(condition_true): Move defenition from arm-tdep.h.
	(bitcount): Likewise.
	* arm-tdep.c (condition_true): Move to arch/arm.c.
	(bitcount): Likewise.
	* arm-tdep.h (Instruction Definitions): Move to arch/arm.h.
	* arm-wince-tdep.c: Include arch/arm.h.
	* armnbsd-tdep.c: Likewise.
---
 gdb/arch/arm.c       | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 gdb/arch/arm.h       | 36 +++++++++++++++++++++++++++++++
 gdb/arm-tdep.c       | 55 -----------------------------------------------
 gdb/arm-tdep.h       | 29 -------------------------
 gdb/arm-wince-tdep.c |  1 +
 gdb/armnbsd-tdep.c   |  1 +
 6 files changed, 96 insertions(+), 86 deletions(-)

diff --git a/gdb/arch/arm.c b/gdb/arch/arm.c
index b11c684..426377f 100644
--- a/gdb/arch/arm.c
+++ b/gdb/arch/arm.c
@@ -20,8 +20,7 @@
 #include "common-defs.h"
 #include "arm.h"
 
-/* Return the size in bytes of the complete Thumb instruction whose
-   first halfword is INST1.  */
+/* See arm.h.  */
 
 int
 thumb_insn_size (unsigned short inst1)
@@ -31,3 +30,60 @@ thumb_insn_size (unsigned short inst1)
   else
     return 2;
 }
+
+/* See arm.h.  */
+
+int
+bitcount (unsigned long val)
+{
+  int nbits;
+  for (nbits = 0; val != 0; nbits++)
+    val &= val - 1;		/* Delete rightmost 1-bit in val.  */
+  return nbits;
+}
+
+/* See arm.h.  */
+
+int
+condition_true (unsigned long cond, unsigned long status_reg)
+{
+  if (cond == INST_AL || cond == INST_NV)
+    return 1;
+
+  switch (cond)
+    {
+    case INST_EQ:
+      return ((status_reg & FLAG_Z) != 0);
+    case INST_NE:
+      return ((status_reg & FLAG_Z) == 0);
+    case INST_CS:
+      return ((status_reg & FLAG_C) != 0);
+    case INST_CC:
+      return ((status_reg & FLAG_C) == 0);
+    case INST_MI:
+      return ((status_reg & FLAG_N) != 0);
+    case INST_PL:
+      return ((status_reg & FLAG_N) == 0);
+    case INST_VS:
+      return ((status_reg & FLAG_V) != 0);
+    case INST_VC:
+      return ((status_reg & FLAG_V) == 0);
+    case INST_HI:
+      return ((status_reg & (FLAG_C | FLAG_Z)) == FLAG_C);
+    case INST_LS:
+      return ((status_reg & (FLAG_C | FLAG_Z)) != FLAG_C);
+    case INST_GE:
+      return (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0));
+    case INST_LT:
+      return (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0));
+    case INST_GT:
+      return (((status_reg & FLAG_Z) == 0)
+	      && (((status_reg & FLAG_N) == 0)
+		  == ((status_reg & FLAG_V) == 0)));
+    case INST_LE:
+      return (((status_reg & FLAG_Z) != 0)
+	      || (((status_reg & FLAG_N) == 0)
+		  != ((status_reg & FLAG_V) == 0)));
+    }
+  return 1;
+}
diff --git a/gdb/arch/arm.h b/gdb/arch/arm.h
index a054776..1a877bc 100644
--- a/gdb/arch/arm.h
+++ b/gdb/arch/arm.h
@@ -58,6 +58,36 @@ enum gdb_regnum {
   ARM_LAST_FP_ARG_REGNUM = ARM_F3_REGNUM
 };
 
+/* Instruction condition field values.  */
+#define INST_EQ		0x0
+#define INST_NE		0x1
+#define INST_CS		0x2
+#define INST_CC		0x3
+#define INST_MI		0x4
+#define INST_PL		0x5
+#define INST_VS		0x6
+#define INST_VC		0x7
+#define INST_HI		0x8
+#define INST_LS		0x9
+#define INST_GE		0xa
+#define INST_LT		0xb
+#define INST_GT		0xc
+#define INST_LE		0xd
+#define INST_AL		0xe
+#define INST_NV		0xf
+
+#define FLAG_N		0x80000000
+#define FLAG_Z		0x40000000
+#define FLAG_C		0x20000000
+#define FLAG_V		0x10000000
+
+#define CPSR_T		0x20
+
+#define XPSR_T		0x01000000
+
+/* Size of integer registers.  */
+#define INT_REGISTER_SIZE		4
+
 /* Addresses for calling Thumb functions have the bit 0 set.
    Here are some macros to test, set, or clear bit 0 of addresses.  */
 #define IS_THUMB_ADDR(addr)    ((addr) & 1)
@@ -68,4 +98,10 @@ enum gdb_regnum {
    first halfword is INST1.  */
 int thumb_insn_size (unsigned short inst1);
 
+/* Returns true if the condition evaluates to true.  */
+int condition_true (unsigned long cond, unsigned long status_reg);
+
+/* Return number of 1-bits in VAL.  */
+int bitcount (unsigned long val);
+
 #endif
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 6ce6f09c..848af97 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -4301,50 +4301,6 @@ convert_to_extended (const struct floatformat *fmt, void *dbl, const void *ptr,
 			       &d, dbl);
 }
 
-static int
-condition_true (unsigned long cond, unsigned long status_reg)
-{
-  if (cond == INST_AL || cond == INST_NV)
-    return 1;
-
-  switch (cond)
-    {
-    case INST_EQ:
-      return ((status_reg & FLAG_Z) != 0);
-    case INST_NE:
-      return ((status_reg & FLAG_Z) == 0);
-    case INST_CS:
-      return ((status_reg & FLAG_C) != 0);
-    case INST_CC:
-      return ((status_reg & FLAG_C) == 0);
-    case INST_MI:
-      return ((status_reg & FLAG_N) != 0);
-    case INST_PL:
-      return ((status_reg & FLAG_N) == 0);
-    case INST_VS:
-      return ((status_reg & FLAG_V) != 0);
-    case INST_VC:
-      return ((status_reg & FLAG_V) == 0);
-    case INST_HI:
-      return ((status_reg & (FLAG_C | FLAG_Z)) == FLAG_C);
-    case INST_LS:
-      return ((status_reg & (FLAG_C | FLAG_Z)) != FLAG_C);
-    case INST_GE:
-      return (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0));
-    case INST_LT:
-      return (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0));
-    case INST_GT:
-      return (((status_reg & FLAG_Z) == 0)
-	      && (((status_reg & FLAG_N) == 0)
-		  == ((status_reg & FLAG_V) == 0)));
-    case INST_LE:
-      return (((status_reg & FLAG_Z) != 0)
-	      || (((status_reg & FLAG_N) == 0)
-		  != ((status_reg & FLAG_V) == 0)));
-    }
-  return 1;
-}
-
 static unsigned long
 shifted_reg_val (struct frame_info *frame, unsigned long inst, int carry,
 		 unsigned long pc_val, unsigned long status_reg)
@@ -4395,17 +4351,6 @@ shifted_reg_val (struct frame_info *frame, unsigned long inst, int carry,
   return res & 0xffffffff;
 }
 
-/* Return number of 1-bits in VAL.  */
-
-static int
-bitcount (unsigned long val)
-{
-  int nbits;
-  for (nbits = 0; val != 0; nbits++)
-    val &= val - 1;		/* Delete rightmost 1-bit in val.  */
-  return nbits;
-}
-
 static int
 thumb_advance_itstate (unsigned int itstate)
 {
diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h
index 3e06f79..9b8447b 100644
--- a/gdb/arm-tdep.h
+++ b/gdb/arm-tdep.h
@@ -26,9 +26,6 @@ struct address_space;
 
 #include "arch/arm.h"
 
-/* Size of integer registers.  */
-#define INT_REGISTER_SIZE		4
-
 /* Say how long FP registers are.  Used for documentation purposes and
    code readability in this header.  IEEE extended doubles are 80
    bits.  DWORD aligned they use 96 bits.  */
@@ -50,32 +47,6 @@ struct address_space;
 #define NUM_GREGS	16	/* Number of general purpose registers.  */
 
 
-/* Instruction condition field values.  */
-#define INST_EQ		0x0
-#define INST_NE		0x1
-#define INST_CS		0x2
-#define INST_CC		0x3
-#define INST_MI		0x4
-#define INST_PL		0x5
-#define INST_VS		0x6
-#define INST_VC		0x7
-#define INST_HI		0x8
-#define INST_LS		0x9
-#define INST_GE		0xa
-#define INST_LT		0xb
-#define INST_GT		0xc
-#define INST_LE		0xd
-#define INST_AL		0xe
-#define INST_NV		0xf
-
-#define FLAG_N		0x80000000
-#define FLAG_Z		0x40000000
-#define FLAG_C		0x20000000
-#define FLAG_V		0x10000000
-
-#define CPSR_T		0x20
-
-#define XPSR_T		0x01000000
 
 /* Type of floating-point code in use by inferior.  There are really 3 models
    that are traditionally supported (plus the endianness issue), but gcc can
diff --git a/gdb/arm-wince-tdep.c b/gdb/arm-wince-tdep.c
index 72295ba..3abd89d 100644
--- a/gdb/arm-wince-tdep.c
+++ b/gdb/arm-wince-tdep.c
@@ -24,6 +24,7 @@
 #include "target.h"
 #include "frame.h"
 
+#include "arch/arm.h"
 #include "arm-tdep.h"
 #include "windows-tdep.h"
 
diff --git a/gdb/armnbsd-tdep.c b/gdb/armnbsd-tdep.c
index 4c128c2..14eceaa 100644
--- a/gdb/armnbsd-tdep.c
+++ b/gdb/armnbsd-tdep.c
@@ -20,6 +20,7 @@
 #include "defs.h"
 #include "osabi.h"
 
+#include "arch/arm.h"
 #include "arm-tdep.h"
 #include "solib-svr4.h"
 
-- 
2.6.3

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v6 6/7] Enable software single stepping for while-stepping actions in GDBServer.
  2015-12-04 18:46 [PATCH v6 0/6] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
                   ` (5 preceding siblings ...)
  2015-12-04 18:47 ` [PATCH v6 7/7] Enable conditional breakpoints for targets that support software single step " Antoine Tremblay
@ 2015-12-04 18:47 ` Antoine Tremblay
  6 siblings, 0 replies; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-04 18:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Antoine Tremblay

This patch enables software single stepping if the targets support it,
to do while-stepping actions.

No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }

gdb/gdbserver/ChangeLog:

	* linux-low.c (single_step): New function.
	(linux_resume_one_lwp_throw): Call single_step.
	(start_step_over): Likewise.
---
 gdb/gdbserver/linux-low.c | 48 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 31 insertions(+), 17 deletions(-)

diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 8a3c438..9ddcf92 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -3977,6 +3977,33 @@ install_software_single_step_breakpoints (struct lwp_info *lwp)
   do_cleanups (old_chain);
 }
 
+/* Single step via hardware or software single step.
+   Return 1 if hardware single stepping, 0 if software single stepping
+   or can't single step.  */
+
+static int
+single_step (struct lwp_info* lwp)
+{
+  int step = 0;
+
+  if (can_hardware_single_step ())
+    {
+      step = 1;
+    }
+  else if (can_software_single_step ())
+    {
+      install_software_single_step_breakpoints (lwp);
+      step = 0;
+    }
+  else
+    {
+      if (debug_threads)
+	debug_printf ("stepping is not implemented on this target");
+    }
+
+  return step;
+}
+
 /* Resume execution of LWP.  If STEP is nonzero, single-step it.  If
    SIGNAL is nonzero, give it that signal.  */
 
@@ -4124,13 +4151,13 @@ linux_resume_one_lwp_throw (struct lwp_info *lwp,
      address, continue, and carry on catching this while-stepping
      action only when that breakpoint is hit.  A future
      enhancement.  */
-  if (thread->while_stepping != NULL
-      && can_hardware_single_step ())
+  if (thread->while_stepping != NULL)
     {
       if (debug_threads)
 	debug_printf ("lwp %ld has a while-stepping action -> forcing step.\n",
 		      lwpid_of (thread));
-      step = 1;
+
+      step = single_step (lwp);
     }
 
   if (proc->tdesc != NULL && the_low_target.get_pc != NULL)
@@ -4536,20 +4563,7 @@ start_step_over (struct lwp_info *lwp)
   uninsert_breakpoints_at (pc);
   uninsert_fast_tracepoint_jumps_at (pc);
 
-  if (can_hardware_single_step ())
-    {
-      step = 1;
-    }
-  else if (can_software_single_step ())
-    {
-      install_software_single_step_breakpoints (lwp);
-      step = 0;
-    }
-  else
-    {
-      internal_error (__FILE__, __LINE__,
-		      "stepping is not implemented on this target");
-    }
+  step = single_step (lwp);
 
   current_thread = saved_thread;
 
-- 
2.6.3

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v6 7/7] Enable conditional breakpoints for targets that support software single step in GDBServer.
  2015-12-04 18:46 [PATCH v6 0/6] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
                   ` (4 preceding siblings ...)
  2015-12-04 18:47 ` [PATCH v6 5/7] Support software single step on ARM " Antoine Tremblay
@ 2015-12-04 18:47 ` Antoine Tremblay
  2015-12-04 18:47 ` [PATCH v6 6/7] Enable software single stepping for while-stepping actions " Antoine Tremblay
  6 siblings, 0 replies; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-04 18:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Antoine Tremblay

This patch enables support for conditional breakpoints if the target supports
software single step.

This was disabled before as the implementations of software single step were too
simple as discussed in
https://sourceware.org/ml/gdb-patches/2015-04/msg01110.html.

Since these issues are now fixed support can be added back.

New tests passing :
PASS: gdb.base/cond-eval-mode.exp: set breakpoint condition-evaluation
target and related...

No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }

gdb/gdbserver/ChangeLog:

	* server.c (handle_query): Call target_supports_software_single_step.
---
 gdb/gdbserver/server.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 5e053b3..728d842 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2290,13 +2290,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 	  strcat (own_buf, ";tracenz+");
 	}
 
-      if (target_supports_hardware_single_step ())
+      if (target_supports_hardware_single_step ()
+	  || target_supports_software_single_step () )
 	{
-	  /* Support target-side breakpoint conditions and commands.
-	     GDBserver needs to step over the breakpoint if the condition
-	     is false.  GDBserver software single step is too simple, so
-	     disable conditional breakpoints if the target doesn't have
-	     hardware single step.  */
 	  strcat (own_buf, ";ConditionalBreakpoints+");
 	}
       strcat (own_buf, ";BreakpointCommands+");
-- 
2.6.3

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v6 5/7] Support software single step on ARM in GDBServer.
  2015-12-04 18:46 [PATCH v6 0/6] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
                   ` (3 preceding siblings ...)
  2015-12-04 18:46 ` [PATCH v6 1/7] Replace breakpoint_reinsert_addr by get_next_pcs operation in GDBServer Antoine Tremblay
@ 2015-12-04 18:47 ` Antoine Tremblay
  2015-12-07 15:18   ` Yao Qi
  2015-12-04 18:47 ` [PATCH v6 7/7] Enable conditional breakpoints for targets that support software single step " Antoine Tremblay
  2015-12-04 18:47 ` [PATCH v6 6/7] Enable software single stepping for while-stepping actions " Antoine Tremblay
  6 siblings, 1 reply; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-04 18:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Antoine Tremblay

This patch teaches GDBServer how to software single step on ARM
linux by sharing code with GDB.

The arm_get_next_pcs function in GDB is now shared with GDBServer.  So that
GDBServer can use the function to return the possible addresses of the next PC.

A proper shared context was also needed so that we could share the code, this
context is described in the arm_get_next_pcs structure.

Testing :

No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }

gdb/ChangeLog:
	* Makefile.in: Add arm-get-next-pcs.c/o, arm-linux.c/o.
	to all arm targets.
	* aarch32-linux-nat.c: Include arch/arm-get-next-pcs.h.
	* arch/arm-get-next-pcs.c: New file.
	* arch/arm-get-next-pcs.h: New file.
	* arch/arm-linux.h: New file.
	* arch/arm-linux.c: New file.
	* arm-linux-nat.c: Include arch/arm.h, arch/arm-get-next-pcs.h
	arch/arm-linux.h.
	* arm-linux-tdep.c: Include arch/arm.h,arch/arm-get-next-pcs.h
	arch/arm-linux.h.
	(arm_linux_get_next_pcs_ops): New struct.
	(ARM_SIGCONTEXT_R0, ARM_UCONTEXT_SIGCONTEXT,
	ARM_OLD_RT_SIGFRAME_SIGINFO, ARM_OLD_RT_SIGFRAME_UCONTEXT,
	ARM_NEW_RT_SIGFRAME_UCONTEXT, ARM_NEW_SIGFRAME_MAGIC): Move stack
	layout defines to arch/arm-linux.h.
	(arm_linux_sigreturn_next_pc_offset): Move to arch/arm-linux.c.
	(arm_linux_software_single_step): Adjust for
	arm_get_next_pcs implementation.
	* arm-tdep.c: Include arch/arm-get-next-pcs.h.
	(arm_get_next_pcs_ops): New struct.
	(submask): Move macro to arm-get-next-pcs.h.
	(bit): Likewise.
	(bits): Likewise.
	(sbits): Likewise.
	(BranchDest): Likewise.
	(thumb_instruction_changes_pc): Move to arm-get-next-pcs.c
	(thumb2_instruction_changes_pc): Likewise.
	(arm_instruction_changes_pc): Likewise.
	(shifted_reg_val): Likewise.
	(thumb_advance_itstate): Likewise.
	(thumb_get_next_pc_raw): Likewise.
	(arm_get_next_pc_raw): Likewise.
	(arm_get_next_pc): Likewise.
	(thumb_deal_with_atomic_sequence_raw): Likewise.
	(arm_deal_with_atomic_sequence_raw): Likewise.
	(arm_deal_with_atomic_sequence): Likewise.
	(arm_get_next_pcs_read_memory_unsigned_integer): New function.
	(arm_get_next_pcs_addr_bits_remove): Likewise.
	(arm_get_next_pcs_syscall_next_pc): Likewise.
	(arm_get_next_pcs_is_thumb): Likewise.
	(arm_software_single_step): Adjust for arm_get_next_pcs
	implementation.
	* arm-tdep.h: (arm_get_next_pc): Remove declaration.
	(arm_get_next_pcs_read_memory_unsigned_integer):
	New declaration.
	(arm_get_next_pcs_addr_bits_remove): Likewise.
	(arm_get_next_pcs_syscall_next_pc): Likewise.
	(arm_software_single_step): Likewise.
	(arm_deal_with_atomic_sequence: Remove declaration.
	(arm_get_next_pcs_is_thumb): New delcaration.
	* arm-wince-tdep.c: Include arch/arm-get-next-pcs.h.
	* armbsd-tdep.c: Likewise.
	* armnbsd-tdep.c: Likewise.
	* armobsd-tdep.c: Likewise.
	* common/gdb_vecs.h: Add CORE_ADDR vector definition.
	* configure.tgt: Add arm-get-next-pcs.o, arm-linux.o to all
	relevent arm targets.
	* symtab.h: Move CORE_ADDR vector definition to gdb_vecs.h.

gdb/gdbserver/ChangeLog:
	* Makefile.in : Add arm-get-next-pcs.c/o, arm-linux.c/o.
	* configure.srv: Add arm-get-next-pcs.o, arm-linux.o
	to needed arm targets.
	* linux-arm-low.c: Include arch/arm-linux.h,
	aarch/arm-get-next-pcs.h.
	(get_next_pcs_ops): New struct.
	(get_next_pcs_addr_bits_remove): New function.
	(get_next_pcs_is_thumb): New function.
	(get_next_pcs_read_memory_unsigned_integer): Likewise.
	(arm_sigreturn_next_pc): Likewise.
	(get_next_pcs_syscall_next_pc): Likewise.
	(arm_gdbserver_get_next_pcs): Likewise.
	(struct linux_target_ops) <arm_gdbserver_get_next_pcs>:
	Initialize.
	* linux-low.h: Move CORE_ADDR vector definition to gdb_vecs.h.
	* server.h: Include gdb_vecs.h.
---
 gdb/Makefile.in               |   16 +-
 gdb/aarch32-linux-nat.c       |    1 +
 gdb/arch/arm-get-next-pcs.c   | 1216 +++++++++++++++++++++++++++++++++
 gdb/arch/arm-get-next-pcs.h   |   95 +++
 gdb/arch/arm-linux.c          |   59 ++
 gdb/arch/arm-linux.h          |   73 ++
 gdb/arm-linux-nat.c           |    2 +
 gdb/arm-linux-tdep.c          |  136 ++--
 gdb/arm-tdep.c                | 1495 +++++------------------------------------
 gdb/arm-tdep.h                |   18 +-
 gdb/arm-wince-tdep.c          |    1 +
 gdb/armbsd-tdep.c             |    1 +
 gdb/armnbsd-tdep.c            |    1 +
 gdb/armobsd-tdep.c            |    1 +
 gdb/common/gdb_vecs.h         |    2 +
 gdb/configure.tgt             |   20 +-
 gdb/gdbserver/Makefile.in     |    9 +-
 gdb/gdbserver/configure.srv   |    2 +
 gdb/gdbserver/linux-arm-low.c |  144 +++-
 gdb/gdbserver/linux-low.h     |    2 -
 gdb/gdbserver/server.h        |    1 +
 gdb/symtab.h                  |    2 -
 22 files changed, 1871 insertions(+), 1426 deletions(-)
 create mode 100644 gdb/arch/arm-get-next-pcs.c
 create mode 100644 gdb/arch/arm-get-next-pcs.h
 create mode 100644 gdb/arch/arm-linux.c
 create mode 100644 gdb/arch/arm-linux.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0306770..a76a968 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -657,7 +657,8 @@ ALL_64_TARGET_OBS = \
 
 # All other target-dependent objects files (used with --enable-targets=all).
 ALL_TARGET_OBS = \
-	armbsd-tdep.o arm.o arm-linux-tdep.o arm-symbian-tdep.o \
+	armbsd-tdep.o arm.o arm-linux.o arm-linux-tdep.o \
+	arm-get-next-pcs.o arm-symbian-tdep.o \
 	armnbsd-tdep.o armobsd-tdep.o \
 	arm-tdep.o arm-wince-tdep.o \
 	avr-tdep.o \
@@ -1661,8 +1662,9 @@ ALLDEPFILES = \
 	amd64-dicos-tdep.c \
 	amd64-linux-nat.c amd64-linux-tdep.c \
 	amd64-sol2-tdep.c \
-	arm.c \
-	arm-linux-nat.c arm-linux-tdep.c arm-symbian-tdep.c arm-tdep.c \
+	arm.c arm-get-next-pcs.c \
+	arm-linux.c arm-linux-nat.c arm-linux-tdep.c \
+	arm-symbian-tdep.c arm-tdep.c \
 	armnbsd-nat.c armbsd-tdep.c armnbsd-tdep.c armobsd-tdep.c \
 	avr-tdep.c \
 	bfin-linux-tdep.c bfin-tdep.c \
@@ -2292,6 +2294,14 @@ arm.o: ${srcdir}/arch/arm.c
 	$(COMPILE) $(srcdir)/arch/arm.c
 	$(POSTCOMPILE)
 
+arm-linux.o: ${srcdir}/arch/arm-linux.c
+	$(COMPILE) $(srcdir)/arch/arm-linux.c
+	$(POSTCOMPILE)
+
+arm-get-next-pcs.o: ${srcdir}/arch/arm-get-next-pcs.c
+	$(COMPILE) $(srcdir)/arch/arm-get-next-pcs.c
+	$(POSTCOMPILE)
+
 # gdb/nat/ dependencies
 #
 # Need to explicitly specify the compile rule as make will do nothing
diff --git a/gdb/aarch32-linux-nat.c b/gdb/aarch32-linux-nat.c
index 3147399..9d1f696 100644
--- a/gdb/aarch32-linux-nat.c
+++ b/gdb/aarch32-linux-nat.c
@@ -18,6 +18,7 @@
 #include "defs.h"
 
 #include "regcache.h"
+#include "arch/arm-get-next-pcs.h"
 #include "arm-tdep.h"
 #include "arm-linux-tdep.h"
 
diff --git a/gdb/arch/arm-get-next-pcs.c b/gdb/arch/arm-get-next-pcs.c
new file mode 100644
index 0000000..5fc39b1
--- /dev/null
+++ b/gdb/arch/arm-get-next-pcs.c
@@ -0,0 +1,1216 @@
+/* Common code for ARM software single stepping support.
+
+   Copyright (C) 1988-2015 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 "common-defs.h"
+#include "gdb_vecs.h"
+#include "common-regcache.h"
+#include "arm.h"
+#include "arm-get-next-pcs.h"
+
+/* See arm-get-next-pcs.h.  */
+
+void
+arm_get_next_pcs_ctor (struct arm_get_next_pcs *self,
+		       struct arm_get_next_pcs_ops *ops,
+		       int byte_order,
+		       int byte_order_for_code,
+		       const gdb_byte *arm_thumb2_breakpoint,
+		       struct regcache *regcache)
+{
+  self->ops = ops;
+  self->byte_order = byte_order;
+  self->byte_order_for_code = byte_order_for_code;
+  self->arm_thumb2_breakpoint = arm_thumb2_breakpoint;
+  self->regcache = regcache;
+}
+
+/* Advance the state of the IT block and return that state.  */
+
+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;
+}
+
+/* See arm-get-next-pcs.h.  */
+
+int
+arm_instruction_changes_pc (uint32_t this_instr)
+{
+  if (bits (this_instr, 28, 31) == INST_NV)
+    /* Unconditional instructions.  */
+    switch (bits (this_instr, 24, 27))
+      {
+      case 0xa:
+      case 0xb:
+	/* Branch with Link and change to Thumb.  */
+	return 1;
+      case 0xc:
+      case 0xd:
+      case 0xe:
+	/* Coprocessor register transfer.  */
+        if (bits (this_instr, 12, 15) == 15)
+	  error (_("Invalid update to pc in instruction"));
+	return 0;
+      default:
+	return 0;
+      }
+  else
+    switch (bits (this_instr, 25, 27))
+      {
+      case 0x0:
+	if (bits (this_instr, 23, 24) == 2 && bit (this_instr, 20) == 0)
+	  {
+	    /* Multiplies and extra load/stores.  */
+	    if (bit (this_instr, 4) == 1 && bit (this_instr, 7) == 1)
+	      /* Neither multiplies nor extension load/stores are allowed
+		 to modify PC.  */
+	      return 0;
+
+	    /* Otherwise, miscellaneous instructions.  */
+
+	    /* BX <reg>, BXJ <reg>, BLX <reg> */
+	    if (bits (this_instr, 4, 27) == 0x12fff1
+		|| bits (this_instr, 4, 27) == 0x12fff2
+		|| bits (this_instr, 4, 27) == 0x12fff3)
+	      return 1;
+
+	    /* Other miscellaneous instructions are unpredictable if they
+	       modify PC.  */
+	    return 0;
+	  }
+	/* Data processing instruction.  Fall through.  */
+
+      case 0x1:
+	if (bits (this_instr, 12, 15) == 15)
+	  return 1;
+	else
+	  return 0;
+
+      case 0x2:
+      case 0x3:
+	/* Media instructions and architecturally undefined instructions.  */
+	if (bits (this_instr, 25, 27) == 3 && bit (this_instr, 4) == 1)
+	  return 0;
+
+	/* Stores.  */
+	if (bit (this_instr, 20) == 0)
+	  return 0;
+
+	/* Loads.  */
+	if (bits (this_instr, 12, 15) == ARM_PC_REGNUM)
+	  return 1;
+	else
+	  return 0;
+
+      case 0x4:
+	/* Load/store multiple.  */
+	if (bit (this_instr, 20) == 1 && bit (this_instr, 15) == 1)
+	  return 1;
+	else
+	  return 0;
+
+      case 0x5:
+	/* Branch and branch with link.  */
+	return 1;
+
+      case 0x6:
+      case 0x7:
+	/* Coprocessor transfers or SWIs can not affect PC.  */
+	return 0;
+
+      default:
+	internal_error (__FILE__, __LINE__, _("bad value in switch"));
+      }
+}
+
+/* See arm-get-next-pcs.h.  */
+
+int
+thumb_instruction_changes_pc (unsigned short inst)
+{
+  if ((inst & 0xff00) == 0xbd00)	/* pop {rlist, pc} */
+    return 1;
+
+  if ((inst & 0xf000) == 0xd000)	/* conditional branch */
+    return 1;
+
+  if ((inst & 0xf800) == 0xe000)	/* unconditional branch */
+    return 1;
+
+  if ((inst & 0xff00) == 0x4700)	/* bx REG, blx REG */
+    return 1;
+
+  if ((inst & 0xff87) == 0x4687)	/* mov pc, REG */
+    return 1;
+
+  if ((inst & 0xf500) == 0xb100)	/* CBNZ or CBZ.  */
+    return 1;
+
+  return 0;
+}
+
+
+/* See arm-get-next-pcs.h.  */
+
+int
+thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2)
+{
+  if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000)
+    {
+      /* Branches and miscellaneous control instructions.  */
+
+      if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000)
+	{
+	  /* B, BL, BLX.  */
+	  return 1;
+	}
+      else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
+	{
+	  /* SUBS PC, LR, #imm8.  */
+	  return 1;
+	}
+      else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380)
+	{
+	  /* Conditional branch.  */
+	  return 1;
+	}
+
+      return 0;
+    }
+
+  if ((inst1 & 0xfe50) == 0xe810)
+    {
+      /* Load multiple or RFE.  */
+
+      if (bit (inst1, 7) && !bit (inst1, 8))
+	{
+	  /* LDMIA or POP */
+	  if (bit (inst2, 15))
+	    return 1;
+	}
+      else if (!bit (inst1, 7) && bit (inst1, 8))
+	{
+	  /* LDMDB */
+	  if (bit (inst2, 15))
+	    return 1;
+	}
+      else if (bit (inst1, 7) && bit (inst1, 8))
+	{
+	  /* RFEIA */
+	  return 1;
+	}
+      else if (!bit (inst1, 7) && !bit (inst1, 8))
+	{
+	  /* RFEDB */
+	  return 1;
+	}
+
+      return 0;
+    }
+
+  if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
+    {
+      /* MOV PC or MOVS PC.  */
+      return 1;
+    }
+
+  if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000)
+    {
+      /* LDR PC.  */
+      if (bits (inst1, 0, 3) == 15)
+	return 1;
+      if (bit (inst1, 7))
+	return 1;
+      if (bit (inst2, 11))
+	return 1;
+      if ((inst2 & 0x0fc0) == 0x0000)
+	return 1;
+
+      return 0;
+    }
+
+  if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
+    {
+      /* TBB.  */
+      return 1;
+    }
+
+  if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
+    {
+      /* TBH.  */
+      return 1;
+    }
+
+  return 0;
+}
+
+/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D}
+   instruction and ending with a STREX{,B,H,D} instruction.  If such a sequence
+   is found, attempt to step through it.  The end of the sequence address is
+   added to the next_pcs list.  */
+
+static int
+thumb_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self,
+				     CORE_ADDR pc,
+				     VEC (CORE_ADDR) **next_pcs)
+{
+  int byte_order_for_code = self->byte_order_for_code;
+  CORE_ADDR breaks[2] = {-1, -1};
+  CORE_ADDR loc = pc;
+  unsigned short insn1, insn2;
+  int insn_count;
+  int index;
+  int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed).  */
+  const int atomic_sequence_length = 16; /* Instruction sequence length.  */
+  ULONGEST status, itstate;
+
+  /* We currently do not support atomic sequences within an IT block.  */
+  status = regcache_raw_get_unsigned (self->regcache, ARM_PS_REGNUM);
+  itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
+  if (itstate & 0x0f)
+    return 0;
+
+  /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction.  */
+  insn1 = self->ops->read_memory_unsigned_integer (loc, 2, byte_order_for_code);
+
+  loc += 2;
+  if (thumb_insn_size (insn1) != 4)
+    return 0;
+
+  insn2 = self->ops->read_memory_unsigned_integer (loc, 2, byte_order_for_code);
+
+  loc += 2;
+  if (!((insn1 & 0xfff0) == 0xe850
+        || ((insn1 & 0xfff0) == 0xe8d0 && (insn2 & 0x00c0) == 0x0040)))
+    return 0;
+
+  /* Assume that no atomic sequence is longer than "atomic_sequence_length"
+     instructions.  */
+  for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
+    {
+      insn1
+	= self->ops->read_memory_unsigned_integer (loc, 2,byte_order_for_code);
+      loc += 2;
+
+      if (thumb_insn_size (insn1) != 4)
+	{
+	  /* Assume that there is at most one conditional branch in the
+	     atomic sequence.  If a conditional branch is found, put a
+	     breakpoint in its destination address.  */
+	  if ((insn1 & 0xf000) == 0xd000 && bits (insn1, 8, 11) != 0x0f)
+	    {
+	      if (last_breakpoint > 0)
+		return 0; /* More than one conditional branch found,
+			     fallback to the standard code.  */
+
+	      breaks[1] = loc + 2 + (sbits (insn1, 0, 7) << 1);
+	      last_breakpoint++;
+	    }
+
+	  /* We do not support atomic sequences that use any *other*
+	     instructions but conditional branches to change the PC.
+	     Fall back to standard code to avoid losing control of
+	     execution.  */
+	  else if (thumb_instruction_changes_pc (insn1))
+	    return 0;
+	}
+      else
+	{
+	  insn2 = self->ops->read_memory_unsigned_integer
+	    (loc, 2, byte_order_for_code);
+
+	  loc += 2;
+
+	  /* Assume that there is at most one conditional branch in the
+	     atomic sequence.  If a conditional branch is found, put a
+	     breakpoint in its destination address.  */
+	  if ((insn1 & 0xf800) == 0xf000
+	      && (insn2 & 0xd000) == 0x8000
+	      && (insn1 & 0x0380) != 0x0380)
+	    {
+	      int sign, j1, j2, imm1, imm2;
+	      unsigned int offset;
+
+	      sign = sbits (insn1, 10, 10);
+	      imm1 = bits (insn1, 0, 5);
+	      imm2 = bits (insn2, 0, 10);
+	      j1 = bit (insn2, 13);
+	      j2 = bit (insn2, 11);
+
+	      offset = (sign << 20) + (j2 << 19) + (j1 << 18);
+	      offset += (imm1 << 12) + (imm2 << 1);
+
+	      if (last_breakpoint > 0)
+		return 0; /* More than one conditional branch found,
+			     fallback to the standard code.  */
+
+	      breaks[1] = loc + offset;
+	      last_breakpoint++;
+	    }
+
+	  /* We do not support atomic sequences that use any *other*
+	     instructions but conditional branches to change the PC.
+	     Fall back to standard code to avoid losing control of
+	     execution.  */
+	  else if (thumb2_instruction_changes_pc (insn1, insn2))
+	    return 0;
+
+	  /* If we find a strex{,b,h,d}, we're done.  */
+	  if ((insn1 & 0xfff0) == 0xe840
+	      || ((insn1 & 0xfff0) == 0xe8c0 && (insn2 & 0x00c0) == 0x0040))
+	    break;
+	}
+    }
+
+  /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence.  */
+  if (insn_count == atomic_sequence_length)
+    return 0;
+
+  /* Insert a breakpoint right after the end of the atomic sequence.  */
+  breaks[0] = loc;
+
+  /* Check for duplicated breakpoints.  Check also for a breakpoint
+     placed (branch instruction's destination) anywhere in sequence.  */
+  if (last_breakpoint
+      && (breaks[1] == breaks[0]
+	  || (breaks[1] >= pc && breaks[1] < loc)))
+    last_breakpoint = 0;
+
+  /* Adds the breakpoints to the list to be inserted.  */
+  for (index = 0; index <= last_breakpoint; index++)
+    VEC_safe_push (CORE_ADDR, *next_pcs, MAKE_THUMB_ADDR (breaks[index]));
+
+  return 1;
+}
+
+/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D}
+   instruction and ending with a STREX{,B,H,D} instruction.  If such a sequence
+   is found, attempt to step through it.  The end of the sequence address is
+   added to the next_pcs list.  */
+
+static int
+arm_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self,
+				   CORE_ADDR pc,
+				   VEC (CORE_ADDR) **next_pcs)
+{
+  int byte_order_for_code = self->byte_order_for_code;
+  CORE_ADDR breaks[2] = {-1, -1};
+  CORE_ADDR loc = pc;
+  unsigned int insn;
+  int insn_count;
+  int index;
+  int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed).  */
+  const int atomic_sequence_length = 16; /* Instruction sequence length.  */
+
+  /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction.
+     Note that we do not currently support conditionally executed atomic
+     instructions.  */
+  insn = self->ops->read_memory_unsigned_integer (loc, 4, byte_order_for_code);
+
+  loc += 4;
+  if ((insn & 0xff9000f0) != 0xe1900090)
+    return 0;
+
+  /* Assume that no atomic sequence is longer than "atomic_sequence_length"
+     instructions.  */
+  for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
+    {
+      insn = self->ops->read_memory_unsigned_integer
+	(loc, 4, byte_order_for_code);
+
+      loc += 4;
+
+      /* Assume that there is at most one conditional branch in the atomic
+         sequence.  If a conditional branch is found, put a breakpoint in
+         its destination address.  */
+      if (bits (insn, 24, 27) == 0xa)
+	{
+          if (last_breakpoint > 0)
+            return 0; /* More than one conditional branch found, fallback
+                         to the standard single-step code.  */
+
+	  breaks[1] = BranchDest (loc - 4, insn);
+	  last_breakpoint++;
+        }
+
+      /* We do not support atomic sequences that use any *other* instructions
+         but conditional branches to change the PC.  Fall back to standard
+	 code to avoid losing control of execution.  */
+      else if (arm_instruction_changes_pc (insn))
+	return 0;
+
+      /* If we find a strex{,b,h,d}, we're done.  */
+      if ((insn & 0xff9000f0) == 0xe1800090)
+	break;
+    }
+
+  /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence.  */
+  if (insn_count == atomic_sequence_length)
+    return 0;
+
+  /* Insert a breakpoint right after the end of the atomic sequence.  */
+  breaks[0] = loc;
+
+  /* Check for duplicated breakpoints.  Check also for a breakpoint
+     placed (branch instruction's destination) anywhere in sequence.  */
+  if (last_breakpoint
+      && (breaks[1] == breaks[0]
+	  || (breaks[1] >= pc && breaks[1] < loc)))
+    last_breakpoint = 0;
+
+  /* Adds the breakpoints to the list to be inserted.  */
+  for (index = 0; index <= last_breakpoint; index++)
+    VEC_safe_push (CORE_ADDR, *next_pcs, breaks[index]);
+
+  return 1;
+}
+
+/* See arm-get-next-pcs.h.  */
+
+VEC (CORE_ADDR) *
+arm_get_next_pcs (struct arm_get_next_pcs *self, CORE_ADDR pc)
+{
+  VEC (CORE_ADDR) *next_pcs = NULL;
+
+  if (self->ops->is_thumb (self))
+    {
+      if (!thumb_deal_with_atomic_sequence_raw (self, pc, &next_pcs))
+	return thumb_get_next_pcs_raw (self, pc, &next_pcs);
+    }
+  else
+    {
+      if (!arm_deal_with_atomic_sequence_raw (self, pc, &next_pcs))
+	return arm_get_next_pcs_raw (self, pc, &next_pcs);
+    }
+
+  return next_pcs;
+}
+
+/* Decode shifted register value.  */
+
+static unsigned long
+shifted_reg_val (struct arm_get_next_pcs* self, unsigned long inst,
+		 int carry, unsigned long pc_val, unsigned long status_reg)
+{
+  unsigned long res, shift;
+  int rm = bits (inst, 0, 3);
+  unsigned long shifttype = bits (inst, 5, 6);
+
+  if (bit (inst, 4))
+    {
+      int rs = bits (inst, 8, 11);
+      shift = (rs == 15
+	       ? pc_val + 8
+	       : regcache_raw_get_unsigned (self->regcache, rs)) & 0xFF;
+    }
+  else
+    shift = bits (inst, 7, 11);
+
+  res = (rm == ARM_PC_REGNUM
+	 ? (pc_val + (bit (inst, 4) ? 12 : 8))
+	 : regcache_raw_get_unsigned (self->regcache, rm));
+
+  switch (shifttype)
+    {
+    case 0:			/* LSL */
+      res = shift >= 32 ? 0 : res << shift;
+      break;
+
+    case 1:			/* LSR */
+      res = shift >= 32 ? 0 : res >> shift;
+      break;
+
+    case 2:			/* ASR */
+      if (shift >= 32)
+	shift = 31;
+      res = ((res & 0x80000000L)
+	     ? ~((~res) >> shift) : res >> shift);
+      break;
+
+    case 3:			/* ROR/RRX */
+      shift &= 31;
+      if (shift == 0)
+	res = (res >> 1) | (carry ? 0x80000000L : 0);
+      else
+	res = (res >> shift) | (res << (32 - shift));
+      break;
+    }
+
+  return res & 0xffffffff;
+}
+
+/* See arm-get-next-pcs.h.  */
+
+VEC (CORE_ADDR) *
+thumb_get_next_pcs_raw (struct arm_get_next_pcs *self,
+			CORE_ADDR pc,
+			VEC (CORE_ADDR) **next_pcs)
+{
+  int byte_order = self->byte_order;
+  int byte_order_for_code = self->byte_order_for_code;
+  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, itstate;
+  struct regcache *regcache = self->regcache;
+
+  nextpc = MAKE_THUMB_ADDR (nextpc);
+  pc_val = MAKE_THUMB_ADDR (pc_val);
+
+  inst1 = self->ops->read_memory_unsigned_integer (pc, 2, byte_order_for_code);
+
+  /* Thumb-2 conditional execution support.  There are eight bits in
+     the CPSR which describe conditional execution state.  Once
+     reconstructed (they're in a funny order), the low five bits
+     describe the low bit of the condition for each instruction and
+     how many instructions remain.  The high three bits describe the
+     base condition.  One of the low four bits will be set if an IT
+     block is active.  These bits read as zero on earlier
+     processors.  */
+  status = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM);
+  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 (self->arm_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 = self->ops->read_memory_unsigned_integer
+		(pc, 2,byte_order_for_code);
+	      pc += thumb_insn_size (inst1);
+	      itstate = thumb_advance_itstate (itstate);
+	    }
+
+	  VEC_safe_push (CORE_ADDR, *next_pcs, MAKE_THUMB_ADDR (pc));
+	  return *next_pcs;
+	}
+      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 = self->ops->read_memory_unsigned_integer
+		    (pc, 2, byte_order_for_code);
+
+		  pc += thumb_insn_size (inst1);
+		  itstate = thumb_advance_itstate (itstate);
+		}
+
+	      VEC_safe_push (CORE_ADDR, *next_pcs, MAKE_THUMB_ADDR (pc));
+	      return *next_pcs;
+	    }
+	  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);
+	      VEC_safe_push (CORE_ADDR, *next_pcs, MAKE_THUMB_ADDR (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 = self->ops->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);
+
+	      VEC_safe_push (CORE_ADDR, *next_pcs, MAKE_THUMB_ADDR (pc));
+
+	      return *next_pcs;
+	    }
+	}
+    }
+  else if (itstate & 0x0f)
+    {
+      /* We are in a conditional block.  Check the condition.  */
+      int cond = itstate >> 4;
+
+      if (! condition_true (cond, status))
+	{
+	  /* Advance to the next instruction.  All the 32-bit
+	     instructions share a common prefix.  */
+	  VEC_safe_push (CORE_ADDR, *next_pcs,
+			 MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1)));
+	}
+
+      return *next_pcs;
+
+      /* Otherwise, handle the instruction normally.  */
+    }
+
+  if ((inst1 & 0xff00) == 0xbd00)	/* pop {rlist, pc} */
+    {
+      CORE_ADDR sp;
+
+      /* Fetch the saved PC from the stack.  It's stored above
+         all of the other registers.  */
+      offset = bitcount (bits (inst1, 0, 7)) * INT_REGISTER_SIZE;
+      sp = regcache_raw_get_unsigned (regcache, ARM_SP_REGNUM);
+      nextpc = self->ops->read_memory_unsigned_integer
+	(sp + offset, 4, byte_order);
+    }
+  else if ((inst1 & 0xf000) == 0xd000)	/* conditional branch */
+    {
+      unsigned long cond = bits (inst1, 8, 11);
+      if (cond == 0x0f)  /* 0x0f = SWI */
+	{
+	  nextpc = self->ops->syscall_next_pc (self, pc);
+	}
+      else if (cond != 0x0f && condition_true (cond, status))
+	nextpc = pc_val + (sbits (inst1, 0, 7) << 1);
+    }
+  else if ((inst1 & 0xf800) == 0xe000)	/* unconditional branch */
+    {
+      nextpc = pc_val + (sbits (inst1, 0, 10) << 1);
+    }
+  else if (thumb_insn_size (inst1) == 4) /* 32-bit instruction */
+    {
+      unsigned short inst2;
+      inst2 = self->ops->read_memory_unsigned_integer
+	(pc + 2, 2, byte_order_for_code);
+
+      /* Default to the next instruction.  */
+      nextpc = pc + 4;
+      nextpc = MAKE_THUMB_ADDR (nextpc);
+
+      if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000)
+	{
+	  /* Branches and miscellaneous control instructions.  */
+
+	  if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000)
+	    {
+	      /* B, BL, BLX.  */
+	      int j1, j2, imm1, imm2;
+
+	      imm1 = sbits (inst1, 0, 10);
+	      imm2 = bits (inst2, 0, 10);
+	      j1 = bit (inst2, 13);
+	      j2 = bit (inst2, 11);
+
+	      offset = ((imm1 << 12) + (imm2 << 1));
+	      offset ^= ((!j2) << 22) | ((!j1) << 23);
+
+	      nextpc = pc_val + offset;
+	      /* For BLX make sure to clear the low bits.  */
+	      if (bit (inst2, 12) == 0)
+		nextpc = nextpc & 0xfffffffc;
+	    }
+	  else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
+	    {
+	      /* SUBS PC, LR, #imm8.  */
+	      nextpc = regcache_raw_get_unsigned (regcache, ARM_LR_REGNUM);
+	      nextpc -= inst2 & 0x00ff;
+	    }
+	  else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380)
+	    {
+	      /* Conditional branch.  */
+	      if (condition_true (bits (inst1, 6, 9), status))
+		{
+		  int sign, j1, j2, imm1, imm2;
+
+		  sign = sbits (inst1, 10, 10);
+		  imm1 = bits (inst1, 0, 5);
+		  imm2 = bits (inst2, 0, 10);
+		  j1 = bit (inst2, 13);
+		  j2 = bit (inst2, 11);
+
+		  offset = (sign << 20) + (j2 << 19) + (j1 << 18);
+		  offset += (imm1 << 12) + (imm2 << 1);
+
+		  nextpc = pc_val + offset;
+		}
+	    }
+	}
+      else if ((inst1 & 0xfe50) == 0xe810)
+	{
+	  /* Load multiple or RFE.  */
+	  int rn, offset, load_pc = 1;
+
+	  rn = bits (inst1, 0, 3);
+	  if (bit (inst1, 7) && !bit (inst1, 8))
+	    {
+	      /* LDMIA or POP */
+	      if (!bit (inst2, 15))
+		load_pc = 0;
+	      offset = bitcount (inst2) * 4 - 4;
+	    }
+	  else if (!bit (inst1, 7) && bit (inst1, 8))
+	    {
+	      /* LDMDB */
+	      if (!bit (inst2, 15))
+		load_pc = 0;
+	      offset = -4;
+	    }
+	  else if (bit (inst1, 7) && bit (inst1, 8))
+	    {
+	      /* RFEIA */
+	      offset = 0;
+	    }
+	  else if (!bit (inst1, 7) && !bit (inst1, 8))
+	    {
+	      /* RFEDB */
+	      offset = -8;
+	    }
+	  else
+	    load_pc = 0;
+
+	  if (load_pc)
+	    {
+	      CORE_ADDR addr = regcache_raw_get_unsigned (regcache, rn);
+	      nextpc = self->ops->read_memory_unsigned_integer
+		(addr + offset, 4, byte_order);
+	    }
+	}
+      else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
+	{
+	  /* MOV PC or MOVS PC.  */
+	  nextpc = regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3));
+	  nextpc = MAKE_THUMB_ADDR (nextpc);
+	}
+      else if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000)
+	{
+	  /* LDR PC.  */
+	  CORE_ADDR base;
+	  int rn, load_pc = 1;
+
+	  rn = bits (inst1, 0, 3);
+	  base = regcache_raw_get_unsigned (regcache, rn);
+	  if (rn == ARM_PC_REGNUM)
+	    {
+	      base = (base + 4) & ~(CORE_ADDR) 0x3;
+	      if (bit (inst1, 7))
+		base += bits (inst2, 0, 11);
+	      else
+		base -= bits (inst2, 0, 11);
+	    }
+	  else if (bit (inst1, 7))
+	    base += bits (inst2, 0, 11);
+	  else if (bit (inst2, 11))
+	    {
+	      if (bit (inst2, 10))
+		{
+		  if (bit (inst2, 9))
+		    base += bits (inst2, 0, 7);
+		  else
+		    base -= bits (inst2, 0, 7);
+		}
+	    }
+	  else if ((inst2 & 0x0fc0) == 0x0000)
+	    {
+	      int shift = bits (inst2, 4, 5), rm = bits (inst2, 0, 3);
+	      base += regcache_raw_get_unsigned (regcache, rm) << shift;
+	    }
+	  else
+	    /* Reserved.  */
+	    load_pc = 0;
+
+	  if (load_pc)
+	    nextpc
+	      = self->ops->read_memory_unsigned_integer (base, 4, byte_order);
+	}
+      else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
+	{
+	  /* TBB.  */
+	  CORE_ADDR tbl_reg, table, offset, length;
+
+	  tbl_reg = bits (inst1, 0, 3);
+	  if (tbl_reg == 0x0f)
+	    table = pc + 4;  /* Regcache copy of PC isn't right yet.  */
+	  else
+	    table = regcache_raw_get_unsigned (regcache, tbl_reg);
+
+	  offset = regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3));
+	  length = 2 * self->ops->read_memory_unsigned_integer
+	    (table + offset, 1, byte_order);
+	  nextpc = pc_val + length;
+	}
+      else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
+	{
+	  /* TBH.  */
+	  CORE_ADDR tbl_reg, table, offset, length;
+
+	  tbl_reg = bits (inst1, 0, 3);
+	  if (tbl_reg == 0x0f)
+	    table = pc + 4;  /* Regcache copy of PC isn't right yet.  */
+	  else
+	    table = regcache_raw_get_unsigned (regcache, tbl_reg);
+
+	  offset = 2 * regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3));
+	  length = 2 * self->ops->read_memory_unsigned_integer
+	    (table + offset, 2, byte_order);
+	  nextpc = pc_val + length;
+	}
+    }
+  else if ((inst1 & 0xff00) == 0x4700)	/* bx REG, blx REG */
+    {
+      if (bits (inst1, 3, 6) == 0x0f)
+	nextpc = UNMAKE_THUMB_ADDR (pc_val);
+      else
+	nextpc = regcache_raw_get_unsigned (regcache, bits (inst1, 3, 6));
+    }
+  else if ((inst1 & 0xff87) == 0x4687)	/* mov pc, REG */
+    {
+      if (bits (inst1, 3, 6) == 0x0f)
+	nextpc = pc_val;
+      else
+	nextpc = regcache_raw_get_unsigned (regcache, bits (inst1, 3, 6));
+
+      nextpc = MAKE_THUMB_ADDR (nextpc);
+    }
+  else if ((inst1 & 0xf500) == 0xb100)
+    {
+      /* CBNZ or CBZ.  */
+      int imm = (bit (inst1, 9) << 6) + (bits (inst1, 3, 7) << 1);
+      ULONGEST reg = regcache_raw_get_unsigned (regcache, bits (inst1, 0, 2));
+
+      if (bit (inst1, 11) && reg != 0)
+	nextpc = pc_val + imm;
+      else if (!bit (inst1, 11) && reg == 0)
+	nextpc = pc_val + imm;
+    }
+
+  VEC_safe_push (CORE_ADDR, *next_pcs, nextpc);
+
+  return *next_pcs;
+}
+
+/* Get the raw next possible addresses.  PC in next_pcs is the current program
+   counter, which is assumed to be executing in ARM mode.
+
+   The values returned have the execution state of the next instruction
+   encoded in it.  Use IS_THUMB_ADDR () to see whether the instruction is
+   in Thumb-State, and gdbarch_addr_bits_remove () to get the plain memory
+   address in GDB and arm_addr_bits_remove in GDBServer.  */
+
+VEC (CORE_ADDR) *
+arm_get_next_pcs_raw (struct arm_get_next_pcs *self,
+		      CORE_ADDR pc,
+		      VEC (CORE_ADDR) **next_pcs)
+{
+  int byte_order = self->byte_order;
+  unsigned long pc_val;
+  unsigned long this_instr = 0;
+  unsigned long status;
+  CORE_ADDR nextpc;
+  struct regcache *regcache = self->regcache;
+
+  pc_val = (unsigned long) pc;
+  this_instr = self->ops->read_memory_unsigned_integer (pc, 4, byte_order);
+
+  status = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM);
+  nextpc = (CORE_ADDR) (pc_val + 4);	/* Default case */
+
+  if (bits (this_instr, 28, 31) == INST_NV)
+    switch (bits (this_instr, 24, 27))
+      {
+      case 0xa:
+      case 0xb:
+	{
+	  /* Branch with Link and change to Thumb.  */
+	  nextpc = BranchDest (pc, this_instr);
+	  nextpc |= bit (this_instr, 24) << 1;
+	  nextpc = MAKE_THUMB_ADDR (nextpc);
+	  break;
+	}
+      case 0xc:
+      case 0xd:
+      case 0xe:
+	/* Coprocessor register transfer.  */
+        if (bits (this_instr, 12, 15) == 15)
+	  error (_("Invalid update to pc in instruction"));
+	break;
+      }
+  else if (condition_true (bits (this_instr, 28, 31), status))
+    {
+      switch (bits (this_instr, 24, 27))
+	{
+	case 0x0:
+	case 0x1:			/* data processing */
+	case 0x2:
+	case 0x3:
+	  {
+	    unsigned long operand1, operand2, result = 0;
+	    unsigned long rn;
+	    int c;
+
+	    if (bits (this_instr, 12, 15) != 15)
+	      break;
+
+	    if (bits (this_instr, 22, 25) == 0
+		&& bits (this_instr, 4, 7) == 9)	/* multiply */
+	      error (_("Invalid update to pc in instruction"));
+
+	    /* BX <reg>, BLX <reg> */
+	    if (bits (this_instr, 4, 27) == 0x12fff1
+		|| bits (this_instr, 4, 27) == 0x12fff3)
+	      {
+		rn = bits (this_instr, 0, 3);
+		nextpc = ((rn == ARM_PC_REGNUM)
+			  ? (pc_val + 8)
+			  : regcache_raw_get_unsigned (regcache, rn));
+
+		VEC_safe_push (CORE_ADDR, *next_pcs, nextpc);
+		return *next_pcs;
+	      }
+
+	    /* Multiply into PC.  */
+	    c = (status & FLAG_C) ? 1 : 0;
+	    rn = bits (this_instr, 16, 19);
+	    operand1 = ((rn == ARM_PC_REGNUM)
+			? (pc_val + 8)
+			: regcache_raw_get_unsigned (regcache, rn));
+
+	    if (bit (this_instr, 25))
+	      {
+		unsigned long immval = bits (this_instr, 0, 7);
+		unsigned long rotate = 2 * bits (this_instr, 8, 11);
+		operand2 = ((immval >> rotate) | (immval << (32 - rotate)))
+		  & 0xffffffff;
+	      }
+	    else		/* operand 2 is a shifted register.  */
+	      operand2 = shifted_reg_val (self, this_instr, c,
+					  pc_val, status);
+
+	    switch (bits (this_instr, 21, 24))
+	      {
+	      case 0x0:	/*and */
+		result = operand1 & operand2;
+		break;
+
+	      case 0x1:	/*eor */
+		result = operand1 ^ operand2;
+		break;
+
+	      case 0x2:	/*sub */
+		result = operand1 - operand2;
+		break;
+
+	      case 0x3:	/*rsb */
+		result = operand2 - operand1;
+		break;
+
+	      case 0x4:	/*add */
+		result = operand1 + operand2;
+		break;
+
+	      case 0x5:	/*adc */
+		result = operand1 + operand2 + c;
+		break;
+
+	      case 0x6:	/*sbc */
+		result = operand1 - operand2 + c;
+		break;
+
+	      case 0x7:	/*rsc */
+		result = operand2 - operand1 + c;
+		break;
+
+	      case 0x8:
+	      case 0x9:
+	      case 0xa:
+	      case 0xb:	/* tst, teq, cmp, cmn */
+		result = (unsigned long) nextpc;
+		break;
+
+	      case 0xc:	/*orr */
+		result = operand1 | operand2;
+		break;
+
+	      case 0xd:	/*mov */
+		/* Always step into a function.  */
+		result = operand2;
+		break;
+
+	      case 0xe:	/*bic */
+		result = operand1 & ~operand2;
+		break;
+
+	      case 0xf:	/*mvn */
+		result = ~operand2;
+		break;
+	      }
+	      nextpc = self->ops->addr_bits_remove (self, result);
+	    break;
+	  }
+
+	case 0x4:
+	case 0x5:		/* data transfer */
+	case 0x6:
+	case 0x7:
+	  if (bits (this_instr, 25, 27) == 0x3 && bit (this_instr, 4) == 1)
+	    {
+	      /* Media instructions and architecturally undefined
+		 instructions.  */
+	      break;
+	    }
+
+	  if (bit (this_instr, 20))
+	    {
+	      /* load */
+	      if (bits (this_instr, 12, 15) == 15)
+		{
+		  /* rd == pc */
+		  unsigned long rn;
+		  unsigned long base;
+
+		  if (bit (this_instr, 22))
+		    error (_("Invalid update to pc in instruction"));
+
+		  /* byte write to PC */
+		  rn = bits (this_instr, 16, 19);
+		  base = ((rn == ARM_PC_REGNUM)
+			  ? (pc_val + 8)
+			  : regcache_raw_get_unsigned (regcache, rn));
+
+		  if (bit (this_instr, 24))
+		    {
+		      /* pre-indexed */
+		      int c = (status & FLAG_C) ? 1 : 0;
+		      unsigned long offset =
+		      (bit (this_instr, 25)
+		       ? shifted_reg_val (self, this_instr, c,
+					  pc_val, status)
+		       : bits (this_instr, 0, 11));
+
+		      if (bit (this_instr, 23))
+			base += offset;
+		      else
+			base -= offset;
+		    }
+		  nextpc =
+		    (CORE_ADDR) self->ops->read_memory_unsigned_integer
+		    ((CORE_ADDR) base, 4, byte_order);
+		}
+	    }
+	  break;
+
+	case 0x8:
+	case 0x9:		/* block transfer */
+	  if (bit (this_instr, 20))
+	    {
+	      /* LDM */
+	      if (bit (this_instr, 15))
+		{
+		  /* loading pc */
+		  int offset = 0;
+		  unsigned long rn_val
+		    = regcache_raw_get_unsigned (regcache,
+						 bits (this_instr, 16, 19));
+
+		  if (bit (this_instr, 23))
+		    {
+		      /* up */
+		      unsigned long reglist = bits (this_instr, 0, 14);
+		      offset = bitcount (reglist) * 4;
+		      if (bit (this_instr, 24))		/* pre */
+			offset += 4;
+		    }
+		  else if (bit (this_instr, 24))
+		    offset = -4;
+
+		  nextpc = (CORE_ADDR) self->ops->read_memory_unsigned_integer
+		    ((CORE_ADDR) (rn_val + offset), 4, byte_order);
+		}
+	    }
+	  break;
+
+	case 0xb:		/* branch & link */
+	case 0xa:		/* branch */
+	  {
+	    nextpc = BranchDest (pc, this_instr);
+	    break;
+	  }
+
+	case 0xc:
+	case 0xd:
+	case 0xe:		/* coproc ops */
+	  break;
+	case 0xf:		/* SWI */
+	  {
+	    nextpc = self->ops->syscall_next_pc (self, pc);
+	  }
+	  break;
+
+	default:
+	  error (_("Bad bit-field extraction\n"));
+	  return *next_pcs;
+	}
+    }
+
+  VEC_safe_push (CORE_ADDR, *next_pcs, nextpc);
+  return *next_pcs;
+}
diff --git a/gdb/arch/arm-get-next-pcs.h b/gdb/arch/arm-get-next-pcs.h
new file mode 100644
index 0000000..39921e0
--- /dev/null
+++ b/gdb/arch/arm-get-next-pcs.h
@@ -0,0 +1,95 @@
+/* Common code for ARM software single stepping support.
+
+   Copyright (C) 1988-2015 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/>.  */
+
+#ifndef ARM_GET_NEXT_PCS_H
+#define ARM_GET_NEXT_PCS_H 1
+
+/* Support routines for instruction parsing.  */
+#define submask(x) ((1L << ((x) + 1)) - 1)
+#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st)))
+#define bit(obj,st) (((obj) >> (st)) & 1)
+#define sbits(obj,st,fn) \
+  ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st))))
+#define BranchDest(addr,instr) \
+  ((CORE_ADDR) (((unsigned long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
+
+
+/* Forward declaration.  */
+struct arm_get_next_pcs;
+
+/* get_next_pcs operations.  */
+struct arm_get_next_pcs_ops
+{
+  ULONGEST (*read_memory_unsigned_integer) (CORE_ADDR memaddr,
+					    int len,
+					    int byte_order);
+  CORE_ADDR (*syscall_next_pc) (struct arm_get_next_pcs *self, CORE_ADDR pc);
+  CORE_ADDR (*addr_bits_remove) (struct arm_get_next_pcs *self, CORE_ADDR val);
+  int (*is_thumb) (struct arm_get_next_pcs *self);
+};
+
+/* Context for a get_next_pcs call on ARM.  */
+struct arm_get_next_pcs
+{
+  /* Operations implementations.  */
+  struct arm_get_next_pcs_ops *ops;
+  /* Byte order for data.  */
+  int byte_order;
+  /* Byte order for code.  */
+  int byte_order_for_code;
+  /* Thumb2 breakpoint instruction.  */
+  const gdb_byte *arm_thumb2_breakpoint;
+  /* Registry cache.  */
+  struct regcache *regcache;
+};
+
+/* Initialize arm_get_next_pcs.  */
+void arm_get_next_pcs_ctor (struct arm_get_next_pcs *self,
+			    struct arm_get_next_pcs_ops *ops,
+			    int byte_order,
+			    int byte_order_for_code,
+			    const gdb_byte *arm_thumb2_breakpoint,
+			    struct regcache *regcache);
+
+/* Find the next possible PCs after the current instruction executes.  */
+VEC (CORE_ADDR) *arm_get_next_pcs (struct arm_get_next_pcs *self,
+				   CORE_ADDR pc);
+
+/* Find the next possible PCs for thumb mode.  */
+VEC (CORE_ADDR) *thumb_get_next_pcs_raw (struct arm_get_next_pcs *self,
+					 CORE_ADDR pc,
+					 VEC (CORE_ADDR) **next_pcs);
+
+/* Find the next possible PCs for arm mode.  */
+VEC (CORE_ADDR) *arm_get_next_pcs_raw (struct arm_get_next_pcs *self,
+				       CORE_ADDR pc,
+				       VEC (CORE_ADDR) **next_pcs);
+
+/* Return 1 if THIS_INSTR might change control flow, 0 otherwise.  */
+int arm_instruction_changes_pc (uint32_t this_instr);
+
+/* Return 1 if the 16-bit Thumb instruction INST might change
+   control flow, 0 otherwise.  */
+int thumb_instruction_changes_pc (unsigned short inst);
+
+/* Return 1 if the 32-bit Thumb instruction in INST1 and INST2
+   might change control flow, 0 otherwise.  */
+int thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2);
+
+#endif /* ARM_GET_NEXT_PCS_H */
diff --git a/gdb/arch/arm-linux.c b/gdb/arch/arm-linux.c
new file mode 100644
index 0000000..336a3c4
--- /dev/null
+++ b/gdb/arch/arm-linux.c
@@ -0,0 +1,59 @@
+/* Common target dependent code for GNU/Linux on ARM systems.
+
+   Copyright (C) 1999-2015 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 "common-defs.h"
+#include "arm-linux.h"
+
+/* Calculate the offset from stack pointer of the pc register on the stack
+   in the case of a sigreturn or sigreturn_rt syscall.  */
+int
+arm_linux_sigreturn_next_pc_offset (unsigned long sp,
+				    unsigned long sp_data,
+				    unsigned long svc_number)
+{
+  /* Offset of R0 register.  */
+  int r0_offset = 0;
+  /* Offset of PC register.  */
+  int pc_offset = 0;
+
+  gdb_assert (svc_number == 119 || svc_number == 173);
+
+  /* sigreturn.  */
+  if (svc_number == 119)
+    {
+      if (sp_data == ARM_NEW_SIGFRAME_MAGIC)
+	r0_offset = ARM_UCONTEXT_SIGCONTEXT + ARM_SIGCONTEXT_R0;
+      else
+	r0_offset = ARM_SIGCONTEXT_R0;
+    }
+  /* rt_sigreturn. */
+  else if (svc_number == 173)
+    {
+      if (sp_data == sp + ARM_OLD_RT_SIGFRAME_SIGINFO)
+	r0_offset = ARM_OLD_RT_SIGFRAME_UCONTEXT + ARM_UCONTEXT_SIGCONTEXT
+	  + ARM_SIGCONTEXT_R0;
+      else
+	r0_offset = ARM_NEW_RT_SIGFRAME_UCONTEXT + ARM_UCONTEXT_SIGCONTEXT
+	  + ARM_SIGCONTEXT_R0;
+    }
+
+  pc_offset = r0_offset + 4 * 15;
+
+  return pc_offset;
+}
diff --git a/gdb/arch/arm-linux.h b/gdb/arch/arm-linux.h
new file mode 100644
index 0000000..2ba8dd9
--- /dev/null
+++ b/gdb/arch/arm-linux.h
@@ -0,0 +1,73 @@
+/* Common target dependent code for GNU/Linux on ARM systems.
+
+   Copyright (C) 1999-2015 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/>.  */
+
+#ifndef ARM_LINUX_H
+#define ARM_LINUX_H
+
+/* There are a couple of different possible stack layouts that
+   we need to support.
+
+   Before version 2.6.18, the kernel used completely independent
+   layouts for non-RT and RT signals.  For non-RT signals the stack
+   began directly with a struct sigcontext.  For RT signals the stack
+   began with two redundant pointers (to the siginfo and ucontext),
+   and then the siginfo and ucontext.
+
+   As of version 2.6.18, the non-RT signal frame layout starts with
+   a ucontext and the RT signal frame starts with a siginfo and then
+   a ucontext.  Also, the ucontext now has a designated save area
+   for coprocessor registers.
+
+   For RT signals, it's easy to tell the difference: we look for
+   pinfo, the pointer to the siginfo.  If it has the expected
+   value, we have an old layout.  If it doesn't, we have the new
+   layout.
+
+   For non-RT signals, it's a bit harder.  We need something in one
+   layout or the other with a recognizable offset and value.  We can't
+   use the return trampoline, because ARM usually uses SA_RESTORER,
+   in which case the stack return trampoline is not filled in.
+   We can't use the saved stack pointer, because sigaltstack might
+   be in use.  So for now we guess the new layout...  */
+
+/* There are three words (trap_no, error_code, oldmask) in
+   struct sigcontext before r0.  */
+#define ARM_SIGCONTEXT_R0 0xc
+
+/* There are five words (uc_flags, uc_link, and three for uc_stack)
+   in the ucontext_t before the sigcontext.  */
+#define ARM_UCONTEXT_SIGCONTEXT 0x14
+
+/* There are three elements in an rt_sigframe before the ucontext:
+   pinfo, puc, and info.  The first two are pointers and the third
+   is a struct siginfo, with size 128 bytes.  We could follow puc
+   to the ucontext, but it's simpler to skip the whole thing.  */
+#define ARM_OLD_RT_SIGFRAME_SIGINFO 0x8
+#define ARM_OLD_RT_SIGFRAME_UCONTEXT 0x88
+
+#define ARM_NEW_RT_SIGFRAME_UCONTEXT 0x80
+
+#define ARM_NEW_SIGFRAME_MAGIC 0x5ac3c35a
+
+int
+arm_linux_sigreturn_next_pc_offset (unsigned long sp,
+				    unsigned long sp_data,
+				    unsigned long svc_number);
+
+#endif /* ARM_LINUX_H */
diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c
index 63fdae1..695d3e5 100644
--- a/gdb/arm-linux-nat.c
+++ b/gdb/arm-linux-nat.c
@@ -27,6 +27,8 @@
 #include "observer.h"
 #include "gdbthread.h"
 
+#include "arch/arm.h"
+#include "arch/arm-get-next-pcs.h"
 #include "arm-tdep.h"
 #include "arm-linux-tdep.h"
 #include "aarch32-linux-nat.h"
diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c
index da8498f..d11c5bd 100644
--- a/gdb/arm-linux-tdep.c
+++ b/gdb/arm-linux-tdep.c
@@ -35,6 +35,9 @@
 #include "auxv.h"
 #include "xml-syscall.h"
 
+#include "arch/arm.h"
+#include "arch/arm-get-next-pcs.h"
+#include "arch/arm-linux.h"
 #include "arm-tdep.h"
 #include "arm-linux-tdep.h"
 #include "linux-tdep.h"
@@ -257,6 +260,14 @@ static const gdb_byte arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa
 #define ARM_LDR_PC_SP_12		0xe49df00c
 #define ARM_LDR_PC_SP_4			0xe49df004
 
+/* Operation function pointers for get_next_pcs.  */
+static struct arm_get_next_pcs_ops arm_linux_get_next_pcs_ops = {
+  arm_get_next_pcs_read_memory_unsigned_integer,
+  arm_get_next_pcs_syscall_next_pc,
+  arm_get_next_pcs_addr_bits_remove,
+  arm_get_next_pcs_is_thumb
+};
+
 static void
 arm_linux_sigtramp_cache (struct frame_info *this_frame,
 			  struct trad_frame_cache *this_cache,
@@ -278,51 +289,7 @@ arm_linux_sigtramp_cache (struct frame_info *this_frame,
   trad_frame_set_id (this_cache, frame_id_build (sp, func));
 }
 
-/* There are a couple of different possible stack layouts that
-   we need to support.
-
-   Before version 2.6.18, the kernel used completely independent
-   layouts for non-RT and RT signals.  For non-RT signals the stack
-   began directly with a struct sigcontext.  For RT signals the stack
-   began with two redundant pointers (to the siginfo and ucontext),
-   and then the siginfo and ucontext.
-
-   As of version 2.6.18, the non-RT signal frame layout starts with
-   a ucontext and the RT signal frame starts with a siginfo and then
-   a ucontext.  Also, the ucontext now has a designated save area
-   for coprocessor registers.
-
-   For RT signals, it's easy to tell the difference: we look for
-   pinfo, the pointer to the siginfo.  If it has the expected
-   value, we have an old layout.  If it doesn't, we have the new
-   layout.
-
-   For non-RT signals, it's a bit harder.  We need something in one
-   layout or the other with a recognizable offset and value.  We can't
-   use the return trampoline, because ARM usually uses SA_RESTORER,
-   in which case the stack return trampoline is not filled in.
-   We can't use the saved stack pointer, because sigaltstack might
-   be in use.  So for now we guess the new layout...  */
-
-/* There are three words (trap_no, error_code, oldmask) in
-   struct sigcontext before r0.  */
-#define ARM_SIGCONTEXT_R0 0xc
-
-/* There are five words (uc_flags, uc_link, and three for uc_stack)
-   in the ucontext_t before the sigcontext.  */
-#define ARM_UCONTEXT_SIGCONTEXT 0x14
-
-/* There are three elements in an rt_sigframe before the ucontext:
-   pinfo, puc, and info.  The first two are pointers and the third
-   is a struct siginfo, with size 128 bytes.  We could follow puc
-   to the ucontext, but it's simpler to skip the whole thing.  */
-#define ARM_OLD_RT_SIGFRAME_SIGINFO 0x8
-#define ARM_OLD_RT_SIGFRAME_UCONTEXT 0x88
-
-#define ARM_NEW_RT_SIGFRAME_UCONTEXT 0x80
-
-#define ARM_NEW_SIGFRAME_MAGIC 0x5ac3c35a
-
+/* See arm-linux.h for stack layout details.  */
 static void
 arm_linux_sigreturn_init (const struct tramp_frame *self,
 			  struct frame_info *this_frame,
@@ -805,44 +772,6 @@ arm_linux_sigreturn_return_addr (struct frame_info *frame,
   return 0;
 }
 
-/* Calculate the offset from stack pointer of the pc register on the stack
-   in the case of a sigreturn or sigreturn_rt syscall.  */
-static int
-arm_linux_sigreturn_next_pc_offset (unsigned long sp,
-				    unsigned long sp_data,
-				    unsigned long svc_number)
-{
-  /* Offset of R0 register.  */
-  int r0_offset = 0;
-  /* Offset of PC register.  */
-  int pc_offset = 0;
-
-  gdb_assert (svc_number == 119 || svc_number == 173);
-
-  /* sigreturn.  */
-  if (svc_number == 119)
-    {
-      if (sp_data == ARM_NEW_SIGFRAME_MAGIC)
-	r0_offset = ARM_UCONTEXT_SIGCONTEXT + ARM_SIGCONTEXT_R0;
-      else
-	r0_offset = ARM_SIGCONTEXT_R0;
-    }
-  /* rt_sigreturn. */
-  else if (svc_number == 173)
-    {
-      if (sp_data == sp + ARM_OLD_RT_SIGFRAME_SIGINFO)
-	r0_offset = ARM_OLD_RT_SIGFRAME_UCONTEXT + ARM_UCONTEXT_SIGCONTEXT
-	  + ARM_SIGCONTEXT_R0;
-      else
-	r0_offset = ARM_NEW_RT_SIGFRAME_UCONTEXT + ARM_UCONTEXT_SIGCONTEXT
-	  + ARM_SIGCONTEXT_R0;
-    }
-
-  pc_offset = r0_offset + 4 * 15;
-
-  return pc_offset;
-}
-
 /* Find the value of the next PC after a sigreturn or rt_sigreturn syscall
    based on current processor state.  */
 static CORE_ADDR
@@ -976,28 +905,41 @@ arm_linux_software_single_step (struct frame_info *frame)
   struct regcache *regcache = get_current_regcache ();
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
   struct address_space *aspace = get_regcache_aspace (regcache);
-
-  CORE_ADDR next_pc;
-
-  if (arm_deal_with_atomic_sequence (regcache))
-    return 1;
+  struct arm_get_next_pcs next_pcs_ctx;
+  CORE_ADDR pc;
+  int i;
+  VEC (CORE_ADDR) *next_pcs = NULL;
+  struct cleanup *old_chain = make_cleanup (VEC_cleanup (CORE_ADDR), &next_pcs);
 
   /* If the target does have hardware single step, GDB doesn't have
      to bother software single step.  */
   if (target_can_do_single_step () == 1)
     return 0;
 
-  next_pc = arm_get_next_pc (regcache, regcache_read_pc (regcache));
+  arm_get_next_pcs_ctor (&next_pcs_ctx,
+			 &arm_linux_get_next_pcs_ops,
+			 gdbarch_byte_order (gdbarch),
+			 gdbarch_byte_order_for_code (gdbarch),
+			 gdbarch_tdep (gdbarch)->thumb2_breakpoint,
+			 regcache);
+
+  next_pcs = arm_get_next_pcs (&next_pcs_ctx, regcache_read_pc (regcache));
 
-  /* The Linux kernel offers some user-mode helpers in a high page.  We can
-     not read this page (as of 2.6.23), and even if we could then we couldn't
-     set breakpoints in it, and even if we could then the atomic operations
-     would fail when interrupted.  They are all called as functions and return
-     to the address in LR, so step to there instead.  */
-  if (next_pc > 0xffff0000)
-    next_pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
+  for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++)
+    {
+      /* The Linux kernel offers some user-mode helpers in a high page.  We can
+	 not read this page (as of 2.6.23), and even if we could then we
+	 couldn't set breakpoints in it, and even if we could then the atomic
+	 operations would fail when interrupted.  They are all called as
+	 functions and return to the address in LR, so step to there
+	 instead.  */
+      if (pc > 0xffff0000)
+	pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
+
+      arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
+    }
 
-  arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
+  do_cleanups (old_chain);
 
   return 1;
 }
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index ebe4c40..52ce8d5 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -46,6 +46,7 @@
 #include "observer.h"
 
 #include "arch/arm.h"
+#include "arch/arm-get-next-pcs.h"
 #include "arm-tdep.h"
 #include "gdb/sim-arm.h"
 
@@ -236,6 +237,14 @@ static void arm_neon_quad_write (struct gdbarch *gdbarch,
 				 struct regcache *regcache,
 				 int regnum, const gdb_byte *buf);
 
+/* get_next_pcs operations.  */
+static struct arm_get_next_pcs_ops arm_get_next_pcs_ops = {
+  arm_get_next_pcs_read_memory_unsigned_integer,
+  arm_get_next_pcs_syscall_next_pc,
+  arm_get_next_pcs_addr_bits_remove,
+  arm_get_next_pcs_is_thumb
+};
+
 struct arm_prologue_cache
 {
   /* The stack pointer at the time this frame was created; i.e. the
@@ -520,15 +529,6 @@ skip_prologue_function (struct gdbarch *gdbarch, CORE_ADDR pc, int is_thumb)
   return 0;
 }
 
-/* Support routines for instruction parsing.  */
-#define submask(x) ((1L << ((x) + 1)) - 1)
-#define bit(obj,st) (((obj) >> (st)) & 1)
-#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st)))
-#define sbits(obj,st,fn) \
-  ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st))))
-#define BranchDest(addr,instr) \
-  ((CORE_ADDR) (((unsigned long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
-
 /* Extract the immediate from instruction movw/movt of encoding T.  INSN1 is
    the first 16-bit of instruction, and INSN2 is the second 16-bit of
    instruction.  */
@@ -568,128 +568,6 @@ thumb_expand_immediate (unsigned int imm)
   return (0x80 | (imm & 0x7f)) << (32 - count);
 }
 
-/* Return 1 if the 16-bit Thumb instruction INST might change
-   control flow, 0 otherwise.  */
-
-static int
-thumb_instruction_changes_pc (unsigned short inst)
-{
-  if ((inst & 0xff00) == 0xbd00)	/* pop {rlist, pc} */
-    return 1;
-
-  if ((inst & 0xf000) == 0xd000)	/* conditional branch */
-    return 1;
-
-  if ((inst & 0xf800) == 0xe000)	/* unconditional branch */
-    return 1;
-
-  if ((inst & 0xff00) == 0x4700)	/* bx REG, blx REG */
-    return 1;
-
-  if ((inst & 0xff87) == 0x4687)	/* mov pc, REG */
-    return 1;
-
-  if ((inst & 0xf500) == 0xb100)	/* CBNZ or CBZ.  */
-    return 1;
-
-  return 0;
-}
-
-/* Return 1 if the 32-bit Thumb instruction in INST1 and INST2
-   might change control flow, 0 otherwise.  */
-
-static int
-thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2)
-{
-  if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000)
-    {
-      /* Branches and miscellaneous control instructions.  */
-
-      if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000)
-	{
-	  /* B, BL, BLX.  */
-	  return 1;
-	}
-      else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
-	{
-	  /* SUBS PC, LR, #imm8.  */
-	  return 1;
-	}
-      else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380)
-	{
-	  /* Conditional branch.  */
-	  return 1;
-	}
-
-      return 0;
-    }
-
-  if ((inst1 & 0xfe50) == 0xe810)
-    {
-      /* Load multiple or RFE.  */
-
-      if (bit (inst1, 7) && !bit (inst1, 8))
-	{
-	  /* LDMIA or POP */
-	  if (bit (inst2, 15))
-	    return 1;
-	}
-      else if (!bit (inst1, 7) && bit (inst1, 8))
-	{
-	  /* LDMDB */
-	  if (bit (inst2, 15))
-	    return 1;
-	}
-      else if (bit (inst1, 7) && bit (inst1, 8))
-	{
-	  /* RFEIA */
-	  return 1;
-	}
-      else if (!bit (inst1, 7) && !bit (inst1, 8))
-	{
-	  /* RFEDB */
-	  return 1;
-	}
-
-      return 0;
-    }
-
-  if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
-    {
-      /* MOV PC or MOVS PC.  */
-      return 1;
-    }
-
-  if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000)
-    {
-      /* LDR PC.  */
-      if (bits (inst1, 0, 3) == 15)
-	return 1;
-      if (bit (inst1, 7))
-	return 1;
-      if (bit (inst2, 11))
-	return 1;
-      if ((inst2 & 0x0fc0) == 0x0000)
-	return 1;	
-
-      return 0;
-    }
-
-  if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
-    {
-      /* TBB.  */
-      return 1;
-    }
-
-  if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
-    {
-      /* TBH.  */
-      return 1;
-    }
-
-  return 0;
-}
-
 /* Return 1 if the 16-bit Thumb instruction INSN restores SP in
    epilogue, 0 otherwise.  */
 
@@ -1517,98 +1395,6 @@ thumb_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR prev_pc,
   thumb_analyze_prologue (gdbarch, prologue_start, prologue_end, cache);
 }
 
-/* Return 1 if THIS_INSTR might change control flow, 0 otherwise.  */
-
-static int
-arm_instruction_changes_pc (uint32_t this_instr)
-{
-  if (bits (this_instr, 28, 31) == INST_NV)
-    /* Unconditional instructions.  */
-    switch (bits (this_instr, 24, 27))
-      {
-      case 0xa:
-      case 0xb:
-	/* Branch with Link and change to Thumb.  */
-	return 1;
-      case 0xc:
-      case 0xd:
-      case 0xe:
-	/* Coprocessor register transfer.  */
-        if (bits (this_instr, 12, 15) == 15)
-	  error (_("Invalid update to pc in instruction"));
-	return 0;
-      default:
-	return 0;
-      }
-  else
-    switch (bits (this_instr, 25, 27))
-      {
-      case 0x0:
-	if (bits (this_instr, 23, 24) == 2 && bit (this_instr, 20) == 0)
-	  {
-	    /* Multiplies and extra load/stores.  */
-	    if (bit (this_instr, 4) == 1 && bit (this_instr, 7) == 1)
-	      /* Neither multiplies nor extension load/stores are allowed
-		 to modify PC.  */
-	      return 0;
-
-	    /* Otherwise, miscellaneous instructions.  */
-
-	    /* BX <reg>, BXJ <reg>, BLX <reg> */
-	    if (bits (this_instr, 4, 27) == 0x12fff1
-		|| bits (this_instr, 4, 27) == 0x12fff2
-		|| bits (this_instr, 4, 27) == 0x12fff3)
-	      return 1;
-
-	    /* Other miscellaneous instructions are unpredictable if they
-	       modify PC.  */
-	    return 0;
-	  }
-	/* Data processing instruction.  Fall through.  */
-
-      case 0x1:
-	if (bits (this_instr, 12, 15) == 15)
-	  return 1;
-	else
-	  return 0;
-
-      case 0x2:
-      case 0x3:
-	/* Media instructions and architecturally undefined instructions.  */
-	if (bits (this_instr, 25, 27) == 3 && bit (this_instr, 4) == 1)
-	  return 0;
-
-	/* Stores.  */
-	if (bit (this_instr, 20) == 0)
-	  return 0;
-
-	/* Loads.  */
-	if (bits (this_instr, 12, 15) == ARM_PC_REGNUM)
-	  return 1;
-	else
-	  return 0;
-
-      case 0x4:
-	/* Load/store multiple.  */
-	if (bit (this_instr, 20) == 1 && bit (this_instr, 15) == 1)
-	  return 1;
-	else
-	  return 0;
-
-      case 0x5:
-	/* Branch and branch with link.  */
-	return 1;
-
-      case 0x6:
-      case 0x7:
-	/* Coprocessor transfers or SWIs can not affect PC.  */
-	return 0;
-
-      default:
-	internal_error (__FILE__, __LINE__, _("bad value in switch"));
-      }
-}
-
 /* Return 1 if the ARM instruction INSN restores SP in epilogue, 0
    otherwise.  */
 
@@ -2827,37 +2613,26 @@ arm_exidx_unwind_sniffer (const struct frame_unwind *self,
 
       /* We also assume exception information is valid if we're currently
 	 blocked in a system call.  The system library is supposed to
-	 ensure this, so that e.g. pthread cancellation works.
-
-	 But before verifying the instruction at the point of call, make
-	 sure this_frame is actually making a call (or, said differently,
-	 that it is not the innermost frame).  For that, we compare
-	 this_frame's PC vs this_frame's addr_in_block. If equal, it means
-	 there is no call (otherwise, the PC would be the return address,
-	 which is the instruction after the call).  */
-
-      if (get_frame_pc (this_frame) != addr_in_block)
+	 ensure this, so that e.g. pthread cancellation works.  */
+      if (arm_frame_is_thumb (this_frame))
 	{
-	  if (arm_frame_is_thumb (this_frame))
-	    {
-	      LONGEST insn;
-
-	      if (safe_read_memory_integer (get_frame_pc (this_frame) - 2, 2,
-					    byte_order_for_code, &insn)
-		  && (insn & 0xff00) == 0xdf00 /* svc */)
-		exc_valid = 1;
-	    }
-	  else
-	    {
-	      LONGEST insn;
+	  LONGEST insn;
 
-	      if (safe_read_memory_integer (get_frame_pc (this_frame) - 4, 4,
-					    byte_order_for_code, &insn)
-		  && (insn & 0x0f000000) == 0x0f000000 /* svc */)
-		exc_valid = 1;
-	    }
+	  if (safe_read_memory_integer (get_frame_pc (this_frame) - 2, 2,
+					byte_order_for_code, &insn)
+	      && (insn & 0xff00) == 0xdf00 /* svc */)
+	    exc_valid = 1;
 	}
+      else
+	{
+	  LONGEST insn;
 
+	  if (safe_read_memory_integer (get_frame_pc (this_frame) - 4, 4,
+					byte_order_for_code, &insn)
+	      && (insn & 0x0f000000) == 0x0f000000 /* svc */)
+	    exc_valid = 1;
+	}
+	
       /* Bail out if we don't know that exception information is valid.  */
       if (!exc_valid)
 	return 0;
@@ -4314,1092 +4089,117 @@ convert_to_extended (const struct floatformat *fmt, void *dbl, const void *ptr,
 			       &d, dbl);
 }
 
-static unsigned long
-shifted_reg_val (struct regcache *regcache, unsigned long inst, int carry,
-		 unsigned long pc_val, unsigned long status_reg)
+/* Like insert_single_step_breakpoint, but make sure we use a breakpoint
+   of the appropriate mode (as encoded in the PC value), even if this
+   differs from what would be expected according to the symbol tables.  */
+
+void
+arm_insert_single_step_breakpoint (struct gdbarch *gdbarch,
+				   struct address_space *aspace,
+				   CORE_ADDR pc)
 {
-  unsigned long res, shift;
-  int rm = bits (inst, 0, 3);
-  unsigned long shifttype = bits (inst, 5, 6);
+  struct cleanup *old_chain
+    = make_cleanup_restore_integer (&arm_override_mode);
 
-  if (bit (inst, 4))
-    {
-      int rs = bits (inst, 8, 11);
-      shift = (rs == 15 ? pc_val + 8
-	       : regcache_raw_get_unsigned (regcache, rs)) & 0xFF;
-    }
-  else
-    shift = bits (inst, 7, 11);
+  arm_override_mode = IS_THUMB_ADDR (pc);
+  pc = gdbarch_addr_bits_remove (gdbarch, pc);
 
-  res = (rm == ARM_PC_REGNUM
-	 ? (pc_val + (bit (inst, 4) ? 12 : 8))
-	 : regcache_raw_get_unsigned (regcache, rm));
+  insert_single_step_breakpoint (gdbarch, aspace, pc);
 
-  switch (shifttype)
-    {
-    case 0:			/* LSL */
-      res = shift >= 32 ? 0 : res << shift;
-      break;
+  do_cleanups (old_chain);
+}
 
-    case 1:			/* LSR */
-      res = shift >= 32 ? 0 : res >> shift;
-      break;
+/* Given BUF, which is OLD_LEN bytes ending at ENDADDR, expand
+   the buffer to be NEW_LEN bytes ending at ENDADDR.  Return
+   NULL if an error occurs.  BUF is freed.  */
 
-    case 2:			/* ASR */
-      if (shift >= 32)
-	shift = 31;
-      res = ((res & 0x80000000L)
-	     ? ~((~res) >> shift) : res >> shift);
-      break;
+static gdb_byte *
+extend_buffer_earlier (gdb_byte *buf, CORE_ADDR endaddr,
+		       int old_len, int new_len)
+{
+  gdb_byte *new_buf;
+  int bytes_to_read = new_len - old_len;
 
-    case 3:			/* ROR/RRX */
-      shift &= 31;
-      if (shift == 0)
-	res = (res >> 1) | (carry ? 0x80000000L : 0);
-      else
-	res = (res >> shift) | (res << (32 - shift));
-      break;
+  new_buf = (gdb_byte *) xmalloc (new_len);
+  memcpy (new_buf + bytes_to_read, buf, old_len);
+  xfree (buf);
+  if (target_read_memory (endaddr - new_len, new_buf, bytes_to_read) != 0)
+    {
+      xfree (new_buf);
+      return NULL;
     }
-
-  return res & 0xffffffff;
+  return new_buf;
 }
 
-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;
-}
+/* An IT block is at most the 2-byte IT instruction followed by
+   four 4-byte instructions.  The furthest back we must search to
+   find an IT block that affects the current instruction is thus
+   2 + 3 * 4 == 14 bytes.  */
+#define MAX_IT_BLOCK_PREFIX 14
 
-/* 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.  */
+/* Use a quick scan if there are more than this many bytes of
+   code.  */
+#define IT_SCAN_THRESHOLD 32
 
+/* Adjust a breakpoint's address to move breakpoints out of IT blocks.
+   A breakpoint in an IT block may not be hit, depending on the
+   condition flags.  */
 static CORE_ADDR
-thumb_get_next_pc_raw (struct regcache *regcache, CORE_ADDR pc)
+arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
 {
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  struct address_space *aspace = get_regcache_aspace (regcache);
-  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, itstate;
-
-  nextpc = MAKE_THUMB_ADDR (nextpc);
-  pc_val = MAKE_THUMB_ADDR (pc_val);
-
-  inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
-
-  /* Thumb-2 conditional execution support.  There are eight bits in
-     the CPSR which describe conditional execution state.  Once
-     reconstructed (they're in a funny order), the low five bits
-     describe the low bit of the condition for each instruction and
-     how many instructions remain.  The high three bits describe the
-     base condition.  One of the low four bits will be set if an IT
-     block is active.  These bits read as zero on earlier
-     processors.  */
-  status = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM);
-  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 MAKE_THUMB_ADDR (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);
+  gdb_byte *buf;
+  char map_type;
+  CORE_ADDR boundary, func_start;
+  int buf_len;
+  enum bfd_endian order = gdbarch_byte_order_for_code (gdbarch);
+  int i, any, last_it, last_it_count;
 
-	      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);
-		}
+  /* If we are using BKPT breakpoints, none of this is necessary.  */
+  if (gdbarch_tdep (gdbarch)->thumb2_breakpoint == NULL)
+    return bpaddr;
 
-	      return MAKE_THUMB_ADDR (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);
-	      arm_insert_single_step_breakpoint (gdbarch, aspace,
-						 MAKE_THUMB_ADDR (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);
+  /* ARM mode does not have this problem.  */
+  if (!arm_pc_is_thumb (gdbarch, bpaddr))
+    return bpaddr;
 
-	      return MAKE_THUMB_ADDR (pc);
-	    }
-	}
-    }
-  else if (itstate & 0x0f)
-    {
-      /* We are in a conditional block.  Check the condition.  */
-      int cond = itstate >> 4;
+  /* We are setting a breakpoint in Thumb code that could potentially
+     contain an IT block.  The first step is to find how much Thumb
+     code there is; we do not need to read outside of known Thumb
+     sequences.  */
+  map_type = arm_find_mapping_symbol (bpaddr, &boundary);
+  if (map_type == 0)
+    /* Thumb-2 code must have mapping symbols to have a chance.  */
+    return bpaddr;
 
-      if (! condition_true (cond, status))
-	/* Advance to the next instruction.  All the 32-bit
-	   instructions share a common prefix.  */
-	return MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1));
+  bpaddr = gdbarch_addr_bits_remove (gdbarch, bpaddr);
 
-      /* Otherwise, handle the instruction normally.  */
-    }
+  if (find_pc_partial_function (bpaddr, NULL, &func_start, NULL)
+      && func_start > boundary)
+    boundary = func_start;
 
-  if ((inst1 & 0xff00) == 0xbd00)	/* pop {rlist, pc} */
-    {
-      CORE_ADDR sp;
+  /* Search for a candidate IT instruction.  We have to do some fancy
+     footwork to distinguish a real IT instruction from the second
+     half of a 32-bit instruction, but there is no need for that if
+     there's no candidate.  */
+  buf_len = min (bpaddr - boundary, MAX_IT_BLOCK_PREFIX);
+  if (buf_len == 0)
+    /* No room for an IT instruction.  */
+    return bpaddr;
 
-      /* Fetch the saved PC from the stack.  It's stored above
-         all of the other registers.  */
-      offset = bitcount (bits (inst1, 0, 7)) * INT_REGISTER_SIZE;
-      sp = regcache_raw_get_unsigned (regcache, ARM_SP_REGNUM);
-      nextpc = read_memory_unsigned_integer (sp + offset, 4, byte_order);
-    }
-  else if ((inst1 & 0xf000) == 0xd000)	/* conditional branch */
+  buf = (gdb_byte *) xmalloc (buf_len);
+  if (target_read_memory (bpaddr - buf_len, buf, buf_len) != 0)
+    return bpaddr;
+  any = 0;
+  for (i = 0; i < buf_len; i += 2)
     {
-      unsigned long cond = bits (inst1, 8, 11);
-      if (cond == 0x0f)  /* 0x0f = SWI */
+      unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
+      if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
 	{
-	  struct gdbarch_tdep *tdep;
-	  tdep = gdbarch_tdep (gdbarch);
-
-	  if (tdep->syscall_next_pc != NULL)
-	    nextpc = tdep->syscall_next_pc (regcache);
-
+	  any = 1;
+	  break;
 	}
-      else if (cond != 0x0f && condition_true (cond, status))
-	nextpc = pc_val + (sbits (inst1, 0, 7) << 1);
     }
-  else if ((inst1 & 0xf800) == 0xe000)	/* unconditional branch */
-    {
-      nextpc = pc_val + (sbits (inst1, 0, 10) << 1);
-    }
-  else if (thumb_insn_size (inst1) == 4) /* 32-bit instruction */
-    {
-      unsigned short inst2;
-      inst2 = read_memory_unsigned_integer (pc + 2, 2, byte_order_for_code);
-
-      /* Default to the next instruction.  */
-      nextpc = pc + 4;
-      nextpc = MAKE_THUMB_ADDR (nextpc);
-
-      if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000)
-	{
-	  /* Branches and miscellaneous control instructions.  */
-
-	  if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000)
-	    {
-	      /* B, BL, BLX.  */
-	      int j1, j2, imm1, imm2;
-
-	      imm1 = sbits (inst1, 0, 10);
-	      imm2 = bits (inst2, 0, 10);
-	      j1 = bit (inst2, 13);
-	      j2 = bit (inst2, 11);
-
-	      offset = ((imm1 << 12) + (imm2 << 1));
-	      offset ^= ((!j2) << 22) | ((!j1) << 23);
-
-	      nextpc = pc_val + offset;
-	      /* For BLX make sure to clear the low bits.  */
-	      if (bit (inst2, 12) == 0)
-		nextpc = nextpc & 0xfffffffc;
-	    }
-	  else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
-	    {
-	      /* SUBS PC, LR, #imm8.  */
-	      nextpc = regcache_raw_get_unsigned (regcache, ARM_LR_REGNUM);
-	      nextpc -= inst2 & 0x00ff;
-	    }
-	  else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380)
-	    {
-	      /* Conditional branch.  */
-	      if (condition_true (bits (inst1, 6, 9), status))
-		{
-		  int sign, j1, j2, imm1, imm2;
-
-		  sign = sbits (inst1, 10, 10);
-		  imm1 = bits (inst1, 0, 5);
-		  imm2 = bits (inst2, 0, 10);
-		  j1 = bit (inst2, 13);
-		  j2 = bit (inst2, 11);
-
-		  offset = (sign << 20) + (j2 << 19) + (j1 << 18);
-		  offset += (imm1 << 12) + (imm2 << 1);
-
-		  nextpc = pc_val + offset;
-		}
-	    }
-	}
-      else if ((inst1 & 0xfe50) == 0xe810)
-	{
-	  /* Load multiple or RFE.  */
-	  int rn, offset, load_pc = 1;
-
-	  rn = bits (inst1, 0, 3);
-	  if (bit (inst1, 7) && !bit (inst1, 8))
-	    {
-	      /* LDMIA or POP */
-	      if (!bit (inst2, 15))
-		load_pc = 0;
-	      offset = bitcount (inst2) * 4 - 4;
-	    }
-	  else if (!bit (inst1, 7) && bit (inst1, 8))
-	    {
-	      /* LDMDB */
-	      if (!bit (inst2, 15))
-		load_pc = 0;
-	      offset = -4;
-	    }
-	  else if (bit (inst1, 7) && bit (inst1, 8))
-	    {
-	      /* RFEIA */
-	      offset = 0;
-	    }
-	  else if (!bit (inst1, 7) && !bit (inst1, 8))
-	    {
-	      /* RFEDB */
-	      offset = -8;
-	    }
-	  else
-	    load_pc = 0;
-
-	  if (load_pc)
-	    {
-	      CORE_ADDR addr = regcache_raw_get_unsigned (regcache, rn);
-	      nextpc = read_memory_unsigned_integer (addr + offset, 4,
-						     byte_order);
-	    }
-	}
-      else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
-	{
-	  /* MOV PC or MOVS PC.  */
-	  nextpc = regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3));
-	  nextpc = MAKE_THUMB_ADDR (nextpc);
-	}
-      else if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000)
-	{
-	  /* LDR PC.  */
-	  CORE_ADDR base;
-	  int rn, load_pc = 1;
-
-	  rn = bits (inst1, 0, 3);
-	  base = regcache_raw_get_unsigned (regcache, rn);
-	  if (rn == ARM_PC_REGNUM)
-	    {
-	      base = (base + 4) & ~(CORE_ADDR) 0x3;
-	      if (bit (inst1, 7))
-		base += bits (inst2, 0, 11);
-	      else
-		base -= bits (inst2, 0, 11);
-	    }
-	  else if (bit (inst1, 7))
-	    base += bits (inst2, 0, 11);
-	  else if (bit (inst2, 11))
-	    {
-	      if (bit (inst2, 10))
-		{
-		  if (bit (inst2, 9))
-		    base += bits (inst2, 0, 7);
-		  else
-		    base -= bits (inst2, 0, 7);
-		}
-	    }
-	  else if ((inst2 & 0x0fc0) == 0x0000)
-	    {
-	      int shift = bits (inst2, 4, 5), rm = bits (inst2, 0, 3);
-	      base += regcache_raw_get_unsigned (regcache, rm) << shift;
-	    }
-	  else
-	    /* Reserved.  */
-	    load_pc = 0;
-
-	  if (load_pc)
-	    nextpc = read_memory_unsigned_integer (base, 4, byte_order);
-	}
-      else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
-	{
-	  /* TBB.  */
-	  CORE_ADDR tbl_reg, table, offset, length;
-
-	  tbl_reg = bits (inst1, 0, 3);
-	  if (tbl_reg == 0x0f)
-	    table = pc + 4;  /* Regcache copy of PC isn't right yet.  */
-	  else
-	    table = regcache_raw_get_unsigned (regcache, tbl_reg);
-
-	  offset = regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3));
-	  length = 2 * read_memory_unsigned_integer (table + offset, 1,
-						     byte_order);
-	  nextpc = pc_val + length;
-	}
-      else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
-	{
-	  /* TBH.  */
-	  CORE_ADDR tbl_reg, table, offset, length;
-
-	  tbl_reg = bits (inst1, 0, 3);
-	  if (tbl_reg == 0x0f)
-	    table = pc + 4;  /* Regcache copy of PC isn't right yet.  */
-	  else
-	    table = regcache_raw_get_unsigned (regcache, tbl_reg);
-
-	  offset = 2 * regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3));
-	  length = 2 * read_memory_unsigned_integer (table + offset, 2,
-						     byte_order);
-	  nextpc = pc_val + length;
-	}
-    }
-  else if ((inst1 & 0xff00) == 0x4700)	/* bx REG, blx REG */
-    {
-      if (bits (inst1, 3, 6) == 0x0f)
-	nextpc = UNMAKE_THUMB_ADDR (pc_val);
-      else
-	nextpc = regcache_raw_get_unsigned (regcache, bits (inst1, 3, 6));
-    }
-  else if ((inst1 & 0xff87) == 0x4687)	/* mov pc, REG */
-    {
-      if (bits (inst1, 3, 6) == 0x0f)
-	nextpc = pc_val;
-      else
-	nextpc = regcache_raw_get_unsigned (regcache, bits (inst1, 3, 6));
-
-      nextpc = MAKE_THUMB_ADDR (nextpc);
-    }
-  else if ((inst1 & 0xf500) == 0xb100)
-    {
-      /* CBNZ or CBZ.  */
-      int imm = (bit (inst1, 9) << 6) + (bits (inst1, 3, 7) << 1);
-      ULONGEST reg = regcache_raw_get_unsigned (regcache, bits (inst1, 0, 2));
-
-      if (bit (inst1, 11) && reg != 0)
-	nextpc = pc_val + imm;
-      else if (!bit (inst1, 11) && reg == 0)
-	nextpc = pc_val + imm;
-    }
-  return nextpc;
-}
-
-/* Get the raw next address.  PC is the current program counter, in 
-   FRAME, which is assumed to be executing in ARM mode.
-
-   The value returned has the execution state of the next instruction 
-   encoded in it.  Use IS_THUMB_ADDR () to see whether the instruction is
-   in Thumb-State, and gdbarch_addr_bits_remove () to get the plain memory
-   address.  */
-
-static CORE_ADDR
-arm_get_next_pc_raw (struct regcache *regcache, CORE_ADDR pc)
-{
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  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 this_instr;
-  unsigned long status;
-  CORE_ADDR nextpc;
-
-  pc_val = (unsigned long) pc;
-  this_instr = read_memory_unsigned_integer (pc, 4, byte_order_for_code);
-
-  status = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM);
-  nextpc = (CORE_ADDR) (pc_val + 4);	/* Default case */
-
-  if (bits (this_instr, 28, 31) == INST_NV)
-    switch (bits (this_instr, 24, 27))
-      {
-      case 0xa:
-      case 0xb:
-	{
-	  /* Branch with Link and change to Thumb.  */
-	  nextpc = BranchDest (pc, this_instr);
-	  nextpc |= bit (this_instr, 24) << 1;
-	  nextpc = MAKE_THUMB_ADDR (nextpc);
-	  break;
-	}
-      case 0xc:
-      case 0xd:
-      case 0xe:
-	/* Coprocessor register transfer.  */
-        if (bits (this_instr, 12, 15) == 15)
-	  error (_("Invalid update to pc in instruction"));
-	break;
-      }
-  else if (condition_true (bits (this_instr, 28, 31), status))
-    {
-      switch (bits (this_instr, 24, 27))
-	{
-	case 0x0:
-	case 0x1:			/* data processing */
-	case 0x2:
-	case 0x3:
-	  {
-	    unsigned long operand1, operand2, result = 0;
-	    unsigned long rn;
-	    int c;
-
-	    if (bits (this_instr, 12, 15) != 15)
-	      break;
-
-	    if (bits (this_instr, 22, 25) == 0
-		&& bits (this_instr, 4, 7) == 9)	/* multiply */
-	      error (_("Invalid update to pc in instruction"));
-
-	    /* BX <reg>, BLX <reg> */
-	    if (bits (this_instr, 4, 27) == 0x12fff1
-		|| bits (this_instr, 4, 27) == 0x12fff3)
-	      {
-		rn = bits (this_instr, 0, 3);
-		nextpc = ((rn == ARM_PC_REGNUM)
-			  ? (pc_val + 8)
-			  : regcache_raw_get_unsigned (regcache, rn));
-
-		return nextpc;
-	      }
-
-	    /* Multiply into PC.  */
-	    c = (status & FLAG_C) ? 1 : 0;
-	    rn = bits (this_instr, 16, 19);
-	    operand1 = ((rn == ARM_PC_REGNUM)
-			? (pc_val + 8)
-			: regcache_raw_get_unsigned (regcache, rn));
-
-	    if (bit (this_instr, 25))
-	      {
-		unsigned long immval = bits (this_instr, 0, 7);
-		unsigned long rotate = 2 * bits (this_instr, 8, 11);
-		operand2 = ((immval >> rotate) | (immval << (32 - rotate)))
-		  & 0xffffffff;
-	      }
-	    else		/* operand 2 is a shifted register.  */
-	      operand2 = shifted_reg_val (regcache, this_instr, c,
-					  pc_val, status);
-
-	    switch (bits (this_instr, 21, 24))
-	      {
-	      case 0x0:	/*and */
-		result = operand1 & operand2;
-		break;
-
-	      case 0x1:	/*eor */
-		result = operand1 ^ operand2;
-		break;
-
-	      case 0x2:	/*sub */
-		result = operand1 - operand2;
-		break;
-
-	      case 0x3:	/*rsb */
-		result = operand2 - operand1;
-		break;
-
-	      case 0x4:	/*add */
-		result = operand1 + operand2;
-		break;
-
-	      case 0x5:	/*adc */
-		result = operand1 + operand2 + c;
-		break;
-
-	      case 0x6:	/*sbc */
-		result = operand1 - operand2 + c;
-		break;
-
-	      case 0x7:	/*rsc */
-		result = operand2 - operand1 + c;
-		break;
-
-	      case 0x8:
-	      case 0x9:
-	      case 0xa:
-	      case 0xb:	/* tst, teq, cmp, cmn */
-		result = (unsigned long) nextpc;
-		break;
-
-	      case 0xc:	/*orr */
-		result = operand1 | operand2;
-		break;
-
-	      case 0xd:	/*mov */
-		/* Always step into a function.  */
-		result = operand2;
-		break;
-
-	      case 0xe:	/*bic */
-		result = operand1 & ~operand2;
-		break;
-
-	      case 0xf:	/*mvn */
-		result = ~operand2;
-		break;
-	      }
-
-            /* In 26-bit APCS the bottom two bits of the result are 
-	       ignored, and we always end up in ARM state.  */
-	    if (!arm_apcs_32)
-	      nextpc = arm_addr_bits_remove (gdbarch, result);
-	    else
-	      nextpc = result;
-
-	    break;
-	  }
-
-	case 0x4:
-	case 0x5:		/* data transfer */
-	case 0x6:
-	case 0x7:
-	  if (bits (this_instr, 25, 27) == 0x3 && bit (this_instr, 4) == 1)
-	    {
-	      /* Media instructions and architecturally undefined
-		 instructions.  */
-	      break;
-	    }
-
-	  if (bit (this_instr, 20))
-	    {
-	      /* load */
-	      if (bits (this_instr, 12, 15) == 15)
-		{
-		  /* rd == pc */
-		  unsigned long rn;
-		  unsigned long base;
-
-		  if (bit (this_instr, 22))
-		    error (_("Invalid update to pc in instruction"));
-
-		  /* byte write to PC */
-		  rn = bits (this_instr, 16, 19);
-		  base = ((rn == ARM_PC_REGNUM)
-			  ? (pc_val + 8)
-			  : regcache_raw_get_unsigned (regcache, rn));
-
-		  if (bit (this_instr, 24))
-		    {
-		      /* pre-indexed */
-		      int c = (status & FLAG_C) ? 1 : 0;
-		      unsigned long offset =
-		      (bit (this_instr, 25)
-		       ? shifted_reg_val (regcache, this_instr, c, pc_val,
-					  status)
-		       : bits (this_instr, 0, 11));
-
-		      if (bit (this_instr, 23))
-			base += offset;
-		      else
-			base -= offset;
-		    }
-		  nextpc =
-		    (CORE_ADDR) read_memory_unsigned_integer ((CORE_ADDR) base,
-							      4, byte_order);
-		}
-	    }
-	  break;
-
-	case 0x8:
-	case 0x9:		/* block transfer */
-	  if (bit (this_instr, 20))
-	    {
-	      /* LDM */
-	      if (bit (this_instr, 15))
-		{
-		  /* loading pc */
-		  int offset = 0;
-		  unsigned long rn_val
-		    = regcache_raw_get_unsigned (regcache,
-						 bits (this_instr, 16, 19));
-
-		  if (bit (this_instr, 23))
-		    {
-		      /* up */
-		      unsigned long reglist = bits (this_instr, 0, 14);
-		      offset = bitcount (reglist) * 4;
-		      if (bit (this_instr, 24))		/* pre */
-			offset += 4;
-		    }
-		  else if (bit (this_instr, 24))
-		    offset = -4;
-
-		  nextpc =
-		    (CORE_ADDR) read_memory_unsigned_integer ((CORE_ADDR)
-							      (rn_val + offset),
-							      4, byte_order);
-		}
-	    }
-	  break;
-
-	case 0xb:		/* branch & link */
-	case 0xa:		/* branch */
-	  {
-	    nextpc = BranchDest (pc, this_instr);
-	    break;
-	  }
-
-	case 0xc:
-	case 0xd:
-	case 0xe:		/* coproc ops */
-	  break;
-	case 0xf:		/* SWI */
-	  {
-	    struct gdbarch_tdep *tdep;
-	    tdep = gdbarch_tdep (gdbarch);
-
-	    if (tdep->syscall_next_pc != NULL)
-	      nextpc = tdep->syscall_next_pc (regcache);
-
-	  }
-	  break;
-
-	default:
-	  fprintf_filtered (gdb_stderr, _("Bad bit-field extraction\n"));
-	  return (pc);
-	}
-    }
-
-  return nextpc;
-}
-
-/* Determine next PC after current instruction executes.  Will call either
-   arm_get_next_pc_raw or thumb_get_next_pc_raw.  Error out if infinite
-   loop is detected.  */
-
-CORE_ADDR
-arm_get_next_pc (struct regcache *regcache, CORE_ADDR pc)
-{
-  CORE_ADDR nextpc;
-
-  if (arm_is_thumb (regcache))
-    nextpc = thumb_get_next_pc_raw (regcache, pc);
-  else
-    nextpc = arm_get_next_pc_raw (regcache, pc);
-
-  return nextpc;
-}
-
-/* Like insert_single_step_breakpoint, but make sure we use a breakpoint
-   of the appropriate mode (as encoded in the PC value), even if this
-   differs from what would be expected according to the symbol tables.  */
-
-void
-arm_insert_single_step_breakpoint (struct gdbarch *gdbarch,
-				   struct address_space *aspace,
-				   CORE_ADDR pc)
-{
-  struct cleanup *old_chain
-    = make_cleanup_restore_integer (&arm_override_mode);
-
-  arm_override_mode = IS_THUMB_ADDR (pc);
-  pc = gdbarch_addr_bits_remove (gdbarch, pc);
-
-  insert_single_step_breakpoint (gdbarch, aspace, pc);
-
-  do_cleanups (old_chain);
-}
-
-/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D}
-   instruction and ending with a STREX{,B,H,D} instruction.  If such a sequence
-   is found, attempt to step through it.  A breakpoint is placed at the end of
-   the sequence.  */
-
-static int
-thumb_deal_with_atomic_sequence_raw (struct regcache *regcache)
-{
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  struct address_space *aspace = get_regcache_aspace (regcache);
-  enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
-  CORE_ADDR pc = regcache_read_pc (regcache);
-  CORE_ADDR breaks[2] = {-1, -1};
-  CORE_ADDR loc = pc;
-  unsigned short insn1, insn2;
-  int insn_count;
-  int index;
-  int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed).  */
-  const int atomic_sequence_length = 16; /* Instruction sequence length.  */
-  ULONGEST status, itstate;
-
-  /* We currently do not support atomic sequences within an IT block.  */
-  status = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM);
-  itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
-  if (itstate & 0x0f)
-    return 0;
-
-  /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction.  */
-  insn1 = read_memory_unsigned_integer (loc, 2, byte_order_for_code);
-  loc += 2;
-  if (thumb_insn_size (insn1) != 4)
-    return 0;
-
-  insn2 = read_memory_unsigned_integer (loc, 2, byte_order_for_code);
-  loc += 2;
-  if (!((insn1 & 0xfff0) == 0xe850
-        || ((insn1 & 0xfff0) == 0xe8d0 && (insn2 & 0x00c0) == 0x0040)))
-    return 0;
-
-  /* Assume that no atomic sequence is longer than "atomic_sequence_length"
-     instructions.  */
-  for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
-    {
-      insn1 = read_memory_unsigned_integer (loc, 2, byte_order_for_code);
-      loc += 2;
-
-      if (thumb_insn_size (insn1) != 4)
-	{
-	  /* Assume that there is at most one conditional branch in the
-	     atomic sequence.  If a conditional branch is found, put a
-	     breakpoint in its destination address.  */
-	  if ((insn1 & 0xf000) == 0xd000 && bits (insn1, 8, 11) != 0x0f)
-	    {
-	      if (last_breakpoint > 0)
-		return 0; /* More than one conditional branch found,
-			     fallback to the standard code.  */
-
-	      breaks[1] = loc + 2 + (sbits (insn1, 0, 7) << 1);
-	      last_breakpoint++;
-	    }
-
-	  /* We do not support atomic sequences that use any *other*
-	     instructions but conditional branches to change the PC.
-	     Fall back to standard code to avoid losing control of
-	     execution.  */
-	  else if (thumb_instruction_changes_pc (insn1))
-	    return 0;
-	}
-      else
-	{
-	  insn2 = read_memory_unsigned_integer (loc, 2, byte_order_for_code);
-	  loc += 2;
-
-	  /* Assume that there is at most one conditional branch in the
-	     atomic sequence.  If a conditional branch is found, put a
-	     breakpoint in its destination address.  */
-	  if ((insn1 & 0xf800) == 0xf000
-	      && (insn2 & 0xd000) == 0x8000
-	      && (insn1 & 0x0380) != 0x0380)
-	    {
-	      int sign, j1, j2, imm1, imm2;
-	      unsigned int offset;
-
-	      sign = sbits (insn1, 10, 10);
-	      imm1 = bits (insn1, 0, 5);
-	      imm2 = bits (insn2, 0, 10);
-	      j1 = bit (insn2, 13);
-	      j2 = bit (insn2, 11);
-
-	      offset = (sign << 20) + (j2 << 19) + (j1 << 18);
-	      offset += (imm1 << 12) + (imm2 << 1);
-
-	      if (last_breakpoint > 0)
-		return 0; /* More than one conditional branch found,
-			     fallback to the standard code.  */
-
-	      breaks[1] = loc + offset;
-	      last_breakpoint++;
-	    }
-
-	  /* We do not support atomic sequences that use any *other*
-	     instructions but conditional branches to change the PC.
-	     Fall back to standard code to avoid losing control of
-	     execution.  */
-	  else if (thumb2_instruction_changes_pc (insn1, insn2))
-	    return 0;
-
-	  /* If we find a strex{,b,h,d}, we're done.  */
-	  if ((insn1 & 0xfff0) == 0xe840
-	      || ((insn1 & 0xfff0) == 0xe8c0 && (insn2 & 0x00c0) == 0x0040))
-	    break;
-	}
-    }
-
-  /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence.  */
-  if (insn_count == atomic_sequence_length)
-    return 0;
-
-  /* Insert a breakpoint right after the end of the atomic sequence.  */
-  breaks[0] = loc;
-
-  /* Check for duplicated breakpoints.  Check also for a breakpoint
-     placed (branch instruction's destination) anywhere in sequence.  */
-  if (last_breakpoint
-      && (breaks[1] == breaks[0]
-	  || (breaks[1] >= pc && breaks[1] < loc)))
-    last_breakpoint = 0;
-
-  /* Effectively inserts the breakpoints.  */
-  for (index = 0; index <= last_breakpoint; index++)
-    arm_insert_single_step_breakpoint (gdbarch, aspace,
-				       MAKE_THUMB_ADDR (breaks[index]));
-
-  return 1;
-}
-
-static int
-arm_deal_with_atomic_sequence_raw (struct regcache *regcache)
-{
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  struct address_space *aspace = get_regcache_aspace (regcache);
-  enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
-  CORE_ADDR pc = regcache_read_pc (regcache);
-  CORE_ADDR breaks[2] = {-1, -1};
-  CORE_ADDR loc = pc;
-  unsigned int insn;
-  int insn_count;
-  int index;
-  int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed).  */
-  const int atomic_sequence_length = 16; /* Instruction sequence length.  */
-
-  /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction.
-     Note that we do not currently support conditionally executed atomic
-     instructions.  */
-  insn = read_memory_unsigned_integer (loc, 4, byte_order_for_code);
-  loc += 4;
-  if ((insn & 0xff9000f0) != 0xe1900090)
-    return 0;
-
-  /* Assume that no atomic sequence is longer than "atomic_sequence_length"
-     instructions.  */
-  for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
-    {
-      insn = read_memory_unsigned_integer (loc, 4, byte_order_for_code);
-      loc += 4;
-
-      /* Assume that there is at most one conditional branch in the atomic
-         sequence.  If a conditional branch is found, put a breakpoint in
-         its destination address.  */
-      if (bits (insn, 24, 27) == 0xa)
-	{
-          if (last_breakpoint > 0)
-            return 0; /* More than one conditional branch found, fallback
-                         to the standard single-step code.  */
-
-	  breaks[1] = BranchDest (loc - 4, insn);
-	  last_breakpoint++;
-        }
-
-      /* We do not support atomic sequences that use any *other* instructions
-         but conditional branches to change the PC.  Fall back to standard
-	 code to avoid losing control of execution.  */
-      else if (arm_instruction_changes_pc (insn))
-	return 0;
-
-      /* If we find a strex{,b,h,d}, we're done.  */
-      if ((insn & 0xff9000f0) == 0xe1800090)
-	break;
-    }
-
-  /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence.  */
-  if (insn_count == atomic_sequence_length)
-    return 0;
-
-  /* Insert a breakpoint right after the end of the atomic sequence.  */
-  breaks[0] = loc;
-
-  /* Check for duplicated breakpoints.  Check also for a breakpoint
-     placed (branch instruction's destination) anywhere in sequence.  */
-  if (last_breakpoint
-      && (breaks[1] == breaks[0]
-	  || (breaks[1] >= pc && breaks[1] < loc)))
-    last_breakpoint = 0;
-
-  /* Effectively inserts the breakpoints.  */
-  for (index = 0; index <= last_breakpoint; index++)
-    arm_insert_single_step_breakpoint (gdbarch, aspace, breaks[index]);
-
-  return 1;
-}
-
-int
-arm_deal_with_atomic_sequence (struct regcache *regcache)
-{
-  if (arm_is_thumb (regcache))
-    return thumb_deal_with_atomic_sequence_raw (regcache);
-  else
-    return arm_deal_with_atomic_sequence_raw (regcache);
-}
-
-/* single_step() is called just before we want to resume the inferior,
-   if we want to single-step it but there is no hardware or kernel
-   single-step support.  We find the target of the coming instruction
-   and breakpoint it.  */
-
-int
-arm_software_single_step (struct frame_info *frame)
-{
-  struct regcache *regcache = get_current_regcache ();
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  struct address_space *aspace = get_regcache_aspace (regcache);
-  CORE_ADDR next_pc;
-
-  if (arm_deal_with_atomic_sequence (regcache))
-    return 1;
-
-  next_pc = arm_get_next_pc (regcache, regcache_read_pc (regcache));
-  arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
-
-  return 1;
-}
-
-/* Given BUF, which is OLD_LEN bytes ending at ENDADDR, expand
-   the buffer to be NEW_LEN bytes ending at ENDADDR.  Return
-   NULL if an error occurs.  BUF is freed.  */
-
-static gdb_byte *
-extend_buffer_earlier (gdb_byte *buf, CORE_ADDR endaddr,
-		       int old_len, int new_len)
-{
-  gdb_byte *new_buf;
-  int bytes_to_read = new_len - old_len;
-
-  new_buf = (gdb_byte *) xmalloc (new_len);
-  memcpy (new_buf + bytes_to_read, buf, old_len);
-  xfree (buf);
-  if (target_read_memory (endaddr - new_len, new_buf, bytes_to_read) != 0)
-    {
-      xfree (new_buf);
-      return NULL;
-    }
-  return new_buf;
-}
 
-/* An IT block is at most the 2-byte IT instruction followed by
-   four 4-byte instructions.  The furthest back we must search to
-   find an IT block that affects the current instruction is thus
-   2 + 3 * 4 == 14 bytes.  */
-#define MAX_IT_BLOCK_PREFIX 14
-
-/* Use a quick scan if there are more than this many bytes of
-   code.  */
-#define IT_SCAN_THRESHOLD 32
-
-/* Adjust a breakpoint's address to move breakpoints out of IT blocks.
-   A breakpoint in an IT block may not be hit, depending on the
-   condition flags.  */
-static CORE_ADDR
-arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
-{
-  gdb_byte *buf;
-  char map_type;
-  CORE_ADDR boundary, func_start;
-  int buf_len;
-  enum bfd_endian order = gdbarch_byte_order_for_code (gdbarch);
-  int i, any, last_it, last_it_count;
-
-  /* If we are using BKPT breakpoints, none of this is necessary.  */
-  if (gdbarch_tdep (gdbarch)->thumb2_breakpoint == NULL)
-    return bpaddr;
-
-  /* ARM mode does not have this problem.  */
-  if (!arm_pc_is_thumb (gdbarch, bpaddr))
-    return bpaddr;
-
-  /* We are setting a breakpoint in Thumb code that could potentially
-     contain an IT block.  The first step is to find how much Thumb
-     code there is; we do not need to read outside of known Thumb
-     sequences.  */
-  map_type = arm_find_mapping_symbol (bpaddr, &boundary);
-  if (map_type == 0)
-    /* Thumb-2 code must have mapping symbols to have a chance.  */
-    return bpaddr;
-
-  bpaddr = gdbarch_addr_bits_remove (gdbarch, bpaddr);
-
-  if (find_pc_partial_function (bpaddr, NULL, &func_start, NULL)
-      && func_start > boundary)
-    boundary = func_start;
-
-  /* Search for a candidate IT instruction.  We have to do some fancy
-     footwork to distinguish a real IT instruction from the second
-     half of a 32-bit instruction, but there is no need for that if
-     there's no candidate.  */
-  buf_len = min (bpaddr - boundary, MAX_IT_BLOCK_PREFIX);
-  if (buf_len == 0)
-    /* No room for an IT instruction.  */
-    return bpaddr;
-
-  buf = (gdb_byte *) xmalloc (buf_len);
-  if (target_read_memory (bpaddr - buf_len, buf, buf_len) != 0)
-    return bpaddr;
-  any = 0;
-  for (i = 0; i < buf_len; i += 2)
-    {
-      unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
-      if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
-	{
-	  any = 1;
-	  break;
-	}
-    }
   if (any == 0)
     {
       xfree (buf);
@@ -7320,6 +6120,81 @@ thumb2_copy_block_xfer (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
   return 0;
 }
 
+/* Wrapper over read_memory_unsigned_integer for use in arm_get_next_pcs.
+ This is used to avoid a dependency on BFD's bfd_endian enum.  */
+
+ULONGEST
+arm_get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr, int len,
+					       int byte_order)
+{
+  return read_memory_unsigned_integer (memaddr, len, byte_order);
+}
+
+/* Wrapper over gdbarch_addr_bits_remove for use in arm_get_next_pcs.  */
+
+CORE_ADDR
+arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
+				   CORE_ADDR val)
+{
+  return gdbarch_addr_bits_remove (get_regcache_arch (self->regcache), val);
+}
+
+/* Wrapper over syscall_next_pc for use in get_next_pcs.  */
+
+CORE_ADDR
+arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc)
+{
+  struct gdbarch_tdep *tdep;
+
+  tdep = gdbarch_tdep (get_regcache_arch (self->regcache));
+  if (tdep->syscall_next_pc != NULL)
+    return tdep->syscall_next_pc (self->regcache);
+
+  return 0;
+}
+
+/* Wrapper over arm_is_thumb for use in arm_get_next_pcs.  */
+
+int
+arm_get_next_pcs_is_thumb (struct arm_get_next_pcs *self)
+{
+  return arm_is_thumb (self->regcache);
+}
+
+/* single_step() is called just before we want to resume the inferior,
+   if we want to single-step it but there is no hardware or kernel
+   single-step support.  We find the target of the coming instructions
+   and breakpoint them.  */
+
+int
+arm_software_single_step (struct frame_info *frame)
+{
+  struct regcache *regcache = get_current_regcache ();
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct address_space *aspace = get_regcache_aspace (regcache);
+  struct arm_get_next_pcs next_pcs_ctx;
+  CORE_ADDR pc;
+  int i;
+  VEC (CORE_ADDR) *next_pcs = NULL;
+  struct cleanup *old_chain = make_cleanup (VEC_cleanup (CORE_ADDR), &next_pcs);
+
+  arm_get_next_pcs_ctor (&next_pcs_ctx,
+			 &arm_get_next_pcs_ops,
+			 gdbarch_byte_order (gdbarch),
+			 gdbarch_byte_order_for_code (gdbarch),
+			 gdbarch_tdep (gdbarch)->thumb2_breakpoint,
+			 regcache);
+
+  next_pcs = arm_get_next_pcs (&next_pcs_ctx, regcache_read_pc (regcache));
+
+  for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++)
+    arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
+
+  do_cleanups (old_chain);
+
+  return 1;
+}
+
 /* Cleanup/copy SVC (SWI) instructions.  These two functions are overridden
    for Linux, where some SVC instructions must be treated specially.  */
 
diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h
index 0063fef..6bee06c 100644
--- a/gdb/arm-tdep.h
+++ b/gdb/arm-tdep.h
@@ -23,6 +23,9 @@
 struct gdbarch;
 struct regset;
 struct address_space;
+struct get_next_pcs;
+struct arm_get_next_pcs;
+struct gdb_get_next_pcs;
 
 #include "arch/arm.h"
 
@@ -250,10 +253,21 @@ extern void
 		       ULONGEST val, enum pc_write_style write_pc);
 
 CORE_ADDR arm_skip_stub (struct frame_info *, CORE_ADDR);
-CORE_ADDR arm_get_next_pc (struct regcache *regcache, CORE_ADDR pc);
+
+ULONGEST arm_get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
+							int len,
+							int byte_order);
+
+CORE_ADDR arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
+					     CORE_ADDR val);
+
+CORE_ADDR arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self,
+					    CORE_ADDR pc);
+
+int arm_get_next_pcs_is_thumb (struct arm_get_next_pcs *self);
+
 void arm_insert_single_step_breakpoint (struct gdbarch *,
 					struct address_space *, CORE_ADDR);
-int arm_deal_with_atomic_sequence (struct regcache *);
 int arm_software_single_step (struct frame_info *);
 int arm_is_thumb (struct regcache *regcache);
 int arm_frame_is_thumb (struct frame_info *frame);
diff --git a/gdb/arm-wince-tdep.c b/gdb/arm-wince-tdep.c
index 3abd89d..d3add93 100644
--- a/gdb/arm-wince-tdep.c
+++ b/gdb/arm-wince-tdep.c
@@ -25,6 +25,7 @@
 #include "frame.h"
 
 #include "arch/arm.h"
+#include "arch/arm-get-next-pcs.h"
 #include "arm-tdep.h"
 #include "windows-tdep.h"
 
diff --git a/gdb/armbsd-tdep.c b/gdb/armbsd-tdep.c
index 1b9bad7..b19048e 100644
--- a/gdb/armbsd-tdep.c
+++ b/gdb/armbsd-tdep.c
@@ -22,6 +22,7 @@
 #include "regcache.h"
 #include "regset.h"
 
+#include "arch/arm-get-next-pcs.h"
 #include "arm-tdep.h"
 
 /* Core file support.  */
diff --git a/gdb/armnbsd-tdep.c b/gdb/armnbsd-tdep.c
index 14eceaa..c67b226 100644
--- a/gdb/armnbsd-tdep.c
+++ b/gdb/armnbsd-tdep.c
@@ -21,6 +21,7 @@
 #include "osabi.h"
 
 #include "arch/arm.h"
+#include "arch/arm-get-next-pcs.h"
 #include "arm-tdep.h"
 #include "solib-svr4.h"
 
diff --git a/gdb/armobsd-tdep.c b/gdb/armobsd-tdep.c
index 58bcc5d..883c63f 100644
--- a/gdb/armobsd-tdep.c
+++ b/gdb/armobsd-tdep.c
@@ -23,6 +23,7 @@
 #include "tramp-frame.h"
 
 #include "obsd-tdep.h"
+#include "arch/arm-get-next-pcs.h"
 #include "arm-tdep.h"
 #include "solib-svr4.h"
 
diff --git a/gdb/common/gdb_vecs.h b/gdb/common/gdb_vecs.h
index b3f56f8..a08d416 100644
--- a/gdb/common/gdb_vecs.h
+++ b/gdb/common/gdb_vecs.h
@@ -31,6 +31,8 @@ DEF_VEC_P (const_char_ptr);
 
 DEF_VEC_I (int);
 
+DEF_VEC_I (CORE_ADDR);
+
 extern void free_char_ptr_vec (VEC (char_ptr) *char_ptr_vec);
 
 extern struct cleanup *
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index dfbb4d1..299dad7 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -44,7 +44,8 @@ aarch64*-*-elf)
 aarch64*-*-linux*)
 	# Target: AArch64 linux
 	gdb_target_obs="aarch64-tdep.o aarch64-linux-tdep.o aarch64-insn.o \
-			arm.o arm-tdep.o arm-linux-tdep.o \
+			arm.o arm-linux.o arm-get-next-pcs.o arm-tdep.o \
+			arm-linux-tdep.o \
 			glibc-tdep.o linux-tdep.o solib-svr4.o \
 			symfile-mem.o linux-record.o"
 	build_gdbserver=yes
@@ -84,31 +85,34 @@ am33_2.0*-*-linux*)
 
 arm*-wince-pe | arm*-*-mingw32ce*)
 	# Target: ARM based machine running Windows CE (win32)
-	gdb_target_obs="arm.o arm-tdep.o arm-wince-tdep.o windows-tdep.o"
+	gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o arm-wince-tdep.o \
+			windows-tdep.o"
 	build_gdbserver=yes
 	;;
 arm*-*-linux*)
 	# Target: ARM based machine running GNU/Linux
-	gdb_target_obs="arm.o arm-tdep.o arm-linux-tdep.o glibc-tdep.o \
+	gdb_target_obs="arm.o arm-linux.o arm-get-next-pcs.o arm-tdep.o \
+                        arm-linux-tdep.o glibc-tdep.o \
 			solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o"
 	build_gdbserver=yes
 	;;
 arm*-*-netbsd* | arm*-*-knetbsd*-gnu)
 	# Target: NetBSD/arm
-	gdb_target_obs="arm.o arm-tdep.o armnbsd-tdep.o solib-svr4.o"
+	gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o armnbsd-tdep.o \
+			solib-svr4.o"
 	;;
 arm*-*-openbsd*)
 	# Target: OpenBSD/arm
-	gdb_target_obs="arm.o arm-tdep.o armbsd-tdep.o armobsd-tdep.o \
-			obsd-tdep.o solib-svr4.o"
+	gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o armbsd-tdep.o \
+			armobsd-tdep.o obsd-tdep.o solib-svr4.o"
 	;;
 arm*-*-symbianelf*)
 	# Target: SymbianOS/arm
-	gdb_target_obs="arm.o arm-tdep.o arm-symbian-tdep.o"
+	gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o arm-symbian-tdep.o"
 	;;
 arm*-*-*)
 	# Target: ARM embedded system
-	gdb_target_obs="arm.o arm-tdep.o"
+	gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o"
 	gdb_sim=../sim/arm/libsim.a
 	;;
 
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 82af44c..c72db67 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -181,7 +181,8 @@ SFILES=	$(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
 	$(srcdir)/common/common-exceptions.c $(srcdir)/symbol.c \
 	$(srcdir)/common/btrace-common.c \
 	$(srcdir)/common/fileio.c $(srcdir)/nat/linux-namespaces.c \
-	$(srcdir)/arch/arm.c $(srcdir)/common/common-regcache.c
+	$(srcdir)/arch/arm.c $(srcdir)/common/common-regcache.c \
+	$(srcdir)/arch/arm-linux.c $(srcdir)/arch/arm-get-next-pcs.c
 
 DEPFILES = @GDBSERVER_DEPFILES@
 
@@ -592,6 +593,12 @@ common-regcache.o: ../common/common-regcache.c
 arm.o: ../arch/arm.c
 	$(COMPILE) $<
 	$(POSTCOMPILE)
+arm-linux.o: ../arch/arm-linux.c
+	$(COMPILE) $<
+	$(POSTCOMPILE)
+arm-get-next-pcs.o: ../arch/arm-get-next-pcs.c
+	$(COMPILE) $<
+	$(POSTCOMPILE)
 
 # Native object files rules from ../nat
 
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index e854110..690da40 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -71,6 +71,8 @@ case "${target}" in
 			srv_tgtobj="$srv_linux_obj linux-arm-low.o"
 			srv_tgtobj="$srv_tgtobj linux-aarch32-low.o"
 			srv_tgtobj="${srv_tgtobj} arm.o"
+			srv_tgtobj="${srv_tgtobj} arm-linux.o"
+			srv_tgtobj="${srv_tgtobj} arm-get-next-pcs.o"
 			srv_xmlfiles="arm-with-iwmmxt.xml"
 			srv_xmlfiles="${srv_xmlfiles} arm-with-vfpv2.xml"
 			srv_xmlfiles="${srv_xmlfiles} arm-with-vfpv3.xml"
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index f7de2b1..f5ace84 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -19,6 +19,8 @@
 #include "server.h"
 #include "linux-low.h"
 #include "arch/arm.h"
+#include "arch/arm-linux.h"
+#include "arch/arm-get-next-pcs.h"
 #include "linux-aarch32-low.h"
 
 #include <sys/uio.h>
@@ -144,6 +146,27 @@ static int arm_regmap[] = {
   64
 };
 
+/* Forward declarations needed for get_next_pcs ops.  */
+static ULONGEST get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
+							   int len,
+							   int byte_order);
+
+static CORE_ADDR get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
+						CORE_ADDR val);
+
+static CORE_ADDR get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self,
+					       CORE_ADDR pc);
+
+static int get_next_pcs_is_thumb (struct arm_get_next_pcs *self);
+
+/* get_next_pcs operations.  */
+static struct arm_get_next_pcs_ops get_next_pcs_ops = {
+  get_next_pcs_read_memory_unsigned_integer,
+  get_next_pcs_syscall_next_pc,
+  get_next_pcs_addr_bits_remove,
+  get_next_pcs_is_thumb
+};
+
 static int
 arm_cannot_store_register (int regno)
 {
@@ -206,6 +229,13 @@ arm_fill_vfpregset (struct regcache *regcache, void *buf)
   arm_fill_vfpregset_num (regcache, buf, num);
 }
 
+/* Wrapper of UNMAKE_THUMB_ADDR for get_next_pcs.  */
+static CORE_ADDR
+get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, CORE_ADDR val)
+{
+  return UNMAKE_THUMB_ADDR (val);
+}
+
 static void
 arm_store_vfpregset (struct regcache *regcache, const void *buf)
 {
@@ -278,6 +308,13 @@ arm_is_thumb_mode (void)
     return 0;
 }
 
+/* Wrapper of arm_is_thumb_mode for get_next_pcs.  */
+static int
+get_next_pcs_is_thumb (struct arm_get_next_pcs *self)
+{
+  return arm_is_thumb_mode ();
+}
+
 /* Returns 1 if there is a software breakpoint at location.  */
 
 static int
@@ -315,6 +352,20 @@ arm_breakpoint_at (CORE_ADDR where)
   return 0;
 }
 
+/* Read memory from the inferiror.
+   BYTE_ORDER is ignored and there to keep compatiblity with GDB's
+   read_memory_unsigned_integer. */
+static ULONGEST
+get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
+					   int len,
+					   int byte_order)
+{
+  ULONGEST res;
+
+  (*the_target->read_memory) (memaddr, (unsigned char *) &res, len);
+  return res;
+}
+
 /* Fetch the thread-local storage pointer for libthread_db.  */
 
 ps_err_e
@@ -799,6 +850,76 @@ arm_prepare_to_resume (struct lwp_info *lwp)
       }
 }
 
+/* Find the next pc for a sigreturn or rt_sigreturn syscall.
+   See arm-linux.h for stack layout details.  */
+static CORE_ADDR
+arm_sigreturn_next_pc (struct regcache *regcache, int svc_number)
+{
+  unsigned long sp;
+  unsigned long sp_data;
+  /* Offset of PC register.  */
+  int pc_offset = 0;
+  CORE_ADDR next_pc = 0;
+
+  gdb_assert (svc_number == 119 || svc_number == 173);
+
+  collect_register_by_name (regcache, "sp", &sp);
+  (*the_target->read_memory) (sp, (unsigned char *) &sp_data, 4);
+
+  pc_offset = arm_linux_sigreturn_next_pc_offset (sp, sp_data, svc_number);
+
+  (*the_target->read_memory) (sp + pc_offset, (unsigned char *) &next_pc, 4);
+
+  return next_pc;
+}
+
+/* When PC is at a syscall instruction, return the PC of the next
+   instruction to be executed.  */
+static CORE_ADDR
+get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc)
+{
+  CORE_ADDR next_pc = 0;
+  int is_thumb = arm_is_thumb_mode ();
+  ULONGEST svc_number = 0;
+  struct regcache *regcache = self->regcache;
+
+  if (is_thumb)
+    {
+      collect_register (regcache, 7, &svc_number);
+      next_pc = pc + 2;
+    }
+  else
+    {
+      unsigned long this_instr;
+      unsigned long svc_operand;
+
+      (*the_target->read_memory) (pc, (unsigned char *) &this_instr, 4);
+      svc_operand = (0x00ffffff & this_instr);
+
+      if (svc_operand)  /* OABI.  */
+	{
+	  svc_number = svc_operand - 0x900000;
+	}
+      else /* EABI.  */
+	{
+	  collect_register (regcache, 7, &svc_number);
+	}
+
+      next_pc = pc + 4;
+    }
+
+  /* This is a sigreturn or sigreturn_rt syscall.  */
+  if (svc_number == 119 || svc_number == 173)
+    {
+      next_pc = arm_sigreturn_next_pc (regcache, svc_number);
+    }
+
+  /* Addresses for calling Thumb functions have the bit 0 set.  */
+  if (is_thumb)
+    next_pc = MAKE_THUMB_ADDR (next_pc);
+
+  return next_pc;
+}
 
 static int
 arm_get_hwcap (unsigned long *valp)
@@ -888,6 +1009,27 @@ arm_arch_setup (void)
     have_ptrace_getregset = 0;
 }
 
+/* Fetch the next possible PCs after the current instruction executes.  */
+
+static VEC (CORE_ADDR) *
+arm_gdbserver_get_next_pcs (CORE_ADDR pc, struct regcache *regcache)
+{
+  struct arm_get_next_pcs next_pcs_ctx;
+  VEC (CORE_ADDR) *next_pcs = NULL;
+
+  arm_get_next_pcs_ctor (&next_pcs_ctx,
+			 &get_next_pcs_ops,
+			 /* Byte order is ignored assumed as host.  */
+			 0,
+			 0,
+			 (const gdb_byte *) &thumb2_breakpoint,
+			 regcache);
+
+  next_pcs = arm_get_next_pcs (&next_pcs_ctx, pc);
+
+  return next_pcs;
+}
+
 /* Support for hardware single step.  */
 
 static int
@@ -1030,7 +1172,7 @@ struct linux_target_ops the_low_target = {
   arm_set_pc,
   arm_breakpoint_kind_from_pc,
   arm_sw_breakpoint_from_kind,
-  NULL, /* get_next_pcs */
+  arm_gdbserver_get_next_pcs,
   0,
   arm_breakpoint_at,
   arm_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index cd455a8..8eaad3e 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -124,8 +124,6 @@ struct process_info_private
 
 struct lwp_info;
 
-DEF_VEC_I (CORE_ADDR);
-
 struct linux_target_ops
 {
   /* Architecture-specific setup.  */
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index dc0361f..2078d01 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -124,6 +124,7 @@ extern void discard_queued_stop_replies (ptid_t ptid);
 
 #include "utils.h"
 #include "debug.h"
+#include "gdb_vecs.h"
 
 /* Maximum number of bytes to read/write at once.  The value here
    is chosen to fill up a packet (the headers account for the 32).  */
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 37c8c93..b522726 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1613,8 +1613,6 @@ void iterate_over_symtabs (const char *name,
 					    void *data),
 			   void *data);
 
-DEF_VEC_I (CORE_ADDR);
-
 VEC (CORE_ADDR) *find_pcs_for_symtab_line (struct symtab *symtab, int line,
 					   struct linetable_entry **best_entry);
 
-- 
2.6.3

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 3/7] Refactor arm_software_single_step to use regcache.
  2015-12-04 18:46 ` [PATCH v6 3/7] Refactor arm_software_single_step to use regcache Antoine Tremblay
@ 2015-12-07 14:32   ` Yao Qi
  2015-12-07 14:48     ` Antoine Tremblay
  2015-12-07 14:51   ` Yao Qi
  1 sibling, 1 reply; 18+ messages in thread
From: Yao Qi @ 2015-12-07 14:32 UTC (permalink / raw)
  To: Antoine Tremblay; +Cc: gdb-patches

Antoine Tremblay <antoine.tremblay@ericsson.com> writes:

> +/* Calculate the offset from stack pointer of the pc register on the stack
> +   in the case of a sigreturn or sigreturn_rt syscall.  */
> +static int
> +arm_linux_sigreturn_next_pc_offset (unsigned long sp,
> +				    unsigned long sp_data,
> +				    unsigned long svc_number)
> +{
> +  /* Offset of R0 register.  */
> +  int r0_offset = 0;
> +  /* Offset of PC register.  */
> +  int pc_offset = 0;
> +
> +  gdb_assert (svc_number == 119 || svc_number == 173);
> +
> +  /* sigreturn.  */
> +  if (svc_number == 119)

Can we get rid of these magic numbers?

> +    {
> +      if (sp_data == ARM_NEW_SIGFRAME_MAGIC)
> +	r0_offset = ARM_UCONTEXT_SIGCONTEXT + ARM_SIGCONTEXT_R0;
> +      else
> +	r0_offset = ARM_SIGCONTEXT_R0;
> +    }
> +  /* rt_sigreturn. */
> +  else if (svc_number == 173)
> +    {
> +      if (sp_data == sp + ARM_OLD_RT_SIGFRAME_SIGINFO)
> +	r0_offset = ARM_OLD_RT_SIGFRAME_UCONTEXT + ARM_UCONTEXT_SIGCONTEXT
> +	  + ARM_SIGCONTEXT_R0;
> +      else
> +	r0_offset = ARM_NEW_RT_SIGFRAME_UCONTEXT + ARM_UCONTEXT_SIGCONTEXT
> +	  + ARM_SIGCONTEXT_R0;
> +    }
> +
> +  pc_offset = r0_offset + 4 * 15;
> +
> +  return pc_offset;
> +}
> +

> +
>  /* At a ptrace syscall-stop, return the syscall number.  This either
>     comes from the SWI instruction (OABI) or from r7 (EABI).
>  
> @@ -862,21 +924,21 @@ arm_linux_get_syscall_number (struct gdbarch *gdbarch,
>     instruction to be executed.  */
>  
>  static CORE_ADDR
> -arm_linux_syscall_next_pc (struct frame_info *frame)
> +arm_linux_syscall_next_pc (struct regcache *regcache)
>  {
> -  CORE_ADDR pc = get_frame_pc (frame);
> -  CORE_ADDR return_addr = 0;
> -  int is_thumb = arm_frame_is_thumb (frame);
> +  CORE_ADDR pc = regcache_read_pc (regcache);
> +  CORE_ADDR next_pc = 0;
> +  int is_thumb = arm_is_thumb (regcache);

Nit: looks you rename return_addr to next_pc.  If you don't that, the patch
can be shorter.  On the other hand, return_addr sounds a good variable
name to me, which means the address after syscall returns.

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 4/7] Share regcache function regcache_raw_read_unsigned.
  2015-12-04 18:46 ` [PATCH v6 4/7] Share regcache function regcache_raw_read_unsigned Antoine Tremblay
@ 2015-12-07 14:40   ` Yao Qi
  2015-12-07 14:49     ` Antoine Tremblay
  0 siblings, 1 reply; 18+ messages in thread
From: Yao Qi @ 2015-12-07 14:40 UTC (permalink / raw)
  To: Antoine Tremblay; +Cc: gdb-patches

Antoine Tremblay <antoine.tremblay@ericsson.com> writes:

> gdb/ChangeLog:
> 	* Makefile.in: Add common-regcache.c/.o.

Need to describe what do we change in Makefile.in, like

 	* Makefile.in (SFILES): Append common/common-regcache.c.
        (COMMON_OBS): Append common/common-regcache.o.
        (common-regcache.o): New rule.

> 	* common/common-regcache.h (register_status) New enum.
> 	(regcache_raw_read_unsigned): New declaration.
> 	* common/common-regcache.c: New file.
> 	* regcache.h (enum register_status): Move to common-regcache.h.
> 	(regcache_raw_read_unsigned): Likewise.
> 	(regcache_raw_get_unsigned): Likewise.
>
> gdb/gdbserver/ChangeLog:
>
> 	* Makefile.in: Add common-regcache.c/.o.

Likewise.

> 	* regcache.c (init_register_cache): Initialize cache to
> 	REG_UNAVAILABLE.
> 	(regcache_raw_read_unsigned): New function.
> 	* regcache.h (REG_UNAVAILABLE, REG_VALID): Replaced by shared
> 	register_status enum.

Patch is good to me, otherwise.

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 3/7] Refactor arm_software_single_step to use regcache.
  2015-12-07 14:32   ` Yao Qi
@ 2015-12-07 14:48     ` Antoine Tremblay
  2015-12-07 15:21       ` Yao Qi
  0 siblings, 1 reply; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-07 14:48 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches



On 12/07/2015 09:32 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> +/* Calculate the offset from stack pointer of the pc register on the stack
>> +   in the case of a sigreturn or sigreturn_rt syscall.  */
>> +static int
>> +arm_linux_sigreturn_next_pc_offset (unsigned long sp,
>> +				    unsigned long sp_data,
>> +				    unsigned long svc_number)
>> +{
>> +  /* Offset of R0 register.  */
>> +  int r0_offset = 0;
>> +  /* Offset of PC register.  */
>> +  int pc_offset = 0;
>> +
>> +  gdb_assert (svc_number == 119 || svc_number == 173);
>> +
>> +  /* sigreturn.  */
>> +  if (svc_number == 119)
>
> Can we get rid of these magic numbers?
>

Yes I can find a place to put a define, good idea.

>> +    {
>> +      if (sp_data == ARM_NEW_SIGFRAME_MAGIC)
>> +	r0_offset = ARM_UCONTEXT_SIGCONTEXT + ARM_SIGCONTEXT_R0;
>> +      else
>> +	r0_offset = ARM_SIGCONTEXT_R0;
>> +    }
>> +  /* rt_sigreturn. */
>> +  else if (svc_number == 173)
>> +    {
>> +      if (sp_data == sp + ARM_OLD_RT_SIGFRAME_SIGINFO)
>> +	r0_offset = ARM_OLD_RT_SIGFRAME_UCONTEXT + ARM_UCONTEXT_SIGCONTEXT
>> +	  + ARM_SIGCONTEXT_R0;
>> +      else
>> +	r0_offset = ARM_NEW_RT_SIGFRAME_UCONTEXT + ARM_UCONTEXT_SIGCONTEXT
>> +	  + ARM_SIGCONTEXT_R0;
>> +    }
>> +
>> +  pc_offset = r0_offset + 4 * 15;
>> +
>> +  return pc_offset;
>> +}
>> +
>
>> +
>>   /* At a ptrace syscall-stop, return the syscall number.  This either
>>      comes from the SWI instruction (OABI) or from r7 (EABI).
>>
>> @@ -862,21 +924,21 @@ arm_linux_get_syscall_number (struct gdbarch *gdbarch,
>>      instruction to be executed.  */
>>
>>   static CORE_ADDR
>> -arm_linux_syscall_next_pc (struct frame_info *frame)
>> +arm_linux_syscall_next_pc (struct regcache *regcache)
>>   {
>> -  CORE_ADDR pc = get_frame_pc (frame);
>> -  CORE_ADDR return_addr = 0;
>> -  int is_thumb = arm_frame_is_thumb (frame);
>> +  CORE_ADDR pc = regcache_read_pc (regcache);
>> +  CORE_ADDR next_pc = 0;
>> +  int is_thumb = arm_is_thumb (regcache);
>
> Nit: looks you rename return_addr to next_pc.  If you don't that, the patch
> can be shorter.  On the other hand, return_addr sounds a good variable
> name to me, which means the address after syscall returns.
>

Yes, I do since the function is called arm_linux_syscall*_next_pc*
and that is called from the get_next_pcs context.  It seems more 
consistent to me to refer to this address as the next program counter in 
that context.

Also I wanted to mark the difference between 
arm_linux_sigreturn_return_addr and arm_linux_sigreturn_next_pc.

Calling return_addr = arm_linux_sigreturn_next_pc seemed weird when 
there was return_addr = arm_linux_sigreturn_return_addr before.

In short I prefer to keep the function name consistent with the return 
value name.

Regards,
Antoine

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 4/7] Share regcache function regcache_raw_read_unsigned.
  2015-12-07 14:40   ` Yao Qi
@ 2015-12-07 14:49     ` Antoine Tremblay
  0 siblings, 0 replies; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-07 14:49 UTC (permalink / raw)
  To: gdb-patches



On 12/07/2015 09:40 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> gdb/ChangeLog:
>> 	* Makefile.in: Add common-regcache.c/.o.
>
> Need to describe what do we change in Makefile.in, like
>
>   	* Makefile.in (SFILES): Append common/common-regcache.c.
>          (COMMON_OBS): Append common/common-regcache.o.
>          (common-regcache.o): New rule.
>

OK, done.

>> 	* common/common-regcache.h (register_status) New enum.
>> 	(regcache_raw_read_unsigned): New declaration.
>> 	* common/common-regcache.c: New file.
>> 	* regcache.h (enum register_status): Move to common-regcache.h.
>> 	(regcache_raw_read_unsigned): Likewise.
>> 	(regcache_raw_get_unsigned): Likewise.
>>
>> gdb/gdbserver/ChangeLog:
>>
>> 	* Makefile.in: Add common-regcache.c/.o.
>
> Likewise.
>
>> 	* regcache.c (init_register_cache): Initialize cache to
>> 	REG_UNAVAILABLE.
>> 	(regcache_raw_read_unsigned): New function.
>> 	* regcache.h (REG_UNAVAILABLE, REG_VALID): Replaced by shared
>> 	register_status enum.
>
> Patch is good to me, otherwise.
>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 3/7] Refactor arm_software_single_step to use regcache.
  2015-12-04 18:46 ` [PATCH v6 3/7] Refactor arm_software_single_step to use regcache Antoine Tremblay
  2015-12-07 14:32   ` Yao Qi
@ 2015-12-07 14:51   ` Yao Qi
  2015-12-07 14:51     ` Antoine Tremblay
  1 sibling, 1 reply; 18+ messages in thread
From: Yao Qi @ 2015-12-07 14:51 UTC (permalink / raw)
  To: Antoine Tremblay; +Cc: gdb-patches

Antoine Tremblay <antoine.tremblay@ericsson.com> writes:

> +  pc_offset = r0_offset + 4 * 15;

Replace magic numbers with INT_REGISTER_SIZE * ARM_PC_REGNUM?

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 3/7] Refactor arm_software_single_step to use regcache.
  2015-12-07 14:51   ` Yao Qi
@ 2015-12-07 14:51     ` Antoine Tremblay
  0 siblings, 0 replies; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-07 14:51 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches



On 12/07/2015 09:51 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> +  pc_offset = r0_offset + 4 * 15;
>
> Replace magic numbers with INT_REGISTER_SIZE * ARM_PC_REGNUM?
>
OK.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 5/7] Support software single step on ARM in GDBServer.
  2015-12-04 18:47 ` [PATCH v6 5/7] Support software single step on ARM " Antoine Tremblay
@ 2015-12-07 15:18   ` Yao Qi
  2015-12-07 15:58     ` Antoine Tremblay
  0 siblings, 1 reply; 18+ messages in thread
From: Yao Qi @ 2015-12-07 15:18 UTC (permalink / raw)
  To: Antoine Tremblay; +Cc: gdb-patches

Antoine Tremblay <antoine.tremblay@ericsson.com> writes:

> gdb/ChangeLog:
> 	* Makefile.in: Add arm-get-next-pcs.c/o, arm-linux.c/o.
> 	to all arm targets.

Please describe how Makefile.in is changed, as I suggested in previous patch.

> 	* aarch32-linux-nat.c: Include arch/arm-get-next-pcs.h.
> 	* arch/arm-get-next-pcs.c: New file.
> 	* arch/arm-get-next-pcs.h: New file.
> 	* arch/arm-linux.h: New file.
> 	* arch/arm-linux.c: New file.
> 	* arm-linux-nat.c: Include arch/arm.h, arch/arm-get-next-pcs.h
> 	arch/arm-linux.h.
> 	* arm-linux-tdep.c: Include arch/arm.h,arch/arm-get-next-pcs.h
> 	arch/arm-linux.h.
> 	(arm_linux_get_next_pcs_ops): New struct.
> 	(ARM_SIGCONTEXT_R0, ARM_UCONTEXT_SIGCONTEXT,
> 	ARM_OLD_RT_SIGFRAME_SIGINFO, ARM_OLD_RT_SIGFRAME_UCONTEXT,
> 	ARM_NEW_RT_SIGFRAME_UCONTEXT, ARM_NEW_SIGFRAME_MAGIC): Move stack
> 	layout defines to arch/arm-linux.h.
> 	(arm_linux_sigreturn_next_pc_offset): Move to arch/arm-linux.c.
> 	(arm_linux_software_single_step): Adjust for
> 	arm_get_next_pcs implementation.
> 	* arm-tdep.c: Include arch/arm-get-next-pcs.h.
> 	(arm_get_next_pcs_ops): New struct.
> 	(submask): Move macro to arm-get-next-pcs.h.
> 	(bit): Likewise.
> 	(bits): Likewise.
> 	(sbits): Likewise.
> 	(BranchDest): Likewise.

> 	(thumb_instruction_changes_pc): Move to arm-get-next-pcs.c
> 	(thumb2_instruction_changes_pc): Likewise.
> 	(arm_instruction_changes_pc): Likewise.

I feel they should be moved to arch/arm.c, because they are not about
getting the next pcs.

> 	(shifted_reg_val): Likewise.
> 	(thumb_advance_itstate): Likewise.

and this one.

> 	(thumb_get_next_pc_raw): Likewise.
> 	(arm_get_next_pc_raw): Likewise.
> 	(arm_get_next_pc): Likewise.
> 	(thumb_deal_with_atomic_sequence_raw): Likewise.
> 	(arm_deal_with_atomic_sequence_raw): Likewise.
> 	(arm_deal_with_atomic_sequence): Likewise.
> 	(arm_get_next_pcs_read_memory_unsigned_integer): New function.
> 	(arm_get_next_pcs_addr_bits_remove): Likewise.
> 	(arm_get_next_pcs_syscall_next_pc): Likewise.
> 	(arm_get_next_pcs_is_thumb): Likewise.
> 	(arm_software_single_step): Adjust for arm_get_next_pcs
> 	implementation.
> 	* arm-tdep.h: (arm_get_next_pc): Remove declaration.
> 	(arm_get_next_pcs_read_memory_unsigned_integer):
> 	New declaration.
> 	(arm_get_next_pcs_addr_bits_remove): Likewise.
> 	(arm_get_next_pcs_syscall_next_pc): Likewise.
> 	(arm_software_single_step): Likewise.
> 	(arm_deal_with_atomic_sequence: Remove declaration.
> 	(arm_get_next_pcs_is_thumb): New delcaration.
> 	* arm-wince-tdep.c: Include arch/arm-get-next-pcs.h.
> 	* armbsd-tdep.c: Likewise.
> 	* armnbsd-tdep.c: Likewise.
> 	* armobsd-tdep.c: Likewise.

Why do we need to include arm-get-next-pcs.h in these *-tdep.c files?

> 	* common/gdb_vecs.h: Add CORE_ADDR vector definition.
> 	* configure.tgt: Add arm-get-next-pcs.o, arm-linux.o to all
> 	relevent arm targets.
> 	* symtab.h: Move CORE_ADDR vector definition to gdb_vecs.h.
>
> gdb/gdbserver/ChangeLog:
> 	* Makefile.in : Add arm-get-next-pcs.c/o, arm-linux.c/o.

Please describe what are changed.

> 	* configure.srv: Add arm-get-next-pcs.o, arm-linux.o
> 	to needed arm targets.
> 	* linux-arm-low.c: Include arch/arm-linux.h,
> 	aarch/arm-get-next-pcs.h.
> 	(get_next_pcs_ops): New struct.
> 	(get_next_pcs_addr_bits_remove): New function.
> 	(get_next_pcs_is_thumb): New function.
> 	(get_next_pcs_read_memory_unsigned_integer): Likewise.
> 	(arm_sigreturn_next_pc): Likewise.
> 	(get_next_pcs_syscall_next_pc): Likewise.
> 	(arm_gdbserver_get_next_pcs): Likewise.
> 	(struct linux_target_ops) <arm_gdbserver_get_next_pcs>:
> 	Initialize.
> 	* linux-low.h: Move CORE_ADDR vector definition to gdb_vecs.h.
> 	* server.h: Include gdb_vecs.h.
> ---
>  gdb/Makefile.in               |   16 +-
>  gdb/aarch32-linux-nat.c       |    1 +
>  gdb/arch/arm-get-next-pcs.c   | 1216 +++++++++++++++++++++++++++++++++
>  gdb/arch/arm-get-next-pcs.h   |   95 +++
>  gdb/arch/arm-linux.c          |   59 ++
>  gdb/arch/arm-linux.h          |   73 ++
>  gdb/arm-linux-nat.c           |    2 +
>  gdb/arm-linux-tdep.c          |  136 ++--
>  gdb/arm-tdep.c                | 1495 +++++------------------------------------
>  gdb/arm-tdep.h                |   18 +-
>  gdb/arm-wince-tdep.c          |    1 +
>  gdb/armbsd-tdep.c             |    1 +
>  gdb/armnbsd-tdep.c            |    1 +
>  gdb/armobsd-tdep.c            |    1 +
>  gdb/common/gdb_vecs.h         |    2 +
>  gdb/configure.tgt             |   20 +-
>  gdb/gdbserver/Makefile.in     |    9 +-
>  gdb/gdbserver/configure.srv   |    2 +
>  gdb/gdbserver/linux-arm-low.c |  144 +++-
>  gdb/gdbserver/linux-low.h     |    2 -
>  gdb/gdbserver/server.h        |    1 +
>  gdb/symtab.h                  |    2 -
>  22 files changed, 1871 insertions(+), 1426 deletions(-)
>  create mode 100644 gdb/arch/arm-get-next-pcs.c
>  create mode 100644 gdb/arch/arm-get-next-pcs.h
>  create mode 100644 gdb/arch/arm-linux.c
>  create mode 100644 gdb/arch/arm-linux.h
>



> +
> +/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D}
> +   instruction and ending with a STREX{,B,H,D} instruction.  If such a sequence
> +   is found, attempt to step through it.  The end of the sequence address is
> +   added to the next_pcs list.  */
> +
> +static int
> +thumb_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self,
> +				     CORE_ADDR pc,
> +				     VEC (CORE_ADDR) **next_pcs)

We can return VEC (CORE_ADDR) * here, if it is NULL, the sequence is not
found, otherwise, the end of the sequence addresses are returned in the vector.

> +
> +/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D}
> +   instruction and ending with a STREX{,B,H,D} instruction.  If such a sequence
> +   is found, attempt to step through it.  The end of the sequence address is
> +   added to the next_pcs list.  */
> +
> +static int
> +arm_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self,
> +				   CORE_ADDR pc,
> +				   VEC (CORE_ADDR) **next_pcs)

Likewise.

> +
> +/* See arm-get-next-pcs.h.  */
> +
> +VEC (CORE_ADDR) *
> +arm_get_next_pcs (struct arm_get_next_pcs *self, CORE_ADDR pc)
> +{
> +  VEC (CORE_ADDR) *next_pcs = NULL;
> +
> +  if (self->ops->is_thumb (self))
> +    {
> +      if (!thumb_deal_with_atomic_sequence_raw (self, pc, &next_pcs))
> +	return thumb_get_next_pcs_raw (self, pc, &next_pcs);

Why do we need to pass next_pcs to thumb_get_next_pcs_raw?  Isn't
simpler to write it like this,

        next_pcs = thumb_deal_with_atomic_sequence_raw (self, pc);
        if (next_pcs == NULL)
          next_pcs = thumb_get_next_pcs_raw (self, pc);

> +    }
> +  else
> +    {
> +      if (!arm_deal_with_atomic_sequence_raw (self, pc, &next_pcs))
> +	return arm_get_next_pcs_raw (self, pc, &next_pcs);
> +    }
> +
> +  return next_pcs;
> +}

> diff --git a/gdb/arch/arm-get-next-pcs.h b/gdb/arch/arm-get-next-pcs.h
> new file mode 100644
> index 0000000..39921e0
> --- /dev/null
> +++ b/gdb/arch/arm-get-next-pcs.h
> @@ -0,0 +1,95 @@
> +/* Common code for ARM software single stepping support.
> +
> +   Copyright (C) 1988-2015 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/>.  */
> +
> +#ifndef ARM_GET_NEXT_PCS_H
> +#define ARM_GET_NEXT_PCS_H 1
> +
> +/* Support routines for instruction parsing.  */
> +#define submask(x) ((1L << ((x) + 1)) - 1)
> +#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st)))
> +#define bit(obj,st) (((obj) >> (st)) & 1)
> +#define sbits(obj,st,fn) \
> +  ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st))))
> +#define BranchDest(addr,instr) \
> +  ((CORE_ADDR) (((unsigned long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
> +

These macros should be moved to arch/arm.h.

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 3/7] Refactor arm_software_single_step to use regcache.
  2015-12-07 14:48     ` Antoine Tremblay
@ 2015-12-07 15:21       ` Yao Qi
  2015-12-07 17:37         ` Antoine Tremblay
  0 siblings, 1 reply; 18+ messages in thread
From: Yao Qi @ 2015-12-07 15:21 UTC (permalink / raw)
  To: Antoine Tremblay; +Cc: Yao Qi, gdb-patches

Antoine Tremblay <antoine.tremblay@ericsson.com> writes:

> Yes I can find a place to put a define, good idea.

We can either use some macros defined in kernel headers, __NR_sigreturn,
or compute the these syscall numbers according to syscalls/arm-linux.xml.

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 5/7] Support software single step on ARM in GDBServer.
  2015-12-07 15:18   ` Yao Qi
@ 2015-12-07 15:58     ` Antoine Tremblay
  0 siblings, 0 replies; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-07 15:58 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches



On 12/07/2015 10:17 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> gdb/ChangeLog:
>> 	* Makefile.in: Add arm-get-next-pcs.c/o, arm-linux.c/o.
>> 	to all arm targets.
>
> Please describe how Makefile.in is changed, as I suggested in previous patch.
>
OK.

>> 	* aarch32-linux-nat.c: Include arch/arm-get-next-pcs.h.
>> 	* arch/arm-get-next-pcs.c: New file.
>> 	* arch/arm-get-next-pcs.h: New file.
>> 	* arch/arm-linux.h: New file.
>> 	* arch/arm-linux.c: New file.
>> 	* arm-linux-nat.c: Include arch/arm.h, arch/arm-get-next-pcs.h
>> 	arch/arm-linux.h.
>> 	* arm-linux-tdep.c: Include arch/arm.h,arch/arm-get-next-pcs.h
>> 	arch/arm-linux.h.
>> 	(arm_linux_get_next_pcs_ops): New struct.
>> 	(ARM_SIGCONTEXT_R0, ARM_UCONTEXT_SIGCONTEXT,
>> 	ARM_OLD_RT_SIGFRAME_SIGINFO, ARM_OLD_RT_SIGFRAME_UCONTEXT,
>> 	ARM_NEW_RT_SIGFRAME_UCONTEXT, ARM_NEW_SIGFRAME_MAGIC): Move stack
>> 	layout defines to arch/arm-linux.h.
>> 	(arm_linux_sigreturn_next_pc_offset): Move to arch/arm-linux.c.
>> 	(arm_linux_software_single_step): Adjust for
>> 	arm_get_next_pcs implementation.
>> 	* arm-tdep.c: Include arch/arm-get-next-pcs.h.
>> 	(arm_get_next_pcs_ops): New struct.
>> 	(submask): Move macro to arm-get-next-pcs.h.
>> 	(bit): Likewise.
>> 	(bits): Likewise.
>> 	(sbits): Likewise.
>> 	(BranchDest): Likewise.
>
>> 	(thumb_instruction_changes_pc): Move to arm-get-next-pcs.c
>> 	(thumb2_instruction_changes_pc): Likewise.
>> 	(arm_instruction_changes_pc): Likewise.
>
> I feel they should be moved to arch/arm.c, because they are not about
> getting the next pcs.
>

OK. I went at first with the idea that if they were only used in 
get_next_pc to leave them with it as the criteria since I was not sure 
where to draw the line. But these do feel more generic.

>> 	(shifted_reg_val): Likewise.
>> 	(thumb_advance_itstate): Likewise.
>
> and this one.
>
OK.

>> 	(thumb_get_next_pc_raw): Likewise.
>> 	(arm_get_next_pc_raw): Likewise.
>> 	(arm_get_next_pc): Likewise.
>> 	(thumb_deal_with_atomic_sequence_raw): Likewise.
>> 	(arm_deal_with_atomic_sequence_raw): Likewise.
>> 	(arm_deal_with_atomic_sequence): Likewise.
>> 	(arm_get_next_pcs_read_memory_unsigned_integer): New function.
>> 	(arm_get_next_pcs_addr_bits_remove): Likewise.
>> 	(arm_get_next_pcs_syscall_next_pc): Likewise.
>> 	(arm_get_next_pcs_is_thumb): Likewise.
>> 	(arm_software_single_step): Adjust for arm_get_next_pcs
>> 	implementation.
>> 	* arm-tdep.h: (arm_get_next_pc): Remove declaration.
>> 	(arm_get_next_pcs_read_memory_unsigned_integer):
>> 	New declaration.
>> 	(arm_get_next_pcs_addr_bits_remove): Likewise.
>> 	(arm_get_next_pcs_syscall_next_pc): Likewise.
>> 	(arm_software_single_step): Likewise.
>> 	(arm_deal_with_atomic_sequence: Remove declaration.
>> 	(arm_get_next_pcs_is_thumb): New delcaration.
>> 	* arm-wince-tdep.c: Include arch/arm-get-next-pcs.h.
>> 	* armbsd-tdep.c: Likewise.
>> 	* armnbsd-tdep.c: Likewise.
>> 	* armobsd-tdep.c: Likewise.
>
> Why do we need to include arm-get-next-pcs.h in these *-tdep.c files?
>

We don't now, some defines were needed before, I forgot to clean that up 
sorry.

>> 	* common/gdb_vecs.h: Add CORE_ADDR vector definition.
>> 	* configure.tgt: Add arm-get-next-pcs.o, arm-linux.o to all
>> 	relevent arm targets.
>> 	* symtab.h: Move CORE_ADDR vector definition to gdb_vecs.h.
>>
>> gdb/gdbserver/ChangeLog:
>> 	* Makefile.in : Add arm-get-next-pcs.c/o, arm-linux.c/o.
>
> Please describe what are changed.
>

OK.

>> 	* configure.srv: Add arm-get-next-pcs.o, arm-linux.o
>> 	to needed arm targets.
>> 	* linux-arm-low.c: Include arch/arm-linux.h,
>> 	aarch/arm-get-next-pcs.h.
>> 	(get_next_pcs_ops): New struct.
>> 	(get_next_pcs_addr_bits_remove): New function.
>> 	(get_next_pcs_is_thumb): New function.
>> 	(get_next_pcs_read_memory_unsigned_integer): Likewise.
>> 	(arm_sigreturn_next_pc): Likewise.
>> 	(get_next_pcs_syscall_next_pc): Likewise.
>> 	(arm_gdbserver_get_next_pcs): Likewise.
>> 	(struct linux_target_ops) <arm_gdbserver_get_next_pcs>:
>> 	Initialize.
>> 	* linux-low.h: Move CORE_ADDR vector definition to gdb_vecs.h.
>> 	* server.h: Include gdb_vecs.h.
>> ---
>>   gdb/Makefile.in               |   16 +-
>>   gdb/aarch32-linux-nat.c       |    1 +
>>   gdb/arch/arm-get-next-pcs.c   | 1216 +++++++++++++++++++++++++++++++++
>>   gdb/arch/arm-get-next-pcs.h   |   95 +++
>>   gdb/arch/arm-linux.c          |   59 ++
>>   gdb/arch/arm-linux.h          |   73 ++
>>   gdb/arm-linux-nat.c           |    2 +
>>   gdb/arm-linux-tdep.c          |  136 ++--
>>   gdb/arm-tdep.c                | 1495 +++++------------------------------------
>>   gdb/arm-tdep.h                |   18 +-
>>   gdb/arm-wince-tdep.c          |    1 +
>>   gdb/armbsd-tdep.c             |    1 +
>>   gdb/armnbsd-tdep.c            |    1 +
>>   gdb/armobsd-tdep.c            |    1 +
>>   gdb/common/gdb_vecs.h         |    2 +
>>   gdb/configure.tgt             |   20 +-
>>   gdb/gdbserver/Makefile.in     |    9 +-
>>   gdb/gdbserver/configure.srv   |    2 +
>>   gdb/gdbserver/linux-arm-low.c |  144 +++-
>>   gdb/gdbserver/linux-low.h     |    2 -
>>   gdb/gdbserver/server.h        |    1 +
>>   gdb/symtab.h                  |    2 -
>>   22 files changed, 1871 insertions(+), 1426 deletions(-)
>>   create mode 100644 gdb/arch/arm-get-next-pcs.c
>>   create mode 100644 gdb/arch/arm-get-next-pcs.h
>>   create mode 100644 gdb/arch/arm-linux.c
>>   create mode 100644 gdb/arch/arm-linux.h
>>
>
>
>
>> +
>> +/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D}
>> +   instruction and ending with a STREX{,B,H,D} instruction.  If such a sequence
>> +   is found, attempt to step through it.  The end of the sequence address is
>> +   added to the next_pcs list.  */
>> +
>> +static int
>> +thumb_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self,
>> +				     CORE_ADDR pc,
>> +				     VEC (CORE_ADDR) **next_pcs)
>
> We can return VEC (CORE_ADDR) * here, if it is NULL, the sequence is not
> found, otherwise, the end of the sequence addresses are returned in the vector.
>

OK.

>> +
>> +/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D}
>> +   instruction and ending with a STREX{,B,H,D} instruction.  If such a sequence
>> +   is found, attempt to step through it.  The end of the sequence address is
>> +   added to the next_pcs list.  */
>> +
>> +static int
>> +arm_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self,
>> +				   CORE_ADDR pc,
>> +				   VEC (CORE_ADDR) **next_pcs)
>
> Likewise.
>
>> +
>> +/* See arm-get-next-pcs.h.  */
>> +
>> +VEC (CORE_ADDR) *
>> +arm_get_next_pcs (struct arm_get_next_pcs *self, CORE_ADDR pc)
>> +{
>> +  VEC (CORE_ADDR) *next_pcs = NULL;
>> +
>> +  if (self->ops->is_thumb (self))
>> +    {
>> +      if (!thumb_deal_with_atomic_sequence_raw (self, pc, &next_pcs))
>> +	return thumb_get_next_pcs_raw (self, pc, &next_pcs);
>
> Why do we need to pass next_pcs to thumb_get_next_pcs_raw?  Isn't
> simpler to write it like this,
>
>          next_pcs = thumb_deal_with_atomic_sequence_raw (self, pc);
>          if (next_pcs == NULL)
>            next_pcs = thumb_get_next_pcs_raw (self, pc);
>

Indeed, I was focused on not losing the VEC allocation/resize and forgot 
  thumb_deal_with_atomic_sequence_raw and thumb_get_next_pcs_raw  will 
not combine their vectors.

Thanks.

>> +    }
>> +  else
>> +    {
>> +      if (!arm_deal_with_atomic_sequence_raw (self, pc, &next_pcs))
>> +	return arm_get_next_pcs_raw (self, pc, &next_pcs);
>> +    }
>> +
>> +  return next_pcs;
>> +}
>
>> diff --git a/gdb/arch/arm-get-next-pcs.h b/gdb/arch/arm-get-next-pcs.h
>> new file mode 100644
>> index 0000000..39921e0
>> --- /dev/null
>> +++ b/gdb/arch/arm-get-next-pcs.h
>> @@ -0,0 +1,95 @@
>> +/* Common code for ARM software single stepping support.
>> +
>> +   Copyright (C) 1988-2015 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/>.  */
>> +
>> +#ifndef ARM_GET_NEXT_PCS_H
>> +#define ARM_GET_NEXT_PCS_H 1
>> +
>> +/* Support routines for instruction parsing.  */
>> +#define submask(x) ((1L << ((x) + 1)) - 1)
>> +#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st)))
>> +#define bit(obj,st) (((obj) >> (st)) & 1)
>> +#define sbits(obj,st,fn) \
>> +  ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st))))
>> +#define BranchDest(addr,instr) \
>> +  ((CORE_ADDR) (((unsigned long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
>> +
>
> These macros should be moved to arch/arm.h.
>

OK.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 3/7] Refactor arm_software_single_step to use regcache.
  2015-12-07 15:21       ` Yao Qi
@ 2015-12-07 17:37         ` Antoine Tremblay
  0 siblings, 0 replies; 18+ messages in thread
From: Antoine Tremblay @ 2015-12-07 17:37 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches



On 12/07/2015 10:21 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> Yes I can find a place to put a define, good idea.
>
> We can either use some macros defined in kernel headers, __NR_sigreturn,
> or compute the these syscall numbers according to syscalls/arm-linux.xml.
>

OK. I think I'll add a is_syscall_by_name to xml-syscall that will 
compare the number with the name from the xml.

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2015-12-07 17:37 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-04 18:46 [PATCH v6 0/6] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
2015-12-04 18:46 ` [PATCH v6 3/7] Refactor arm_software_single_step to use regcache Antoine Tremblay
2015-12-07 14:32   ` Yao Qi
2015-12-07 14:48     ` Antoine Tremblay
2015-12-07 15:21       ` Yao Qi
2015-12-07 17:37         ` Antoine Tremblay
2015-12-07 14:51   ` Yao Qi
2015-12-07 14:51     ` Antoine Tremblay
2015-12-04 18:46 ` [PATCH v6 2/7] Share some ARM target dependent code from GDB with GDBServer Antoine Tremblay
2015-12-04 18:46 ` [PATCH v6 4/7] Share regcache function regcache_raw_read_unsigned Antoine Tremblay
2015-12-07 14:40   ` Yao Qi
2015-12-07 14:49     ` Antoine Tremblay
2015-12-04 18:46 ` [PATCH v6 1/7] Replace breakpoint_reinsert_addr by get_next_pcs operation in GDBServer Antoine Tremblay
2015-12-04 18:47 ` [PATCH v6 5/7] Support software single step on ARM " Antoine Tremblay
2015-12-07 15:18   ` Yao Qi
2015-12-07 15:58     ` Antoine Tremblay
2015-12-04 18:47 ` [PATCH v6 7/7] Enable conditional breakpoints for targets that support software single step " Antoine Tremblay
2015-12-04 18:47 ` [PATCH v6 6/7] Enable software single stepping for while-stepping actions " Antoine Tremblay

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).