* [PATCH 3/7] Support multiple breakpoint types per target in GDBServer.
2015-09-11 12:13 [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
2015-09-11 12:13 ` [PATCH 2/7] Move some integer operations to common Antoine Tremblay
2015-09-11 12:13 ` [PATCH 1/7] Fix instruction skipping when using software single step in GDBServer Antoine Tremblay
@ 2015-09-11 12:14 ` Antoine Tremblay
2015-09-11 12:14 ` [PATCH 7/7] Support tracepoints and software breakpoints on ARM aarch32-linux " Antoine Tremblay
` (4 subsequent siblings)
7 siblings, 0 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 12:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
This patch is in preparation for software single stepping on ARM
aarch32-linux. It refactors breakpoint and breakpoint_len into
breakpoint_from_pc to prepare the case where we have multiple types of
breakpoints.
breakpoint_from_pc returns the breakpoint for this PC as a string of bytes,
the length of the breakpoint and ajusts the PC to the real memory location in
case a flag was present in the PC.
No regressions, tested on Ubuntu 14.04 on ARMv7 and x86
Also since the target_ops have been changed compilation was tested on all
affected archs namely : aarch64, arm, bfin, cris, crisv32, m32r, m68k, mips, nios2,
ppc, s390, sh, sparc, tic6x, tile, x86, xtensa
gdbserver/ChangeLog:
* linux-aarch64-low.c
(aarch64_breakpoint_from_pc): New function.
* linux-arm-low.c (arm_breakpoint_from_pc): Likewise.
* linux-bfin-low.c (bfin_breakpoint_from_pc): Likewise.
* linux-cris-low.c (cris_breakpoint_from_pc): Likewise.
* linux-crisv32-low.c: (cris_breakpoint_from_pc): Likewise.
* linux-low.c (linux_wait_1): Add call to breakpoint_from_pc.
(linux_breakpoint_from_pc): New function.
(initialize_low): Add call to breakpoint_from_pc.
* linux-low.h: Add breakpoint_from_pc operation.
* linux-m32r-low.c (m32r_breakpoint_from_pc): New function.
* linux-m68k-low.c (m68k_breakpoint_from_pc): Likewise.
* linux-mips-low.c (mips_breakpoint_from_pc): Likewise.
* linux-nios2-low.c (nios2_breakpoint_from_pc): Likewise.
* linux-ppc-low.c (ppc_breakpoint_from_pc): Likewise.
* linux-s390-low.c (s390_breakpoint_from_pc): Likewise.
* linux-sh-low.c (sh_breakpoint_from_pc): Likewise.
* linux-sparc-low.c (sparc_breakpoint_from_pc): Likewise.
* linux-tic6x-low.c (tic6x_breakpoint_from_pc): Likewise.
* linux-tile-low.c (tile_breakpoint_from_pc): Likewise.
* linux-x86-low.c (x86_breakpoint_from_pc): Likewise.
* linux-xtensa-low.c(xtensa_breakpoint_from_pc): Likewise.
* target.h (struct target_ops): Add breakpoint_from_pc operation.
* win32-arm-low.c (arm_wince_breakpoint_from_pc): New Function.
* win32-i386-low.c(i386_win32_breakpoint_from_pc): Likewise.
---
gdb/gdbserver/linux-aarch64-low.c | 10 ++++++++--
gdb/gdbserver/linux-arm-low.c | 28 ++++++++++++++++------------
gdb/gdbserver/linux-bfin-low.c | 10 ++++++++--
gdb/gdbserver/linux-cris-low.c | 12 +++++++++---
gdb/gdbserver/linux-crisv32-low.c | 10 ++++++++--
gdb/gdbserver/linux-low.c | 28 +++++++++++++++++++++++++---
gdb/gdbserver/linux-low.h | 9 +++++++--
gdb/gdbserver/linux-m32r-low.c | 10 ++++++++--
gdb/gdbserver/linux-m68k-low.c | 10 ++++++++--
gdb/gdbserver/linux-mips-low.c | 10 ++++++++--
gdb/gdbserver/linux-nios2-low.c | 22 +++++++++++++---------
gdb/gdbserver/linux-ppc-low.c | 10 ++++++++--
gdb/gdbserver/linux-s390-low.c | 10 ++++++++--
gdb/gdbserver/linux-sh-low.c | 10 ++++++++--
gdb/gdbserver/linux-sparc-low.c | 9 +++++++--
gdb/gdbserver/linux-tic6x-low.c | 13 +++++++++----
gdb/gdbserver/linux-tile-low.c | 10 ++++++++--
gdb/gdbserver/linux-x86-low.c | 10 ++++++++--
gdb/gdbserver/linux-xtensa-low.c | 10 ++++++++--
gdb/gdbserver/target.h | 5 +++++
gdb/gdbserver/win32-arm-low.c | 10 ++++++++--
gdb/gdbserver/win32-i386-low.c | 10 ++++++++--
22 files changed, 203 insertions(+), 63 deletions(-)
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index aebf1e3..0b2c5f1 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -200,6 +200,13 @@ aarch64_set_pc (struct regcache *regcache, CORE_ADDR pc)
(aarch64_default_breakpoint). */
static const gdb_byte aarch64_breakpoint[] = {0x00, 0x00, 0x20, 0xd4};
+static const unsigned char *
+aarch64_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = aarch64_breakpoint_len;
+ return (const unsigned char *) &aarch64_breakpoint;
+}
+
/* Implementation of linux_target_ops method "breakpoint_at". */
static int
@@ -569,8 +576,7 @@ struct linux_target_ops the_low_target =
NULL, /* fetch_register */
aarch64_get_pc,
aarch64_set_pc,
- (const unsigned char *) &aarch64_breakpoint,
- aarch64_breakpoint_len,
+ aarch64_breakpoint_from_pc,
NULL, /* breakpoint_reinsert_addr */
0, /* decr_pc_after_break */
aarch64_breakpoint_at,
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index a277bb6..367c704 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -913,6 +913,21 @@ arm_regs_info (void)
return ®s_info_arm;
}
+static const unsigned char *
+arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = arm_breakpoint_len;
+ /* Define an ARM-mode breakpoint; we only set breakpoints in the C
+ library, which is most likely to be ARM. If the kernel supports
+ clone events, we will never insert a breakpoint, so even a Thumb
+ C library will work; so will mixing EABI/non-EABI gdbserver and
+ application. */
+#ifndef __ARM_EABI__
+ return (const unsigned char *) &arm_breakpoint;
+#else
+ return (const unsigned char *) &arm_eabi_breakpoint;
+#endif
+}
struct linux_target_ops the_low_target = {
arm_arch_setup,
arm_regs_info,
@@ -921,18 +936,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
arm_get_pc,
arm_set_pc,
-
- /* Define an ARM-mode breakpoint; we only set breakpoints in the C
- library, which is most likely to be ARM. If the kernel supports
- clone events, we will never insert a breakpoint, so even a Thumb
- C library will work; so will mixing EABI/non-EABI gdbserver and
- application. */
-#ifndef __ARM_EABI__
- (const unsigned char *) &arm_breakpoint,
-#else
- (const unsigned char *) &arm_eabi_breakpoint,
-#endif
- arm_breakpoint_len,
+ arm_breakpoint_from_pc,
arm_reinsert_addr,
0,
arm_breakpoint_at,
diff --git a/gdb/gdbserver/linux-bfin-low.c b/gdb/gdbserver/linux-bfin-low.c
index 4002f22..1c0e1e9 100644
--- a/gdb/gdbserver/linux-bfin-low.c
+++ b/gdb/gdbserver/linux-bfin-low.c
@@ -75,6 +75,13 @@ bfin_set_pc (struct regcache *regcache, CORE_ADDR pc)
#define bfin_breakpoint_len 2
static const unsigned char bfin_breakpoint[bfin_breakpoint_len] = {0xa1, 0x00};
+static const unsigned char *
+bfin_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = bfin_breakpoint_len;
+ return (const unsigned char *) &bfin_breakpoint;
+}
+
static int
bfin_breakpoint_at (CORE_ADDR where)
{
@@ -122,8 +129,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
bfin_get_pc,
bfin_set_pc,
- bfin_breakpoint,
- bfin_breakpoint_len,
+ bfin_breakpoint_from_pc,
NULL, /* breakpoint_reinsert_addr */
2,
bfin_breakpoint_at,
diff --git a/gdb/gdbserver/linux-cris-low.c b/gdb/gdbserver/linux-cris-low.c
index e0bfa1a..da5876d 100644
--- a/gdb/gdbserver/linux-cris-low.c
+++ b/gdb/gdbserver/linux-cris-low.c
@@ -62,7 +62,7 @@ cris_cannot_fetch_register (int regno)
extern int debug_threads;
static CORE_ADDR
-cris_get_pc (struct regcache *regcache, void)
+cris_get_pc (struct regcache *regcache)
{
unsigned long pc;
collect_register_by_name (regcache, "pc", &pc);
@@ -81,6 +81,13 @@ cris_set_pc (struct regcache *regcache, CORE_ADDR pc)
static const unsigned short cris_breakpoint = 0xe938;
#define cris_breakpoint_len 2
+static const unsigned char *
+cris_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = cris_breakpoint_len;
+ return (const unsigned char *) &cris_breakpoint;
+}
+
static int
cris_breakpoint_at (CORE_ADDR where)
{
@@ -140,8 +147,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
cris_get_pc,
cris_set_pc,
- (const unsigned char *) &cris_breakpoint,
- cris_breakpoint_len,
+ cris_breakpoint_from_pc,
cris_reinsert_addr,
0,
cris_breakpoint_at,
diff --git a/gdb/gdbserver/linux-crisv32-low.c b/gdb/gdbserver/linux-crisv32-low.c
index 5120863..d2dba91 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -77,6 +77,13 @@ cris_set_pc (struct regcache *regcache, CORE_ADDR pc)
static const unsigned short cris_breakpoint = 0xe938;
#define cris_breakpoint_len 2
+static const unsigned char *
+cris_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = cris_breakpoint_len;
+ return (const unsigned char *) &cris_breakpoint;
+}
+
static int
cris_breakpoint_at (CORE_ADDR where)
{
@@ -420,8 +427,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
cris_get_pc,
cris_set_pc,
- (const unsigned char *) &cris_breakpoint,
- cris_breakpoint_len,
+ cris_breakpoint_from_pc,
cris_reinsert_addr,
0,
cris_breakpoint_at,
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 7d979f8..ef6075b 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -2929,7 +2929,11 @@ linux_wait_1 (ptid_t ptid,
(event_child->stepping ||
!reinsert_breakpoint_inserted_here (event_child->stop_pc)))
{
- unsigned int increment_pc = the_low_target.breakpoint_len;
+ int increment_pc = 0;
+ CORE_ADDR stop_pc = event_child->stop_pc;
+
+ (*the_low_target.breakpoint_from_pc)
+ (&stop_pc, &increment_pc);
if (debug_threads)
{
@@ -6861,6 +6865,17 @@ current_lwp_ptid (void)
return ptid_of (current_thread);
}
+const unsigned char *
+linux_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
+{
+ if (the_low_target.breakpoint_from_pc != NULL)
+ {
+ return (*the_low_target.breakpoint_from_pc) (pcptr, lenptr);
+ }
+ else
+ return NULL;
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_arch_setup,
@@ -6954,6 +6969,7 @@ static struct target_ops linux_target_ops = {
linux_mntns_open_cloexec,
linux_mntns_unlink,
linux_mntns_readlink,
+ linux_breakpoint_from_pc,
};
static void
@@ -6981,10 +6997,16 @@ void
initialize_low (void)
{
struct sigaction sigchld_action;
+ int breakpoint_len = 0;
+ const unsigned char *breakpoint = NULL;
+
memset (&sigchld_action, 0, sizeof (sigchld_action));
set_target_ops (&linux_target_ops);
- set_breakpoint_data (the_low_target.breakpoint,
- the_low_target.breakpoint_len);
+
+ breakpoint = the_target->breakpoint_from_pc (NULL, &breakpoint_len);
+
+ set_breakpoint_data (breakpoint,
+ breakpoint_len);
linux_init_signals ();
linux_ptrace_init_warnings ();
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index f8f6e78..c623150 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -141,8 +141,13 @@ struct linux_target_ops
CORE_ADDR (*get_pc) (struct regcache *regcache);
void (*set_pc) (struct regcache *regcache, CORE_ADDR newpc);
- const unsigned char *breakpoint;
- int breakpoint_len;
+
+ /* Return the raw breakpoint for this target based on PC. Note that the PC
+ can be NULL, the default breakpoint for the target should be returned in
+ this case. The PC is ajusted to the real memory location in case a flag was
+ present in the PC. */
+ const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
+
CORE_ADDR (*breakpoint_reinsert_addr) (void);
int decr_pc_after_break;
diff --git a/gdb/gdbserver/linux-m32r-low.c b/gdb/gdbserver/linux-m32r-low.c
index 8ffeda2..4712a32 100644
--- a/gdb/gdbserver/linux-m32r-low.c
+++ b/gdb/gdbserver/linux-m32r-low.c
@@ -73,6 +73,13 @@ m32r_set_pc (struct regcache *regcache, CORE_ADDR pc)
static const unsigned short m32r_breakpoint = 0x10f1;
#define m32r_breakpoint_len 2
+static const unsigned char *
+m32r_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = m32r_breakpoint_len;
+ return (const unsigned char *) &m32r_breakpoint;
+}
+
static int
m32r_breakpoint_at (CORE_ADDR where)
{
@@ -120,8 +127,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
m32r_get_pc,
m32r_set_pc,
- (const unsigned char *) &m32r_breakpoint,
- m32r_breakpoint_len,
+ m32r_breakpoint_from_pc,
NULL,
0,
m32r_breakpoint_at,
diff --git a/gdb/gdbserver/linux-m68k-low.c b/gdb/gdbserver/linux-m68k-low.c
index 39c9cc5..39a9753 100644
--- a/gdb/gdbserver/linux-m68k-low.c
+++ b/gdb/gdbserver/linux-m68k-low.c
@@ -125,6 +125,13 @@ static struct regset_info m68k_regsets[] = {
static const unsigned char m68k_breakpoint[] = { 0x4E, 0x4F };
#define m68k_breakpoint_len 2
+static const unsigned char *
+m68k_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = m68k_breakpoint_len;
+ return (unsigned char*) &m68k_breakpoint;
+}
+
static CORE_ADDR
m68k_get_pc (struct regcache *regcache)
{
@@ -215,8 +222,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
m68k_get_pc,
m68k_set_pc,
- m68k_breakpoint,
- m68k_breakpoint_len,
+ m68k_breakpoint_from_pc,
NULL,
2,
m68k_breakpoint_at,
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index d1181b6..d5333ab 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -266,6 +266,13 @@ mips_set_pc (struct regcache *regcache, CORE_ADDR pc)
static const unsigned int mips_breakpoint = 0x0005000d;
#define mips_breakpoint_len 4
+static const unsigned char *
+mips_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = mips_breakpoint_len;
+ return (const unsigned char *) &mips_breakpoint;
+}
+
/* We only place breakpoints in empty marker functions, and thread locking
is outside of the function. So rather than importing software single-step,
we can just run until exit. */
@@ -881,8 +888,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
mips_get_pc,
mips_set_pc,
- (const unsigned char *) &mips_breakpoint,
- mips_breakpoint_len,
+ mips_breakpoint_from_pc,
mips_reinsert_addr,
0,
mips_breakpoint_at,
diff --git a/gdb/gdbserver/linux-nios2-low.c b/gdb/gdbserver/linux-nios2-low.c
index 71542b4..bf9ecc2 100644
--- a/gdb/gdbserver/linux-nios2-low.c
+++ b/gdb/gdbserver/linux-nios2-low.c
@@ -119,7 +119,6 @@ nios2_set_pc (struct regcache *regcache, CORE_ADDR pc)
/* Breakpoint support. Also see comments on nios2_breakpoint_from_pc
in nios2-tdep.c. */
-
#if defined(__nios2_arch__) && __nios2_arch__ == 2
#define NIOS2_BREAKPOINT 0xb7fd0020
#define CDX_BREAKPOINT 0xd7c9
@@ -127,9 +126,21 @@ nios2_set_pc (struct regcache *regcache, CORE_ADDR pc)
#define NIOS2_BREAKPOINT 0x003b6ffa
#endif
+/* We only register the 4-byte breakpoint, even on R2 targets which also
+ support 2-byte breakpoints. Since there is no supports_z_point_type
+ function provided, gdbserver never inserts software breakpoints itself
+ and instead relies on GDB to insert the breakpoint of the correct length
+ via a memory write. */
static const unsigned int nios2_breakpoint = NIOS2_BREAKPOINT;
#define nios2_breakpoint_len 4
+static const unsigned char *
+nios2_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = nios2_breakpoint_len;
+ return (const unsigned char *) &nios2_breakpoint;
+}
+
/* Implement the breakpoint_reinsert_addr linux_target_ops method. */
static CORE_ADDR
@@ -263,14 +274,7 @@ struct linux_target_ops the_low_target =
NULL,
nios2_get_pc,
nios2_set_pc,
-
- /* We only register the 4-byte breakpoint, even on R2 targets which also
- support 2-byte breakpoints. Since there is no supports_z_point_type
- function provided, gdbserver never inserts software breakpoints itself
- and instead relies on GDB to insert the breakpoint of the correct length
- via a memory write. */
- (const unsigned char *) &nios2_breakpoint,
- nios2_breakpoint_len,
+ nios2_breakpoint_from_pc,
nios2_reinsert_addr,
0,
nios2_breakpoint_at,
diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c
index 188fac0..4c71cd9 100644
--- a/gdb/gdbserver/linux-ppc-low.c
+++ b/gdb/gdbserver/linux-ppc-low.c
@@ -486,6 +486,13 @@ ppc_arch_setup (void)
static const unsigned int ppc_breakpoint = 0x7d821008;
#define ppc_breakpoint_len 4
+static const unsigned char *
+ppc_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = ppc_breakpoint_len;
+ return (const unsigned char *) &ppc_breakpoint;
+}
+
static int
ppc_breakpoint_at (CORE_ADDR where)
{
@@ -685,8 +692,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
ppc_get_pc,
ppc_set_pc,
- (const unsigned char *) &ppc_breakpoint,
- ppc_breakpoint_len,
+ ppc_breakpoint_from_pc,
NULL,
0,
ppc_breakpoint_at,
diff --git a/gdb/gdbserver/linux-s390-low.c b/gdb/gdbserver/linux-s390-low.c
index 8a0a689..f76f867 100644
--- a/gdb/gdbserver/linux-s390-low.c
+++ b/gdb/gdbserver/linux-s390-low.c
@@ -397,6 +397,13 @@ static struct regset_info s390_regsets[] = {
static const unsigned char s390_breakpoint[] = { 0, 1 };
#define s390_breakpoint_len 2
+static const unsigned char *
+s390_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = s390_breakpoint_len;
+ return (const unsigned char *) &s390_breakpoint;
+}
+
static CORE_ADDR
s390_get_pc (struct regcache *regcache)
{
@@ -665,8 +672,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
s390_get_pc,
s390_set_pc,
- s390_breakpoint,
- s390_breakpoint_len,
+ s390_breakpoint_from_pc,
NULL,
s390_breakpoint_len,
s390_breakpoint_at,
diff --git a/gdb/gdbserver/linux-sh-low.c b/gdb/gdbserver/linux-sh-low.c
index 218d4d3..5c11fd7 100644
--- a/gdb/gdbserver/linux-sh-low.c
+++ b/gdb/gdbserver/linux-sh-low.c
@@ -77,6 +77,13 @@ sh_set_pc (struct regcache *regcache, CORE_ADDR pc)
static const unsigned short sh_breakpoint = 0xc3c3;
#define sh_breakpoint_len 2
+static const unsigned char *
+sh_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = sh_breakpoint_len;
+ return (const unsigned char *) &sh_breakpoint;
+}
+
static int
sh_breakpoint_at (CORE_ADDR where)
{
@@ -148,8 +155,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
sh_get_pc,
sh_set_pc,
- (const unsigned char *) &sh_breakpoint,
- sh_breakpoint_len,
+ sh_breakpoint_from_pc,
NULL,
0,
sh_breakpoint_at,
diff --git a/gdb/gdbserver/linux-sparc-low.c b/gdb/gdbserver/linux-sparc-low.c
index 796af8a..35820fb 100644
--- a/gdb/gdbserver/linux-sparc-low.c
+++ b/gdb/gdbserver/linux-sparc-low.c
@@ -240,6 +240,12 @@ static const unsigned char sparc_breakpoint[INSN_SIZE] = {
};
#define sparc_breakpoint_len INSN_SIZE
+static const unsigned char *
+sparc_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = sparc_breakpoint_len;
+ return (const unsigned char *) &sparc_breakpoint;
+}
static int
sparc_breakpoint_at (CORE_ADDR where)
@@ -323,8 +329,7 @@ struct linux_target_ops the_low_target = {
sparc_get_pc,
/* No sparc_set_pc is needed. */
NULL,
- (const unsigned char *) sparc_breakpoint,
- sparc_breakpoint_len,
+ sparc_breakpoint_from_pc,
sparc_reinsert_addr,
0,
sparc_breakpoint_at,
diff --git a/gdb/gdbserver/linux-tic6x-low.c b/gdb/gdbserver/linux-tic6x-low.c
index a2ac3ee..86b433c 100644
--- a/gdb/gdbserver/linux-tic6x-low.c
+++ b/gdb/gdbserver/linux-tic6x-low.c
@@ -171,6 +171,14 @@ extern struct linux_target_ops the_low_target;
static int *tic6x_regmap;
static unsigned int tic6x_breakpoint;
+#define tic6x_breakpoint_len 4
+
+static const unsigned char *
+tic6x_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = tic6x_breakpoint_len;
+ return (const unsigned char *) &tic6x_breakpoint;
+}
/* Forward definition. */
static struct usrregs_info tic6x_usrregs_info;
@@ -247,8 +255,6 @@ tic6x_set_pc (struct regcache *regcache, CORE_ADDR pc)
supply_register_by_name (regcache, "PC", newpc.buf);
}
-#define tic6x_breakpoint_len 4
-
static int
tic6x_breakpoint_at (CORE_ADDR where)
{
@@ -367,8 +373,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
tic6x_get_pc,
tic6x_set_pc,
- (const unsigned char *) &tic6x_breakpoint,
- tic6x_breakpoint_len,
+ tic6x_breakpoint_from_pc,
NULL,
0,
tic6x_breakpoint_at,
diff --git a/gdb/gdbserver/linux-tile-low.c b/gdb/gdbserver/linux-tile-low.c
index 6aaea6a..802812b 100644
--- a/gdb/gdbserver/linux-tile-low.c
+++ b/gdb/gdbserver/linux-tile-low.c
@@ -88,6 +88,13 @@ tile_set_pc (struct regcache *regcache, CORE_ADDR pc)
static uint64_t tile_breakpoint = 0x400b3cae70166000ULL;
#define tile_breakpoint_len 8
+static const unsigned char *
+tile_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = tile_breakpoint_len;
+ return (const unsigned char *) &tile_breakpoint;
+}
+
static int
tile_breakpoint_at (CORE_ADDR where)
{
@@ -182,8 +189,7 @@ struct linux_target_ops the_low_target =
NULL,
tile_get_pc,
tile_set_pc,
- (const unsigned char *) &tile_breakpoint,
- tile_breakpoint_len,
+ tile_breakpoint_from_pc,
NULL,
0,
tile_breakpoint_at,
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 20d4257..699eb4d 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -3243,6 +3243,13 @@ x86_emit_ops (void)
return &i386_emit_ops;
}
+static const unsigned char *
+x86_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = x86_breakpoint_len;
+ return x86_breakpoint;
+}
+
static int
x86_supports_range_stepping (void)
{
@@ -3261,8 +3268,7 @@ struct linux_target_ops the_low_target =
NULL, /* fetch_register */
x86_get_pc,
x86_set_pc,
- x86_breakpoint,
- x86_breakpoint_len,
+ x86_breakpoint_from_pc,
NULL,
1,
x86_breakpoint_at,
diff --git a/gdb/gdbserver/linux-xtensa-low.c b/gdb/gdbserver/linux-xtensa-low.c
index debe467..1e2cf94 100644
--- a/gdb/gdbserver/linux-xtensa-low.c
+++ b/gdb/gdbserver/linux-xtensa-low.c
@@ -154,6 +154,13 @@ static struct regset_info xtensa_regsets[] = {
static const unsigned char xtensa_breakpoint[] = XTENSA_BREAKPOINT;
#define xtensa_breakpoint_len 2
+static const unsigned char *
+xtensa_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = xtensa_breakpoint_len;
+ return xtensa_breakpoint;
+}
+
static CORE_ADDR
xtensa_get_pc (struct regcache *regcache)
{
@@ -234,8 +241,7 @@ struct linux_target_ops the_low_target = {
NULL, /* fetch_register */
xtensa_get_pc,
xtensa_set_pc,
- xtensa_breakpoint,
- xtensa_breakpoint_len,
+ xtensa_breakpoint_from_pc,
NULL,
0,
xtensa_breakpoint_at,
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 7df8df3..fc2dbd7 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -438,6 +438,11 @@ struct target_ops
readlink(2). */
ssize_t (*multifs_readlink) (int pid, const char *filename,
char *buf, size_t bufsiz);
+
+ /* Return the raw breakpoint for this target based on PC. Note that the PC
+ can be NULL, the default breakpoint for the target should be returned in
+ this case. */
+ const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
};
extern struct target_ops *the_target;
diff --git a/gdb/gdbserver/win32-arm-low.c b/gdb/gdbserver/win32-arm-low.c
index d4b2c6f..1b469a3 100644
--- a/gdb/gdbserver/win32-arm-low.c
+++ b/gdb/gdbserver/win32-arm-low.c
@@ -113,6 +113,13 @@ arm_arch_setup (void)
static const unsigned long arm_wince_breakpoint = 0xe6000010;
#define arm_wince_breakpoint_len 4
+static const unsigned char *
+arm_wince_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = arm_wince_breakpoint_len;
+ return (const unsigned char *) &arm_wince_breakpoint;
+}
+
struct win32_target_ops the_low_target = {
arm_arch_setup,
sizeof (mappings) / sizeof (mappings[0]),
@@ -123,8 +130,7 @@ struct win32_target_ops the_low_target = {
arm_fetch_inferior_register,
arm_store_inferior_register,
NULL, /* single_step */
- (const unsigned char *) &arm_wince_breakpoint,
- arm_wince_breakpoint_len,
+ arm_wince_breakpoint_from_pc,
/* Watchpoint related functions. See target.h for comments. */
NULL, /* supports_z_point_type */
NULL, /* insert_point */
diff --git a/gdb/gdbserver/win32-i386-low.c b/gdb/gdbserver/win32-i386-low.c
index 7c22f05..b9d60a7 100644
--- a/gdb/gdbserver/win32-i386-low.c
+++ b/gdb/gdbserver/win32-i386-low.c
@@ -444,6 +444,13 @@ i386_store_inferior_register (struct regcache *regcache,
static const unsigned char i386_win32_breakpoint = 0xcc;
#define i386_win32_breakpoint_len 1
+static const unsigned char *
+i386_win32_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+ *len = i386_win32_breakpoint_len;
+ return (const unsigned char *) &i386_win32_breakpoint;
+}
+
static void
i386_arch_setup (void)
{
@@ -466,8 +473,7 @@ struct win32_target_ops the_low_target = {
i386_fetch_inferior_register,
i386_store_inferior_register,
i386_single_step,
- &i386_win32_breakpoint,
- i386_win32_breakpoint_len,
+ i386_win32_breakpoint_from_pc,
i386_supports_z_point_type,
i386_insert_point,
i386_remove_point,
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 6/7] Support conditional breakpoints on targets that can software single step in GDBServer.
2015-09-11 12:13 [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
` (3 preceding siblings ...)
2015-09-11 12:14 ` [PATCH 7/7] Support tracepoints and software breakpoints on ARM aarch32-linux " Antoine Tremblay
@ 2015-09-11 12:14 ` Antoine Tremblay
2015-09-11 12:14 ` [PATCH 5/7] Add support for software single step on ARM aarch32-linux " Antoine Tremblay
` (2 subsequent siblings)
7 siblings, 0 replies; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 12:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
First a bit of background :
Since this patch https://sourceware.org/ml/gdb-patches/2015-04/msg01110.html
conditional breakpoints have been disabled on targets that do not support
software single step properly.
This was due to the get_next_pcs beeing to simple to be used or broken entirely.
This in effect made the get_next_pcs callback implemented on those targets dead
code since there is no situation in which a step over will occur as there is also
no software breakpoint support on those targets.
In fact the only purpose of the code after this change is to populate the
function pointer so that can_hardware_single_step can return false since the
previous way to know if a target supported hardware single step was to check if
a software single step function was implemented.
This patch cleans this up a bit so that targets that do support software single
step properly can enable conditional breakpoints and that we do not keep dead
code. It also makes it clear that if a target can't hardware or software single
step memory breakpoints are not supported.
To do this, on linux a new low target callback supports_hardware_single_step is
implemented and is to return true if the target does support such a
feature. This way can_hardware_single_step will use that callback rather than
check if the get_next_pcs implementation is present.
This enables the bad get_next_pcs functions to be removed and to be able to
trust that if get_next_pcs is implemented it means that the target really
supports software single step and that we can use it for conditional breakpoints
for example.
Note that the hardware single step support was enabled as per the current
behavior, I did not check if tile for example really has ptrace singlestep
support but since the current implementation assumed it had I kept it that way.
No regressions, tested on ubuntu 14.04 ARMv7 and x86.
Also compilation was tested on aarch64, bfin, cris, crisv32,
m32r, mips, nios2, ppc, s390, sparc, tic6x, tile, xtensa
gdb/gdbserver/ChangeLog:
* linux-aarch64-low.c
(aarch64_supports_hardware_single_step): New function.
(struct linux_target_ops): New field supports_hardware_single_step.
* linux-arm-low.c (arm_supports_hardware_single_step): New function.
* linux-bfin-low.c (bfin_supports_hardware_single_step): Likewise.
* linux-cris-low.c (cris_get_next_pcs): Remove.
* linux-crisv32-low.c
(cris_supports_hardware_single_step): New function.
* linux-low.c (can_hardware_single_step): Use supports_hardware_single_step.
(can_software_single_step): New function.
(supports_breakpoints): Add single step support requirement.
(linux_resume_one_lwp_throw): Add error if target can't single step.
(start_step_over): Likewise.
(linux_supports_software_single_step): New function.
* linux-m32r-low.c (m32r_supports_hardware_single_step): New function.
* linux-mips-low.c (mips_get_next_pcs): Remove.
* linux-nios2-low.c (nios2_get_next_pcs): Remove.
* linux-ppc-low.c (ppc_supports_hardware_single_step): New function.
* linux-s390-low.c (s390_supports_hardware_single_step): Likewise.
* linux-sh-low.c (sh_supports_hardware_single_step): Likewise.
* linux-sparc-low.c (sparc_get_next_pcs): Remove.
* linux-tic6x-low.c (tic6x_supports_hardware_single_step): New function.
* linux-tile-low.c (tile_supports_hardware_single_step): Likewise.
* linux-x86-low.c (x86_supports_hardware_single_step): Likewise.
* linux-xtensa-low.c (xtensa_supports_hardware_single_step): Likewise.
* server.c (handle_query): Support conditional breakpoints for software
single step.
* target.c (target_can_do_software_single_step): New function.
* target.h (struct target_ops): New field target_can_do_software_single_step.
---
gdb/gdbserver/linux-aarch64-low.c | 9 +++++++
gdb/gdbserver/linux-arm-low.c | 9 +++++++
gdb/gdbserver/linux-bfin-low.c | 28 +++++++++++++++++++++
gdb/gdbserver/linux-cris-low.c | 18 +------------
gdb/gdbserver/linux-crisv32-low.c | 41 +++++++++++++++++-------------
gdb/gdbserver/linux-low.c | 53 +++++++++++++++++++++++++++++++--------
gdb/gdbserver/linux-low.h | 3 +++
gdb/gdbserver/linux-m32r-low.c | 28 +++++++++++++++++++++
gdb/gdbserver/linux-mips-low.c | 17 +------------
gdb/gdbserver/linux-nios2-low.c | 14 +----------
gdb/gdbserver/linux-ppc-low.c | 21 ++++++++++++++++
gdb/gdbserver/linux-s390-low.c | 21 ++++++++++++++++
gdb/gdbserver/linux-sh-low.c | 28 +++++++++++++++++++++
gdb/gdbserver/linux-sparc-low.c | 15 +----------
gdb/gdbserver/linux-tic6x-low.c | 28 +++++++++++++++++++++
gdb/gdbserver/linux-tile-low.c | 30 +++++++++++++++++++++-
gdb/gdbserver/linux-x86-low.c | 9 +++++++
gdb/gdbserver/linux-xtensa-low.c | 28 +++++++++++++++++++++
gdb/gdbserver/server.c | 8 ++----
gdb/gdbserver/target.c | 7 ++++++
gdb/gdbserver/target.h | 9 +++++++
21 files changed, 329 insertions(+), 95 deletions(-)
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 2956211..567ab98 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -571,6 +571,14 @@ aarch64_supports_range_stepping (void)
return 1;
}
+/* Support for hardware single step. */
+
+static int
+aarch64_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
struct linux_target_ops the_low_target =
{
aarch64_arch_setup,
@@ -603,6 +611,7 @@ struct linux_target_ops the_low_target =
NULL, /* emit_ops */
NULL, /* get_min_fast_tracepoint_insn_len */
aarch64_supports_range_stepping,
+ aarch64_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index eb98a36..a9ed91d 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -1112,6 +1112,14 @@ arm_arch_setup (void)
arm_set_byte_order ();
}
+/* Support for hardware single step. */
+
+static int
+arm_supports_hardware_single_step (void)
+{
+ return 0;
+}
+
/* Register sets without using PTRACE_GETREGSET. */
static struct regset_info arm_regsets[] = {
@@ -1190,6 +1198,7 @@ struct linux_target_ops the_low_target = {
NULL, /* emit_ops */
NULL, /* get_min_fast_tracepoint_insn_len */
NULL, /* supports_range_stepping */
+ arm_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-bfin-low.c b/gdb/gdbserver/linux-bfin-low.c
index 3032a34..18b8e43 100644
--- a/gdb/gdbserver/linux-bfin-low.c
+++ b/gdb/gdbserver/linux-bfin-low.c
@@ -103,6 +103,14 @@ bfin_arch_setup (void)
current_process ()->tdesc = tdesc_bfin;
}
+/* Support for hardware single step. */
+
+static int
+bfin_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static struct usrregs_info bfin_usrregs_info =
{
bfin_num_regs,
@@ -133,6 +141,26 @@ struct linux_target_ops the_low_target = {
NULL, /* get_next_pcs */
2,
bfin_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ bfin_supports_hardware_single_step,
};
diff --git a/gdb/gdbserver/linux-cris-low.c b/gdb/gdbserver/linux-cris-low.c
index cd1ae4d..b56827e 100644
--- a/gdb/gdbserver/linux-cris-low.c
+++ b/gdb/gdbserver/linux-cris-low.c
@@ -104,18 +104,6 @@ cris_breakpoint_at (CORE_ADDR where)
return 0;
}
-/* We only place breakpoints in empty marker functions, and thread locking
- is outside of the function. So rather than importing software single-step,
- we can just run until exit. */
-static void
-cris_get_next_pcs (struct get_next_pcs *next_pcs)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- CORE_ADDR pc;
- collect_register_by_name (regcache, "srp", &pc);
- VEC_safe_push (CORE_ADDR, next_pcs->result, pc);
-}
-
static void
cris_arch_setup (void)
{
@@ -149,13 +137,9 @@ struct linux_target_ops the_low_target = {
cris_get_pc,
cris_set_pc,
cris_breakpoint_from_pc,
- cris_get_next_pcs,
+ NULL,
0,
cris_breakpoint_at,
- 0,
- 0,
- 0,
- 0,
};
void
diff --git a/gdb/gdbserver/linux-crisv32-low.c b/gdb/gdbserver/linux-crisv32-low.c
index 05ad85a..4e9ebc1 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -100,22 +100,6 @@ cris_breakpoint_at (CORE_ADDR where)
return 0;
}
-/* We only place breakpoints in empty marker functions, and thread locking
- is outside of the function. So rather than importing software single-step,
- we can just run until exit. */
-
-/* FIXME: This function should not be needed, since we have PTRACE_SINGLESTEP
- for CRISv32. Without it, td_ta_event_getmsg in thread_db_create_event
- will fail when debugging multi-threaded applications. */
-static void
-cris_get_next_pcs (struct get_next_pcs *next_pcs)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- CORE_ADDR pc;
- collect_register_by_name (regcache, "srp", &pc);
- VEC_safe_push (CORE_ADDR, next_pcs->result, pc);
-}
-
static void
cris_write_data_breakpoint (struct regcache *regcache,
int bp, unsigned long start, unsigned long end)
@@ -386,6 +370,14 @@ cris_arch_setup (void)
current_process ()->tdesc = tdesc_crisv32;
}
+/* Support for hardware single step. */
+
+static int
+cris_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static struct regset_info cris_regsets[] = {
{ PTRACE_GETREGS, PTRACE_SETREGS, 0, cris_num_regs * 4,
GENERAL_REGS, cris_fill_gregset, cris_store_gregset },
@@ -428,7 +420,7 @@ struct linux_target_ops the_low_target = {
cris_get_pc,
cris_set_pc,
cris_breakpoint_from_pc,
- cris_get_next_pcs,
+ NULL,
0,
cris_breakpoint_at,
cris_supports_z_point_type,
@@ -436,6 +428,21 @@ struct linux_target_ops the_low_target = {
cris_remove_point,
cris_stopped_by_watchpoint,
cris_stopped_data_address,
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ cris_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 229df46..f19769d 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -272,22 +272,35 @@ static void complete_ongoing_step_over (void);
being stepped. */
ptid_t step_over_bkpt;
-/* True if the low target can hardware single-step. Such targets
- don't need a GET_NEXT_PCS callback. */
+/* True if the low target can hardware single-step. */
static int
can_hardware_single_step (void)
{
- return (the_low_target.get_next_pcs == NULL);
+ if (the_low_target.supports_hardware_single_step != NULL)
+ return the_low_target.supports_hardware_single_step ();
+ else
+ return 0;
+}
+
+/* True if the low target can software single-step. Such targets
+ implement the GET_NEXT_PCS callback. */
+
+static int
+can_software_single_step (void)
+{
+ return (the_low_target.get_next_pcs != NULL);
}
-/* True if the low target supports memory breakpoints. If so, we'll
- have a GET_PC implementation. */
+/* True if the low target supports memory breakpoints. If so, we'll have a
+ GET_PC implementation. We will also need to support hardware or software
+ single stepping. */
static int
supports_breakpoints (void)
{
- return (the_low_target.get_pc != NULL);
+ return (the_low_target.get_pc != NULL &&
+ (can_hardware_single_step () || can_software_single_step ()));
}
/* Returns true if this target can support fast tracepoints. This
@@ -4026,10 +4039,16 @@ linux_resume_one_lwp_throw (struct lwp_info *lwp,
{
step = 1;
}
- else {
- install_software_single_step_breakpoints (lwp);
- step = 0;
- }
+ 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");
+ }
}
if (proc->tdesc != NULL && the_low_target.get_pc != NULL)
@@ -4439,11 +4458,16 @@ start_step_over (struct lwp_info *lwp)
{
step = 1;
}
- else
+ 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");
+ }
current_thread = saved_thread;
@@ -5638,6 +5662,12 @@ linux_supports_hardware_single_step (void)
}
static int
+linux_supports_software_single_step (void)
+{
+ return can_software_single_step ();
+}
+
+static int
linux_stopped_by_watchpoint (void)
{
struct lwp_info *lwp = get_thread_lwp (current_thread);
@@ -7064,6 +7094,7 @@ static struct target_ops linux_target_ops = {
linux_mntns_unlink,
linux_mntns_readlink,
linux_breakpoint_from_pc,
+ linux_supports_software_single_step,
};
static void
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index 597c36d..b6f8175 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -240,6 +240,9 @@ struct linux_target_ops
/* Returns true if the low target supports range stepping. */
int (*supports_range_stepping) (void);
+
+ /* Returns true if the low target supports hardware single step. */
+ int (*supports_hardware_single_step) (void);
};
extern struct linux_target_ops the_low_target;
diff --git a/gdb/gdbserver/linux-m32r-low.c b/gdb/gdbserver/linux-m32r-low.c
index 4712a32..3767f35 100644
--- a/gdb/gdbserver/linux-m32r-low.c
+++ b/gdb/gdbserver/linux-m32r-low.c
@@ -101,6 +101,14 @@ m32r_arch_setup (void)
current_process ()->tdesc = tdesc_m32r;
}
+/* Support for hardware single step. */
+
+static int
+m32r_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static struct usrregs_info m32r_usrregs_info =
{
m32r_num_regs,
@@ -131,6 +139,26 @@ struct linux_target_ops the_low_target = {
NULL,
0,
m32r_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ m32r_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index d462f2c..e752b7e 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -274,21 +274,6 @@ mips_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
return (const unsigned char *) &mips_breakpoint;
}
-/* We only place breakpoints in empty marker functions, and thread locking
- is outside of the function. So rather than importing software single-step,
- we can just run until exit. */
-static void
-mips_get_next_pcs (struct get_next_pcs *next_pcs)
-{
- CORE_ADDR result;
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- union mips_register ra;
- collect_register_by_name (regcache, "r31", ra.buf);
- result = register_size (regcache->tdesc, 0) == 4 ? ra.reg32 : ra.reg64;
-
- VEC_safe_push (CORE_ADDR, next_pcs->result, result);
-}
-
static int
mips_breakpoint_at (CORE_ADDR where)
{
@@ -893,7 +878,7 @@ struct linux_target_ops the_low_target = {
mips_get_pc,
mips_set_pc,
mips_breakpoint_from_pc,
- mips_get_next_pcs,
+ 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 a24eb23..cb3addd 100644
--- a/gdb/gdbserver/linux-nios2-low.c
+++ b/gdb/gdbserver/linux-nios2-low.c
@@ -146,18 +146,6 @@ nios2_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
return (const unsigned char *) &nios2_breakpoint;
}
-/* Implement the get_next_pcs linux_target_ops method. */
-
-static void
-nios2_get_next_pcs (struct get_next_pcs *next_pcs)
-{
- union nios2_register ra;
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
-
- collect_register_by_name (regcache, "ra", ra.buf);
- VEC_safe_push (CORE_ADDR, next_pcs->result, ra.reg32);
-}
-
/* Implement the breakpoint_at linux_target_ops method. */
static int
@@ -280,7 +268,7 @@ struct linux_target_ops the_low_target =
nios2_get_pc,
nios2_set_pc,
nios2_breakpoint_from_pc,
- nios2_get_next_pcs,
+ NULL, /* get_next_pcs */
0,
nios2_breakpoint_at,
};
diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c
index fa87e19..4939cfa 100644
--- a/gdb/gdbserver/linux-ppc-low.c
+++ b/gdb/gdbserver/linux-ppc-low.c
@@ -641,6 +641,14 @@ ppc_store_evrregset (struct regcache *regcache, const void *buf)
supply_register_by_name (regcache, "spefscr", ®set->spefscr);
}
+/* Support for hardware single step. */
+
+static int
+ppc_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static struct regset_info ppc_regsets[] = {
/* List the extra register sets before GENERAL_REGS. That way we will
fetch them every time, but still fall back to PTRACE_PEEKUSER for the
@@ -701,6 +709,19 @@ struct linux_target_ops the_low_target = {
NULL,
ppc_collect_ptrace_register,
ppc_supply_ptrace_register,
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ ppc_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-s390-low.c b/gdb/gdbserver/linux-s390-low.c
index 44d3f26..794a621 100644
--- a/gdb/gdbserver/linux-s390-low.c
+++ b/gdb/gdbserver/linux-s390-low.c
@@ -611,6 +611,14 @@ s390_breakpoint_at (CORE_ADDR pc)
return memcmp (c, s390_breakpoint, s390_breakpoint_len) == 0;
}
+/* Support for hardware single step. */
+
+static int
+s390_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static struct usrregs_info s390_usrregs_info =
{
s390_num_regs,
@@ -687,6 +695,19 @@ struct linux_target_ops the_low_target = {
NULL,
s390_collect_ptrace_register,
s390_supply_ptrace_register,
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ s390_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-sh-low.c b/gdb/gdbserver/linux-sh-low.c
index 5c11fd7..6a210bf 100644
--- a/gdb/gdbserver/linux-sh-low.c
+++ b/gdb/gdbserver/linux-sh-low.c
@@ -98,6 +98,14 @@ sh_breakpoint_at (CORE_ADDR where)
return 0;
}
+/* Support for hardware single step. */
+
+static int
+sh_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
/* Provide only a fill function for the general register set. ps_lgetregs
will use this for NPTL support. */
@@ -159,6 +167,26 @@ struct linux_target_ops the_low_target = {
NULL,
0,
sh_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ sh_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-sparc-low.c b/gdb/gdbserver/linux-sparc-low.c
index e804281..25546e3 100644
--- a/gdb/gdbserver/linux-sparc-low.c
+++ b/gdb/gdbserver/linux-sparc-low.c
@@ -264,19 +264,6 @@ sparc_breakpoint_at (CORE_ADDR where)
return 0;
}
-/* We only place breakpoints in empty marker functions, and thread locking
- is outside of the function. So rather than importing software single-step,
- we can just run until exit. */
-static void
-sparc_get_next_pcs (struct get_next_pcs *next_pcs)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- CORE_ADDR lr;
- /* O7 is the equivalent to the 'lr' of other archs. */
- collect_register_by_name (regcache, "o7", &lr);
- VEC_safe_push (CORE_ADDR, next_pcs->result, lr);
-}
-
static void
sparc_arch_setup (void)
{
@@ -331,7 +318,7 @@ struct linux_target_ops the_low_target = {
/* No sparc_set_pc is needed. */
NULL,
sparc_breakpoint_from_pc,
- sparc_get_next_pcs,
+ NULL,
0,
sparc_breakpoint_at,
NULL, /* supports_z_point_type */
diff --git a/gdb/gdbserver/linux-tic6x-low.c b/gdb/gdbserver/linux-tic6x-low.c
index 86b433c..22bffe2 100644
--- a/gdb/gdbserver/linux-tic6x-low.c
+++ b/gdb/gdbserver/linux-tic6x-low.c
@@ -339,6 +339,14 @@ tic6x_arch_setup (void)
current_process ()->tdesc = tic6x_read_description ();
}
+/* Support for hardware single step. */
+
+static int
+tic6x_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static struct regsets_info tic6x_regsets_info =
{
tic6x_regsets, /* regsets */
@@ -377,6 +385,26 @@ struct linux_target_ops the_low_target = {
NULL,
0,
tic6x_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ tic6x_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-tile-low.c b/gdb/gdbserver/linux-tile-low.c
index 802812b..b712b81 100644
--- a/gdb/gdbserver/linux-tile-low.c
+++ b/gdb/gdbserver/linux-tile-low.c
@@ -179,6 +179,14 @@ tile_arch_setup (void)
current_process ()->tdesc = tdesc_tilegx;
}
+/* Support for hardware single step. */
+
+static int
+tile_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
struct linux_target_ops the_low_target =
{
@@ -190,9 +198,29 @@ struct linux_target_ops the_low_target =
tile_get_pc,
tile_set_pc,
tile_breakpoint_from_pc,
- NULL,
+ NULL, /* get_next_pcs */
0,
tile_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ tile_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 0731141..0c5b6a2 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -3256,6 +3256,14 @@ x86_supports_range_stepping (void)
return 1;
}
+/* Support for hardware single step. */
+
+static int
+x86_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
/* This is initialized assuming an amd64 target.
x86_arch_setup will correct it for i386 or amd64 targets. */
@@ -3295,6 +3303,7 @@ struct linux_target_ops the_low_target =
x86_emit_ops,
x86_get_min_fast_tracepoint_insn_len,
x86_supports_range_stepping,
+ x86_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-xtensa-low.c b/gdb/gdbserver/linux-xtensa-low.c
index 1e2cf94..432b1b7 100644
--- a/gdb/gdbserver/linux-xtensa-low.c
+++ b/gdb/gdbserver/linux-xtensa-low.c
@@ -227,6 +227,14 @@ xtensa_arch_setup (void)
current_process ()->tdesc = tdesc_xtensa;
}
+/* Support for hardware single step. */
+
+static int
+xtensa_supports_hardware_single_step (void)
+{
+ return 1;
+}
+
static const struct regs_info *
xtensa_regs_info (void)
{
@@ -245,6 +253,26 @@ struct linux_target_ops the_low_target = {
NULL,
0,
xtensa_breakpoint_at,
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* new_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ xtensa_supports_hardware_single_step,
};
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 5c0d83d..fb0e843 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2192,13 +2192,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+");
diff --git a/gdb/gdbserver/target.c b/gdb/gdbserver/target.c
index 17ff7a6..1ae42cc 100644
--- a/gdb/gdbserver/target.c
+++ b/gdb/gdbserver/target.c
@@ -224,3 +224,10 @@ target_can_do_hardware_single_step (void)
{
return 1;
}
+
+/* Target can do software single step. */
+int
+target_can_do_software_single_step (void)
+{
+ return 0;
+}
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index fc2dbd7..aa40c4a 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -443,6 +443,9 @@ struct target_ops
can be NULL, the default breakpoint for the target should be returned in
this case. */
const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
+
+ /* Returns true if the target can do software single step. */
+ int (*supports_software_single_step) (void);
};
extern struct target_ops *the_target;
@@ -621,6 +624,10 @@ int kill_inferior (int);
(the_target->stopped_by_hw_breakpoint ? \
(*the_target->stopped_by_hw_breakpoint) () : 0)
+#define target_supports_software_single_step() \
+ (the_target->supports_software_single_step ? \
+ (*the_target->supports_software_single_step) () : 0)
+
/* Start non-stop mode, returns 0 on success, -1 on failure. */
int start_non_stop (int nonstop);
@@ -655,4 +662,6 @@ const char *target_pid_to_str (ptid_t);
int target_can_do_hardware_single_step (void);
+int target_can_do_software_single_step (void);
+
#endif /* TARGET_H */
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 5/7] Add support for software single step on ARM aarch32-linux in GDBServer.
2015-09-11 12:13 [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux in GDBServer Antoine Tremblay
` (4 preceding siblings ...)
2015-09-11 12:14 ` [PATCH 6/7] Support conditional breakpoints on targets that can software single step " Antoine Tremblay
@ 2015-09-11 12:14 ` Antoine Tremblay
2015-09-14 11:00 ` Yao Qi
2015-09-11 12:14 ` [PATCH 4/7] Make breakpoint and breakpoint_len local variables " Antoine Tremblay
2015-09-14 10:33 ` [PATCH 0/7] Support tracepoints and software breakpoints on ARM aarch32-linux " Yao Qi
7 siblings, 1 reply; 32+ messages in thread
From: Antoine Tremblay @ 2015-09-11 12:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
This patch teaches GDBserver how to software single step on ARM
aarch32-linux by sharing the code with GDB.
There's multiple things to consider to achieve that :
- GDBServer needs to know the next PCs to put a breakpoint on for stepping.
- GDBServer needs to handle multiple addresses to put a breakpoint on.
- GDBServer needs to know what kind of breakpoint to insert since breakpoints
can differ based on the instruction set used on ARM.
- GDBServer needs to know the correct endianness to write that breakpoint
properly.
The next PC issue is resolved by sharing arm_get_next_pcs function in GDB.
Since this function will return the possible addresses of the next PC.
Since there can be multiple addresses returned by the get_next_pcs function,
GDBServer need to handle this situation. To achieve this, the target ops :
breakpoint_reinsert_addr is replaced with get_next_pcs. This function fills the
get_next_pcs struct that contains a vector of all the pcs to put a breakpoint
on. arm_software_single_step then iterates on that vector and inserts
the right breakpoint.
The type of breakpoint issue in GDBServer is handled by the
breakpoint_from_pc target ops introduced in a previous patch, this
patch adds the arm_breakpoint_from_pc implementation so that the
proper breakpoint type is returned based on the current pc and
register values.
Also, to set the right breakpoint on ARM the endianness of the program is needed
so that we write the breakpoint bytes in the right order. To achieve this the
arm_set_byte_order function is called and reads from the ELF header the
endianness of the code.
So this patch teaches GDBserver how to read the elf header for 32 and 64 bit
ELF.
In order to share code between GDB and GDBServer, some code needs to be moved
around to the common subdir, here's how the files are arranged and their
function :
get-next-pcs.h : Common code for software single stepping support.
arm-common.h/c : Common code for generic ARM support.
arm-get-next-pcs.h/c : Common code for ARM software single stepping support.
arm-linux-common.h : Common code for GNU/Linux on ARM.
Note that the arm* files could potentially be in an common/arch/ directory but
as there's only one needed now I though it would be ok in common.
A proper shared context was also needed so that we could share the code, this
context is described as this data structure (expressed as a class
hierarchy) :
struct get_next_pcs
struct arch_get_next_pcs
struct gdb|gdbserver_arch_get_next_pcs
Where arch can by replaced by arm for this patch. This structure should be
flexible enough to accomodate any arch or os that would need a get_next_pcs
context.
For ARM this context contains a series of operations and variables so that the
shared functions can be called from GDB or GDBServer.
Limitations :
GDBServer can't step over a sigreturn or rt_sigreturn syscall since this would
require the DWARF information to identify the caller PC. As such the
get_next_pcs sigreturn_return_addr operation is set to NULL in GDBServer for the
moment.
Testing :
No regressions, tested on ubuntu 14.04 ARMv7 and x86.
Also compilation was tested on aarch32, aarch64, arm, bfin, cris,
crisv32, mips, nios2, ppc, s390, sparc, x86 as those archs were modified.
gdb/ChangeLog:
* Makefile.in: Add arm-get-next-pcs.c/o arm-common.c/o.
* arm-linux-nat.c: Add includes for common/arm*.
* arm-linux-tdep.c (arm_linux_sigreturn_return_addr):
Move part of the code in arm_syscalL_next_pc.
(arm_linux_software_single_step): Refactor to use get_next_pcs context.
(arm_linux_init_abi): Change syscall_next_pc for sigreturn_return_addr.
* arm-tdep.c (thumb_instruction_changes_pc): Move to arm-get-next-pcs.c.
(thumb2_instruction_changes_pc): Likewise.
(arm_instruction_changes_pc): Likewise.
(condition_true): Move to arm-common.c.
(thumb_insn_size): Likewise.
(thumb_advance_itstate): Move to arm-get-next-pcs.c.
(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): Refactor to use get_next_pcs context.
(arm_get_next_pcs_addr_bits_remove): New function.
(arm_get_next_pcs_collect_register_unsigned): New function.
* arm-tdep.h (struct gdbarch_tdep): Change syscall_next_pc for sigreturn_return_addr.
* arm-wince-tdep.c: Add include common/arm-common.h.
* armnbsd-tdep.c: Likewise.
* common/arm-common.c: New file.
* common/arm-common.h: New file.
* common/arm-get-next-pcs.c: New file.
* common/arm-get-next-pcs.h: New file.
* common/arm-linux-common.h: New file.
* common/common-defs.h: Add MAX_REGISTER_SIZE.
* common/get-next-pcs.h: New file.
* configure.tgt: Add to arm*-*-linux* arm-common.o arm-get-next-pcs.o.
* defs.h: Move MAX_REGISTER_SIZE to common-defs.h.
gdbserver/ChangeLog:
* Makefile.in: Add common/arm-get-next-pcs.c/o,
common/int-utils.c/o, common/arm-common.c/o. common/int-utils.c/o.
* configure.srv: Add arm-get-next-pcs.o, arm-common.o, int-utils.o.
* linux-arm-low.c (arm_addr_bits_remove): New function.
(arm_breakpoint_at): Use new breakpoint byte arrays.
(get_next_pcs_collect_register_unsigned): New function.
(read_memory_unsigned_integer): New function.
(arm_breakpoint_from_pc): Likewise.
(arm_set_breakpoints): Likewise.
(arm_set_byte_order): Likewise.
(arm_gdbserver_get_next_pcs): Likewise.
(struct linux_target_ops): Change arm_reinsert_addr to
arm_gdbserver_get_next_pcs.
* linux-cris-low.c (cris_get_next_pcs): New function.
* linux-crisv32-low.c (cris_get_next_pcs): New function.
* linux-low.c (can_hardware_single_step): Change
breakpoint_reinsert_addr to get_next_pcs.
(elf_64_read_header): New function.
(elf_64_file_read_header): Likewise.
(elf_32_header_p): Likewise.
(elf_32_read_header): Likewise.
(elf_32_file_read_header): Likewise.
(elf_64_file_p): Likewise.
(install_software_single_step_breakpoints): Likewise.
(linux_resume_one_lwp_throw): Try to software single step if
we can't hardware single step.
(start_step_over): Use install_software_single_step_breakpoints.
* linux-low.h: Add elf.h include.
* linux-mips-low.c (mips_get_next_pcs): New function.
* linux-nios2-low.c (nios2_get_next_pcs): Likewise.
* linux-ppc-low.c: Adjust elf includes.
* linux-s390-low.c: Likewise.
* linux-sparc-low.c (sparc_get_next_pcs): Likewise.
* linux-x86-low.c: Adjust elf includes.
---
gdb/Makefile.in | 16 +-
gdb/arm-linux-nat.c | 3 +
gdb/arm-linux-tdep.c | 148 ++--
gdb/arm-tdep.c | 1534 +++++--------------------------------
gdb/arm-tdep.h | 61 +-
gdb/arm-wince-tdep.c | 1 +
gdb/armnbsd-tdep.c | 1 +
gdb/common/arm-common.c | 90 +++
gdb/common/arm-common.h | 64 ++
gdb/common/arm-get-next-pcs.c | 1273 ++++++++++++++++++++++++++++++
gdb/common/arm-get-next-pcs.h | 112 +++
gdb/common/arm-linux-common.h | 72 ++
gdb/common/common-defs.h | 6 +
gdb/common/get-next-pcs.h | 36 +
gdb/configure.tgt | 3 +-
gdb/defs.h | 5 -
gdb/gdbserver/Makefile.in | 13 +-
gdb/gdbserver/configure.srv | 3 +
gdb/gdbserver/linux-aarch32-low.c | 5 -
gdb/gdbserver/linux-aarch64-low.c | 6 +-
gdb/gdbserver/linux-arm-low.c | 344 +++++++--
gdb/gdbserver/linux-bfin-low.c | 2 +-
gdb/gdbserver/linux-cris-low.c | 11 +-
gdb/gdbserver/linux-crisv32-low.c | 12 +-
gdb/gdbserver/linux-low.c | 146 +++-
gdb/gdbserver/linux-low.h | 18 +-
gdb/gdbserver/linux-mips-low.c | 12 +-
gdb/gdbserver/linux-nios2-low.c | 15 +-
gdb/gdbserver/linux-ppc-low.c | 2 -
gdb/gdbserver/linux-s390-low.c | 6 +-
gdb/gdbserver/linux-sparc-low.c | 9 +-
gdb/gdbserver/linux-x86-low.c | 2 +-
32 files changed, 2415 insertions(+), 1616 deletions(-)
create mode 100644 gdb/common/arm-common.c
create mode 100644 gdb/common/arm-common.h
create mode 100644 gdb/common/arm-get-next-pcs.c
create mode 100644 gdb/common/arm-get-next-pcs.h
create mode 100644 gdb/common/arm-linux-common.h
create mode 100644 gdb/common/get-next-pcs.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index e20c5a6..ffd78a9 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-linux-tdep.o arm-symbian-tdep.o \
+ armbsd-tdep.o arm-common.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,7 +1662,9 @@ ALLDEPFILES = \
amd64-dicos-tdep.c \
amd64-linux-nat.c amd64-linux-tdep.c \
amd64-sol2-tdep.c \
- arm-linux-nat.c arm-linux-tdep.c arm-symbian-tdep.c arm-tdep.c \
+ common/arm-common.c common/arm-get-next-pcs.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 \
@@ -2266,10 +2269,19 @@ btrace-common.o: ${srcdir}/common/btrace-common.c
fileio.o: ${srcdir}/common/fileio.c
$(COMPILE) $(srcdir)/common/fileio.c
$(POSTCOMPILE)
+
+arm-get-next-pcs.o: ${srcdir}/common/arm-get-next-pcs.c
+ $(COMPILE) $(srcdir)/common/arm-get-next-pcs.c
+ $(POSTCOMPILE)
+
int-utils.o: ${srcdir}/common/int-utils.c
$(COMPILE) $(srcdir)/common/int-utils.c
$(POSTCOMPILE)
+arm-common.o: ${srcdir}/common/arm-common.c
+ $(COMPILE) $(srcdir)/common/arm-common.c
+ $(POSTCOMPILE)
+
#
# gdb/target/ dependencies
#
diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c
index a63b181..d6afafb 100644
--- a/gdb/arm-linux-nat.c
+++ b/gdb/arm-linux-nat.c
@@ -27,6 +27,9 @@
#include "observer.h"
#include "gdbthread.h"
+#include "common/arm-common.h"
+#include "common/arm-linux-common.h"
+#include "common/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 b3ad868..88c1510 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 "common/arm-common.h"
+#include "common/arm-linux-common.h"
+#include "common/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "arm-linux-tdep.h"
#include "linux-tdep.h"
@@ -54,42 +57,8 @@
#include "user-regs.h"
#include <ctype.h>
#include "elf/common.h"
-extern int arm_apcs_32;
-
-/* Under ARM GNU/Linux the traditional way of performing a breakpoint
- is to execute a particular software interrupt, rather than use a
- particular undefined instruction to provoke a trap. Upon exection
- of the software interrupt the kernel stops the inferior with a
- SIGTRAP, and wakes the debugger. */
-
-static const gdb_byte arm_linux_arm_le_breakpoint[] = { 0x01, 0x00, 0x9f, 0xef };
-
-static const gdb_byte arm_linux_arm_be_breakpoint[] = { 0xef, 0x9f, 0x00, 0x01 };
-
-/* However, the EABI syscall interface (new in Nov. 2005) does not look at
- the operand of the swi if old-ABI compatibility is disabled. Therefore,
- use an undefined instruction instead. This is supported as of kernel
- version 2.5.70 (May 2003), so should be a safe assumption for EABI
- binaries. */
-
-static const gdb_byte eabi_linux_arm_le_breakpoint[] = { 0xf0, 0x01, 0xf0, 0xe7 };
-
-static const gdb_byte eabi_linux_arm_be_breakpoint[] = { 0xe7, 0xf0, 0x01, 0xf0 };
-/* All the kernels which support Thumb support using a specific undefined
- instruction for the Thumb breakpoint. */
-
-static const gdb_byte arm_linux_thumb_be_breakpoint[] = {0xde, 0x01};
-
-static const gdb_byte arm_linux_thumb_le_breakpoint[] = {0x01, 0xde};
-
-/* Because the 16-bit Thumb breakpoint is affected by Thumb-2 IT blocks,
- we must use a length-appropriate breakpoint for 32-bit Thumb
- instructions. See also thumb_get_next_pc. */
-
-static const gdb_byte arm_linux_thumb2_be_breakpoint[] = { 0xf7, 0xf0, 0xa0, 0x00 };
-
-static const gdb_byte arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa0 };
+extern int arm_apcs_32;
/* Description of the longjmp buffer. The buffer is treated as an array of
elements of size ARM_LINUX_JB_ELEMENT_SIZE.
@@ -257,6 +226,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. */
+struct arm_get_next_pcs_ops arm_linux_get_next_pcs_ops = {
+ read_memory_unsigned_integer,
+ arm_get_next_pcs_collect_register_unsigned,
+ arm_get_next_pcs_sigreturn_return_addr,
+ arm_get_next_pcs_addr_bits_remove
+};
+
static void
arm_linux_sigtramp_cache (struct frame_info *this_frame,
struct trad_frame_cache *this_cache,
@@ -778,20 +755,19 @@ arm_linux_core_read_description (struct gdbarch *gdbarch,
return NULL;
}
-
/* Copy the value of next pc of sigreturn and rt_sigrturn into PC,
return 1. In addition, set IS_THUMB depending on whether we
will return to ARM or Thumb code. Return 0 if it is not a
rt_sigreturn/sigreturn syscall. */
static int
-arm_linux_sigreturn_return_addr (struct frame_info *frame,
+arm_linux_sigreturn_return_addr (struct frame_info* frame,
unsigned long svc_number,
CORE_ADDR *pc, int *is_thumb)
{
/* Is this a sigreturn or rt_sigreturn syscall? */
if (svc_number == 119 || svc_number == 173)
{
- if (get_frame_type (frame) == SIGTRAMP_FRAME)
+ if (get_frame_type (frame) == SIGTRAMP_FRAME)
{
ULONGEST t_bit = arm_psr_thumb_bit (frame_unwind_arch (frame));
CORE_ADDR cpsr
@@ -858,53 +834,6 @@ arm_linux_get_syscall_number (struct gdbarch *gdbarch,
return svc_number;
}
-/* When FRAME is at a syscall instruction, return the PC of the next
- instruction to be executed. */
-
-static CORE_ADDR
-arm_linux_syscall_next_pc (struct frame_info *frame)
-{
- CORE_ADDR pc = get_frame_pc (frame);
- CORE_ADDR return_addr = 0;
- int is_thumb = arm_frame_is_thumb (frame);
- ULONGEST svc_number = 0;
-
- if (is_thumb)
- {
- svc_number = get_frame_register_unsigned (frame, 7);
- return_addr = pc + 2;
- }
- else
- {
- struct gdbarch *gdbarch = get_frame_arch (frame);
- enum bfd_endian byte_order_for_code =
- gdbarch_byte_order_for_code (gdbarch);
- unsigned long this_instr =
- read_memory_unsigned_integer (pc, 4, byte_order_for_code);
-
- unsigned long svc_operand = (0x00ffffff & this_instr);
- if (svc_operand) /* OABI. */
- {
- svc_number = svc_operand - 0x900000;
- }
- else /* EABI. */
- {
- svc_number = get_frame_register_unsigned (frame, 7);
- }
-
- return_addr = pc + 4;
- }
-
- arm_linux_sigreturn_return_addr (frame, svc_number, &return_addr, &is_thumb);
-
- /* Addresses for calling Thumb functions have the bit 0 set. */
- if (is_thumb)
- return_addr |= 1;
-
- return return_addr;
-}
-
-
/* Insert a single step breakpoint at the next executed instruction. */
static int
@@ -912,22 +841,41 @@ 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);
- CORE_ADDR next_pc;
-
- if (arm_deal_with_atomic_sequence (frame))
- return 1;
-
- next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
+ struct arm_gdb_get_next_pcs next_pcs;
+ CORE_ADDR pc;
+ int 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 (next_pc > 0xffff0000)
- next_pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
+ next_pcs.base.base.result = VEC_alloc (CORE_ADDR, 1);
+ next_pcs.gdbarch = gdbarch;
+ next_pcs.frame = frame;
+ next_pcs.base.ops = &arm_linux_get_next_pcs_ops;
+ next_pcs.base.byte_order = gdbarch_byte_order (gdbarch);
+ next_pcs.base.byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
+ next_pcs.base.is_thumb = arm_frame_is_thumb (frame);
+ next_pcs.base.arm_apcs_32 = arm_apcs_32;
+ next_pcs.base.base.pc = get_frame_pc (frame);
+ next_pcs.base.arm_linux_thumb2_breakpoint =
+ gdbarch_tdep (gdbarch)->thumb2_breakpoint;
+
+ arm_get_next_pcs ((struct arm_get_next_pcs *) &next_pcs);
+
+ for (i = 0;
+ VEC_iterate (CORE_ADDR, next_pcs.base.base.result, 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);
+ VEC_free (CORE_ADDR, next_pcs.base.base.result);
return 1;
}
@@ -1462,7 +1410,7 @@ arm_linux_init_abi (struct gdbarch_info info,
set_gdbarch_stap_parse_special_token (gdbarch,
arm_stap_parse_special_token);
- tdep->syscall_next_pc = arm_linux_syscall_next_pc;
+ tdep->sigreturn_return_addr = arm_linux_sigreturn_return_addr;
/* `catch syscall' */
set_xml_syscall_file_name (gdbarch, "syscalls/arm-linux.xml");
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index bcee29c..e99b49f 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -45,6 +45,8 @@
#include "user-regs.h"
#include "observer.h"
+#include "common/arm-common.h"
+#include "common/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "gdb/sim-arm.h"
@@ -235,7 +237,13 @@ static void arm_neon_quad_write (struct gdbarch *gdbarch,
struct regcache *regcache,
int regnum, const gdb_byte *buf);
-static int thumb_insn_size (unsigned short inst1);
+/* Operation function pointers for get_next_pcs. */
+struct arm_get_next_pcs_ops arm_get_next_pcs_ops = {
+ read_memory_unsigned_integer,
+ arm_get_next_pcs_collect_register_unsigned,
+ arm_get_next_pcs_sigreturn_return_addr,
+ arm_get_next_pcs_addr_bits_remove
+};
struct arm_prologue_cache
{
@@ -267,14 +275,7 @@ static CORE_ADDR arm_analyze_prologue (struct gdbarch *gdbarch,
#define DISPLACED_STEPPING_ARCH_VERSION 5
-/* 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)
-#define MAKE_THUMB_ADDR(addr) ((addr) | 1)
-#define UNMAKE_THUMB_ADDR(addr) ((addr) & ~1)
-
/* Set to true if the 32-bit mode is in use. */
-
int arm_apcs_32 = 1;
/* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode. */
@@ -513,15 +514,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. */
@@ -561,128 +553,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. */
@@ -1510,98 +1380,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. */
@@ -4256,1158 +4034,120 @@ 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;
-}
+/* 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. */
-static unsigned long
-shifted_reg_val (struct frame_info *frame, unsigned long inst, int carry,
- unsigned long pc_val, unsigned long status_reg)
+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
- : get_frame_register_unsigned (frame, 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))
- : get_frame_register_unsigned (frame, 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 = 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;
}
-/* Return number of 1-bits in VAL. */
+/* 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
-static int
-bitcount (unsigned long val)
+/* 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)
{
- int nbits;
- for (nbits = 0; val != 0; nbits++)
- val &= val - 1; /* Delete rightmost 1-bit in val. */
- return nbits;
-}
+ 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;
-/* Return the size in bytes of the complete Thumb instruction whose
- first halfword is INST1. */
+ /* If we are using BKPT breakpoints, none of this is necessary. */
+ if (gdbarch_tdep (gdbarch)->thumb2_breakpoint == NULL)
+ return bpaddr;
-static int
-thumb_insn_size (unsigned short inst1)
-{
- if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
- return 4;
- else
- return 2;
-}
+ /* ARM mode does not have this problem. */
+ if (!arm_pc_is_thumb (gdbarch, bpaddr))
+ return bpaddr;
-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);
+ /* 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 we have finished the IT block, clear the state. */
- if ((itstate & 0x0f) == 0)
- itstate = 0;
+ bpaddr = gdbarch_addr_bits_remove (gdbarch, bpaddr);
- return itstate;
-}
+ if (find_pc_partial_function (bpaddr, NULL, &func_start, NULL)
+ && func_start > boundary)
+ boundary = func_start;
-/* 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. */
+ /* 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;
-static CORE_ADDR
-thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
-{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- unsigned long pc_val = ((unsigned long) pc) + 4; /* PC after prefetch */
- unsigned short inst1;
- CORE_ADDR nextpc = pc + 2; /* Default is next instruction. */
- unsigned long offset;
- ULONGEST status, 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 = get_frame_register_unsigned (frame, 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)
+ buf = 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)
{
- /* 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);
-
- 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 & 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);
-
- return MAKE_THUMB_ADDR (pc);
- }
+ any = 1;
+ break;
}
}
- else if (itstate & 0x0f)
+ if (any == 0)
{
- /* 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. */
- return MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1));
-
- /* 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 = get_frame_register_unsigned (frame, ARM_SP_REGNUM);
- nextpc = 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 */
- {
- struct gdbarch_tdep *tdep;
- tdep = gdbarch_tdep (gdbarch);
-
- if (tdep->syscall_next_pc != NULL)
- nextpc = tdep->syscall_next_pc (frame);
-
- }
- 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 = get_frame_register_unsigned (frame, 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 = get_frame_register_unsigned (frame, rn);
- nextpc = get_frame_memory_unsigned (frame, addr + offset, 4);
- }
- }
- else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
- {
- /* MOV PC or MOVS PC. */
- nextpc = get_frame_register_unsigned (frame, 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 = get_frame_register_unsigned (frame, 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 += get_frame_register_unsigned (frame, rm) << shift;
- }
- else
- /* Reserved. */
- load_pc = 0;
-
- if (load_pc)
- nextpc = get_frame_memory_unsigned (frame, base, 4);
- }
- 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 = get_frame_register_unsigned (frame, tbl_reg);
-
- offset = get_frame_register_unsigned (frame, bits (inst2, 0, 3));
- length = 2 * get_frame_memory_unsigned (frame, table + offset, 1);
- 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 = get_frame_register_unsigned (frame, tbl_reg);
-
- offset = 2 * get_frame_register_unsigned (frame, bits (inst2, 0, 3));
- length = 2 * get_frame_memory_unsigned (frame, table + offset, 2);
- 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 = get_frame_register_unsigned (frame, 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 = 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 = get_frame_register_unsigned (frame, 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 frame_info *frame, CORE_ADDR pc)
-{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- unsigned long pc_val;
- unsigned long 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 = get_frame_register_unsigned (frame, 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)
- : get_frame_register_unsigned (frame, 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)
- : get_frame_register_unsigned (frame, 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 (frame, 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)
- : get_frame_register_unsigned (frame, 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 (frame, 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
- = get_frame_register_unsigned (frame,
- 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 (frame);
-
- }
- 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 frame_info *frame, CORE_ADDR pc)
-{
- CORE_ADDR nextpc;
-
- if (arm_frame_is_thumb (frame))
- nextpc = thumb_get_next_pc_raw (frame, pc);
- else
- nextpc = arm_get_next_pc_raw (frame, 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 frame_info *frame)
-{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- CORE_ADDR pc = get_frame_pc (frame);
- 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 = get_frame_register_unsigned (frame, 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 frame_info *frame)
-{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- CORE_ADDR pc = get_frame_pc (frame);
- 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 frame_info *frame)
-{
- if (arm_frame_is_thumb (frame))
- return thumb_deal_with_atomic_sequence_raw (frame);
- else
- return arm_deal_with_atomic_sequence_raw (frame);
-}
-
-/* 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 gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- CORE_ADDR next_pc;
-
- if (arm_deal_with_atomic_sequence (frame))
- return 1;
-
- next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
- 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 = 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 = 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);
- return bpaddr;
+ xfree (buf);
+ return bpaddr;
}
/* OK, the code bytes before this instruction contain at least one
@@ -7324,6 +6064,86 @@ thumb2_copy_block_xfer (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
return 0;
}
+/* 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 *arm_next_pcs,
+ CORE_ADDR val)
+{
+ struct arm_gdb_get_next_pcs* next_pcs =
+ (struct arm_gdb_get_next_pcs*) arm_next_pcs;
+
+ return gdbarch_addr_bits_remove (next_pcs->gdbarch, val);
+}
+
+/* Wrapper over gdbarch_addr_bits_remove for use in arm_get_next_pcs. */
+
+ULONGEST
+arm_get_next_pcs_collect_register_unsigned (struct get_next_pcs *self, int n)
+{
+ struct arm_gdb_get_next_pcs* next_pcs =
+ (struct arm_gdb_get_next_pcs*) self;
+
+ return get_frame_register_unsigned (next_pcs->frame, n);
+}
+
+/* Wrapper over sigreturn_return_addr for use in get_next_pcs. */
+int
+arm_get_next_pcs_sigreturn_return_addr (struct arm_get_next_pcs *arm_next_pcs,
+ unsigned long svc_number,
+ CORE_ADDR *pc, int *is_thumb)
+{
+ struct arm_gdb_get_next_pcs* next_pcs =
+ (struct arm_gdb_get_next_pcs*) arm_next_pcs;
+ struct gdbarch_tdep *tdep;
+
+ tdep = gdbarch_tdep (next_pcs->gdbarch);
+ if (tdep->sigreturn_return_addr != NULL)
+ return tdep->sigreturn_return_addr (next_pcs->frame, svc_number,
+ pc, is_thumb);
+ return 0;
+}
+
+/* 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 gdbarch *gdbarch = get_frame_arch (frame);
+ struct address_space *aspace = get_frame_address_space (frame);
+ struct arm_gdb_get_next_pcs next_pcs;
+ CORE_ADDR pc;
+ int i;
+
+ next_pcs.base.base.result = VEC_alloc (CORE_ADDR, 1);
+ next_pcs.gdbarch = gdbarch;
+ next_pcs.frame = frame;
+ next_pcs.base.ops = &arm_get_next_pcs_ops;
+ next_pcs.base.byte_order = gdbarch_byte_order (gdbarch);
+ next_pcs.base.byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
+ next_pcs.base.is_thumb = arm_frame_is_thumb (frame);
+ next_pcs.base.arm_apcs_32 = arm_apcs_32;
+ next_pcs.base.base.pc = get_frame_pc (frame);
+ next_pcs.base.arm_linux_thumb2_breakpoint =
+ gdbarch_tdep (gdbarch)->thumb2_breakpoint;
+
+ arm_get_next_pcs ((struct arm_get_next_pcs*)&next_pcs);
+
+ for (i = 0;
+ VEC_iterate (CORE_ADDR, next_pcs.base.base.result, i, pc);
+ ++i)
+ {
+ arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
+ }
+
+ VEC_free (CORE_ADDR, next_pcs.base.base.result);
+
+ 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 3e06f79..bda6a06 100644
--- a/gdb/arm-tdep.h
+++ b/gdb/arm-tdep.h
@@ -23,12 +23,12 @@
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"
-/* 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 +50,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
@@ -164,9 +138,10 @@ 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
- instruction. */
- CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
+ /* Copy the expected next PC after a sigreturn/rt_sigreturn syscall */
+ int (*sigreturn_return_addr) (struct frame_info* frame,
+ unsigned long svc_number,
+ CORE_ADDR *pc, int *is_thumb);
/* syscall record. */
int (*arm_syscall_record) (struct regcache *regcache, unsigned long svc_number);
@@ -279,11 +254,27 @@ 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);
+
+/* 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
+ *arm_next_pcs, CORE_ADDR val);
+
+/* Wrapper over get_frame_register_unsigned for use in arm_get_next_pcs. */
+ULONGEST arm_get_next_pcs_collect_register_unsigned (struct get_next_pcs *self,
+ int n);
+
+/* Wrapper over sigreturn_return_addr for use in get_next_pcs. */
+int arm_get_next_pcs_sigreturn_return_addr (struct arm_get_next_pcs
+ *arm_next_pcs,
+ unsigned long svc_number,
+ CORE_ADDR *pc,
+ int *is_thumb);
+
+int arm_software_single_step (struct frame_info *frame);
+
void arm_insert_single_step_breakpoint (struct gdbarch *,
struct address_space *, CORE_ADDR);
-int arm_deal_with_atomic_sequence (struct frame_info *);
-int arm_software_single_step (struct frame_info *);
+
int arm_frame_is_thumb (struct frame_info *frame);
extern struct displaced_step_closure *
diff --git a/gdb/arm-wince-tdep.c b/gdb/arm-wince-tdep.c
index 72295ba..206c132 100644
--- a/gdb/arm-wince-tdep.c
+++ b/gdb/arm-wince-tdep.c
@@ -24,6 +24,7 @@
#include "target.h"
#include "frame.h"
+#include "common/arm-common.h"
#include "arm-tdep.h"
#include "windows-tdep.h"
diff --git a/gdb/armnbsd-tdep.c b/gdb/armnbsd-tdep.c
index 4c128c2..774687b 100644
--- a/gdb/armnbsd-tdep.c
+++ b/gdb/armnbsd-tdep.c
@@ -20,6 +20,7 @@
#include "defs.h"
#include "osabi.h"
+#include "common/arm-common.h"
#include "arm-tdep.h"
#include "solib-svr4.h"
diff --git a/gdb/common/arm-common.c b/gdb/common/arm-common.c
new file mode 100644
index 0000000..87a2c6c
--- /dev/null
+++ b/gdb/common/arm-common.c
@@ -0,0 +1,90 @@
+/* Common code for generic ARM support.
+
+ 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 "arm-common.h"
+
+/* Return the size in bytes of the complete Thumb instruction whose
+ first halfword is INST1. */
+
+int
+thumb_insn_size (unsigned short inst1)
+{
+ if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
+ return 4;
+ else
+ return 2;
+}
+
+
+/* Return number of 1-bits in VAL. */
+
+int
+bitcount (unsigned long val)
+{
+ int nbits;
+ for (nbits = 0; val != 0; nbits++)
+ val &= val - 1; /* Delete rightmost 1-bit in val. */
+ return nbits;
+}
+
+/* Returns true of the condition evaluates to true. */
+
+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/common/arm-common.h b/gdb/common/arm-common.h
new file mode 100644
index 0000000..21a3b71
--- /dev/null
+++ b/gdb/common/arm-common.h
@@ -0,0 +1,64 @@
+/* Common code for generic ARM support.
+
+ 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/>. */
+
+#ifndef ARM_COMMON_H
+#define ARM_COMMON_H 1
+
+/* 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
+
+
+/* Return the size in bytes of the complete Thumb instruction whose
+ first halfword is INST1. */
+int thumb_insn_size (unsigned short inst1);
+
+/* Returns true of 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 /* ARM_COMMON_H */
diff --git a/gdb/common/arm-get-next-pcs.c b/gdb/common/arm-get-next-pcs.c
new file mode 100644
index 0000000..59966dc
--- /dev/null
+++ b/gdb/common/arm-get-next-pcs.c
@@ -0,0 +1,1273 @@
+/* Common code for ARM software single stepping support.
+
+ 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 "vec.h"
+
+#include "arm-common.h"
+#include "arm-get-next-pcs.h"
+
+enum arm_regnum {
+ ARM_SP_REGNUM = 13, /* Contains address of top of stack */
+ ARM_LR_REGNUM = 14, /* address to return to from a function call */
+ ARM_PC_REGNUM = 15, /* Contains program counter */
+ ARM_PS_REGNUM = 25
+};
+
+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;
+}
+
+/* Return 1 if THIS_INSTR might change control flow, 0 otherwise. */
+
+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 16-bit Thumb instruction INST might change
+ control flow, 0 otherwise. */
+
+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. */
+
+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;
+}
+
+/* When PC is at a syscall instruction, return the PC of the next
+ instruction to be executed. */
+
+CORE_ADDR
+arm_syscall_next_pc (struct arm_get_next_pcs* next_pcs)
+{
+ CORE_ADDR pc = next_pcs->base.pc;
+ CORE_ADDR return_addr = 0;
+ int is_thumb = next_pcs->is_thumb;
+ ULONGEST svc_number = 0;
+
+ if (is_thumb)
+ {
+ svc_number = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 7);
+ return_addr = pc + 2;
+ }
+ else
+ {
+ unsigned long this_instr = next_pcs->ops->read_memory_unsigned_integer
+ ((CORE_ADDR) pc, 4, next_pcs->byte_order_for_code);
+
+ unsigned long svc_operand = (0x00ffffff & this_instr);
+ if (svc_operand) /* OABI. */
+ {
+ svc_number = svc_operand - 0x900000;
+ }
+ else /* EABI. */
+ {
+ svc_number = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 7);
+ }
+
+ return_addr = pc + 4;
+ }
+
+ /* FIXME add support on gdbserver */
+ if (next_pcs->ops->sigreturn_return_addr != NULL)
+ next_pcs->ops->sigreturn_return_addr (next_pcs, svc_number,
+ &return_addr, &is_thumb);
+
+ /* Addresses for calling Thumb functions have the bit 0 set. */
+ if (is_thumb)
+ return_addr |= 1;
+
+ return return_addr;
+}
+
+
+/* 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* next_pcs)
+{
+ enum bfd_endian byte_order_for_code = next_pcs->byte_order_for_code;
+ CORE_ADDR pc = next_pcs->base.pc;
+ 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 = next_pcs->ops->collect_register_unsigned
+ (&next_pcs->base, 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 = next_pcs->ops->read_memory_unsigned_integer
+ (loc, 2, byte_order_for_code);
+ loc += 2;
+ if (thumb_insn_size (insn1) != 4)
+ return 0;
+
+ insn2 = next_pcs->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 = next_pcs->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 = next_pcs->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->base.result,
+ MAKE_THUMB_ADDR (breaks[index]));
+ }
+
+ return 1;
+}
+
+static int
+arm_deal_with_atomic_sequence_raw (struct arm_get_next_pcs* next_pcs)
+{
+ enum bfd_endian byte_order_for_code = next_pcs->byte_order_for_code;
+ CORE_ADDR pc = next_pcs->base.pc;
+ 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 = next_pcs->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 = next_pcs->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->base.result,
+ breaks[index]);
+ }
+
+ return 1;
+}
+
+/* Find the next possible PCs after the current instruction executes. */
+
+void
+arm_get_next_pcs (struct arm_get_next_pcs *next_pcs)
+{
+ if (next_pcs->is_thumb) {
+ if (!thumb_deal_with_atomic_sequence_raw (next_pcs))
+ thumb_get_next_pcs_raw (next_pcs);
+ }
+ else {
+ if (!arm_deal_with_atomic_sequence_raw (next_pcs))
+ arm_get_next_pcs_raw (next_pcs);
+ }
+}
+
+static unsigned long
+shifted_reg_val (struct arm_get_next_pcs* next_pcs, 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
+ : next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, rs)) & 0xFF;
+ }
+ else
+ shift = bits (inst, 7, 11);
+
+ res = (rm == ARM_PC_REGNUM
+ ? (pc_val + (bit (inst, 4) ? 12 : 8))
+ : next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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;
+}
+
+/* Find the next possible PCs after the current instruction executes. */
+
+void
+thumb_get_next_pcs_raw (struct arm_get_next_pcs* next_pcs)
+{
+ enum bfd_endian byte_order = next_pcs->byte_order;
+ enum bfd_endian byte_order_for_code = next_pcs->byte_order_for_code;
+ CORE_ADDR pc = next_pcs->base.pc;
+ 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 =
+ next_pcs->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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 (next_pcs->arm_linux_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 = next_pcs->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->base.result,
+ MAKE_THUMB_ADDR (pc));
+ return;
+ }
+ 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 = next_pcs->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->base.result,
+ MAKE_THUMB_ADDR (pc));
+ return;
+ }
+ 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->base.result,
+ 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 = next_pcs->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->base.result,
+ MAKE_THUMB_ADDR (pc));
+
+ return;
+ }
+ }
+ }
+ 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->base.result,
+ MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1)));
+
+ return;
+
+ /* 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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, ARM_SP_REGNUM);
+
+ nextpc = next_pcs->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 = arm_syscall_next_pc (next_pcs);
+ }
+ 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 = next_pcs->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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, rn);
+ nextpc = next_pcs->ops->read_memory_unsigned_integer
+ (addr + offset, 4, byte_order);
+ }
+ }
+ else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
+ {
+ /* MOV PC or MOVS PC. */
+ nextpc = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 += next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, rm) << shift;
+ }
+ else
+ /* Reserved. */
+ load_pc = 0;
+
+ if (load_pc)
+ nextpc = next_pcs->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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, tbl_reg);
+
+ offset = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs,bits (inst2, 0, 3));
+ length = 2 * next_pcs->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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, tbl_reg);
+
+ offset = 2 * next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, bits (inst2, 0, 3));
+ length = 2 * next_pcs->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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, bits (inst1, 3, 6));
+ }
+ else if ((inst1 & 0xff87) == 0x4687) /* mov pc, REG */
+ {
+ if (bits (inst1, 3, 6) == 0x0f)
+ nextpc = pc_val;
+ else
+ nextpc = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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->base.result, nextpc);
+ return;
+}
+
+
+/* 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. */
+
+void
+arm_get_next_pcs_raw (struct arm_get_next_pcs *next_pcs)
+{
+ enum bfd_endian byte_order = next_pcs->byte_order;
+ CORE_ADDR pc = next_pcs->base.pc;
+ unsigned long pc_val;
+ unsigned long this_instr = 0;
+ unsigned long status;
+ CORE_ADDR nextpc;
+
+ pc_val = (unsigned long) pc;
+ this_instr =
+ next_pcs->ops->read_memory_unsigned_integer (pc, 4, byte_order);
+
+ status = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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)
+ : next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, rn));
+
+ VEC_safe_push (CORE_ADDR, next_pcs->base.result, nextpc);
+ return;
+ }
+
+ /* Multiply into PC. */
+ c = (status & FLAG_C) ? 1 : 0;
+ rn = bits (this_instr, 16, 19);
+ operand1 = ((rn == ARM_PC_REGNUM)
+ ? (pc_val + 8)
+ : next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 (next_pcs, 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 (!next_pcs->arm_apcs_32)
+ nextpc = next_pcs->ops->addr_bits_remove (next_pcs, 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)
+ : next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs, 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 (next_pcs, this_instr, c,
+ pc_val, status)
+ : bits (this_instr, 0, 11));
+
+ if (bit (this_instr, 23))
+ base += offset;
+ else
+ base -= offset;
+ }
+ nextpc =
+ (CORE_ADDR) next_pcs->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
+ = next_pcs->ops->collect_register_unsigned
+ ((struct get_next_pcs*) next_pcs,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) next_pcs->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 = arm_syscall_next_pc (next_pcs);
+ }
+ break;
+
+ default:
+ error (_("Bad bit-field extraction\n"));
+ return;
+ }
+ }
+
+ VEC_safe_push (CORE_ADDR, next_pcs->base.result, nextpc);
+ return;
+}
+
diff --git a/gdb/common/arm-get-next-pcs.h b/gdb/common/arm-get-next-pcs.h
new file mode 100644
index 0000000..dcd06d9
--- /dev/null
+++ b/gdb/common/arm-get-next-pcs.h
@@ -0,0 +1,112 @@
+/* Common code for ARM software single stepping support.
+
+ 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/>. */
+
+#ifndef ARM_GET_NEXT_PCS_H
+#define ARM_GET_NEXT_PCS_H 1
+
+#include "get-next-pcs.h"
+
+/* 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)
+#define MAKE_THUMB_ADDR(addr) ((addr) | 1)
+#define UNMAKE_THUMB_ADDR(addr) ((addr) & ~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,
+ enum bfd_endian byte_order);
+ ULONGEST (*collect_register_unsigned) (struct get_next_pcs* self, int n);
+ int (*sigreturn_return_addr) (struct arm_get_next_pcs* next_pcs,
+ unsigned long svc_number,
+ CORE_ADDR *pc, int *is_thumb);
+ CORE_ADDR (*addr_bits_remove) (struct arm_get_next_pcs *next_pcs,
+ CORE_ADDR val);
+};
+
+/* Context for a get_next_pcs call on ARM. */
+struct arm_get_next_pcs
+{
+ struct get_next_pcs base;
+ struct arm_get_next_pcs_ops *ops;
+ enum bfd_endian byte_order;
+ enum bfd_endian byte_order_for_code;
+ int is_thumb;
+ int arm_apcs_32;
+ const gdb_byte *arm_linux_thumb2_breakpoint;
+};
+
+/* Context for a get_next_pcs call on ARM in GDB. */
+struct arm_gdb_get_next_pcs
+{
+ struct arm_get_next_pcs base;
+ struct frame_info *frame;
+ struct gdbarch *gdbarch;
+};
+
+/* Context for a get_next_pcs call on ARM in GDBServer. */
+struct arm_gdbserver_get_next_pcs
+{
+ struct arm_get_next_pcs base;
+};
+
+/* Find the next possible PCs after the current instruction executes. */
+void arm_get_next_pcs (struct arm_get_next_pcs *next_pcs);
+
+/* Find the next possible PCs for thumb mode. */
+void thumb_get_next_pcs_raw (struct arm_get_next_pcs *next_pcs);
+
+/* Find the next possible PCs for arm mode. */
+void arm_get_next_pcs_raw (struct arm_get_next_pcs *next_pcs);
+
+/* When PC is at a syscall instruction, return the PC of the next
+ instruction to be executed. */
+CORE_ADDR arm_syscall_next_pc (struct arm_get_next_pcs* next_pcs);
+
+/* Checks for an atomic sequence of instructions and add the end of the
+ sequence to the next_pcs list. */
+int arm_deal_with_atomic_sequence (struct arm_get_next_pcs *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/common/arm-linux-common.h b/gdb/common/arm-linux-common.h
new file mode 100644
index 0000000..c3f0ba2
--- /dev/null
+++ b/gdb/common/arm-linux-common.h
@@ -0,0 +1,72 @@
+/* Common code for GNU/Linux on ARM.
+
+ 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/>. */
+
+
+#ifndef ARM_LINUX_COMMON_H
+#define ARM_LINUX_COMMON_H 1
+
+/* Under ARM GNU/Linux the traditional way of performing a breakpoint
+ is to execute a particular software interrupt, rather than use a
+ particular undefined instruction to provoke a trap. Upon exection
+ of the software interrupt the kernel stops the inferior with a
+ SIGTRAP, and wakes the debugger. */
+
+static const gdb_byte arm_linux_arm_le_breakpoint[] =
+ { 0x01, 0x00, 0x9f, 0xef };
+
+static const gdb_byte arm_linux_arm_be_breakpoint[] =
+ { 0xef, 0x9f, 0x00, 0x01 };
+
+#define arm_linux_arm_breakpoint_size 4
+
+/* However, the EABI syscall interface (new in Nov. 2005) does not look at
+ the operand of the swi if old-ABI compatibility is disabled. Therefore,
+ use an undefined instruction instead. This is supported as of kernel
+ version 2.5.70 (May 2003), so should be a safe assumption for EABI
+ binaries. */
+
+static const gdb_byte eabi_linux_arm_le_breakpoint[] =
+ { 0xf0, 0x01, 0xf0, 0xe7 };
+
+static const gdb_byte eabi_linux_arm_be_breakpoint[] =
+ { 0xe7, 0xf0, 0x01, 0xf0 };
+
+/* All the kernels which support Thumb support using a specific undefined
+ instruction for the Thumb breakpoint. */
+
+static const gdb_byte arm_linux_thumb_be_breakpoint[] = {0xde, 0x01};
+
+static const gdb_byte arm_linux_thumb_le_breakpoint[] = {0x01, 0xde};
+
+#define arm_linux_thumb_breakpoint_size 2
+
+/* Because the 16-bit Thumb breakpoint is affected by Thumb-2 IT blocks,
+ we must use a length-appropriate breakpoint for 32-bit Thumb
+ instructions. See also thumb_get_next_pc. */
+
+static const gdb_byte arm_linux_thumb2_be_breakpoint[] =
+ { 0xf7, 0xf0, 0xa0, 0x00 };
+
+static const gdb_byte arm_linux_thumb2_le_breakpoint[] =
+ { 0xf0, 0xf7, 0x00, 0xa0 };
+
+#define arm_linux_thumb2_breakpoint_size 4
+
+
+#endif /* ARM_LINUX_COMMON_H */
diff --git a/gdb/common/common-defs.h b/gdb/common/common-defs.h
index cb79234..9fa2d68 100644
--- a/gdb/common/common-defs.h
+++ b/gdb/common/common-defs.h
@@ -61,4 +61,10 @@
# define EXTERN_C_POP
#endif
+/* * Maximum size of a register. Something small, but large enough for
+ all known ISAs. If it turns out to be too small, make it bigger. */
+
+enum { MAX_REGISTER_SIZE = 64 };
+
+
#endif /* COMMON_DEFS_H */
diff --git a/gdb/common/get-next-pcs.h b/gdb/common/get-next-pcs.h
new file mode 100644
index 0000000..cf59aca
--- /dev/null
+++ b/gdb/common/get-next-pcs.h
@@ -0,0 +1,36 @@
+/* Common code for software single stepping support.
+
+ 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/>. */
+
+#ifndef GET_NEXT_PCS_H
+#define GET_NEXT_PCS_H 1
+
+#if !defined (SYMTAB_H)
+DEF_VEC_I(CORE_ADDR);
+#endif
+
+struct get_next_pcs
+{
+ /* Resulting vector of possible next addresses. */
+ VEC (CORE_ADDR) *result;
+ /* Base PC from which to get the next pcs. */
+ CORE_ADDR pc;
+ struct regcache* regcache;
+};
+
+#endif /* GET_NEXT_PCS_H */
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index c42b4df..a2265f1 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -89,7 +89,8 @@ arm*-wince-pe | arm*-*-mingw32ce*)
;;
arm*-*-linux*)
# Target: ARM based machine running GNU/Linux
- gdb_target_obs="arm-tdep.o arm-linux-tdep.o glibc-tdep.o \
+ gdb_target_obs="arm-common.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
;;
diff --git a/gdb/defs.h b/gdb/defs.h
index e292977..5a6d032 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -589,11 +589,6 @@ extern double atof (const char *); /* X3.159-1989 4.10.1.1 */
/* Dynamic target-system-dependent parameters for GDB. */
#include "gdbarch.h"
-/* * Maximum size of a register. Something small, but large enough for
- all known ISAs. If it turns out to be too small, make it bigger. */
-
-enum { MAX_REGISTER_SIZE = 64 };
-
/* In findvar.c. */
extern CORE_ADDR extract_typed_address (const gdb_byte *buf,
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index b715a32..85d3915 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -180,7 +180,9 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
$(srcdir)/common/common-debug.c $(srcdir)/common/cleanups.c \
$(srcdir)/common/common-exceptions.c $(srcdir)/symbol.c \
$(srcdir)/common/btrace-common.c \
- $(srcdir)/common/fileio.c $(srcdir)/nat/linux-namespaces.c
+ $(srcdir)/common/fileio.c $(srcdir)/nat/linux-namespaces.c \
+ $(srcdir)/common/arm-get-next-pcs.c $(srcdir)/common/int-utils.c \
+ $(srcdir)/common/arm-common.c
DEPFILES = @GDBSERVER_DEPFILES@
@@ -576,6 +578,15 @@ waitstatus.o: ../target/waitstatus.c
fileio.o: ../common/fileio.c
$(COMPILE) $<
$(POSTCOMPILE)
+arm-get-next-pcs.o: ../common/arm-get-next-pcs.c
+ $(COMPILE) $<
+ $(POSTCOMPILE)
+int-utils.o: ../common/int-utils.c
+ $(COMPILE) $<
+ $(POSTCOMPILE)
+arm-common.o: ../common/arm-common.c
+ $(COMPILE) $<
+ $(POSTCOMPILE)
# Native object files rules from ../nat
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index aa232f8..8da37e2 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -68,6 +68,9 @@ case "${target}" in
srv_regobj="${srv_regobj} arm-with-neon.o"
srv_tgtobj="$srv_linux_obj linux-arm-low.o"
srv_tgtobj="$srv_tgtobj linux-aarch32-low.o"
+ srv_tgtobj="${srv_tgtobj} arm-get-next-pcs.o"
+ srv_tgtobj="${srv_tgtobj} arm-common.o"
+ srv_tgtobj="${srv_tgtobj} int-utils.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-aarch32-low.c b/gdb/gdbserver/linux-aarch32-low.c
index 5876b13..026eafc 100644
--- a/gdb/gdbserver/linux-aarch32-low.c
+++ b/gdb/gdbserver/linux-aarch32-low.c
@@ -21,11 +21,6 @@
#include "linux-aarch32-low.h"
#include <sys/ptrace.h>
-/* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
- On Bionic elf.h and linux/elf.h have conflicting definitions. */
-#ifndef ELFMAG0
-#include <elf.h>
-#endif
/* Some older versions of GNU/Linux and Android do not define
the following macros. */
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 0b2c5f1..2956211 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -24,7 +24,11 @@
#include "nat/aarch64-linux.h"
#include "nat/aarch64-linux-hw-point.h"
#include "linux-aarch32-low.h"
+/* Don't include elf/common.h if linux/elf.h got included by
+ linux-low.h or gdb_proc_service.h. */
+#ifndef ELFMAG0
#include "elf/common.h"
+#endif
#include <signal.h>
#include <sys/user.h>
@@ -577,7 +581,7 @@ struct linux_target_ops the_low_target =
aarch64_get_pc,
aarch64_set_pc,
aarch64_breakpoint_from_pc,
- 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 367c704..eb98a36 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -22,14 +22,14 @@
#include "linux-aarch32-low.h"
#include <sys/uio.h>
-/* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
- On Bionic elf.h and linux/elf.h have conflicting definitions. */
-#ifndef ELFMAG0
-#include <elf.h>
-#endif
#include "nat/gdb_ptrace.h"
#include <signal.h>
+#include "common/int-utils.h"
+#include "common/arm-common.h"
+#include "common/arm-linux-common.h"
+#include "common/arm-get-next-pcs.h"
+
/* Defined in auto-generated files. */
void init_registers_arm (void);
extern const struct target_desc *tdesc_arm;
@@ -97,6 +97,13 @@ struct arm_linux_hw_breakpoint
#define MAX_BPTS 32
#define MAX_WPTS 32
+/* Only arm 32-bit mode is supported for now */
+int arm_apcs_32 = 1;
+
+/* M-Profile is not supported at the moment this is left
+ for future use. */
+int arm_is_m = 0;
+
/* Per-process arch-specific data we want to keep. */
struct arch_process_info
{
@@ -136,6 +143,27 @@ static int arm_regmap[] = {
64
};
+/* Forward declarations needed for get_next_pcs ops. */
+static ULONGEST
+read_memory_unsigned_integer (CORE_ADDR memaddr, int len,
+ enum bfd_endian byte_order);
+
+static ULONGEST
+get_next_pcs_collect_register_unsigned (struct get_next_pcs* self, int n);
+
+static CORE_ADDR
+arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
+ CORE_ADDR val);
+
+/* get_next_pcs operations. */
+struct arm_get_next_pcs_ops get_next_pcs_ops = {
+ read_memory_unsigned_integer,
+ get_next_pcs_collect_register_unsigned,
+ NULL,
+ arm_get_next_pcs_addr_bits_remove
+};
+
+
static int
arm_cannot_store_register (int regno)
{
@@ -198,6 +226,28 @@ arm_fill_vfpregset (struct regcache *regcache, void *buf)
arm_fill_vfpregset_num (regcache, buf, num);
}
+/* Remove useless bits from addresses in a running program. */
+static CORE_ADDR
+arm_addr_bits_remove (CORE_ADDR val)
+{
+ /* On M-profile devices, do not strip the low bit from EXC_RETURN
+ (the magic exception return address). */
+ if (arm_is_m && (val & 0xfffffff0) == 0xfffffff0)
+ return val;
+
+ if (arm_apcs_32)
+ return UNMAKE_THUMB_ADDR (val);
+ else
+ return (val & 0x03fffffc);
+}
+
+/* Wrapper for arm_addr_bits_remove in the get_next_pcs context*/
+static CORE_ADDR
+arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, CORE_ADDR val)
+{
+ return arm_addr_bits_remove (val);
+}
+
static void
arm_store_vfpregset (struct regcache *regcache, const void *buf)
{
@@ -233,20 +283,27 @@ arm_set_pc (struct regcache *regcache, CORE_ADDR pc)
supply_register_by_name (regcache, "pc", &newpc);
}
-/* Correct in either endianness. */
-static const unsigned long arm_breakpoint = 0xef9f0001;
-#define arm_breakpoint_len 4
-static const unsigned short thumb_breakpoint = 0xde01;
-static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
+/* Default breakpoint definitions. */
+static const gdb_byte *thumb_breakpoint = arm_linux_thumb_le_breakpoint;
+static const gdb_byte *thumb2_breakpoint = arm_linux_thumb2_le_breakpoint;
+static const gdb_byte *arm_eabi_breakpoint = eabi_linux_arm_le_breakpoint;
+static const gdb_byte *arm_abi_breakpoint = arm_linux_arm_le_breakpoint;
+
+/* Only supports the gdbserver EABI for breakpoint insertion */
+#ifndef __ARM_EABI__
+static const gdb_byte *arm_breakpoint = arm_linux_arm_le_breakpoint;
+#else
+static const gdb_byte *arm_breakpoint = eabi_linux_arm_le_breakpoint;
+#endif
+
+/* Byte order for data , defaults to LE. */
+enum bfd_endian byte_order = BFD_ENDIAN_LITTLE;;
+/* Byte order for code , defaults to LE. */
+enum bfd_endian byte_order_for_code = BFD_ENDIAN_LITTLE;
-/* For new EABI binaries. We recognize it regardless of which ABI
- is used for gdbserver, so single threaded debugging should work
- OK, but for multi-threaded debugging we only insert the current
- ABI's breakpoint instruction. For now at least. */
-static const unsigned long arm_eabi_breakpoint = 0xe7f001f0;
+/* Returns 1 if the currnet instruction mode is thumb. */
-static int
-arm_breakpoint_at (CORE_ADDR where)
+static int arm_is_thumb_mode()
{
struct regcache *regcache = get_thread_regcache (current_thread, 1);
unsigned long cpsr;
@@ -254,47 +311,233 @@ arm_breakpoint_at (CORE_ADDR where)
collect_register_by_name (regcache, "cpsr", &cpsr);
if (cpsr & 0x20)
+ return 1;
+ else
+ return 0;
+}
+
+/* Returns 1 if there is a software breakpoint at location. */
+
+static int
+arm_breakpoint_at (CORE_ADDR where)
+{
+ if (arm_is_thumb_mode())
{
/* Thumb mode. */
- unsigned short insn;
+ gdb_byte insn[2];
- (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
- if (insn == thumb_breakpoint)
+ (*the_target->read_memory) (where, (gdb_byte *) &insn, 2);
+ if (insn[0] == thumb_breakpoint[0] && insn[1] == thumb_breakpoint[1])
return 1;
- if (insn == thumb2_breakpoint[0])
+ if (insn[0] == thumb2_breakpoint[0] && insn[1] == thumb2_breakpoint[1])
{
- (*the_target->read_memory) (where + 2, (unsigned char *) &insn, 2);
- if (insn == thumb2_breakpoint[1])
+ (*the_target->read_memory) (where + 2, (gdb_byte *) &insn, 2);
+ if (insn[0] == thumb2_breakpoint[2] &&
+ insn[1] == thumb2_breakpoint[3])
return 1;
}
}
else
{
/* ARM mode. */
- unsigned long insn;
+ gdb_byte insn[4];
+ int i;
+ int result = 1;
(*the_target->read_memory) (where, (unsigned char *) &insn, 4);
- if (insn == arm_breakpoint)
- return 1;
+ for (i = 0; i < 4; i++)
+ {
+ if (insn[i] != arm_abi_breakpoint[i])
+ result = 0;
+ }
- if (insn == arm_eabi_breakpoint)
- return 1;
+ if (!result)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ if (insn[i] != arm_eabi_breakpoint[i])
+ result = 0;
+ }
+ }
+ return result;
}
return 0;
}
-/* We only place breakpoints in empty marker functions, and thread locking
- is outside of the function. So rather than importing software single-step,
- we can just run until exit. */
-static CORE_ADDR
-arm_reinsert_addr (void)
+/* Wrapper to collect_register for get_next_pcs. */
+
+static ULONGEST
+get_next_pcs_collect_register_unsigned (struct get_next_pcs* self, int n)
{
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- unsigned long pc;
- collect_register_by_name (regcache, "lr", &pc);
- return pc;
+ struct arm_gdbserver_get_next_pcs *next_pcs =
+ (struct arm_gdbserver_get_next_pcs*) self;
+ gdb_byte buf[MAX_REGISTER_SIZE];
+ int size = register_size (next_pcs->base.base.regcache->tdesc, n);
+
+ collect_register (next_pcs->base.base.regcache, n, &buf);
+ return extract_unsigned_integer (buf, size, next_pcs->base.byte_order);
+}
+
+static ULONGEST
+read_memory_unsigned_integer (CORE_ADDR memaddr, int len,
+ enum bfd_endian byte_order)
+{
+ gdb_byte buf[sizeof (ULONGEST)];
+ (*the_target->read_memory) (memaddr, (unsigned char *) &buf, len);
+ return extract_unsigned_integer (buf, len, byte_order);
+}
+
+/* Determine the type and size of breakpoint to insert at PCPTR. Uses
+ the program counter value to determine whether a 16-bit or 32-bit
+ breakpoint should be used. It returns a pointer to a string of
+ bytes that encode a breakpoint instruction, stores the length of
+ the string to *lenptr, and adjusts the program counter (if
+ necessary) to point to the actual memory location where the
+ breakpoint should be inserted. */
+
+static const unsigned char *
+arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
+{
+ /* Default if no pc is set to arm breakpoint. */
+ if (pcptr == NULL)
+ {
+ *lenptr = arm_linux_arm_breakpoint_size;
+ return arm_breakpoint;
+ }
+
+ if (IS_THUMB_ADDR (*pcptr))
+ {
+ *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
+
+ /* If we have a separate 32-bit breakpoint instruction for Thumb-2,
+ check whether we are replacing a 32-bit instruction. */
+ if (thumb2_breakpoint != NULL)
+ {
+ gdb_byte buf[2];
+ if ((*the_target->read_memory) (*pcptr, buf, 2) == 0)
+ {
+ unsigned short inst1;
+ inst1 = extract_unsigned_integer (buf, 2, byte_order_for_code);
+ if (thumb_insn_size (inst1) == 4)
+ {
+ *lenptr = arm_linux_thumb2_breakpoint_size;
+ return thumb2_breakpoint;
+ }
+ }
+ }
+
+ *lenptr = arm_linux_thumb_breakpoint_size;
+ return thumb_breakpoint;
+ }
+ else
+ {
+ *lenptr = arm_linux_arm_breakpoint_size;
+ return arm_breakpoint;
+ }
+}
+
+/* Sets the breakpoints to the endianness in argument. */
+
+static void
+arm_set_breakpoints (enum bfd_endian endian)
+{
+ switch (endian) {
+ case BFD_ENDIAN_LITTLE:
+ thumb_breakpoint = arm_linux_thumb_le_breakpoint;
+ thumb2_breakpoint = arm_linux_thumb2_le_breakpoint;
+
+ arm_eabi_breakpoint = eabi_linux_arm_le_breakpoint;
+
+ arm_abi_breakpoint = arm_linux_arm_le_breakpoint;
+
+ /* Only supports the gdbserver EABI for breakpoint insertion */
+#ifndef __ARM_EABI__
+ arm_breakpoint = arm_linux_arm_le_breakpoint;
+#else
+ arm_breakpoint = eabi_linux_arm_le_breakpoint;
+#endif
+ break;
+ case BFD_ENDIAN_BIG:
+ thumb_breakpoint = arm_linux_thumb_be_breakpoint;
+ thumb2_breakpoint = arm_linux_thumb2_be_breakpoint;
+
+ arm_eabi_breakpoint = eabi_linux_arm_be_breakpoint;
+
+ arm_abi_breakpoint = arm_linux_arm_be_breakpoint;
+
+ /* Only supports the gdbserver EABI for breakpoint insertion */
+#ifndef __ARM_EABI__
+ arm_breakpoint = arm_linux_arm_be_breakpoint;
+#else
+ arm_breakpoint = eabi_linux_arm_be_breakpoint;
+#endif
+ break;
+ default:
+ break;
+ }
+}
+
+/* Sets the byte order based on ELF endianness flags. */
+
+static void
+arm_set_byte_order (void)
+{
+ int tid = lwpid_of (current_thread);
+ char file[PATH_MAX];
+ unsigned int machine;
+ Elf64_Ehdr header_64;
+ Elf32_Ehdr header_32;
+ int is64;
+
+ sprintf (file, "/proc/%d/exe", tid);
+
+ is64 = linux_pid_exe_is_elf_64_file (tid, &machine);
+ if (is64)
+ elf_64_file_read_header (file, &header_64, &machine);
+ else
+ elf_32_file_read_header (file, &header_32, &machine);
+
+ switch (is64 ? header_64.e_ident[EI_DATA] : header_32.e_ident[EI_DATA]) {
+ case ELFDATA2LSB:
+ byte_order = BFD_ENDIAN_LITTLE;
+ byte_order_for_code = BFD_ENDIAN_LITTLE;
+ break;
+ case ELFDATA2MSB:
+ byte_order = BFD_ENDIAN_BIG;
+ if ((is64 ? header_64.e_flags : header_32.e_flags) & EF_ARM_BE8)
+ {
+ byte_order_for_code = BFD_ENDIAN_LITTLE;
+ }
+ else
+ {
+ byte_order_for_code = BFD_ENDIAN_BIG;
+ }
+ break;
+ default:
+ break;
+ }
+
+ arm_set_breakpoints (byte_order_for_code);
+}
+
+/* Fetch the next possible PCs after the current instruction executes. */
+
+static void
+arm_gdbserver_get_next_pcs (struct get_next_pcs *base_next_pcs)
+{
+ struct arm_gdbserver_get_next_pcs next_pcs;
+
+ next_pcs.base.ops = &get_next_pcs_ops;
+ next_pcs.base.base = *base_next_pcs;
+ next_pcs.base.byte_order = byte_order;
+ next_pcs.base.byte_order_for_code = byte_order_for_code;
+ next_pcs.base.is_thumb = arm_is_thumb_mode();
+ next_pcs.base.arm_apcs_32 = arm_apcs_32;
+ next_pcs.base.arm_linux_thumb2_breakpoint = thumb2_breakpoint;
+
+ arm_get_next_pcs ((struct arm_get_next_pcs *) &next_pcs);
}
/* Fetch the thread-local storage pointer for libthread_db. */
@@ -864,6 +1107,9 @@ arm_arch_setup (void)
have_ptrace_getregset = 1;
else
have_ptrace_getregset = 0;
+
+ /* Fetch the global byte order from ELF. */
+ arm_set_byte_order ();
}
/* Register sets without using PTRACE_GETREGSET. */
@@ -913,21 +1159,6 @@ arm_regs_info (void)
return ®s_info_arm;
}
-static const unsigned char *
-arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
-{
- *len = arm_breakpoint_len;
- /* Define an ARM-mode breakpoint; we only set breakpoints in the C
- library, which is most likely to be ARM. If the kernel supports
- clone events, we will never insert a breakpoint, so even a Thumb
- C library will work; so will mixing EABI/non-EABI gdbserver and
- application. */
-#ifndef __ARM_EABI__
- return (const unsigned char *) &arm_breakpoint;
-#else
- return (const unsigned char *) &arm_eabi_breakpoint;
-#endif
-}
struct linux_target_ops the_low_target = {
arm_arch_setup,
arm_regs_info,
@@ -937,7 +1168,7 @@ struct linux_target_ops the_low_target = {
arm_get_pc,
arm_set_pc,
arm_breakpoint_from_pc,
- arm_reinsert_addr,
+ arm_gdbserver_get_next_pcs,
0,
arm_breakpoint_at,
arm_supports_z_point_type,
@@ -952,6 +1183,13 @@ struct linux_target_ops the_low_target = {
arm_new_thread,
arm_new_fork,
arm_prepare_to_resume,
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
};
void
diff --git a/gdb/gdbserver/linux-bfin-low.c b/gdb/gdbserver/linux-bfin-low.c
index 1c0e1e9..3032a34 100644
--- a/gdb/gdbserver/linux-bfin-low.c
+++ b/gdb/gdbserver/linux-bfin-low.c
@@ -130,7 +130,7 @@ struct linux_target_ops the_low_target = {
bfin_get_pc,
bfin_set_pc,
bfin_breakpoint_from_pc,
- NULL, /* breakpoint_reinsert_addr */
+ NULL, /* get_next_pcs */
2,
bfin_breakpoint_at,
};
diff --git a/gdb/gdbserver/linux-cris-low.c b/gdb/gdbserver/linux-cris-low.c
index da5876d..cd1ae4d 100644
--- a/gdb/gdbserver/linux-cris-low.c
+++ b/gdb/gdbserver/linux-cris-low.c
@@ -19,6 +19,7 @@
#include "server.h"
#include "linux-low.h"
#include "nat/gdb_ptrace.h"
+#include "common/get-next-pcs.h"
/* Defined in auto-generated file reg-cris.c. */
void init_registers_cris (void);
@@ -106,13 +107,13 @@ cris_breakpoint_at (CORE_ADDR where)
/* We only place breakpoints in empty marker functions, and thread locking
is outside of the function. So rather than importing software single-step,
we can just run until exit. */
-static CORE_ADDR
-cris_reinsert_addr (void)
+static void
+cris_get_next_pcs (struct get_next_pcs *next_pcs)
{
struct regcache *regcache = get_thread_regcache (current_thread, 1);
- unsigned long pc;
+ CORE_ADDR pc;
collect_register_by_name (regcache, "srp", &pc);
- return pc;
+ VEC_safe_push (CORE_ADDR, next_pcs->result, pc);
}
static void
@@ -148,7 +149,7 @@ struct linux_target_ops the_low_target = {
cris_get_pc,
cris_set_pc,
cris_breakpoint_from_pc,
- cris_reinsert_addr,
+ cris_get_next_pcs,
0,
cris_breakpoint_at,
0,
diff --git a/gdb/gdbserver/linux-crisv32-low.c b/gdb/gdbserver/linux-crisv32-low.c
index d2dba91..05ad85a 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -19,6 +19,7 @@
#include "server.h"
#include "linux-low.h"
#include "nat/gdb_ptrace.h"
+#include "common/get-next-pcs.h"
/* Defined in auto-generated file reg-crisv32.c. */
void init_registers_crisv32 (void);
@@ -106,14 +107,13 @@ cris_breakpoint_at (CORE_ADDR where)
/* FIXME: This function should not be needed, since we have PTRACE_SINGLESTEP
for CRISv32. Without it, td_ta_event_getmsg in thread_db_create_event
will fail when debugging multi-threaded applications. */
-
-static CORE_ADDR
-cris_reinsert_addr (void)
+static void
+cris_get_next_pcs (struct get_next_pcs *next_pcs)
{
struct regcache *regcache = get_thread_regcache (current_thread, 1);
- unsigned long pc;
+ CORE_ADDR pc;
collect_register_by_name (regcache, "srp", &pc);
- return pc;
+ VEC_safe_push (CORE_ADDR, next_pcs->result, pc);
}
static void
@@ -428,7 +428,7 @@ struct linux_target_ops the_low_target = {
cris_get_pc,
cris_set_pc,
cris_breakpoint_from_pc,
- cris_reinsert_addr,
+ cris_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 402db9c..229df46 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -46,14 +46,8 @@
#include "filestuff.h"
#include "tracepoint.h"
#include "hostio.h"
-#ifndef ELFMAG0
-/* Don't include <linux/elf.h> here. If it got included by gdb_proc_service.h
- then ELFMAG0 will have been defined. If it didn't get included by
- gdb_proc_service.h then including it will likely introduce a duplicate
- definition of elf_fpregset_t. */
-#include <elf.h>
-#endif
#include "nat/linux-namespaces.h"
+#include "common/get-next-pcs.h"
#ifndef SPUFS_MAGIC
#define SPUFS_MAGIC 0x23c9b64e
@@ -279,12 +273,12 @@ static void complete_ongoing_step_over (void);
ptid_t step_over_bkpt;
/* True if the low target can hardware single-step. Such targets
- don't need a BREAKPOINT_REINSERT_ADDR callback. */
+ don't need a GET_NEXT_PCS callback. */
static int
can_hardware_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
@@ -351,28 +345,100 @@ elf_64_header_p (const Elf64_Ehdr *header, unsigned int *machine)
return -1;
}
-/* Return non-zero if FILE is a 64-bit ELF file,
- zero if the file is not a 64-bit ELF file,
- and -1 if the file is not accessible or doesn't exist. */
+/* Returns -1 if the file is not ELF64, fills the Elf64 header otherwise
+ and return non-zero. */
static int
-elf_64_file_p (const char *file, unsigned int *machine)
+elf_64_read_header (int fd, Elf64_Ehdr *header, unsigned int *machine)
+{
+ if (read (fd, header, sizeof (*header)) != sizeof (*header))
+ {
+ return 0;
+ }
+
+ return elf_64_header_p (header, machine);
+}
+
+/* Returns -1 if the file is not ELF64 or not accessible,
+ fills the Elf64 header otherwise and return non-zero. */
+
+int
+elf_64_file_read_header (const char *file, Elf64_Ehdr *header,
+ unsigned int *machine)
{
- Elf64_Ehdr header;
int fd;
+ int result;
fd = open (file, O_RDONLY);
if (fd < 0)
return -1;
- if (read (fd, &header, sizeof (header)) != sizeof (header))
+ result = elf_64_read_header (fd, header, machine);
+ close (fd);
+
+ return result;
+}
+
+/* Return non-zero if HEADER is a 32-bit ELF file. */
+
+static int
+elf_32_header_p (const Elf32_Ehdr *header, unsigned int *machine)
+{
+ if (header->e_ident[EI_MAG0] == ELFMAG0
+ && header->e_ident[EI_MAG1] == ELFMAG1
+ && header->e_ident[EI_MAG2] == ELFMAG2
+ && header->e_ident[EI_MAG3] == ELFMAG3)
+ {
+ *machine = header->e_machine;
+ return header->e_ident[EI_CLASS] == ELFCLASS32;
+
+ }
+ *machine = EM_NONE;
+ return -1;
+}
+
+/* Returns -1 if the file is not ELF32, fills the Elf32 header otherwise
+ and return non-zero. */
+
+static int
+elf_32_read_header (int fd, Elf32_Ehdr *header, unsigned int *machine)
+{
+ if (read (fd, header, sizeof (*header)) != sizeof (*header))
{
- close (fd);
return 0;
}
+
+ return elf_32_header_p (header, machine);
+}
+
+/* Returns -1 if the file is not ELF32 or not accessible,
+ fills the Elf32 header otherwise and return non-zero. */
+int
+elf_32_file_read_header (const char *file, Elf32_Ehdr *header,
+ unsigned int *machine)
+{
+ int fd;
+ int result;
+
+ fd = open (file, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ result = elf_32_read_header (fd, header, machine);
close (fd);
- return elf_64_header_p (&header, machine);
+ return result;
+}
+/* Return non-zero if FILE is a 64-bit ELF file,
+ zero if the file is not a 64-bit ELF file,
+ and -1 if the file is not accessible or doesn't exist. */
+
+static int
+elf_64_file_p (const char *file, unsigned int *machine)
+{
+ Elf64_Ehdr header;
+ int result = elf_64_file_read_header (file, &header, machine);
+ return result;
}
/* Accepts an integer PID; Returns true if the executable PID is
@@ -3783,6 +3849,29 @@ 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)
+{
+ struct get_next_pcs next_pcs;
+ int i;
+ CORE_ADDR pc;
+
+ struct regcache *regcache = get_thread_regcache (current_thread, 1);
+ next_pcs.pc = get_pc(lwp);
+ next_pcs.regcache = regcache;
+ next_pcs.result = VEC_alloc (CORE_ADDR, 1);
+ (*the_low_target.get_next_pcs) (&next_pcs);
+
+ for (i = 0; VEC_iterate (CORE_ADDR, next_pcs.result, i, pc); ++i)
+ {
+ set_reinsert_breakpoint (pc);
+ }
+
+ VEC_free (CORE_ADDR, next_pcs.result);
+}
+
/* Resume execution of LWP. If STEP is nonzero, single-step it. If
SIGNAL is nonzero, give it that signal. */
@@ -3928,14 +4017,20 @@ 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 (debug_threads)
- debug_printf ("lwp %ld has a while-stepping action -> forcing step.\n",
- lwpid_of (thread));
- step = 1;
+ if (thread->while_stepping != NULL) {
+ if (debug_threads)
+ debug_printf ("lwp %ld has a while-stepping action -> forcing step.\n",
+ lwpid_of (thread));
+
+ if (can_hardware_single_step ())
+ {
+ step = 1;
+ }
+ else {
+ install_software_single_step_breakpoints (lwp);
+ step = 0;
}
+ }
if (proc->tdesc != NULL && the_low_target.get_pc != NULL)
{
@@ -4346,8 +4441,7 @@ start_step_over (struct lwp_info *lwp)
}
else
{
- CORE_ADDR raddr = (*the_low_target.breakpoint_reinsert_addr) ();
- set_reinsert_breakpoint (raddr);
+ install_software_single_step_breakpoints (lwp);
step = 0;
}
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index c623150..597c36d 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -27,8 +27,19 @@
#include "nat/linux-ptrace.h"
#include "target/waitstatus.h" /* For enum target_stop_reason. */
+#ifndef ELFMAG0
+/* Don't include <linux/elf.h> here. If it got included by gdb_proc_service.h
+ then ELFMAG0 will have been defined. If it didn't get included by
+ gdb_proc_service.h then including it will likely introduce a duplicate
+ definition of elf_fpregset_t. */
+#include <elf.h>
+#endif
+
#define PTRACE_XFER_TYPE long
+/* Forward declaration for get_next_pcs. */
+struct get_next_pcs;
+
#ifdef HAVE_LINUX_REGSETS
typedef void (*regset_fill_func) (struct regcache *, void *);
typedef void (*regset_store_func) (struct regcache *, const void *);
@@ -148,7 +159,8 @@ struct linux_target_ops
present in the PC. */
const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
- CORE_ADDR (*breakpoint_reinsert_addr) (void);
+ /* Find the next possible PCs after the current instruction executes. */
+ void (*get_next_pcs) (struct get_next_pcs *next_pcs);
int decr_pc_after_break;
int (*breakpoint_at) (CORE_ADDR pc);
@@ -353,6 +365,10 @@ struct lwp_info
};
int linux_pid_exe_is_elf_64_file (int pid, unsigned int *machine);
+int elf_64_file_read_header (const char *file, Elf64_Ehdr *header,
+ unsigned int *machine);
+int elf_32_file_read_header (const char *file, Elf32_Ehdr *header,
+ unsigned int *machine);
/* Attach to PTID. Returns 0 on success, non-zero otherwise (an
errno). */
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index d5333ab..d462f2c 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -24,6 +24,7 @@
#include "nat/mips-linux-watch.h"
#include "gdb_proc_service.h"
+#include "common/get-next-pcs.h"
/* Defined in auto-generated file mips-linux.c. */
void init_registers_mips_linux (void);
@@ -276,13 +277,16 @@ mips_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
/* We only place breakpoints in empty marker functions, and thread locking
is outside of the function. So rather than importing software single-step,
we can just run until exit. */
-static CORE_ADDR
-mips_reinsert_addr (void)
+static void
+mips_get_next_pcs (struct get_next_pcs *next_pcs)
{
+ CORE_ADDR result;
struct regcache *regcache = get_thread_regcache (current_thread, 1);
union mips_register ra;
collect_register_by_name (regcache, "r31", ra.buf);
- return register_size (regcache->tdesc, 0) == 4 ? ra.reg32 : ra.reg64;
+ result = register_size (regcache->tdesc, 0) == 4 ? ra.reg32 : ra.reg64;
+
+ VEC_safe_push (CORE_ADDR, next_pcs->result, result);
}
static int
@@ -889,7 +893,7 @@ struct linux_target_ops the_low_target = {
mips_get_pc,
mips_set_pc,
mips_breakpoint_from_pc,
- mips_reinsert_addr,
+ mips_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 bf9ecc2..a24eb23 100644
--- a/gdb/gdbserver/linux-nios2-low.c
+++ b/gdb/gdbserver/linux-nios2-low.c
@@ -21,11 +21,16 @@
#include "server.h"
#include "linux-low.h"
+/* Don't include elf/common.h if linux/elf.h got included by
+ linux-low.h or gdb_proc_service.h. */
+#ifndef ELFMAG0
#include "elf/common.h"
+#endif
#include "nat/gdb_ptrace.h"
#include <endian.h>
#include "gdb_proc_service.h"
#include <asm/ptrace.h>
+#include "common/get-next-pcs.h"
#ifndef PTRACE_GET_THREAD_AREA
#define PTRACE_GET_THREAD_AREA 25
@@ -141,16 +146,16 @@ nios2_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
return (const unsigned char *) &nios2_breakpoint;
}
-/* Implement the breakpoint_reinsert_addr linux_target_ops method. */
+/* Implement the get_next_pcs linux_target_ops method. */
-static CORE_ADDR
-nios2_reinsert_addr (void)
+static void
+nios2_get_next_pcs (struct get_next_pcs *next_pcs)
{
union nios2_register ra;
struct regcache *regcache = get_thread_regcache (current_thread, 1);
collect_register_by_name (regcache, "ra", ra.buf);
- return ra.reg32;
+ VEC_safe_push (CORE_ADDR, next_pcs->result, ra.reg32);
}
/* Implement the breakpoint_at linux_target_ops method. */
@@ -275,7 +280,7 @@ struct linux_target_ops the_low_target =
nios2_get_pc,
nios2_set_pc,
nios2_breakpoint_from_pc,
- nios2_reinsert_addr,
+ nios2_get_next_pcs,
0,
nios2_breakpoint_at,
};
diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c
index 4c71cd9..fa87e19 100644
--- a/gdb/gdbserver/linux-ppc-low.c
+++ b/gdb/gdbserver/linux-ppc-low.c
@@ -19,8 +19,6 @@
#include "server.h"
#include "linux-low.h"
-
-#include <elf.h>
#include <asm/ptrace.h>
#include "nat/ppc-linux.h"
diff --git a/gdb/gdbserver/linux-s390-low.c b/gdb/gdbserver/linux-s390-low.c
index f76f867..44d3f26 100644
--- a/gdb/gdbserver/linux-s390-low.c
+++ b/gdb/gdbserver/linux-s390-low.c
@@ -21,12 +21,16 @@
#include "server.h"
#include "linux-low.h"
+
+/* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
+ On Bionic elf.h and linux/elf.h have conflicting definitions. */
+#ifndef ELFMAG0
#include "elf/common.h"
+#endif
#include <asm/ptrace.h>
#include "nat/gdb_ptrace.h"
#include <sys/uio.h>
-#include <elf.h>
#ifndef HWCAP_S390_HIGH_GPRS
#define HWCAP_S390_HIGH_GPRS 512
diff --git a/gdb/gdbserver/linux-sparc-low.c b/gdb/gdbserver/linux-sparc-low.c
index 35820fb..e804281 100644
--- a/gdb/gdbserver/linux-sparc-low.c
+++ b/gdb/gdbserver/linux-sparc-low.c
@@ -22,6 +22,7 @@
#include "nat/gdb_ptrace.h"
#include "gdb_proc_service.h"
+#include "common/get-next-pcs.h"
/* The stack pointer is offset from the stack frame by a BIAS of 2047
(0x7ff) for 64-bit code. BIAS is likely to be defined on SPARC
@@ -266,14 +267,14 @@ sparc_breakpoint_at (CORE_ADDR where)
/* We only place breakpoints in empty marker functions, and thread locking
is outside of the function. So rather than importing software single-step,
we can just run until exit. */
-static CORE_ADDR
-sparc_reinsert_addr (void)
+static void
+sparc_get_next_pcs (struct get_next_pcs *next_pcs)
{
struct regcache *regcache = get_thread_regcache (current_thread, 1);
CORE_ADDR lr;
/* O7 is the equivalent to the 'lr' of other archs. */
collect_register_by_name (regcache, "o7", &lr);
- return lr;
+ VEC_safe_push (CORE_ADDR, next_pcs->result, lr);
}
static void
@@ -330,7 +331,7 @@ struct linux_target_ops the_low_target = {
/* No sparc_set_pc is needed. */
NULL,
sparc_breakpoint_from_pc,
- sparc_reinsert_addr,
+ sparc_get_next_pcs,
0,
sparc_breakpoint_at,
NULL, /* supports_z_point_type */
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 699eb4d..0731141 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -29,7 +29,7 @@
#include "gdb_proc_service.h"
/* Don't include elf/common.h if linux/elf.h got included by
- gdb_proc_service.h. */
+ linux-low.h or gdb_proc_service.h. */
#ifndef ELFMAG0
#include "elf/common.h"
#endif
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread