* [PATCH v3 02/10] Fix instruction skipping when using software single step in GDBServer
2015-11-23 14:14 [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 09/10] Enable software single stepping for while-stepping actions " Antoine Tremblay
@ 2015-11-23 14:14 ` Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 05/10] Remove too simple breakpoint_reinsert_addr implementations Antoine Tremblay
` (8 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-23 14:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
Without this patch, when doing a software single step, with for example
a conditional breakpoint, gdbserver would wrongly avance the pc of
breakpoint_len and skips an instruction.
This is due to gdbserver assuming that it's hardware single stepping.
When it resumes from the breakpoint address it expects the trap to be
caused by ptrace and if it's rather caused by a software breakpoint
it assumes this is a permanent breakpoint and that it needs to skip
over it.
However when software single stepping, this breakpoint is legitimate as
it's the reinsert breakpoint gdbserver has put in place to break at
the next instruction. Thus gdbserver wrongly advances the pc and skips
an instruction.
This patch fixes this behavior so that gdbserver checks if it is a
reinsert breakpoint from software single stepping. If it is it won't
advance the pc. And if there's no reinsert breakpoint there we assume
then that it's a permanent breakpoint and advance the pc.
Here's a commented log of what would happen before and after the fix on
gdbserver :
/* Here there is a conditional breakpoint at 0x10428 that needs to be
stepped over. */
Need step over [LWP 11204]? yes, found breakpoint at 0x10428
...
/* e7f001f0 is a breakpoint instruction on arm
Here gdbserver writes the software breakpoint we would like to hit
*/
Writing e7f001f0 to 0x0001042c in process 11204
...
Resuming lwp 11220 (continue, signal 0, stop not expected)
pending reinsert at 0x10428
stop pc is 00010428
continue from pc 0x10428
...
/* Here gdbserver hit the software breakpoint that was in place
for the step over */
stop pc is 0001042c
pc is 0x1042c
step-over for LWP 11220.11220 executed software breakpoint
Finished step over.
Could not find fast tracepoint jump at 0x10428 in list (reinserting).
/* Here gdbserver writes back the original instruction */
Writing e50b3008 to 0x0001042c in process 11220
Step-over finished.
Need step over [LWP 11220]? No
/* Here because gdbserver assumes this is a permenant breakpoint it advances
the pc of breakpoint_len, in this case 4 bytes, so we have just skipped
the instruction that was written back here :
Writing e50b3008 to 0x0001042c in process 11220
*/
stop pc is 00010430
pc is 0x10430
Need step over [LWP 11220]? No, no breakpoint found at 0x10430
Proceeding, no step-over needed
proceed_one_lwp: lwp 11220
stop pc is 00010430
This patch fixes this situation and we get the right behavior :
Writing e50b3008 to 0x0001042c in process 11245
Hit a gdbserver breakpoint.
Hit a gdbserver breakpoint.
Step-over finished.
proceeding all threads.
Need step over [LWP 11245]? No
stop pc is 0001042c
pc is 0x1042c
Need step over [LWP 11245]? No, no breakpoint found at 0x1042c
Proceeding, no step-over needed
proceed_one_lwp: lwp 11245
stop pc is 0001042c
pc is 0x1042c
Resuming lwp 11245 (continue, signal 0, stop not expected)
stop pc is 0001042c
continue from pc 0x1042c
It also works if the value at 0x0001042c is a permanent breakpoint.
If so gdbserver will finish the step over, remove the reinserted breakpoint,
resume at that location and on the next SIGTRAP gdbserver will trigger
the advance PC condition as reinsert_breakpoint_inserted_here will be false.
I also tested this against bp-permanent.exp on arm (with a work in progress
software single step patchset) without any regressions.
It's also tested against x86 bp-permanent.exp without any regression.
So both software and hardware single step are tested.
No regressions on Ubuntu 14.04 on ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }
gdb/gdbserver/ChangeLog:
* linux-low.c (linux_wait_1): Fix pc advance condition.
* mem-break.c (reinsert_breakpoint_inserted_here): New function.
* mem-break.h (reinsert_breakpoint_inserted_here): New declaration.
---
gdb/gdbserver/linux-low.c | 21 ++++++++++++++-------
gdb/gdbserver/mem-break.c | 17 +++++++++++++++++
gdb/gdbserver/mem-break.h | 4 ++++
3 files changed, 35 insertions(+), 7 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 3b6c131..63ac8b3 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -2993,14 +2993,21 @@ linux_wait_1 (ptid_t ptid,
return ptid_of (current_thread);
}
- /* If step-over executes a breakpoint instruction, it means a
- gdb/gdbserver breakpoint had been planted on top of a permanent
- breakpoint. The PC has been adjusted by
- check_stopped_by_breakpoint to point at the breakpoint address.
- Advance the PC manually past the breakpoint, otherwise the
- program would keep trapping the permanent breakpoint forever. */
+ /* If step-over executes a breakpoint instruction, in the case of a
+ hardware single step it means a gdb/gdbserver breakpoint had been
+ planted on top of a permanent breakpoint, in the case of a software
+ single step it may just mean that gdbserver hit the reinsert breakpoint.
+ The PC has been adjusted by check_stopped_by_breakpoint to point at
+ the breakpoint address.
+ So in the case of the hardware single step advance the PC manually
+ past the breakpoint and in the case of software single step advance only
+ if it's not the reinsert_breakpoint we are hitting.
+ This avoids that a program would keep trapping a permanent breakpoint
+ forever. */
if (!ptid_equal (step_over_bkpt, null_ptid)
- && event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT)
+ && event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
+ && (event_child->stepping
+ || !reinsert_breakpoint_inserted_here (event_child->stop_pc)))
{
int increment_pc = 0;
int breakpoint_kind = 0;
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index c808a84..267f29d 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -1665,6 +1665,23 @@ hardware_breakpoint_inserted_here (CORE_ADDR addr)
return 0;
}
+/* See mem-break.h. */
+
+int
+reinsert_breakpoint_inserted_here (CORE_ADDR addr)
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp;
+
+ for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+ if (bp->type == reinsert_breakpoint
+ && bp->raw->pc == addr
+ && bp->raw->inserted)
+ return 1;
+
+ return 0;
+}
+
static int
validate_inserted_breakpoint (struct raw_breakpoint *bp)
{
diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h
index d199cc4..40b66a7 100644
--- a/gdb/gdbserver/mem-break.h
+++ b/gdb/gdbserver/mem-break.h
@@ -100,6 +100,10 @@ int software_breakpoint_inserted_here (CORE_ADDR addr);
int hardware_breakpoint_inserted_here (CORE_ADDR addr);
+/* Returns TRUE if there's any reinsert breakpoint at ADDR. */
+
+int reinsert_breakpoint_inserted_here (CORE_ADDR addr);
+
/* Clear all breakpoint conditions and commands associated with a
breakpoint. */
--
2.6.3
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer.
@ 2015-11-23 14:14 Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 09/10] Enable software single stepping for while-stepping actions " Antoine Tremblay
` (10 more replies)
0 siblings, 11 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-23 14:14 UTC (permalink / raw)
To: gdb-patches
In this v3:
* Refactored the get_next_pcs call in GDBServer as such :
VEC (CORE_ADDR) *(*get_next_pcs) (CORE_ADDR pc, struct regcache *regcache);
This allows the removal of the get_next_pcs base structure that was in
common/get-next-pcs.h, in fact it removes the whole file.
The common arm_get_next_pcs call has also been refactored the same way like so :
VEC (CORE_ADDR) *arm_get_next_pcs (struct arm_get_next_pcs *self,
CORE_ADDR pc);
* Use ctor functions to construct gdb|gdbserver_get_next_pcs context.
* Some style fixes.
Patches 1 to 3: are OK already.
Patch 4: Needs v3 review.
Patch 5: is OK.
Patch 6 to 8: Needs v3 review.
Patch 9 and 10: are OK.
----
This patch series adds support for software single step and conditional
breakpoints on ARM in GDBServer.
Patches 1 and 2 fix general issues in the software single step control flow.
Patches 3 to 5 Removes the too simple implementations of software single
step that were in place and paves to way for full software single step to
be implemented.
Patches 6 and 7 Prepares the sharing of the software single step code for
ARM in GDB with GDBServer.
Patch 8 Implements the support for ARM software single step.
Patch 9 Adds support for while-stepping actions.
Patch 10 Adds supports for conditional breakpoints in GDBServer.
This patchset has no observed regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }
Note also that while I could not test directly thumbv1 instructions with gcc
-marmv4t , manual testing of the software single step was done for thumv1
instructions.
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH v3 09/10] Enable software single stepping for while-stepping actions in GDBServer.
2015-11-23 14:14 [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
@ 2015-11-23 14:14 ` Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 02/10] Fix instruction skipping when using software single step " Antoine Tremblay
` (9 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-23 14:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
This patch enables software single stepping if the targets supports it,
to do while-stepping actions.
No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }
gdb/gdbserver/ChangeLog:
* linux-low.c (single_step): New function.
(linux_resume_one_lwp_throw): Call single_step.
(start_step_over): Likewise.
---
gdb/gdbserver/linux-low.c | 48 ++++++++++++++++++++++++++++++-----------------
1 file changed, 31 insertions(+), 17 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 85a8e3f..79d2438 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -3897,6 +3897,33 @@ install_software_single_step_breakpoints (struct lwp_info *lwp)
VEC_free (CORE_ADDR, next_pcs);
}
+/* Single step via hardware or software single step.
+ Return 1 if hardware single stepping, 0 if software single stepping
+ or can't single step. */
+
+static int
+single_step (struct lwp_info* lwp)
+{
+ int step = 0;
+
+ if (can_hardware_single_step ())
+ {
+ step = 1;
+ }
+ else if (can_software_single_step ())
+ {
+ install_software_single_step_breakpoints (lwp);
+ step = 0;
+ }
+ else
+ {
+ if (debug_threads)
+ debug_printf ("stepping is not implemented on this target");
+ }
+
+ return step;
+}
+
/* Resume execution of LWP. If STEP is nonzero, single-step it. If
SIGNAL is nonzero, give it that signal. */
@@ -4042,13 +4069,13 @@ linux_resume_one_lwp_throw (struct lwp_info *lwp,
address, continue, and carry on catching this while-stepping
action only when that breakpoint is hit. A future
enhancement. */
- if (thread->while_stepping != NULL
- && can_hardware_single_step ())
+ if (thread->while_stepping != NULL)
{
if (debug_threads)
debug_printf ("lwp %ld has a while-stepping action -> forcing step.\n",
lwpid_of (thread));
- step = 1;
+
+ step = single_step (lwp);
}
if (proc->tdesc != NULL && the_low_target.get_pc != NULL)
@@ -4454,20 +4481,7 @@ start_step_over (struct lwp_info *lwp)
uninsert_breakpoints_at (pc);
uninsert_fast_tracepoint_jumps_at (pc);
- if (can_hardware_single_step ())
- {
- step = 1;
- }
- else if (can_software_single_step ())
- {
- install_software_single_step_breakpoints (lwp);
- step = 0;
- }
- else
- {
- internal_error (__FILE__, __LINE__,
- "stepping is not implemented on this target");
- }
+ step = single_step (lwp);
current_thread = saved_thread;
--
2.6.3
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH v3 06/10] Replace breakpoint_reinsert_addr by get_next_pcs operation in GDBServer.
2015-11-23 14:14 [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
` (3 preceding siblings ...)
2015-11-23 14:14 ` [PATCH v3 04/10] Remove support for thread events without PTRACE_EVENT_CLONE in GDBServer Antoine Tremblay
@ 2015-11-23 14:14 ` Antoine Tremblay
2015-11-26 10:30 ` Yao Qi
2015-11-26 10:50 ` Pedro Alves
2015-11-23 14:14 ` [PATCH v3 01/10] Fix breakpoint size when stepping over a permanent breakpoint " Antoine Tremblay
` (5 subsequent siblings)
10 siblings, 2 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-23 14:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
In this v3:
* Changed get_next_pcs signature.
* Remove common/get-next-pcs.h file.
* CORE_ADDR is now in linux-low.h temporarely.
---
This patch in preparation for software single step support on ARM. It refactors
breakpoint_reinsert_addr into get_next_pcs so that multiple location can be
returned.
When software single stepping there can be multiple possible next addresses
because we're stepping over a conditional branch instruction for example.
The operation get_next_pcs handles that by returning a vector of all the
possible next addresses.
Software breakpoints are installed at each location returned.
No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }
gdb/gdbserver/ChangeLog:
* linux-aarch64-low.c (struct linux_target_ops): Rename
breakpoint_reinsert_addr to get_next_pcs.
* linux-arm-low.c (struct linux_target_ops): Likewise.
* linux-bfin-low.c (struct linux_target_ops): Likewise.
* linux-cris-low.c (struct linux_target_ops): Likewise.
* linux-crisv32-low.c (struct linux_target_ops): Likewise.
* linux-low.c (can_software_single_step): Likewise.
(install_software_single_step_breakpoints): New function.
(start_step_over): Use install_software_single_step_breakpoints.
* linux-low.h: New CORE_ADDR vector.
(struct linux_target_ops) Rename breakpoint_reinsert_addr to
get_next_pcs.
* linux-mips-low.c (struct linux_target_ops): Likewise.
* linux-nios2-low.c (struct linux_target_ops): Likewise.
* linux-sparc-low.c (struct linux_target_ops): Likewise.
---
gdb/gdbserver/linux-aarch64-low.c | 2 +-
gdb/gdbserver/linux-arm-low.c | 2 +-
gdb/gdbserver/linux-bfin-low.c | 2 +-
gdb/gdbserver/linux-cris-low.c | 2 +-
gdb/gdbserver/linux-crisv32-low.c | 2 +-
gdb/gdbserver/linux-low.c | 28 ++++++++++++++++++++++++----
gdb/gdbserver/linux-low.h | 5 ++++-
gdb/gdbserver/linux-mips-low.c | 2 +-
gdb/gdbserver/linux-nios2-low.c | 2 +-
gdb/gdbserver/linux-sparc-low.c | 2 +-
10 files changed, 36 insertions(+), 13 deletions(-)
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 17798ff..3b8a9ad 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -2963,7 +2963,7 @@ struct linux_target_ops the_low_target =
aarch64_set_pc,
NULL, /* breakpoint_kind_from_pc */
aarch64_sw_breakpoint_from_kind,
- NULL, /* breakpoint_reinsert_addr */
+ NULL, /* get_next_pcs */
0, /* decr_pc_after_break */
aarch64_breakpoint_at,
aarch64_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 885aec9..78a4c8f 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -1032,7 +1032,7 @@ struct linux_target_ops the_low_target = {
arm_set_pc,
arm_breakpoint_kind_from_pc,
arm_sw_breakpoint_from_kind,
- NULL, /* breakpoint_reinsert_addr */
+ NULL, /* get_next_pcs */
0,
arm_breakpoint_at,
arm_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-bfin-low.c b/gdb/gdbserver/linux-bfin-low.c
index 912d253..1de2f78 100644
--- a/gdb/gdbserver/linux-bfin-low.c
+++ b/gdb/gdbserver/linux-bfin-low.c
@@ -141,7 +141,7 @@ struct linux_target_ops the_low_target = {
bfin_set_pc,
NULL, /* breakpoint_kind_from_pc */
bfin_sw_breakpoint_from_kind,
- NULL, /* breakpoint_reinsert_addr */
+ NULL, /* get_next_pcs */
2,
bfin_breakpoint_at,
NULL, /* supports_z_point_type */
diff --git a/gdb/gdbserver/linux-cris-low.c b/gdb/gdbserver/linux-cris-low.c
index 9f4519c..6902a45 100644
--- a/gdb/gdbserver/linux-cris-low.c
+++ b/gdb/gdbserver/linux-cris-low.c
@@ -139,7 +139,7 @@ struct linux_target_ops the_low_target = {
cris_set_pc,
NULL, /* breakpoint_kind_from_pc */
cris_sw_breakpoint_from_kind,
- NULL, /* breakpoint_reinsert_addr */
+ NULL, /* get_next_pcs */
0,
cris_breakpoint_at,
};
diff --git a/gdb/gdbserver/linux-crisv32-low.c b/gdb/gdbserver/linux-crisv32-low.c
index 2404d0e..28c1981 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -422,7 +422,7 @@ struct linux_target_ops the_low_target = {
cris_set_pc,
NULL, /* breakpoint_kind_from_pc */
cris_sw_breakpoint_from_kind,
- NULL, /* breakpoint_reinsert_addr */
+ NULL, /* get_next_pcs */
0,
cris_breakpoint_at,
cris_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index ce9cc2e..85a8e3f 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -281,12 +281,12 @@ can_hardware_single_step (void)
}
/* True if the low target can software single-step. Such targets
- implement the BREAKPOINT_REINSERT_ADDR callback. */
+ implement the GET_NEXT_PCS callback. */
static int
can_software_single_step (void)
{
- return (the_low_target.breakpoint_reinsert_addr != NULL);
+ return (the_low_target.get_next_pcs != NULL);
}
/* True if the low target supports memory breakpoints. If so, we'll
@@ -3876,6 +3876,27 @@ enqueue_pending_signal (struct lwp_info *lwp, int signal, siginfo_t *info)
lwp->pending_signals = p_sig;
}
+/* Install breakpoints for software single stepping. */
+
+static void
+install_software_single_step_breakpoints (struct lwp_info *lwp)
+{
+ int i;
+ CORE_ADDR pc;
+ struct regcache *regcache = get_thread_regcache (current_thread, 1);
+ VEC (CORE_ADDR) *next_pcs = NULL;
+
+ pc = get_pc (lwp);
+ next_pcs = (*the_low_target.get_next_pcs) (pc, regcache);
+
+ for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); ++i)
+ {
+ set_reinsert_breakpoint (pc);
+ }
+
+ VEC_free (CORE_ADDR, next_pcs);
+}
+
/* Resume execution of LWP. If STEP is nonzero, single-step it. If
SIGNAL is nonzero, give it that signal. */
@@ -4439,8 +4460,7 @@ start_step_over (struct lwp_info *lwp)
}
else if (can_software_single_step ())
{
- CORE_ADDR raddr = (*the_low_target.breakpoint_reinsert_addr) ();
- set_reinsert_breakpoint (raddr);
+ install_software_single_step_breakpoints (lwp);
step = 0;
}
else
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index d60d97d..ee8d88f 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -124,6 +124,8 @@ struct process_info_private
struct lwp_info;
+DEF_VEC_I (CORE_ADDR);
+
struct linux_target_ops
{
/* Architecture-specific setup. */
@@ -153,7 +155,8 @@ struct linux_target_ops
/* See target.h for details. */
const gdb_byte *(*sw_breakpoint_from_kind) (int kind, int *size);
- CORE_ADDR (*breakpoint_reinsert_addr) (void);
+ /* Find the next possible PCs after the current instruction executes. */
+ VEC (CORE_ADDR) *(*get_next_pcs) (CORE_ADDR pc, struct regcache *regcache);
int decr_pc_after_break;
int (*breakpoint_at) (CORE_ADDR pc);
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index b8f8805..9da4610 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -880,7 +880,7 @@ struct linux_target_ops the_low_target = {
mips_set_pc,
NULL, /* breakpoint_kind_from_pc */
mips_sw_breakpoint_from_kind,
- NULL, /* breakpoint_reinsert_addr */
+ NULL, /* get_next_pcs */
0,
mips_breakpoint_at,
mips_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-nios2-low.c b/gdb/gdbserver/linux-nios2-low.c
index 9380c3b..1accc03 100644
--- a/gdb/gdbserver/linux-nios2-low.c
+++ b/gdb/gdbserver/linux-nios2-low.c
@@ -267,7 +267,7 @@ struct linux_target_ops the_low_target =
nios2_set_pc,
NULL, /* breakpoint_kind_from_pc */
nios2_sw_breakpoint_from_kind,
- NULL, /* breakpoint_reinsert_addr */
+ NULL, /* get_next_pcs */
0,
nios2_breakpoint_at,
};
diff --git a/gdb/gdbserver/linux-sparc-low.c b/gdb/gdbserver/linux-sparc-low.c
index 54a849c..bfdd8c1 100644
--- a/gdb/gdbserver/linux-sparc-low.c
+++ b/gdb/gdbserver/linux-sparc-low.c
@@ -320,7 +320,7 @@ struct linux_target_ops the_low_target = {
NULL,
NULL, /* breakpoint_kind_from_pc */
sparc_sw_breakpoint_from_kind,
- NULL, /* breakpoint_reinsert_addr */
+ NULL, /* get_next_pcs */
0,
sparc_breakpoint_at,
NULL, /* supports_z_point_type */
--
2.6.3
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH v3 04/10] Remove support for thread events without PTRACE_EVENT_CLONE in GDBServer.
2015-11-23 14:14 [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
` (2 preceding siblings ...)
2015-11-23 14:14 ` [PATCH v3 05/10] Remove too simple breakpoint_reinsert_addr implementations Antoine Tremblay
@ 2015-11-23 14:14 ` Antoine Tremblay
2015-11-25 16:48 ` Yao Qi
2015-11-23 14:14 ` [PATCH v3 06/10] Replace breakpoint_reinsert_addr by get_next_pcs operation " Antoine Tremblay
` (6 subsequent siblings)
10 siblings, 1 reply; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-23 14:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
This patch removes support for thread events if PTRACE_EVENT_CLONE is not
supported in GDBServer.
Before, on systems that did not support PTRACE_EVENT_CLONE, both GDB and
GDBServer coordinated with libthread_db.so to insert breakpoints at magic
locations in libpthread.so, in order to break at thread creation and thread
death.
Simple software single stepping support was implemented to step over these
breakpoints in case there was no hardware single stepping support. However,
these simple software single stepping implementations were not fit for any other
use as discussed in :
https://sourceware.org/ml/gdb-patches/2015-04/msg01110.html
These too simple implementations conflict with ongoing work to make proper
implementations of software single stepping in GDBServer.
The problem is that if some implementations are correct and others are not and
only there for the thread magic breakpoint, we can't enable features based
solely software single step support since some would be broken.
To keep the incorrect implementations and allow the new proper ones at the same
time we would need to implement fallback code and it quickly becomes ugly and
confusing with multiple checks for legacy software single step or proper
software single step.
However, PTRACE_EVENT_CLONE was first introduced in Linux 2.5.46,
released in November 2002.
So I think it's reasonable to just remove support for kernels that don't support
PTRACE_EVENT_CLONE, and sidestep the libthread_db breakpoints issues entirely.
This thread on the mailling list discusses the issue :
https://sourceware.org/ml/gdb/2015-10/msg00078.html
No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }
gdb/gdbserver/ChangeLog:
* linux-low.c (linux_look_up_symbols): Don't call
linux_supports_traceclone.
* linux-low.h (thread_db_init): Remove use_events argument.
* thread-db.c (thread_db_use_event): Remove global variable.
(struct thread_db) <td_thr_event_enable_p>: Remove field.
(struct thread_db) <td_create_bp>: Remove field.
(thread_db_create_event): Remove function.
(thread_db_enable_reporting): Likewise.
(find_one_thread): Don't check for thread_db_use_events.
(attach_thread): Likewise.
(thread_db_load_search): Remove td_thr_event_enable_p initialization.
(try_thread_db_load_1): Don't check for thread_db_use_events.
(thread_db_init): Remove use_events argument and thread events
handling.
(remove_thread_event_breakpoints): Remove function.
(thread_db_detach): Remove call to remove_thred_event_breakpoints.
---
gdb/gdbserver/linux-low.c | 2 +-
gdb/gdbserver/linux-low.h | 2 +-
gdb/gdbserver/thread-db.c | 153 ++--------------------------------------------
3 files changed, 6 insertions(+), 151 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index f793a78..ce9cc2e 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5515,7 +5515,7 @@ linux_look_up_symbols (void)
/* If the kernel supports tracing clones, then we don't need to
use the magic thread event breakpoint to learn about
threads. */
- thread_db_init (!linux_supports_traceclone ());
+ thread_db_init ();
#endif
}
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index 2e1f32f..d60d97d 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -379,7 +379,7 @@ void initialize_regsets_info (struct regsets_info *regsets_info);
void initialize_low_arch (void);
/* From thread-db.c */
-int thread_db_init (int use_events);
+int thread_db_init (void);
void thread_db_detach (struct process_info *);
void thread_db_mourn (struct process_info *);
int thread_db_handle_monitor_command (char *);
diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c
index 3df10ff..8b3057a 100644
--- a/gdb/gdbserver/thread-db.c
+++ b/gdb/gdbserver/thread-db.c
@@ -24,8 +24,6 @@
extern int debug_threads;
-static int thread_db_use_events;
-
#include "gdb_proc_service.h"
#include "nat/gdb_thread_db.h"
#include "gdb_vecs.h"
@@ -56,16 +54,6 @@ struct thread_db
void *handle;
#endif
- /* Thread creation event breakpoint. The code at this location in
- the child process will be called by the pthread library whenever
- a new thread is created. By setting a special breakpoint at this
- location, GDB can detect when a new thread is created. We obtain
- this location via the td_ta_event_addr call. Note that if the
- running kernel supports tracing clones, then we don't need to use
- (and in fact don't use) this magic thread event breakpoint to
- learn about threads. */
- struct breakpoint *td_create_bp;
-
/* Addresses of libthread_db functions. */
td_ta_new_ftype *td_ta_new_p;
td_ta_event_getmsg_ftype * td_ta_event_getmsg_p;
@@ -73,7 +61,6 @@ struct thread_db
td_ta_event_addr_ftype *td_ta_event_addr_p;
td_ta_map_lwp2thr_ftype *td_ta_map_lwp2thr_p;
td_thr_get_info_ftype *td_thr_get_info_p;
- td_thr_event_enable_ftype *td_thr_event_enable_p;
td_ta_thr_iter_ftype *td_ta_thr_iter_p;
td_thr_tls_get_addr_ftype *td_thr_tls_get_addr_p;
td_thr_tlsbase_ftype *td_thr_tlsbase_p;
@@ -172,84 +159,6 @@ thread_db_state_str (td_thr_state_e state)
#endif
static int
-thread_db_create_event (CORE_ADDR where)
-{
- td_event_msg_t msg;
- td_err_e err;
- struct lwp_info *lwp;
- struct thread_db *thread_db = current_process ()->priv->thread_db;
-
- gdb_assert (thread_db->td_ta_event_getmsg_p != NULL);
-
- if (debug_threads)
- debug_printf ("Thread creation event.\n");
-
- /* FIXME: This assumes we don't get another event.
- In the LinuxThreads implementation, this is safe,
- because all events come from the manager thread
- (except for its own creation, of course). */
- err = thread_db->td_ta_event_getmsg_p (thread_db->thread_agent, &msg);
- if (err != TD_OK)
- fprintf (stderr, "thread getmsg err: %s\n",
- thread_db_err_str (err));
-
- /* If we do not know about the main thread yet, this would be a good time to
- find it. We need to do this to pick up the main thread before any newly
- created threads. */
- lwp = get_thread_lwp (current_thread);
- if (lwp->thread_known == 0)
- find_one_thread (current_thread->entry.id);
-
- /* msg.event == TD_EVENT_CREATE */
-
- find_new_threads_callback (msg.th_p, NULL);
-
- return 0;
-}
-
-static int
-thread_db_enable_reporting (void)
-{
- td_thr_events_t events;
- td_notify_t notify;
- td_err_e err;
- struct thread_db *thread_db = current_process ()->priv->thread_db;
-
- if (thread_db->td_ta_set_event_p == NULL
- || thread_db->td_ta_event_addr_p == NULL
- || thread_db->td_ta_event_getmsg_p == NULL)
- /* This libthread_db is missing required support. */
- return 0;
-
- /* Set the process wide mask saying which events we're interested in. */
- td_event_emptyset (&events);
- td_event_addset (&events, TD_CREATE);
-
- err = thread_db->td_ta_set_event_p (thread_db->thread_agent, &events);
- if (err != TD_OK)
- {
- warning ("Unable to set global thread event mask: %s",
- thread_db_err_str (err));
- return 0;
- }
-
- /* Get address for thread creation breakpoint. */
- err = thread_db->td_ta_event_addr_p (thread_db->thread_agent, TD_CREATE,
- ¬ify);
- if (err != TD_OK)
- {
- warning ("Unable to get location for thread creation breakpoint: %s",
- thread_db_err_str (err));
- return 0;
- }
- thread_db->td_create_bp
- = set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr,
- thread_db_create_event);
-
- return 1;
-}
-
-static int
find_one_thread (ptid_t ptid)
{
td_thrhandle_t th;
@@ -287,14 +196,6 @@ find_one_thread (ptid_t ptid)
return 0;
}
- if (thread_db_use_events)
- {
- err = thread_db->td_thr_event_enable_p (&th, 1);
- if (err != TD_OK)
- error ("Cannot enable thread event reporting for %d: %s",
- ti.ti_lid, thread_db_err_str (err));
- }
-
/* If the new thread ID is zero, a final thread ID will be available
later. Do not enable thread debugging yet. */
if (ti.ti_tid == 0)
@@ -334,17 +235,6 @@ attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
lwp->thread_known = 1;
lwp->th = *th_p;
- if (thread_db_use_events)
- {
- td_err_e err;
- struct thread_db *thread_db = proc->priv->thread_db;
-
- err = thread_db->td_thr_event_enable_p (th_p, 1);
- if (err != TD_OK)
- error ("Cannot enable thread event reporting for %d: %s",
- ti_p->ti_lid, thread_db_err_str (err));
- }
-
return 1;
}
@@ -584,9 +474,6 @@ thread_db_load_search (void)
tdb->td_ta_thr_iter_p = &td_ta_thr_iter;
tdb->td_symbol_list_p = &td_symbol_list;
- /* This is required only when thread_db_use_events is on. */
- tdb->td_thr_event_enable_p = &td_thr_event_enable;
-
/* These are not essential. */
tdb->td_ta_event_addr_p = &td_ta_event_addr;
tdb->td_ta_set_event_p = &td_ta_set_event;
@@ -654,9 +541,6 @@ try_thread_db_load_1 (void *handle)
CHK (1, TDB_DLSYM (tdb, td_ta_thr_iter));
CHK (1, TDB_DLSYM (tdb, td_symbol_list));
- /* This is required only when thread_db_use_events is on. */
- CHK (thread_db_use_events, TDB_DLSYM (tdb, td_thr_event_enable));
-
/* These are not essential. */
CHK (0, TDB_DLSYM (tdb, td_ta_event_addr));
CHK (0, TDB_DLSYM (tdb, td_ta_set_event));
@@ -824,7 +708,7 @@ thread_db_load_search (void)
#endif /* USE_LIBTHREAD_DB_DIRECTLY */
int
-thread_db_init (int use_events)
+thread_db_init (void)
{
struct process_info *proc = current_process ();
@@ -839,31 +723,21 @@ thread_db_init (int use_events)
This isn't the only place in gdbserver that assumes that the first
process in the list is the thread group leader. */
- thread_db_use_events = use_events;
-
if (thread_db_load_search ())
{
- if (use_events && thread_db_enable_reporting () == 0)
- {
- /* Keep trying; maybe event reporting will work later. */
- thread_db_mourn (proc);
- return 0;
- }
-
/* It's best to avoid td_ta_thr_iter if possible. That walks
data structures in the inferior's address space that may be
corrupted, or, if the target is running, the list may change
while we walk it. In the latter case, it's possible that a
thread exits just at the exact time that causes GDBserver to
- get stuck in an infinite loop. If the kernel supports clone
- events, and /proc/PID/task/ exits, then we already know about
+ get stuck in an infinite loop. As the kernel supports clone
+ events and /proc/PID/task/ exists, then we already know about
all threads in the process. When we need info out of
thread_db on a given thread (e.g., for TLS), we'll use
find_one_thread then. That uses thread_db entry points that
do not walk libpthread's thread list, so should be safe, as
well as more efficient. */
- if (use_events
- || !linux_proc_task_list_dir_exists (pid_of (proc)))
+ if (!linux_proc_task_list_dir_exists (pid_of (proc)))
thread_db_find_new_threads ();
thread_db_look_up_symbols ();
return 1;
@@ -929,24 +803,6 @@ disable_thread_event_reporting (struct process_info *proc)
}
}
-static void
-remove_thread_event_breakpoints (struct process_info *proc)
-{
- struct thread_db *thread_db = proc->priv->thread_db;
-
- if (thread_db->td_create_bp != NULL)
- {
- struct thread_info *saved_thread = current_thread;
-
- switch_to_process (proc);
-
- delete_breakpoint (thread_db->td_create_bp);
- thread_db->td_create_bp = NULL;
-
- current_thread = saved_thread;
- }
-}
-
void
thread_db_detach (struct process_info *proc)
{
@@ -955,7 +811,6 @@ thread_db_detach (struct process_info *proc)
if (thread_db)
{
disable_thread_event_reporting (proc);
- remove_thread_event_breakpoints (proc);
}
}
--
2.6.3
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH v3 05/10] Remove too simple breakpoint_reinsert_addr implementations.
2015-11-23 14:14 [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 09/10] Enable software single stepping for while-stepping actions " Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 02/10] Fix instruction skipping when using software single step " Antoine Tremblay
@ 2015-11-23 14:14 ` Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 04/10] Remove support for thread events without PTRACE_EVENT_CLONE in GDBServer Antoine Tremblay
` (7 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-23 14:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
This patch removes too simple implementations of the breakpoint_reinsert_addr
operation.
The only reason to keep them around was to support thread events when
PTRACE_EVENT_CLONE was not present but this support has been removed in a
previous patch.
No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }
Also compilation was tested on aarch64, bfin, cris, crisv32,
m32r, mips, nios2, ppc, s390, sparc, tic6x, tile, xtensa.
gdb/gdbserver/ChangeLog:
* linux-arm-low.c (arm_reinsert_addr): Remove function.
(struct linux_target_ops <breakpoint_reinsert_addr>: Set to NULL.
* linux-cris-low.c (cris_reinsert_addr> Remove function.
(struct linux_target_ops) <breakpoint_reinsert_addr>: Set to NULL.
* linux-crisv32-low.c (cris_reinsert_addr): Remove function.
(struct linux_target_ops) <breakpoint_reinsert_addr>: Set to NULL.
* linux-mips-low.c (mips_reinsert_addr): Remove function.
(struct linux_target_ops) <breakpoint_reinsert_addr>: Set to NULL.
* linux-nios2-low.c (nios2_reinsert_addr): Remove function.
(struct linux_target_ops) <breakpoint_reinsert_addr>: Set to NULL.
* linux-sparc-low.c (sparc_reinsert_addr): Remove function.
(struct linux_target_ops) <breakpoint_reinsert_addr>: Set to NULL.
---
gdb/gdbserver/linux-arm-low.c | 14 +-------------
gdb/gdbserver/linux-cris-low.c | 18 +-----------------
gdb/gdbserver/linux-crisv32-low.c | 19 +------------------
gdb/gdbserver/linux-mips-low.c | 14 +-------------
gdb/gdbserver/linux-nios2-low.c | 14 +-------------
gdb/gdbserver/linux-sparc-low.c | 15 +--------------
6 files changed, 6 insertions(+), 88 deletions(-)
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 0c78888..885aec9 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -317,18 +317,6 @@ arm_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 CORE_ADDR
-arm_reinsert_addr (void)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- unsigned long pc;
- collect_register_by_name (regcache, "lr", &pc);
- return pc;
-}
-
/* Fetch the thread-local storage pointer for libthread_db. */
ps_err_e
@@ -1044,7 +1032,7 @@ struct linux_target_ops the_low_target = {
arm_set_pc,
arm_breakpoint_kind_from_pc,
arm_sw_breakpoint_from_kind,
- arm_reinsert_addr,
+ NULL, /* breakpoint_reinsert_addr */
0,
arm_breakpoint_at,
arm_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-cris-low.c b/gdb/gdbserver/linux-cris-low.c
index d7b70e3..9f4519c 100644
--- a/gdb/gdbserver/linux-cris-low.c
+++ b/gdb/gdbserver/linux-cris-low.c
@@ -105,18 +105,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 CORE_ADDR
-cris_reinsert_addr (void)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- unsigned long pc;
- collect_register_by_name (regcache, "srp", &pc);
- return pc;
-}
-
static void
cris_arch_setup (void)
{
@@ -151,13 +139,9 @@ struct linux_target_ops the_low_target = {
cris_set_pc,
NULL, /* breakpoint_kind_from_pc */
cris_sw_breakpoint_from_kind,
- cris_reinsert_addr,
+ NULL, /* breakpoint_reinsert_addr */
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 4f9afa6..2404d0e 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -101,23 +101,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 CORE_ADDR
-cris_reinsert_addr (void)
-{
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
- unsigned long pc;
- collect_register_by_name (regcache, "srp", &pc);
- return pc;
-}
-
static void
cris_write_data_breakpoint (struct regcache *regcache,
int bp, unsigned long start, unsigned long end)
@@ -439,7 +422,7 @@ struct linux_target_ops the_low_target = {
cris_set_pc,
NULL, /* breakpoint_kind_from_pc */
cris_sw_breakpoint_from_kind,
- cris_reinsert_addr,
+ NULL, /* breakpoint_reinsert_addr */
0,
cris_breakpoint_at,
cris_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index 459f4fc..b8f8805 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -275,18 +275,6 @@ mips_sw_breakpoint_from_kind (int kind, int *size)
return (const gdb_byte *) &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 CORE_ADDR
-mips_reinsert_addr (void)
-{
- 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;
-}
-
static int
mips_breakpoint_at (CORE_ADDR where)
{
@@ -892,7 +880,7 @@ struct linux_target_ops the_low_target = {
mips_set_pc,
NULL, /* breakpoint_kind_from_pc */
mips_sw_breakpoint_from_kind,
- mips_reinsert_addr,
+ NULL, /* breakpoint_reinsert_addr */
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 369e89c..9380c3b 100644
--- a/gdb/gdbserver/linux-nios2-low.c
+++ b/gdb/gdbserver/linux-nios2-low.c
@@ -144,18 +144,6 @@ nios2_sw_breakpoint_from_kind (int kind, int *size)
return (const gdb_byte *) &nios2_breakpoint;
}
-/* Implement the breakpoint_reinsert_addr linux_target_ops method. */
-
-static CORE_ADDR
-nios2_reinsert_addr (void)
-{
- union nios2_register ra;
- struct regcache *regcache = get_thread_regcache (current_thread, 1);
-
- collect_register_by_name (regcache, "ra", ra.buf);
- return ra.reg32;
-}
-
/* Implement the breakpoint_at linux_target_ops method. */
static int
@@ -279,7 +267,7 @@ struct linux_target_ops the_low_target =
nios2_set_pc,
NULL, /* breakpoint_kind_from_pc */
nios2_sw_breakpoint_from_kind,
- nios2_reinsert_addr,
+ NULL, /* breakpoint_reinsert_addr */
0,
nios2_breakpoint_at,
};
diff --git a/gdb/gdbserver/linux-sparc-low.c b/gdb/gdbserver/linux-sparc-low.c
index e6a4f84..54a849c 100644
--- a/gdb/gdbserver/linux-sparc-low.c
+++ b/gdb/gdbserver/linux-sparc-low.c
@@ -265,19 +265,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 CORE_ADDR
-sparc_reinsert_addr (void)
-{
- 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;
-}
-
static void
sparc_arch_setup (void)
{
@@ -333,7 +320,7 @@ struct linux_target_ops the_low_target = {
NULL,
NULL, /* breakpoint_kind_from_pc */
sparc_sw_breakpoint_from_kind,
- sparc_reinsert_addr,
+ NULL, /* breakpoint_reinsert_addr */
0,
sparc_breakpoint_at,
NULL, /* supports_z_point_type */
--
2.6.3
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH v3 07/10] Share some ARM target dependant code from GDB with GDBServer.
2015-11-23 14:14 [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
` (6 preceding siblings ...)
2015-11-23 14:14 ` [PATCH v3 03/10] Refactor queries for hardware and software single stepping support " Antoine Tremblay
@ 2015-11-23 14:14 ` Antoine Tremblay
2015-11-25 17:01 ` Yao Qi
2015-11-26 10:38 ` Yao Qi
2015-11-23 14:15 ` [PATCH v3 08/10] Support software single step on ARM in GDBServer Antoine Tremblay
` (2 subsequent siblings)
10 siblings, 2 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-23 14:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
This patch is in preparation for software single stepping support on ARM it
shares some functions and defenitions that will be needed.
No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }
Not tested : wince/bsd build.
gdb/ChangeLog:
* arch/arm.c (bitcount): Move from arm-tdep.c.
(condition_true): Likewise.
* arch/arm.h (Instruction Definitions): Move form arm-tdep.h.
(condition_true): Move defenition from arm-tdep.h.
(bitcount): Likewise.
* arm-tdep.c (condition_true): Move to arch/arm.c.
(bitcount): Likewise.
* arm-tdep.h (Instruction Definitions): Move to arch/arm.h.
* arm-wince-tdep.c: Include arch/arm.h.
* armnbsd-tdep.c: Likewise.
---
gdb/arch/arm.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++--
gdb/arch/arm.h | 36 +++++++++++++++++++++++++++++++
gdb/arm-tdep.c | 55 -----------------------------------------------
gdb/arm-tdep.h | 29 -------------------------
gdb/arm-wince-tdep.c | 1 +
gdb/armnbsd-tdep.c | 1 +
6 files changed, 96 insertions(+), 86 deletions(-)
diff --git a/gdb/arch/arm.c b/gdb/arch/arm.c
index b11c684..426377f 100644
--- a/gdb/arch/arm.c
+++ b/gdb/arch/arm.c
@@ -20,8 +20,7 @@
#include "common-defs.h"
#include "arm.h"
-/* Return the size in bytes of the complete Thumb instruction whose
- first halfword is INST1. */
+/* See arm.h. */
int
thumb_insn_size (unsigned short inst1)
@@ -31,3 +30,60 @@ thumb_insn_size (unsigned short inst1)
else
return 2;
}
+
+/* See arm.h. */
+
+int
+bitcount (unsigned long val)
+{
+ int nbits;
+ for (nbits = 0; val != 0; nbits++)
+ val &= val - 1; /* Delete rightmost 1-bit in val. */
+ return nbits;
+}
+
+/* See arm.h. */
+
+int
+condition_true (unsigned long cond, unsigned long status_reg)
+{
+ if (cond == INST_AL || cond == INST_NV)
+ return 1;
+
+ switch (cond)
+ {
+ case INST_EQ:
+ return ((status_reg & FLAG_Z) != 0);
+ case INST_NE:
+ return ((status_reg & FLAG_Z) == 0);
+ case INST_CS:
+ return ((status_reg & FLAG_C) != 0);
+ case INST_CC:
+ return ((status_reg & FLAG_C) == 0);
+ case INST_MI:
+ return ((status_reg & FLAG_N) != 0);
+ case INST_PL:
+ return ((status_reg & FLAG_N) == 0);
+ case INST_VS:
+ return ((status_reg & FLAG_V) != 0);
+ case INST_VC:
+ return ((status_reg & FLAG_V) == 0);
+ case INST_HI:
+ return ((status_reg & (FLAG_C | FLAG_Z)) == FLAG_C);
+ case INST_LS:
+ return ((status_reg & (FLAG_C | FLAG_Z)) != FLAG_C);
+ case INST_GE:
+ return (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0));
+ case INST_LT:
+ return (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0));
+ case INST_GT:
+ return (((status_reg & FLAG_Z) == 0)
+ && (((status_reg & FLAG_N) == 0)
+ == ((status_reg & FLAG_V) == 0)));
+ case INST_LE:
+ return (((status_reg & FLAG_Z) != 0)
+ || (((status_reg & FLAG_N) == 0)
+ != ((status_reg & FLAG_V) == 0)));
+ }
+ return 1;
+}
diff --git a/gdb/arch/arm.h b/gdb/arch/arm.h
index a054776..1a877bc 100644
--- a/gdb/arch/arm.h
+++ b/gdb/arch/arm.h
@@ -58,6 +58,36 @@ enum gdb_regnum {
ARM_LAST_FP_ARG_REGNUM = ARM_F3_REGNUM
};
+/* Instruction condition field values. */
+#define INST_EQ 0x0
+#define INST_NE 0x1
+#define INST_CS 0x2
+#define INST_CC 0x3
+#define INST_MI 0x4
+#define INST_PL 0x5
+#define INST_VS 0x6
+#define INST_VC 0x7
+#define INST_HI 0x8
+#define INST_LS 0x9
+#define INST_GE 0xa
+#define INST_LT 0xb
+#define INST_GT 0xc
+#define INST_LE 0xd
+#define INST_AL 0xe
+#define INST_NV 0xf
+
+#define FLAG_N 0x80000000
+#define FLAG_Z 0x40000000
+#define FLAG_C 0x20000000
+#define FLAG_V 0x10000000
+
+#define CPSR_T 0x20
+
+#define XPSR_T 0x01000000
+
+/* Size of integer registers. */
+#define INT_REGISTER_SIZE 4
+
/* Addresses for calling Thumb functions have the bit 0 set.
Here are some macros to test, set, or clear bit 0 of addresses. */
#define IS_THUMB_ADDR(addr) ((addr) & 1)
@@ -68,4 +98,10 @@ enum gdb_regnum {
first halfword is INST1. */
int thumb_insn_size (unsigned short inst1);
+/* Returns true if the condition evaluates to true. */
+int condition_true (unsigned long cond, unsigned long status_reg);
+
+/* Return number of 1-bits in VAL. */
+int bitcount (unsigned long val);
+
#endif
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index ef1a007..c361f62 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -4290,50 +4290,6 @@ convert_to_extended (const struct floatformat *fmt, void *dbl, const void *ptr,
&d, dbl);
}
-static int
-condition_true (unsigned long cond, unsigned long status_reg)
-{
- if (cond == INST_AL || cond == INST_NV)
- return 1;
-
- switch (cond)
- {
- case INST_EQ:
- return ((status_reg & FLAG_Z) != 0);
- case INST_NE:
- return ((status_reg & FLAG_Z) == 0);
- case INST_CS:
- return ((status_reg & FLAG_C) != 0);
- case INST_CC:
- return ((status_reg & FLAG_C) == 0);
- case INST_MI:
- return ((status_reg & FLAG_N) != 0);
- case INST_PL:
- return ((status_reg & FLAG_N) == 0);
- case INST_VS:
- return ((status_reg & FLAG_V) != 0);
- case INST_VC:
- return ((status_reg & FLAG_V) == 0);
- case INST_HI:
- return ((status_reg & (FLAG_C | FLAG_Z)) == FLAG_C);
- case INST_LS:
- return ((status_reg & (FLAG_C | FLAG_Z)) != FLAG_C);
- case INST_GE:
- return (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0));
- case INST_LT:
- return (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0));
- case INST_GT:
- return (((status_reg & FLAG_Z) == 0)
- && (((status_reg & FLAG_N) == 0)
- == ((status_reg & FLAG_V) == 0)));
- case INST_LE:
- return (((status_reg & FLAG_Z) != 0)
- || (((status_reg & FLAG_N) == 0)
- != ((status_reg & FLAG_V) == 0)));
- }
- return 1;
-}
-
static unsigned long
shifted_reg_val (struct frame_info *frame, unsigned long inst, int carry,
unsigned long pc_val, unsigned long status_reg)
@@ -4384,17 +4340,6 @@ shifted_reg_val (struct frame_info *frame, unsigned long inst, int carry,
return res & 0xffffffff;
}
-/* Return number of 1-bits in VAL. */
-
-static int
-bitcount (unsigned long val)
-{
- int nbits;
- for (nbits = 0; val != 0; nbits++)
- val &= val - 1; /* Delete rightmost 1-bit in val. */
- return nbits;
-}
-
static int
thumb_advance_itstate (unsigned int itstate)
{
diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h
index 3e06f79..9b8447b 100644
--- a/gdb/arm-tdep.h
+++ b/gdb/arm-tdep.h
@@ -26,9 +26,6 @@ struct address_space;
#include "arch/arm.h"
-/* Size of integer registers. */
-#define INT_REGISTER_SIZE 4
-
/* Say how long FP registers are. Used for documentation purposes and
code readability in this header. IEEE extended doubles are 80
bits. DWORD aligned they use 96 bits. */
@@ -50,32 +47,6 @@ struct address_space;
#define NUM_GREGS 16 /* Number of general purpose registers. */
-/* Instruction condition field values. */
-#define INST_EQ 0x0
-#define INST_NE 0x1
-#define INST_CS 0x2
-#define INST_CC 0x3
-#define INST_MI 0x4
-#define INST_PL 0x5
-#define INST_VS 0x6
-#define INST_VC 0x7
-#define INST_HI 0x8
-#define INST_LS 0x9
-#define INST_GE 0xa
-#define INST_LT 0xb
-#define INST_GT 0xc
-#define INST_LE 0xd
-#define INST_AL 0xe
-#define INST_NV 0xf
-
-#define FLAG_N 0x80000000
-#define FLAG_Z 0x40000000
-#define FLAG_C 0x20000000
-#define FLAG_V 0x10000000
-
-#define CPSR_T 0x20
-
-#define XPSR_T 0x01000000
/* Type of floating-point code in use by inferior. There are really 3 models
that are traditionally supported (plus the endianness issue), but gcc can
diff --git a/gdb/arm-wince-tdep.c b/gdb/arm-wince-tdep.c
index 72295ba..3abd89d 100644
--- a/gdb/arm-wince-tdep.c
+++ b/gdb/arm-wince-tdep.c
@@ -24,6 +24,7 @@
#include "target.h"
#include "frame.h"
+#include "arch/arm.h"
#include "arm-tdep.h"
#include "windows-tdep.h"
diff --git a/gdb/armnbsd-tdep.c b/gdb/armnbsd-tdep.c
index 4c128c2..14eceaa 100644
--- a/gdb/armnbsd-tdep.c
+++ b/gdb/armnbsd-tdep.c
@@ -20,6 +20,7 @@
#include "defs.h"
#include "osabi.h"
+#include "arch/arm.h"
#include "arm-tdep.h"
#include "solib-svr4.h"
--
2.6.3
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH v3 03/10] Refactor queries for hardware and software single stepping support in GDBServer.
2015-11-23 14:14 [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
` (5 preceding siblings ...)
2015-11-23 14:14 ` [PATCH v3 01/10] Fix breakpoint size when stepping over a permanent breakpoint " Antoine Tremblay
@ 2015-11-23 14:14 ` Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 07/10] Share some ARM target dependant code from GDB with GDBServer Antoine Tremblay
` (3 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-23 14:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
Before this patch there was only one call: can_hardware_single_step. Its
implementation was a check on breakpoint_reinsert_addr if NULL it assumed
that the target could hardware single step.
This patch prepares for the case where this is not true anymore.
In order to improve software single stepping in GDBServer the
breakpoint_reinsert_addr operation of targets that had a very simple
software implementation used only for stepping over thread creation events
will be removed.
This will create a case where a target does not support hardware single
step and has the operation breakpoint_reinsert_addr set to NULL, thus
can_hardware_single_step needs to be implemented another way.
A new target operation supports_hardware_single_step is introduced and is
to return true if the target does support such a feature, support for the
feature is manually hardcoded.
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 on Ubuntu 14.04 on ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }
Compilation tested on: aarch64,arm,bfind,crisv32,m32r,ppc,s390,tic6x,tile,
xtensa.
Not tested : sh.
gdb/gdbserver/ChangeLog:
* linux-aarch64-low.c (aarch64_supports_hardware_single_step):
New function.
(struct linux_target_ops) <supports_hardware_single_step>: Initialize.
* linux-arm-low.c (arm_supports_hardware_single_step): New function.
(struct linux_target_ops) <supports_hardware_single_step>: Initialize.
* linux-bfin-low.c (bfin_supports_hardware_single_step): New function.
(struct linux_target_ops) <bfin_supports_hardware_single_step>:
Initialize.
* linux-crisv32-low.c (cris_supports_hardware_single_step):
New function.
(struct linux_target_ops) <supports_hardware_single_step>: Initialize.
* linux-low.c (can_hardware_single_step): Use
supports_hardware_single_step.
(can_software_single_step): New function.
(start_step_over): Call can_software_single_step.
(linux_supports_hardware_single_step): New function.
(struct target_ops) <supports_software_single_step>: Initialize.
* linux-low.h (struct linux_target_ops)
<supports_hardware_single_step>: Initialize.
* linux-m32r-low.c (m32r_supports_hardware_single_step): New function.
(struct linux_target_ops) <supports_hardware_single_step>: Initialize.
* linux-ppc-low.c (ppc_supports_hardware_single_step): New function.
(struct linux_target_ops) <supports_hardware_single_step> Initialize.
* linux-s390-low.c (s390_supports_hardware_single_step): New function.
(struct linux_target_ops) <supports_hardware_single_step>: Initialize.
* linux-sh-low.c (sh_supports_hardware_single_step): New function.
(struct linux_target_ops) <supports_hardware_single_step>: Initialize.
* linux-tic6x-low.c (tic6x_supports_hardware_single_step): New function.
(struct linux_target_ops) <tic6x_supports_hardware_single_step>:
Initialize.
* linux-tile-low.c (tile_supports_hardware_single_step): New function.
(struct linux_target_ops) <tile_supports_hardware_single_step>:
Initialize.
* linux-x86-low.c (x86_supports_hardware_single_step) New function.
(struct linux_target_ops) <supports_hardware_single_step>: Initialize.
* linux-xtensa-low.c (xtensa_supports_hardware_single_step):
New function.
(struct linux_target_ops) <supports_hardware_single_step>: Initialize.
* target.h (struct target_ops): <supports_software_single_step>:
New field.
(target_supports_software_single_step): New macro.
---
gdb/gdbserver/linux-aarch64-low.c | 10 ++++++++++
gdb/gdbserver/linux-arm-low.c | 11 ++++++++++-
gdb/gdbserver/linux-bfin-low.c | 29 +++++++++++++++++++++++++++++
gdb/gdbserver/linux-crisv32-low.c | 24 ++++++++++++++++++++++++
gdb/gdbserver/linux-low.c | 33 ++++++++++++++++++++++++++++-----
gdb/gdbserver/linux-low.h | 3 +++
gdb/gdbserver/linux-m32r-low.c | 29 +++++++++++++++++++++++++++++
gdb/gdbserver/linux-ppc-low.c | 22 ++++++++++++++++++++++
gdb/gdbserver/linux-s390-low.c | 22 ++++++++++++++++++++++
gdb/gdbserver/linux-sh-low.c | 29 +++++++++++++++++++++++++++++
gdb/gdbserver/linux-tic6x-low.c | 29 +++++++++++++++++++++++++++++
gdb/gdbserver/linux-tile-low.c | 29 +++++++++++++++++++++++++++++
gdb/gdbserver/linux-x86-low.c | 11 +++++++++++
gdb/gdbserver/linux-xtensa-low.c | 29 +++++++++++++++++++++++++++++
gdb/gdbserver/target.h | 7 +++++++
15 files changed, 311 insertions(+), 6 deletions(-)
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index cb78666..17798ff 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -2944,6 +2944,14 @@ aarch64_sw_breakpoint_from_kind (int kind, int *size)
return aarch64_breakpoint;
}
+/* 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,
@@ -2977,6 +2985,8 @@ struct linux_target_ops the_low_target =
aarch64_emit_ops,
aarch64_get_min_fast_tracepoint_insn_len,
aarch64_supports_range_stepping,
+ NULL, /* breakpoint_kind_from_current_state */
+ aarch64_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 6f37f58..0c78888 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -902,6 +902,14 @@ arm_arch_setup (void)
have_ptrace_getregset = 0;
}
+/* 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[] = {
@@ -1058,7 +1066,8 @@ struct linux_target_ops the_low_target = {
NULL, /* emit_ops */
NULL, /* get_min_fast_tracepoint_insn_len */
NULL, /* supports_range_stepping */
- arm_breakpoint_kind_from_current_state
+ arm_breakpoint_kind_from_current_state,
+ arm_supports_hardware_single_step
};
void
diff --git a/gdb/gdbserver/linux-bfin-low.c b/gdb/gdbserver/linux-bfin-low.c
index d3b83fc..912d253 100644
--- a/gdb/gdbserver/linux-bfin-low.c
+++ b/gdb/gdbserver/linux-bfin-low.c
@@ -105,6 +105,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,
@@ -136,6 +144,27 @@ struct linux_target_ops the_low_target = {
NULL, /* breakpoint_reinsert_addr */
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 */
+ NULL, /* breakpoint_kind_from_current_state */
+ bfin_supports_hardware_single_step,
};
diff --git a/gdb/gdbserver/linux-crisv32-low.c b/gdb/gdbserver/linux-crisv32-low.c
index 8d9ef04..4f9afa6 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -388,6 +388,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 },
@@ -439,6 +447,22 @@ 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 */
+ NULL, /* breakpoint_kind_from_current_state */
+ cris_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 63ac8b3..f793a78 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -269,13 +269,24 @@ 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 BREAKPOINT_REINSERT_ADDR callback. */
+/* True if the low target can hardware single-step. */
static int
can_hardware_single_step (void)
{
- return (the_low_target.breakpoint_reinsert_addr == 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 BREAKPOINT_REINSERT_ADDR callback. */
+
+static int
+can_software_single_step (void)
+{
+ return (the_low_target.breakpoint_reinsert_addr != NULL);
}
/* True if the low target supports memory breakpoints. If so, we'll
@@ -4426,12 +4437,17 @@ start_step_over (struct lwp_info *lwp)
{
step = 1;
}
- else
+ else if (can_software_single_step ())
{
CORE_ADDR raddr = (*the_low_target.breakpoint_reinsert_addr) ();
set_reinsert_breakpoint (raddr);
step = 0;
}
+ else
+ {
+ internal_error (__FILE__, __LINE__,
+ "stepping is not implemented on this target");
+ }
current_thread = saved_thread;
@@ -5626,6 +5642,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,7 +7086,8 @@ static struct target_ops linux_target_ops = {
linux_mntns_readlink,
linux_breakpoint_kind_from_pc,
linux_sw_breakpoint_from_kind,
- linux_breakpoint_kind_from_current_state
+ linux_breakpoint_kind_from_current_state,
+ linux_supports_software_single_step
};
static void
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index fb15136..2e1f32f 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -236,6 +236,9 @@ struct linux_target_ops
/* See target.h. */
int (*breakpoint_kind_from_current_state) (CORE_ADDR *pcptr);
+
+ /* See target.h. */
+ 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 bb1002f..c6d79b6 100644
--- a/gdb/gdbserver/linux-m32r-low.c
+++ b/gdb/gdbserver/linux-m32r-low.c
@@ -103,6 +103,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,
@@ -134,6 +142,27 @@ 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 */
+ NULL, /* breakpoint_kind_from_current_state */
+ m32r_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c
index 995a725..decee86 100644
--- a/gdb/gdbserver/linux-ppc-low.c
+++ b/gdb/gdbserver/linux-ppc-low.c
@@ -547,6 +547,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
@@ -705,6 +713,20 @@ 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 */
+ NULL, /* breakpoint_kind_from_current_state */
+ ppc_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-s390-low.c b/gdb/gdbserver/linux-s390-low.c
index efede2d..7127373 100644
--- a/gdb/gdbserver/linux-s390-low.c
+++ b/gdb/gdbserver/linux-s390-low.c
@@ -609,6 +609,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,
@@ -686,6 +694,20 @@ 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 */
+ NULL, /* breakpoint_kind_from_current_state */
+ s390_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-sh-low.c b/gdb/gdbserver/linux-sh-low.c
index 207e87e..1b3189b 100644
--- a/gdb/gdbserver/linux-sh-low.c
+++ b/gdb/gdbserver/linux-sh-low.c
@@ -100,6 +100,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. */
@@ -162,6 +170,27 @@ 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 */
+ NULL, /* breakpoint_kind_from_current_state */
+ sh_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-tic6x-low.c b/gdb/gdbserver/linux-tic6x-low.c
index cf8b5fb..e4aba15 100644
--- a/gdb/gdbserver/linux-tic6x-low.c
+++ b/gdb/gdbserver/linux-tic6x-low.c
@@ -341,6 +341,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 */
@@ -380,6 +388,27 @@ 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 */
+ NULL, /* breakpoint_kind_from_current_state */
+ tic6x_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-tile-low.c b/gdb/gdbserver/linux-tile-low.c
index 47ca245..6f16355 100644
--- a/gdb/gdbserver/linux-tile-low.c
+++ b/gdb/gdbserver/linux-tile-low.c
@@ -181,6 +181,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 =
{
@@ -196,6 +204,27 @@ struct linux_target_ops the_low_target =
NULL,
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 */
+ NULL, /* breakpoint_kind_from_current_state */
+ tile_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 89ec4e5..7c63919 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -3258,6 +3258,15 @@ x86_supports_range_stepping (void)
return 1;
}
+/* Implementation of linux_target_ops method "supports_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. */
@@ -3298,6 +3307,8 @@ struct linux_target_ops the_low_target =
x86_emit_ops,
x86_get_min_fast_tracepoint_insn_len,
x86_supports_range_stepping,
+ NULL, /* breakpoint_kind_from_current_state */
+ x86_supports_hardware_single_step,
};
void
diff --git a/gdb/gdbserver/linux-xtensa-low.c b/gdb/gdbserver/linux-xtensa-low.c
index 7c71631..b48e8dd 100644
--- a/gdb/gdbserver/linux-xtensa-low.c
+++ b/gdb/gdbserver/linux-xtensa-low.c
@@ -229,6 +229,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)
{
@@ -248,6 +256,27 @@ 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 */
+ NULL, /* breakpoint_kind_from_current_state */
+ xtensa_supports_hardware_single_step,
};
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 72f18d8..f142489 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -457,6 +457,9 @@ struct target_ops
PC. The PCPTR is adjusted to the real memory location in case a flag
(e.g., the Thumb bit on ARM) is present in the PC. */
int (*breakpoint_kind_from_current_state) (CORE_ADDR *pcptr);
+
+ /* Returns true if the target can software single step. */
+ int (*supports_software_single_step) (void);
};
extern struct target_ops *the_target;
@@ -649,6 +652,10 @@ int kill_inferior (int);
? (*the_target->breakpoint_kind_from_current_state) (pcptr) \
: target_breakpoint_kind_from_pc (pcptr))
+#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);
--
2.6.3
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH v3 01/10] Fix breakpoint size when stepping over a permanent breakpoint in GDBServer.
2015-11-23 14:14 [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
` (4 preceding siblings ...)
2015-11-23 14:14 ` [PATCH v3 06/10] Replace breakpoint_reinsert_addr by get_next_pcs operation " Antoine Tremblay
@ 2015-11-23 14:14 ` Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 03/10] Refactor queries for hardware and software single stepping support " Antoine Tremblay
` (4 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-23 14:14 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
When manually stepping over a permanent breakpoint on ARM we need to fetch the
right breakpoint size based on the current instruction set used.
Since this is not encoded in the stop_pc, the instruction mode needs to be
fetched from the CPSR register.
This is done by introducing a new target operation called :
breakpoint_kind_from_current_state.
For other targets that do not need this, breakpoint_kind_from_pc is used.
No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }
gdb/gdbserver/ChangeLog:
* linux-arm-low.c (arm_is_thumb_mode): New function.
(arm_breakpoint_at): Use arm_is_thumb_mode.
(arm_breakpoint_kind_from_current_state): New function.
(struct linux_target_ops) <breakpoint_kind_from_current_state>:
Initialize.
* linux-low.c (linux_wait_1): Call breakpoint_kind_from_current_state.
(linux_breakpoint_kind_from_current_state): New function.
(struct target_ops <breakpoint_kind_from_current_state>: Initialize.
* linux-low.h (struct linux_target_ops)
<breakpoint_kind_from_current_state>: New field.
* target.h (struct target_ops): Likewise.
(target_breakpoint_kind_from_current_state): New macro.
---
gdb/gdbserver/linux-arm-low.c | 40 +++++++++++++++++++++++++++++++++++++++-
gdb/gdbserver/linux-low.c | 18 ++++++++++++++++--
gdb/gdbserver/linux-low.h | 3 +++
gdb/gdbserver/target.h | 11 +++++++++++
4 files changed, 69 insertions(+), 3 deletions(-)
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index dda37cb..6f37f58 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -264,8 +264,10 @@ static const unsigned short thumb_breakpoint = 0xde01;
static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
#define thumb2_breakpoint_len 4
+/* Returns 1 if the current instruction set is thumb, 0 otherwise. */
+
static int
-arm_breakpoint_at (CORE_ADDR where)
+arm_is_thumb_mode (void)
{
struct regcache *regcache = get_thread_regcache (current_thread, 1);
unsigned long cpsr;
@@ -273,6 +275,17 @@ 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;
@@ -996,6 +1009,23 @@ arm_sw_breakpoint_from_kind (int kind , int *size)
return NULL;
}
+/* Implementation of the linux_target_ops method
+ "breakpoint_kind_from_current_state". */
+
+static int
+arm_breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
+{
+ if (arm_is_thumb_mode ())
+ {
+ *pcptr = MAKE_THUMB_ADDR (*pcptr);
+ return arm_breakpoint_kind_from_pc (pcptr);
+ }
+ else
+ {
+ return arm_breakpoint_kind_from_pc (pcptr);
+ }
+}
+
struct linux_target_ops the_low_target = {
arm_arch_setup,
arm_regs_info,
@@ -1021,6 +1051,14 @@ 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 */
+ arm_breakpoint_kind_from_current_state
};
void
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 41ab510..3b6c131 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -3006,7 +3006,8 @@ linux_wait_1 (ptid_t ptid,
int breakpoint_kind = 0;
CORE_ADDR stop_pc = event_child->stop_pc;
- breakpoint_kind = the_target->breakpoint_kind_from_pc (&stop_pc);
+ breakpoint_kind =
+ the_target->breakpoint_kind_from_current_state (&stop_pc);
the_target->sw_breakpoint_from_kind (breakpoint_kind, &increment_pc);
if (debug_threads)
@@ -6948,6 +6949,18 @@ linux_sw_breakpoint_from_kind (int kind, int *size)
return (*the_low_target.sw_breakpoint_from_kind) (kind, size);
}
+/* Implementation of the target_ops method
+ "breakpoint_kind_from_current_state". */
+
+static int
+linux_breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
+{
+ if (the_low_target.breakpoint_kind_from_current_state != NULL)
+ return (*the_low_target.breakpoint_kind_from_current_state) (pcptr);
+ else
+ return linux_breakpoint_kind_from_pc (pcptr);
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_arch_setup,
@@ -7043,7 +7056,8 @@ static struct target_ops linux_target_ops = {
linux_mntns_unlink,
linux_mntns_readlink,
linux_breakpoint_kind_from_pc,
- linux_sw_breakpoint_from_kind
+ linux_sw_breakpoint_from_kind,
+ linux_breakpoint_kind_from_current_state
};
static void
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index ccf4c94..fb15136 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -233,6 +233,9 @@ struct linux_target_ops
/* Returns true if the low target supports range stepping. */
int (*supports_range_stepping) (void);
+
+ /* See target.h. */
+ int (*breakpoint_kind_from_current_state) (CORE_ADDR *pcptr);
};
extern struct linux_target_ops the_low_target;
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 769c876..72f18d8 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -451,6 +451,12 @@ struct target_ops
specific meaning like the Z0 kind parameter.
SIZE is set to the software breakpoint's length in memory. */
const gdb_byte *(*sw_breakpoint_from_kind) (int kind, int *size);
+
+ /* Return the breakpoint kind for this target based on the current
+ processor state (e.g. the current instruction mode on ARM) and the
+ PC. The PCPTR is adjusted to the real memory location in case a flag
+ (e.g., the Thumb bit on ARM) is present in the PC. */
+ int (*breakpoint_kind_from_current_state) (CORE_ADDR *pcptr);
};
extern struct target_ops *the_target;
@@ -638,6 +644,11 @@ int kill_inferior (int);
? (*the_target->breakpoint_kind_from_pc) (pcptr) \
: default_breakpoint_kind_from_pc (pcptr))
+#define target_breakpoint_kind_from_current_state(pcptr) \
+ (the_target->breakpoint_kind_from_current_state \
+ ? (*the_target->breakpoint_kind_from_current_state) (pcptr) \
+ : target_breakpoint_kind_from_pc (pcptr))
+
/* Start non-stop mode, returns 0 on success, -1 on failure. */
int start_non_stop (int nonstop);
--
2.6.3
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH v3 08/10] Support software single step on ARM in GDBServer.
2015-11-23 14:14 [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
` (7 preceding siblings ...)
2015-11-23 14:14 ` [PATCH v3 07/10] Share some ARM target dependant code from GDB with GDBServer Antoine Tremblay
@ 2015-11-23 14:15 ` Antoine Tremblay
2015-11-26 10:49 ` Pedro Alves
2015-11-26 12:48 ` Yao Qi
2015-11-23 14:15 ` [PATCH v3 10/10] Enable conditional breakpoints for targets that support software single step " Antoine Tremblay
2015-11-27 9:27 ` [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM " Yao Qi
10 siblings, 2 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-23 14:15 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
In this v3:
* The common arm_get_next_pcs call has been refactored the same way like
so : VEC (CORE_ADDR) *arm_get_next_pcs (struct arm_get_next_pcs *ctx,
CORE_ADDR pc);
* Use ctor functions to construct gdb|gdbserver_get_next_pcs context.
* Some style fixes.
---
This patch teaches GDBserver how to software single step on ARM
linux by sharing code with GDB.
The arm_get_next_pcs function in GDB is now shared with GDBServer. So that
GDBServer can use the function to return the possible addresses of the next PC.
A proper shared context was also needed so that we could share the code, this
context is described as this data structure (expressed as a class
hierarchy):
struct arch_get_next_pcs
struct arch_(gdb|gdbserver)_get_next_pcs
Where arch can by replaced by arm for this patch. This structure should be
flexible enough to accomodate any arch that would need a get_next_pcs
context.
Limitations :
GDBServer can't step over a sigreturn or rt_sigreturn syscall since this would
require the DWARF information to identify the caller PC. This situation
only prints a warning for the moment.
Testing :
No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }
gdb/ChangeLog:
* Makefile.in: Add arm-get-next-pcs.c/o to all arm targets.
* aarch32-linux-nat.c: Include arch/arm-get-next-pcs.h.
* arch/arm-get-next-pcs.c: New file.
* arch/arm-get-next-pcs.h: New file.
* arm-linux-nat.c: Include arch/arm.h and arch/arm-get-next-pcs.h
* arm-linux-tdep.c: (arm_linux_software_single_step): Adjust for
arm_get_next_pcs implementation.
* arm-tdep.c (submask): Move macro to arm-get-next-pcs.h.
(bit): Likewise.
(bits): Likewise.
(sbits): Likewise.
(BranchDest): Likewise.
(thumb_instruction_changes_pc): Move to arm-get-next-pcs.c
(thumb2_instruction_changes_pc): Likewise.
(arm_instruction_changes_pc): Likewise.
(thumb_advance_itstate): Likewise.
(thumb_get_next_pc_raw): Likewise.
(arm_get_next_pc_raw): Likewise.
(arm_get_next_pc): Likewise.
(thumb_deal_with_atomic_sequence_raw): Likewise.
(arm_deal_with_atomic_sequence_raw): Likewise.
(arm_deal_with_atomic_sequence): Likewise.
(arm_get_next_pcs_read_memory_unsigned_integer): New function.
(arm_get_next_pcs_addr_bits_remove): Likewise.
(arm_get_next_pcs_collect_register_unsigned): Likewise.
(arm_get_next_pcs_syscall_next_pc): Likewise.
(arm_software_single_step): Adjust for arm_get_next_pcs
implementation.
(arm_gdb_get_next_pcs_ctor): New function.
* arm-tdep.h (arm_get_next_pcs_read_memory_unsigned_integer):
New declaration.
(arm_get_next_pcs_addr_bits_remove): Likewise.
(arm_get_next_pcs_collect_register_unsigned): Likewise.
(arm_get_next_pcs_syscall_next_pc): Likewise.
(arm_software_single_step): Likewise.
(arm_deal_with_atomic_sequence: Remove declaration.
(arm_software_single_step): Likewise.
(struct arm_gdb_get_next_pcs): New struct.
(arm_gdb_get_next_pcs_ctor) New function declaration.
* arm-wince-tdep.c: Include arch/arm-get-next-pcs.h.
* armbsd-tdep.c: Likewise.
* armnbsd-tdep.c: Likewise.
* armobsd-tdep.c: Likewise.
* configure.tgt: Add arm-get-next-pcs.o to all arm targets.
* common/gdb_vecs.h: Add CORE_ADDR vector definition.
* symtab.h: Move CORE_ADDR vector definition to gdb_vecs.h.
gdb/gdbserver/ChangeLog:
* Makefile.in : Add arm-get-next-pcs.c/o.
* configure.srv: Add arm-get-next-pcs.o to needed arm targets.
* linux-arm-low.c (get_next_pcs_addr_bits_remove): New function.
(get_next_pcs_collect_register_unsigned): Likewise.
(get_next_pcs_read_memory_unsigned_integer): Likewise.
(get_next_pcs_syscall_next_pc): Likewise.
(arm_gdbserver_get_next_pcs): Likewise.
(struct linux_target_ops) <arm_gdbserver_get_next_pcs>:
Initialize.
(struct arm_gdbserver_get_next_pcs): New struct.
(arm_gdbserver_get_next_pcs_ctor): New function.
* linux-low.h: Move CORE_ADDR vector definition to gdb_vecs.h.
* server.h: Include gdb_vecs.h.
---
gdb/Makefile.in | 12 +-
gdb/aarch32-linux-nat.c | 1 +
gdb/arch/arm-get-next-pcs.c | 1234 +++++++++++++++++++++++++++++++++
gdb/arch/arm-get-next-pcs.h | 98 +++
gdb/arm-linux-nat.c | 2 +
gdb/arm-linux-tdep.c | 53 +-
gdb/arm-tdep.c | 1507 ++++++-----------------------------------
gdb/arm-tdep.h | 46 +-
gdb/arm-wince-tdep.c | 1 +
gdb/armbsd-tdep.c | 1 +
gdb/armnbsd-tdep.c | 1 +
gdb/armobsd-tdep.c | 1 +
gdb/common/gdb_vecs.h | 2 +
gdb/configure.tgt | 19 +-
gdb/gdbserver/Makefile.in | 5 +-
gdb/gdbserver/configure.srv | 1 +
gdb/gdbserver/linux-arm-low.c | 168 ++++-
gdb/gdbserver/linux-low.h | 2 -
gdb/gdbserver/server.h | 1 +
gdb/symtab.h | 2 -
20 files changed, 1833 insertions(+), 1324 deletions(-)
create mode 100644 gdb/arch/arm-get-next-pcs.c
create mode 100644 gdb/arch/arm-get-next-pcs.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 14ad405..670b4eb 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -657,7 +657,8 @@ ALL_64_TARGET_OBS = \
# All other target-dependent objects files (used with --enable-targets=all).
ALL_TARGET_OBS = \
- armbsd-tdep.o arm.o arm-linux-tdep.o arm-symbian-tdep.o \
+ armbsd-tdep.o arm.o arm-linux-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 \
@@ -1660,8 +1661,9 @@ ALLDEPFILES = \
amd64-dicos-tdep.c \
amd64-linux-nat.c amd64-linux-tdep.c \
amd64-sol2-tdep.c \
- arm.c \
- arm-linux-nat.c arm-linux-tdep.c arm-symbian-tdep.c arm-tdep.c \
+ arm.c arm-get-next-pcs.c \
+ arm-linux-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 \
@@ -2286,6 +2288,10 @@ arm.o: ${srcdir}/arch/arm.c
$(COMPILE) $(srcdir)/arch/arm.c
$(POSTCOMPILE)
+arm-get-next-pcs.o: ${srcdir}/arch/arm-get-next-pcs.c
+ $(COMPILE) $(srcdir)/arch/arm-get-next-pcs.c
+ $(POSTCOMPILE)
+
# gdb/nat/ dependencies
#
# Need to explicitly specify the compile rule as make will do nothing
diff --git a/gdb/aarch32-linux-nat.c b/gdb/aarch32-linux-nat.c
index 3147399..9d1f696 100644
--- a/gdb/aarch32-linux-nat.c
+++ b/gdb/aarch32-linux-nat.c
@@ -18,6 +18,7 @@
#include "defs.h"
#include "regcache.h"
+#include "arch/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "arm-linux-tdep.h"
diff --git a/gdb/arch/arm-get-next-pcs.c b/gdb/arch/arm-get-next-pcs.c
new file mode 100644
index 0000000..58586c4
--- /dev/null
+++ b/gdb/arch/arm-get-next-pcs.c
@@ -0,0 +1,1234 @@
+/* Common code for ARM software single stepping support.
+
+ Copyright (C) 1988-2015 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "common-defs.h"
+#include "gdb_vecs.h"
+#include "arm.h"
+#include "arm-get-next-pcs.h"
+
+/* See arm-get-next-pcs.h. */
+
+void
+arm_get_next_pcs_ctor (struct arm_get_next_pcs *self,
+ struct arm_get_next_pcs_ops *ops,
+ int byte_order,
+ int byte_order_for_code,
+ int is_thumb,
+ int arm_apcs_32,
+ const gdb_byte *arm_thumb2_breakpoint)
+{
+ self->ops = ops;
+ self->byte_order = byte_order;
+ self->byte_order_for_code = byte_order_for_code;
+ self->is_thumb = is_thumb;
+ self->arm_apcs_32 = arm_apcs_32;
+ self->arm_thumb2_breakpoint = arm_thumb2_breakpoint;
+}
+
+/* Advance the state of the IT block and return that state. */
+
+static int
+thumb_advance_itstate (unsigned int itstate)
+{
+ /* Preserve IT[7:5], the first three bits of the condition. Shift
+ the upcoming condition flags left by one bit. */
+ itstate = (itstate & 0xe0) | ((itstate << 1) & 0x1f);
+
+ /* If we have finished the IT block, clear the state. */
+ if ((itstate & 0x0f) == 0)
+ itstate = 0;
+
+ return itstate;
+}
+
+/* See arm-get-next-pcs.h. */
+
+int
+arm_instruction_changes_pc (uint32_t this_instr)
+{
+ if (bits (this_instr, 28, 31) == INST_NV)
+ /* Unconditional instructions. */
+ switch (bits (this_instr, 24, 27))
+ {
+ case 0xa:
+ case 0xb:
+ /* Branch with Link and change to Thumb. */
+ return 1;
+ case 0xc:
+ case 0xd:
+ case 0xe:
+ /* Coprocessor register transfer. */
+ if (bits (this_instr, 12, 15) == 15)
+ error (_("Invalid update to pc in instruction"));
+ return 0;
+ default:
+ return 0;
+ }
+ else
+ switch (bits (this_instr, 25, 27))
+ {
+ case 0x0:
+ if (bits (this_instr, 23, 24) == 2 && bit (this_instr, 20) == 0)
+ {
+ /* Multiplies and extra load/stores. */
+ if (bit (this_instr, 4) == 1 && bit (this_instr, 7) == 1)
+ /* Neither multiplies nor extension load/stores are allowed
+ to modify PC. */
+ return 0;
+
+ /* Otherwise, miscellaneous instructions. */
+
+ /* BX <reg>, BXJ <reg>, BLX <reg> */
+ if (bits (this_instr, 4, 27) == 0x12fff1
+ || bits (this_instr, 4, 27) == 0x12fff2
+ || bits (this_instr, 4, 27) == 0x12fff3)
+ return 1;
+
+ /* Other miscellaneous instructions are unpredictable if they
+ modify PC. */
+ return 0;
+ }
+ /* Data processing instruction. Fall through. */
+
+ case 0x1:
+ if (bits (this_instr, 12, 15) == 15)
+ return 1;
+ else
+ return 0;
+
+ case 0x2:
+ case 0x3:
+ /* Media instructions and architecturally undefined instructions. */
+ if (bits (this_instr, 25, 27) == 3 && bit (this_instr, 4) == 1)
+ return 0;
+
+ /* Stores. */
+ if (bit (this_instr, 20) == 0)
+ return 0;
+
+ /* Loads. */
+ if (bits (this_instr, 12, 15) == ARM_PC_REGNUM)
+ return 1;
+ else
+ return 0;
+
+ case 0x4:
+ /* Load/store multiple. */
+ if (bit (this_instr, 20) == 1 && bit (this_instr, 15) == 1)
+ return 1;
+ else
+ return 0;
+
+ case 0x5:
+ /* Branch and branch with link. */
+ return 1;
+
+ case 0x6:
+ case 0x7:
+ /* Coprocessor transfers or SWIs can not affect PC. */
+ return 0;
+
+ default:
+ internal_error (__FILE__, __LINE__, _("bad value in switch"));
+ }
+}
+
+/* See arm-get-next-pcs.h. */
+
+int
+thumb_instruction_changes_pc (unsigned short inst)
+{
+ if ((inst & 0xff00) == 0xbd00) /* pop {rlist, pc} */
+ return 1;
+
+ if ((inst & 0xf000) == 0xd000) /* conditional branch */
+ return 1;
+
+ if ((inst & 0xf800) == 0xe000) /* unconditional branch */
+ return 1;
+
+ if ((inst & 0xff00) == 0x4700) /* bx REG, blx REG */
+ return 1;
+
+ if ((inst & 0xff87) == 0x4687) /* mov pc, REG */
+ return 1;
+
+ if ((inst & 0xf500) == 0xb100) /* CBNZ or CBZ. */
+ return 1;
+
+ return 0;
+}
+
+
+/* See arm-get-next-pcs.h. */
+
+int
+thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2)
+{
+ if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000)
+ {
+ /* Branches and miscellaneous control instructions. */
+
+ if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000)
+ {
+ /* B, BL, BLX. */
+ return 1;
+ }
+ else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
+ {
+ /* SUBS PC, LR, #imm8. */
+ return 1;
+ }
+ else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380)
+ {
+ /* Conditional branch. */
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if ((inst1 & 0xfe50) == 0xe810)
+ {
+ /* Load multiple or RFE. */
+
+ if (bit (inst1, 7) && !bit (inst1, 8))
+ {
+ /* LDMIA or POP */
+ if (bit (inst2, 15))
+ return 1;
+ }
+ else if (!bit (inst1, 7) && bit (inst1, 8))
+ {
+ /* LDMDB */
+ if (bit (inst2, 15))
+ return 1;
+ }
+ else if (bit (inst1, 7) && bit (inst1, 8))
+ {
+ /* RFEIA */
+ return 1;
+ }
+ else if (!bit (inst1, 7) && !bit (inst1, 8))
+ {
+ /* RFEDB */
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
+ {
+ /* MOV PC or MOVS PC. */
+ return 1;
+ }
+
+ if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000)
+ {
+ /* LDR PC. */
+ if (bits (inst1, 0, 3) == 15)
+ return 1;
+ if (bit (inst1, 7))
+ return 1;
+ if (bit (inst2, 11))
+ return 1;
+ if ((inst2 & 0x0fc0) == 0x0000)
+ return 1;
+
+ return 0;
+ }
+
+ if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
+ {
+ /* TBB. */
+ return 1;
+ }
+
+ if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
+ {
+ /* TBH. */
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D}
+ instruction and ending with a STREX{,B,H,D} instruction. If such a sequence
+ is found, attempt to step through it. The end of the sequence address is
+ added to the next_pcs list. */
+
+static int
+thumb_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self,
+ CORE_ADDR pc,
+ VEC (CORE_ADDR) **next_pcs)
+{
+ int byte_order_for_code = self->byte_order_for_code;
+ CORE_ADDR breaks[2] = {-1, -1};
+ CORE_ADDR loc = pc;
+ unsigned short insn1, insn2;
+ int insn_count;
+ int index;
+ int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */
+ const int atomic_sequence_length = 16; /* Instruction sequence length. */
+ ULONGEST status, itstate;
+
+ /* We currently do not support atomic sequences within an IT block. */
+ status = self->ops->collect_register_unsigned (self, ARM_PS_REGNUM);
+
+ itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
+ if (itstate & 0x0f)
+ return 0;
+
+ /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction. */
+ insn1 = self->ops->read_memory_unsigned_integer (loc, 2, byte_order_for_code);
+
+ loc += 2;
+ if (thumb_insn_size (insn1) != 4)
+ return 0;
+
+ insn2 = self->ops->read_memory_unsigned_integer (loc, 2, byte_order_for_code);
+
+ loc += 2;
+ if (!((insn1 & 0xfff0) == 0xe850
+ || ((insn1 & 0xfff0) == 0xe8d0 && (insn2 & 0x00c0) == 0x0040)))
+ return 0;
+
+ /* Assume that no atomic sequence is longer than "atomic_sequence_length"
+ instructions. */
+ for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
+ {
+ insn1
+ = self->ops->read_memory_unsigned_integer (loc, 2,byte_order_for_code);
+ loc += 2;
+
+ if (thumb_insn_size (insn1) != 4)
+ {
+ /* Assume that there is at most one conditional branch in the
+ atomic sequence. If a conditional branch is found, put a
+ breakpoint in its destination address. */
+ if ((insn1 & 0xf000) == 0xd000 && bits (insn1, 8, 11) != 0x0f)
+ {
+ if (last_breakpoint > 0)
+ return 0; /* More than one conditional branch found,
+ fallback to the standard code. */
+
+ breaks[1] = loc + 2 + (sbits (insn1, 0, 7) << 1);
+ last_breakpoint++;
+ }
+
+ /* We do not support atomic sequences that use any *other*
+ instructions but conditional branches to change the PC.
+ Fall back to standard code to avoid losing control of
+ execution. */
+ else if (thumb_instruction_changes_pc (insn1))
+ return 0;
+ }
+ else
+ {
+ insn2 = self->ops->read_memory_unsigned_integer
+ (loc, 2, byte_order_for_code);
+
+ loc += 2;
+
+ /* Assume that there is at most one conditional branch in the
+ atomic sequence. If a conditional branch is found, put a
+ breakpoint in its destination address. */
+ if ((insn1 & 0xf800) == 0xf000
+ && (insn2 & 0xd000) == 0x8000
+ && (insn1 & 0x0380) != 0x0380)
+ {
+ int sign, j1, j2, imm1, imm2;
+ unsigned int offset;
+
+ sign = sbits (insn1, 10, 10);
+ imm1 = bits (insn1, 0, 5);
+ imm2 = bits (insn2, 0, 10);
+ j1 = bit (insn2, 13);
+ j2 = bit (insn2, 11);
+
+ offset = (sign << 20) + (j2 << 19) + (j1 << 18);
+ offset += (imm1 << 12) + (imm2 << 1);
+
+ if (last_breakpoint > 0)
+ return 0; /* More than one conditional branch found,
+ fallback to the standard code. */
+
+ breaks[1] = loc + offset;
+ last_breakpoint++;
+ }
+
+ /* We do not support atomic sequences that use any *other*
+ instructions but conditional branches to change the PC.
+ Fall back to standard code to avoid losing control of
+ execution. */
+ else if (thumb2_instruction_changes_pc (insn1, insn2))
+ return 0;
+
+ /* If we find a strex{,b,h,d}, we're done. */
+ if ((insn1 & 0xfff0) == 0xe840
+ || ((insn1 & 0xfff0) == 0xe8c0 && (insn2 & 0x00c0) == 0x0040))
+ break;
+ }
+ }
+
+ /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence. */
+ if (insn_count == atomic_sequence_length)
+ return 0;
+
+ /* Insert a breakpoint right after the end of the atomic sequence. */
+ breaks[0] = loc;
+
+ /* Check for duplicated breakpoints. Check also for a breakpoint
+ placed (branch instruction's destination) anywhere in sequence. */
+ if (last_breakpoint
+ && (breaks[1] == breaks[0]
+ || (breaks[1] >= pc && breaks[1] < loc)))
+ last_breakpoint = 0;
+
+ /* Adds the breakpoints to the list to be inserted. */
+ for (index = 0; index <= last_breakpoint; index++)
+ {
+ VEC_safe_push (CORE_ADDR, *next_pcs, MAKE_THUMB_ADDR (breaks[index]));
+ }
+
+ return 1;
+}
+
+/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D}
+ instruction and ending with a STREX{,B,H,D} instruction. If such a sequence
+ is found, attempt to step through it. The end of the sequence address is
+ added to the next_pcs list. */
+
+static int
+arm_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self,
+ CORE_ADDR pc,
+ VEC (CORE_ADDR) **next_pcs)
+{
+ int byte_order_for_code = self->byte_order_for_code;
+ CORE_ADDR breaks[2] = {-1, -1};
+ CORE_ADDR loc = pc;
+ unsigned int insn;
+ int insn_count;
+ int index;
+ int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */
+ const int atomic_sequence_length = 16; /* Instruction sequence length. */
+
+ /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction.
+ Note that we do not currently support conditionally executed atomic
+ instructions. */
+ insn = self->ops->read_memory_unsigned_integer (loc, 4, byte_order_for_code);
+
+ loc += 4;
+ if ((insn & 0xff9000f0) != 0xe1900090)
+ return 0;
+
+ /* Assume that no atomic sequence is longer than "atomic_sequence_length"
+ instructions. */
+ for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
+ {
+ insn = self->ops->read_memory_unsigned_integer
+ (loc, 4, byte_order_for_code);
+
+ loc += 4;
+
+ /* Assume that there is at most one conditional branch in the atomic
+ sequence. If a conditional branch is found, put a breakpoint in
+ its destination address. */
+ if (bits (insn, 24, 27) == 0xa)
+ {
+ if (last_breakpoint > 0)
+ return 0; /* More than one conditional branch found, fallback
+ to the standard single-step code. */
+
+ breaks[1] = BranchDest (loc - 4, insn);
+ last_breakpoint++;
+ }
+
+ /* We do not support atomic sequences that use any *other* instructions
+ but conditional branches to change the PC. Fall back to standard
+ code to avoid losing control of execution. */
+ else if (arm_instruction_changes_pc (insn))
+ return 0;
+
+ /* If we find a strex{,b,h,d}, we're done. */
+ if ((insn & 0xff9000f0) == 0xe1800090)
+ break;
+ }
+
+ /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence. */
+ if (insn_count == atomic_sequence_length)
+ return 0;
+
+ /* Insert a breakpoint right after the end of the atomic sequence. */
+ breaks[0] = loc;
+
+ /* Check for duplicated breakpoints. Check also for a breakpoint
+ placed (branch instruction's destination) anywhere in sequence. */
+ if (last_breakpoint
+ && (breaks[1] == breaks[0]
+ || (breaks[1] >= pc && breaks[1] < loc)))
+ last_breakpoint = 0;
+
+ /* Adds the breakpoints to the list to be inserted. */
+ for (index = 0; index <= last_breakpoint; index++)
+ {
+ VEC_safe_push (CORE_ADDR, *next_pcs, breaks[index]);
+ }
+
+ return 1;
+}
+
+/* See arm-get-next-pcs.h. */
+
+VEC (CORE_ADDR) *
+arm_get_next_pcs (struct arm_get_next_pcs *self, CORE_ADDR pc)
+{
+ VEC (CORE_ADDR) *next_pcs = NULL;
+
+ if (self->is_thumb)
+ {
+ if (!thumb_deal_with_atomic_sequence_raw (self, pc, &next_pcs))
+ return thumb_get_next_pcs_raw (self, pc, &next_pcs);
+ }
+ else
+ {
+ if (!arm_deal_with_atomic_sequence_raw (self, pc, &next_pcs))
+ return arm_get_next_pcs_raw (self, pc, &next_pcs);
+ }
+
+ return next_pcs;
+}
+
+/* Decode shifted register value. */
+
+static unsigned long
+shifted_reg_val (struct arm_get_next_pcs* self, unsigned long inst,
+ int carry, unsigned long pc_val, unsigned long status_reg)
+{
+ unsigned long res, shift;
+ int rm = bits (inst, 0, 3);
+ unsigned long shifttype = bits (inst, 5, 6);
+
+ if (bit (inst, 4))
+ {
+ int rs = bits (inst, 8, 11);
+ shift = (rs == 15
+ ? pc_val + 8
+ : self->ops->collect_register_unsigned (self, rs)) & 0xFF;
+ }
+ else
+ shift = bits (inst, 7, 11);
+
+ res = (rm == ARM_PC_REGNUM
+ ? (pc_val + (bit (inst, 4) ? 12 : 8))
+ : self->ops->collect_register_unsigned (self, rm));
+
+ switch (shifttype)
+ {
+ case 0: /* LSL */
+ res = shift >= 32 ? 0 : res << shift;
+ break;
+
+ case 1: /* LSR */
+ res = shift >= 32 ? 0 : res >> shift;
+ break;
+
+ case 2: /* ASR */
+ if (shift >= 32)
+ shift = 31;
+ res = ((res & 0x80000000L)
+ ? ~((~res) >> shift) : res >> shift);
+ break;
+
+ case 3: /* ROR/RRX */
+ shift &= 31;
+ if (shift == 0)
+ res = (res >> 1) | (carry ? 0x80000000L : 0);
+ else
+ res = (res >> shift) | (res << (32 - shift));
+ break;
+ }
+
+ return res & 0xffffffff;
+}
+
+/* See arm-get-next-pcs.h. */
+
+VEC (CORE_ADDR) *
+thumb_get_next_pcs_raw (struct arm_get_next_pcs *self,
+ CORE_ADDR pc,
+ VEC (CORE_ADDR) **next_pcs)
+{
+ int byte_order = self->byte_order;
+ int byte_order_for_code = self->byte_order_for_code;
+ unsigned long pc_val = ((unsigned long) pc) + 4; /* PC after prefetch */
+ unsigned short inst1;
+ CORE_ADDR nextpc = pc + 2; /* Default is next instruction. */
+ unsigned long offset;
+ ULONGEST status, itstate;
+
+ nextpc = MAKE_THUMB_ADDR (nextpc);
+ pc_val = MAKE_THUMB_ADDR (pc_val);
+
+ inst1 = self->ops->read_memory_unsigned_integer (pc, 2, byte_order_for_code);
+
+ /* Thumb-2 conditional execution support. There are eight bits in
+ the CPSR which describe conditional execution state. Once
+ reconstructed (they're in a funny order), the low five bits
+ describe the low bit of the condition for each instruction and
+ how many instructions remain. The high three bits describe the
+ base condition. One of the low four bits will be set if an IT
+ block is active. These bits read as zero on earlier
+ processors. */
+ status = self->ops->collect_register_unsigned (self, ARM_PS_REGNUM);
+
+ itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
+
+ /* If-Then handling. On GNU/Linux, where this routine is used, we
+ use an undefined instruction as a breakpoint. Unlike BKPT, IT
+ can disable execution of the undefined instruction. So we might
+ miss the breakpoint if we set it on a skipped conditional
+ instruction. Because conditional instructions can change the
+ flags, affecting the execution of further instructions, we may
+ need to set two breakpoints. */
+
+ if (self->arm_thumb2_breakpoint != NULL)
+ {
+ if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
+ {
+ /* An IT instruction. Because this instruction does not
+ modify the flags, we can accurately predict the next
+ executed instruction. */
+ itstate = inst1 & 0x00ff;
+ pc += thumb_insn_size (inst1);
+
+ while (itstate != 0 && ! condition_true (itstate >> 4, status))
+ {
+ inst1 = self->ops->read_memory_unsigned_integer
+ (pc, 2,byte_order_for_code);
+ pc += thumb_insn_size (inst1);
+ itstate = thumb_advance_itstate (itstate);
+ }
+
+ VEC_safe_push (CORE_ADDR, *next_pcs, MAKE_THUMB_ADDR (pc));
+ return *next_pcs;
+ }
+ else if (itstate != 0)
+ {
+ /* We are in a conditional block. Check the condition. */
+ if (! condition_true (itstate >> 4, status))
+ {
+ /* Advance to the next executed instruction. */
+ pc += thumb_insn_size (inst1);
+ itstate = thumb_advance_itstate (itstate);
+
+ while (itstate != 0 && ! condition_true (itstate >> 4, status))
+ {
+ inst1 = self->ops->read_memory_unsigned_integer
+ (pc, 2, byte_order_for_code);
+
+ pc += thumb_insn_size (inst1);
+ itstate = thumb_advance_itstate (itstate);
+ }
+
+ VEC_safe_push (CORE_ADDR, *next_pcs, MAKE_THUMB_ADDR (pc));
+ return *next_pcs;
+ }
+ else if ((itstate & 0x0f) == 0x08)
+ {
+ /* This is the last instruction of the conditional
+ block, and it is executed. We can handle it normally
+ because the following instruction is not conditional,
+ and we must handle it normally because it is
+ permitted to branch. Fall through. */
+ }
+ else
+ {
+ int cond_negated;
+
+ /* There are conditional instructions after this one.
+ If this instruction modifies the flags, then we can
+ not predict what the next executed instruction will
+ be. Fortunately, this instruction is architecturally
+ forbidden to branch; we know it will fall through.
+ Start by skipping past it. */
+ pc += thumb_insn_size (inst1);
+ itstate = thumb_advance_itstate (itstate);
+
+ /* Set a breakpoint on the following instruction. */
+ gdb_assert ((itstate & 0x0f) != 0);
+ VEC_safe_push (CORE_ADDR, *next_pcs, MAKE_THUMB_ADDR (pc));
+
+ cond_negated = (itstate >> 4) & 1;
+
+ /* Skip all following instructions with the same
+ condition. If there is a later instruction in the IT
+ block with the opposite condition, set the other
+ breakpoint there. If not, then set a breakpoint on
+ the instruction after the IT block. */
+ do
+ {
+ inst1 = self->ops->read_memory_unsigned_integer
+ (pc, 2, byte_order_for_code);
+
+ pc += thumb_insn_size (inst1);
+ itstate = thumb_advance_itstate (itstate);
+ }
+ while (itstate != 0 && ((itstate >> 4) & 1) == cond_negated);
+
+ VEC_safe_push (CORE_ADDR, *next_pcs, MAKE_THUMB_ADDR (pc));
+
+ return *next_pcs;
+ }
+ }
+ }
+ else if (itstate & 0x0f)
+ {
+ /* We are in a conditional block. Check the condition. */
+ int cond = itstate >> 4;
+
+ if (! condition_true (cond, status))
+ /* Advance to the next instruction. All the 32-bit
+ instructions share a common prefix. */
+ VEC_safe_push (CORE_ADDR, *next_pcs,
+ MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1)));
+
+ return *next_pcs;
+
+ /* Otherwise, handle the instruction normally. */
+ }
+
+ if ((inst1 & 0xff00) == 0xbd00) /* pop {rlist, pc} */
+ {
+ CORE_ADDR sp;
+
+ /* Fetch the saved PC from the stack. It's stored above
+ all of the other registers. */
+ offset = bitcount (bits (inst1, 0, 7)) * INT_REGISTER_SIZE;
+
+ sp = self->ops->collect_register_unsigned (self, ARM_SP_REGNUM);
+
+ nextpc = self->ops->read_memory_unsigned_integer
+ (sp + offset, 4, byte_order);
+ }
+ else if ((inst1 & 0xf000) == 0xd000) /* conditional branch */
+ {
+ unsigned long cond = bits (inst1, 8, 11);
+ if (cond == 0x0f) /* 0x0f = SWI */
+ {
+ nextpc = self->ops->syscall_next_pc (self, pc);
+ }
+ else if (cond != 0x0f && condition_true (cond, status))
+ nextpc = pc_val + (sbits (inst1, 0, 7) << 1);
+ }
+ else if ((inst1 & 0xf800) == 0xe000) /* unconditional branch */
+ {
+ nextpc = pc_val + (sbits (inst1, 0, 10) << 1);
+ }
+ else if (thumb_insn_size (inst1) == 4) /* 32-bit instruction */
+ {
+ unsigned short inst2;
+ inst2 = self->ops->read_memory_unsigned_integer
+ (pc + 2, 2, byte_order_for_code);
+
+ /* Default to the next instruction. */
+ nextpc = pc + 4;
+ nextpc = MAKE_THUMB_ADDR (nextpc);
+
+ if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000)
+ {
+ /* Branches and miscellaneous control instructions. */
+
+ if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000)
+ {
+ /* B, BL, BLX. */
+ int j1, j2, imm1, imm2;
+
+ imm1 = sbits (inst1, 0, 10);
+ imm2 = bits (inst2, 0, 10);
+ j1 = bit (inst2, 13);
+ j2 = bit (inst2, 11);
+
+ offset = ((imm1 << 12) + (imm2 << 1));
+ offset ^= ((!j2) << 22) | ((!j1) << 23);
+
+ nextpc = pc_val + offset;
+ /* For BLX make sure to clear the low bits. */
+ if (bit (inst2, 12) == 0)
+ nextpc = nextpc & 0xfffffffc;
+ }
+ else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
+ {
+ /* SUBS PC, LR, #imm8. */
+ nextpc
+ = self->ops->collect_register_unsigned (self, 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 = self->ops->collect_register_unsigned (self, rn);
+ nextpc = self->ops->read_memory_unsigned_integer
+ (addr + offset, 4, byte_order);
+ }
+ }
+ else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
+ {
+ /* MOV PC or MOVS PC. */
+ nextpc
+ = self->ops->collect_register_unsigned (self, 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 = self->ops->collect_register_unsigned (self, 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 += self->ops->collect_register_unsigned (self, rm) << shift;
+ }
+ else
+ /* Reserved. */
+ load_pc = 0;
+
+ if (load_pc)
+ nextpc
+ = self->ops->read_memory_unsigned_integer (base, 4, byte_order);
+ }
+ else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
+ {
+ /* TBB. */
+ CORE_ADDR tbl_reg, table, offset, length;
+
+ tbl_reg = bits (inst1, 0, 3);
+ if (tbl_reg == 0x0f)
+ table = pc + 4; /* Regcache copy of PC isn't right yet. */
+ else
+ table = self->ops->collect_register_unsigned (self, tbl_reg);
+
+ offset
+ = self->ops->collect_register_unsigned (self, bits (inst2, 0, 3));
+ length = 2 * self->ops->read_memory_unsigned_integer
+ (table + offset, 1, byte_order);
+ nextpc = pc_val + length;
+ }
+ else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
+ {
+ /* TBH. */
+ CORE_ADDR tbl_reg, table, offset, length;
+
+ tbl_reg = bits (inst1, 0, 3);
+ if (tbl_reg == 0x0f)
+ table = pc + 4; /* Regcache copy of PC isn't right yet. */
+ else
+ table = self->ops->collect_register_unsigned (self, tbl_reg);
+
+ offset = 2 * self->ops->collect_register_unsigned
+ (self, bits (inst2, 0, 3));
+ length = 2 * self->ops->read_memory_unsigned_integer
+ (table + offset, 2, byte_order);
+ nextpc = pc_val + length;
+ }
+ }
+ else if ((inst1 & 0xff00) == 0x4700) /* bx REG, blx REG */
+ {
+ if (bits (inst1, 3, 6) == 0x0f)
+ nextpc = UNMAKE_THUMB_ADDR (pc_val);
+ else
+ nextpc
+ = self->ops->collect_register_unsigned (self, bits (inst1, 3, 6));
+ }
+ else if ((inst1 & 0xff87) == 0x4687) /* mov pc, REG */
+ {
+ if (bits (inst1, 3, 6) == 0x0f)
+ nextpc = pc_val;
+ else
+ nextpc
+ = self->ops->collect_register_unsigned (self, 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
+ = self->ops->collect_register_unsigned (self, bits (inst1, 0, 2));
+
+ if (bit (inst1, 11) && reg != 0)
+ nextpc = pc_val + imm;
+ else if (!bit (inst1, 11) && reg == 0)
+ nextpc = pc_val + imm;
+ }
+
+ VEC_safe_push (CORE_ADDR, *next_pcs, nextpc);
+
+ return *next_pcs;
+}
+
+/* Get the raw next possible addresses. PC in next_pcs is the current program
+ counter, which is assumed to be executing in ARM mode.
+
+ The values returned have the execution state of the next instruction
+ encoded in it. Use IS_THUMB_ADDR () to see whether the instruction is
+ in Thumb-State, and gdbarch_addr_bits_remove () to get the plain memory
+ address in GDB and arm_addr_bits_remove in GDBServer. */
+
+VEC (CORE_ADDR) *
+arm_get_next_pcs_raw (struct arm_get_next_pcs *self,
+ CORE_ADDR pc,
+ VEC (CORE_ADDR) **next_pcs)
+{
+ int byte_order = self->byte_order;
+ unsigned long pc_val;
+ unsigned long this_instr = 0;
+ unsigned long status;
+ CORE_ADDR nextpc;
+
+ pc_val = (unsigned long) pc;
+ this_instr = self->ops->read_memory_unsigned_integer (pc, 4, byte_order);
+
+ status = self->ops->collect_register_unsigned (self, 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)
+ : self->ops->collect_register_unsigned (self, rn));
+
+ VEC_safe_push (CORE_ADDR, *next_pcs, nextpc);
+ return *next_pcs;
+ }
+
+ /* Multiply into PC. */
+ c = (status & FLAG_C) ? 1 : 0;
+ rn = bits (this_instr, 16, 19);
+ operand1 = ((rn == ARM_PC_REGNUM)
+ ? (pc_val + 8)
+ : self->ops->collect_register_unsigned (self, rn));
+
+ if (bit (this_instr, 25))
+ {
+ unsigned long immval = bits (this_instr, 0, 7);
+ unsigned long rotate = 2 * bits (this_instr, 8, 11);
+ operand2 = ((immval >> rotate) | (immval << (32 - rotate)))
+ & 0xffffffff;
+ }
+ else /* operand 2 is a shifted register. */
+ operand2 = shifted_reg_val (self, this_instr, c,
+ pc_val, status);
+
+ switch (bits (this_instr, 21, 24))
+ {
+ case 0x0: /*and */
+ result = operand1 & operand2;
+ break;
+
+ case 0x1: /*eor */
+ result = operand1 ^ operand2;
+ break;
+
+ case 0x2: /*sub */
+ result = operand1 - operand2;
+ break;
+
+ case 0x3: /*rsb */
+ result = operand2 - operand1;
+ break;
+
+ case 0x4: /*add */
+ result = operand1 + operand2;
+ break;
+
+ case 0x5: /*adc */
+ result = operand1 + operand2 + c;
+ break;
+
+ case 0x6: /*sbc */
+ result = operand1 - operand2 + c;
+ break;
+
+ case 0x7: /*rsc */
+ result = operand2 - operand1 + c;
+ break;
+
+ case 0x8:
+ case 0x9:
+ case 0xa:
+ case 0xb: /* tst, teq, cmp, cmn */
+ result = (unsigned long) nextpc;
+ break;
+
+ case 0xc: /*orr */
+ result = operand1 | operand2;
+ break;
+
+ case 0xd: /*mov */
+ /* Always step into a function. */
+ result = operand2;
+ break;
+
+ case 0xe: /*bic */
+ result = operand1 & ~operand2;
+ break;
+
+ case 0xf: /*mvn */
+ result = ~operand2;
+ break;
+ }
+
+ /* In 26-bit APCS the bottom two bits of the result are
+ ignored, and we always end up in ARM state. */
+ if (!self->arm_apcs_32)
+ nextpc = self->ops->addr_bits_remove (self, 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)
+ : self->ops->collect_register_unsigned (self, rn));
+
+ if (bit (this_instr, 24))
+ {
+ /* pre-indexed */
+ int c = (status & FLAG_C) ? 1 : 0;
+ unsigned long offset =
+ (bit (this_instr, 25)
+ ? shifted_reg_val (self, this_instr, c,
+ pc_val, status)
+ : bits (this_instr, 0, 11));
+
+ if (bit (this_instr, 23))
+ base += offset;
+ else
+ base -= offset;
+ }
+ nextpc =
+ (CORE_ADDR) self->ops->read_memory_unsigned_integer
+ ((CORE_ADDR) base, 4, byte_order);
+ }
+ }
+ break;
+
+ case 0x8:
+ case 0x9: /* block transfer */
+ if (bit (this_instr, 20))
+ {
+ /* LDM */
+ if (bit (this_instr, 15))
+ {
+ /* loading pc */
+ int offset = 0;
+ unsigned long rn_val
+ = self->ops->collect_register_unsigned
+ (self, bits (this_instr, 16, 19));
+
+ if (bit (this_instr, 23))
+ {
+ /* up */
+ unsigned long reglist = bits (this_instr, 0, 14);
+ offset = bitcount (reglist) * 4;
+ if (bit (this_instr, 24)) /* pre */
+ offset += 4;
+ }
+ else if (bit (this_instr, 24))
+ offset = -4;
+
+ nextpc = (CORE_ADDR) self->ops->read_memory_unsigned_integer
+ ((CORE_ADDR) (rn_val + offset), 4, byte_order);
+ }
+ }
+ break;
+
+ case 0xb: /* branch & link */
+ case 0xa: /* branch */
+ {
+ nextpc = BranchDest (pc, this_instr);
+ break;
+ }
+
+ case 0xc:
+ case 0xd:
+ case 0xe: /* coproc ops */
+ break;
+ case 0xf: /* SWI */
+ {
+ nextpc = self->ops->syscall_next_pc (self, pc);
+ }
+ break;
+
+ default:
+ error (_("Bad bit-field extraction\n"));
+ return *next_pcs;
+ }
+ }
+
+ VEC_safe_push (CORE_ADDR, *next_pcs, nextpc);
+ return *next_pcs;
+}
diff --git a/gdb/arch/arm-get-next-pcs.h b/gdb/arch/arm-get-next-pcs.h
new file mode 100644
index 0000000..8e58bf8
--- /dev/null
+++ b/gdb/arch/arm-get-next-pcs.h
@@ -0,0 +1,98 @@
+/* Common code for ARM software single stepping support.
+
+ Copyright (C) 1988-2015 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef ARM_GET_NEXT_PCS_H
+#define ARM_GET_NEXT_PCS_H 1
+
+/* Support routines for instruction parsing. */
+#define submask(x) ((1L << ((x) + 1)) - 1)
+#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st)))
+#define bit(obj,st) (((obj) >> (st)) & 1)
+#define sbits(obj,st,fn) \
+ ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st))))
+#define BranchDest(addr,instr) \
+ ((CORE_ADDR) (((unsigned long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
+
+
+/* Forward declaration. */
+struct arm_get_next_pcs;
+
+/* get_next_pcs operations. */
+struct arm_get_next_pcs_ops
+{
+ ULONGEST (*read_memory_unsigned_integer) (CORE_ADDR memaddr,
+ int len,
+ int byte_order);
+ ULONGEST (*collect_register_unsigned) (struct arm_get_next_pcs *self, int n);
+ CORE_ADDR (*syscall_next_pc) (struct arm_get_next_pcs *self, CORE_ADDR pc);
+ CORE_ADDR (*addr_bits_remove) (struct arm_get_next_pcs *self, CORE_ADDR val);
+};
+
+/* Context for a get_next_pcs call on ARM. */
+struct arm_get_next_pcs
+{
+ /* Operations implementations. */
+ struct arm_get_next_pcs_ops *ops;
+ /* Byte order for data. */
+ int byte_order;
+ /* Byte order for code. */
+ int byte_order_for_code;
+ /* Is the pc in thumb mode. */
+ int is_thumb;
+ /* Use 32bit or 26 bit pc. */
+ int arm_apcs_32;
+ /* Thumb2 breakpoint instruction. */
+ const gdb_byte *arm_thumb2_breakpoint;
+};
+
+/* Initialize arm_get_next_pcs. */
+void arm_get_next_pcs_ctor (struct arm_get_next_pcs *self,
+ struct arm_get_next_pcs_ops *ops,
+ int byte_order,
+ int byte_order_for_code,
+ int is_thumb,
+ int arm_apcs_32,
+ const gdb_byte *arm_thumb2_breakpoint);
+
+/* Find the next possible PCs after the current instruction executes. */
+VEC (CORE_ADDR) *arm_get_next_pcs (struct arm_get_next_pcs *self,
+ CORE_ADDR pc);
+
+/* Find the next possible PCs for thumb mode. */
+VEC (CORE_ADDR) *thumb_get_next_pcs_raw (struct arm_get_next_pcs *self,
+ CORE_ADDR pc,
+ VEC (CORE_ADDR) **next_pcs);
+
+/* Find the next possible PCs for arm mode. */
+VEC (CORE_ADDR) *arm_get_next_pcs_raw (struct arm_get_next_pcs *self,
+ CORE_ADDR pc,
+ VEC (CORE_ADDR) **next_pcs);
+
+/* Return 1 if THIS_INSTR might change control flow, 0 otherwise. */
+int arm_instruction_changes_pc (uint32_t this_instr);
+
+/* Return 1 if the 16-bit Thumb instruction INST might change
+ control flow, 0 otherwise. */
+int thumb_instruction_changes_pc (unsigned short inst);
+
+/* Return 1 if the 32-bit Thumb instruction in INST1 and INST2
+ might change control flow, 0 otherwise. */
+int thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2);
+
+#endif /* ARM_GET_NEXT_PCS_H */
diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c
index 63fdae1..695d3e5 100644
--- a/gdb/arm-linux-nat.c
+++ b/gdb/arm-linux-nat.c
@@ -27,6 +27,8 @@
#include "observer.h"
#include "gdbthread.h"
+#include "arch/arm.h"
+#include "arch/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "arm-linux-tdep.h"
#include "aarch32-linux-nat.h"
diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c
index 73e1271..d8d67b3 100644
--- a/gdb/arm-linux-tdep.c
+++ b/gdb/arm-linux-tdep.c
@@ -35,6 +35,8 @@
#include "auxv.h"
#include "xml-syscall.h"
+#include "arch/arm.h"
+#include "arch/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "arm-linux-tdep.h"
#include "linux-tdep.h"
@@ -257,6 +259,14 @@ static const gdb_byte arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa
#define ARM_LDR_PC_SP_12 0xe49df00c
#define ARM_LDR_PC_SP_4 0xe49df004
+/* Operation function pointers for get_next_pcs. */
+static struct arm_get_next_pcs_ops arm_linux_get_next_pcs_ops = {
+ arm_get_next_pcs_read_memory_unsigned_integer,
+ arm_get_next_pcs_collect_register_unsigned,
+ arm_get_next_pcs_syscall_next_pc,
+ arm_get_next_pcs_addr_bits_remove
+};
+
static void
arm_linux_sigtramp_cache (struct frame_info *this_frame,
struct trad_frame_cache *this_cache,
@@ -912,27 +922,44 @@ 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;
+ struct arm_gdb_get_next_pcs next_pcs_ctx;
+ CORE_ADDR pc;
+ int i;
+ VEC (CORE_ADDR) *next_pcs = NULL;
/* If the target does have hardware single step, GDB doesn't have
to bother software single step. */
if (target_can_do_single_step () == 1)
return 0;
- next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
+ arm_gdb_get_next_pcs_ctor (&next_pcs_ctx,
+ &arm_linux_get_next_pcs_ops,
+ gdbarch_byte_order (gdbarch),
+ gdbarch_byte_order_for_code (gdbarch),
+ arm_frame_is_thumb (frame),
+ arm_apcs_32,
+ gdbarch_tdep (gdbarch)->thumb2_breakpoint,
+ frame,
+ gdbarch);
- /* 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 = arm_get_next_pcs ((struct arm_get_next_pcs *) &next_pcs_ctx,
+ get_frame_pc (frame));
+
+ for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++)
+ {
+ /* The Linux kernel offers some user-mode helpers in a high page. We can
+ not read this page (as of 2.6.23), and even if we could then we
+ couldn't set breakpoints in it, and even if we could then the atomic
+ operations would fail when interrupted. They are all called as
+ functions and return to the address in LR, so step to there
+ instead. */
+ if (pc > 0xffff0000)
+ pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
+
+ arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
+ }
- arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
+ VEC_free (CORE_ADDR, next_pcs);
return 1;
}
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index c361f62..373f628 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -46,6 +46,7 @@
#include "observer.h"
#include "arch/arm.h"
+#include "arch/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "gdb/sim-arm.h"
@@ -236,6 +237,14 @@ static void arm_neon_quad_write (struct gdbarch *gdbarch,
struct regcache *regcache,
int regnum, const gdb_byte *buf);
+/* get_next_pcs operations. */
+static struct arm_get_next_pcs_ops arm_get_next_pcs_ops = {
+ arm_get_next_pcs_read_memory_unsigned_integer,
+ arm_get_next_pcs_collect_register_unsigned,
+ arm_get_next_pcs_syscall_next_pc,
+ arm_get_next_pcs_addr_bits_remove
+};
+
struct arm_prologue_cache
{
/* The stack pointer at the time this frame was created; i.e. the
@@ -507,15 +516,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. */
@@ -555,128 +555,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. */
@@ -1504,98 +1382,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. */
@@ -4290,1102 +4076,131 @@ convert_to_extended (const struct floatformat *fmt, void *dbl, const void *ptr,
&d, dbl);
}
-static unsigned long
-shifted_reg_val (struct frame_info *frame, unsigned long inst, int carry,
- unsigned long pc_val, unsigned long status_reg)
+/* Like insert_single_step_breakpoint, but make sure we use a breakpoint
+ of the appropriate mode (as encoded in the PC value), even if this
+ differs from what would be expected according to the symbol tables. */
+
+void
+arm_insert_single_step_breakpoint (struct gdbarch *gdbarch,
+ struct address_space *aspace,
+ CORE_ADDR pc)
{
- unsigned long res, shift;
- int rm = bits (inst, 0, 3);
- unsigned long shifttype = bits (inst, 5, 6);
+ struct cleanup *old_chain
+ = make_cleanup_restore_integer (&arm_override_mode);
- if (bit (inst, 4))
- {
- int rs = bits (inst, 8, 11);
- shift = (rs == 15 ? pc_val + 8
- : 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 = (gdb_byte *) xmalloc (new_len);
+ memcpy (new_buf + bytes_to_read, buf, old_len);
+ xfree (buf);
+ if (target_read_memory (endaddr - new_len, new_buf, bytes_to_read) != 0)
+ {
+ xfree (new_buf);
+ return NULL;
}
-
- return res & 0xffffffff;
+ return new_buf;
}
-static int
-thumb_advance_itstate (unsigned int itstate)
-{
- /* Preserve IT[7:5], the first three bits of the condition. Shift
- the upcoming condition flags left by one bit. */
- itstate = (itstate & 0xe0) | ((itstate << 1) & 0x1f);
-
- /* If we have finished the IT block, clear the state. */
- if ((itstate & 0x0f) == 0)
- itstate = 0;
-
- return itstate;
-}
+/* An IT block is at most the 2-byte IT instruction followed by
+ four 4-byte instructions. The furthest back we must search to
+ find an IT block that affects the current instruction is thus
+ 2 + 3 * 4 == 14 bytes. */
+#define MAX_IT_BLOCK_PREFIX 14
-/* Find the next PC after the current instruction executes. In some
- cases we can not statically determine the answer (see the IT state
- handling in this function); in that case, a breakpoint may be
- inserted in addition to the returned PC, which will be used to set
- another breakpoint by our caller. */
+/* Use a quick scan if there are more than this many bytes of
+ code. */
+#define IT_SCAN_THRESHOLD 32
+/* Adjust a breakpoint's address to move breakpoints out of IT blocks.
+ A breakpoint in an IT block may not be hit, depending on the
+ condition flags. */
static CORE_ADDR
-thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
+arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
{
- 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)
- {
- if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
- {
- /* An IT instruction. Because this instruction does not
- modify the flags, we can accurately predict the next
- executed instruction. */
- itstate = inst1 & 0x00ff;
- pc += thumb_insn_size (inst1);
-
- while (itstate != 0 && ! condition_true (itstate >> 4, status))
- {
- inst1 = read_memory_unsigned_integer (pc, 2,
- byte_order_for_code);
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
- }
-
- return MAKE_THUMB_ADDR (pc);
- }
- else if (itstate != 0)
- {
- /* We are in a conditional block. Check the condition. */
- if (! condition_true (itstate >> 4, status))
- {
- /* Advance to the next executed instruction. */
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
+ gdb_byte *buf;
+ char map_type;
+ CORE_ADDR boundary, func_start;
+ int buf_len;
+ enum bfd_endian order = gdbarch_byte_order_for_code (gdbarch);
+ int i, any, last_it, last_it_count;
- while (itstate != 0 && ! condition_true (itstate >> 4, status))
- {
- inst1 = read_memory_unsigned_integer (pc, 2,
- byte_order_for_code);
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
- }
+ /* If we are using BKPT breakpoints, none of this is necessary. */
+ if (gdbarch_tdep (gdbarch)->thumb2_breakpoint == NULL)
+ return bpaddr;
- return MAKE_THUMB_ADDR (pc);
- }
- else if ((itstate & 0x0f) == 0x08)
- {
- /* This is the last instruction of the conditional
- block, and it is executed. We can handle it normally
- because the following instruction is not conditional,
- and we must handle it normally because it is
- permitted to branch. Fall through. */
- }
- else
- {
- int cond_negated;
-
- /* There are conditional instructions after this one.
- If this instruction modifies the flags, then we can
- not predict what the next executed instruction will
- be. Fortunately, this instruction is architecturally
- forbidden to branch; we know it will fall through.
- Start by skipping past it. */
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
-
- /* Set a breakpoint on the following instruction. */
- gdb_assert ((itstate & 0x0f) != 0);
- arm_insert_single_step_breakpoint (gdbarch, aspace,
- MAKE_THUMB_ADDR (pc));
- cond_negated = (itstate >> 4) & 1;
-
- /* Skip all following instructions with the same
- condition. If there is a later instruction in the IT
- block with the opposite condition, set the other
- breakpoint there. If not, then set a breakpoint on
- the instruction after the IT block. */
- do
- {
- inst1 = read_memory_unsigned_integer (pc, 2,
- byte_order_for_code);
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
- }
- while (itstate != 0 && ((itstate >> 4) & 1) == cond_negated);
+ /* ARM mode does not have this problem. */
+ if (!arm_pc_is_thumb (gdbarch, bpaddr))
+ return bpaddr;
- return MAKE_THUMB_ADDR (pc);
- }
- }
- }
- else if (itstate & 0x0f)
- {
- /* We are in a conditional block. Check the condition. */
- int cond = itstate >> 4;
+ /* We are setting a breakpoint in Thumb code that could potentially
+ contain an IT block. The first step is to find how much Thumb
+ code there is; we do not need to read outside of known Thumb
+ sequences. */
+ map_type = arm_find_mapping_symbol (bpaddr, &boundary);
+ if (map_type == 0)
+ /* Thumb-2 code must have mapping symbols to have a chance. */
+ return bpaddr;
- if (! condition_true (cond, status))
- /* Advance to the next instruction. All the 32-bit
- instructions share a common prefix. */
- return MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1));
+ bpaddr = gdbarch_addr_bits_remove (gdbarch, bpaddr);
- /* Otherwise, handle the instruction normally. */
- }
+ if (find_pc_partial_function (bpaddr, NULL, &func_start, NULL)
+ && func_start > boundary)
+ boundary = func_start;
- if ((inst1 & 0xff00) == 0xbd00) /* pop {rlist, pc} */
- {
- CORE_ADDR sp;
+ /* Search for a candidate IT instruction. We have to do some fancy
+ footwork to distinguish a real IT instruction from the second
+ half of a 32-bit instruction, but there is no need for that if
+ there's no candidate. */
+ buf_len = min (bpaddr - boundary, MAX_IT_BLOCK_PREFIX);
+ if (buf_len == 0)
+ /* No room for an IT instruction. */
+ return bpaddr;
- /* Fetch the saved PC from the stack. It's stored above
- all of the other registers. */
- offset = bitcount (bits (inst1, 0, 7)) * INT_REGISTER_SIZE;
- sp = 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 */
+ buf = (gdb_byte *) xmalloc (buf_len);
+ if (target_read_memory (bpaddr - buf_len, buf, buf_len) != 0)
+ return bpaddr;
+ any = 0;
+ for (i = 0; i < buf_len; i += 2)
{
- unsigned long cond = bits (inst1, 8, 11);
- if (cond == 0x0f) /* 0x0f = SWI */
+ unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
+ if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
{
- struct gdbarch_tdep *tdep;
- tdep = gdbarch_tdep (gdbarch);
-
- if (tdep->syscall_next_pc != NULL)
- nextpc = tdep->syscall_next_pc (frame);
-
+ any = 1;
+ break;
}
- else if (cond != 0x0f && condition_true (cond, status))
- nextpc = pc_val + (sbits (inst1, 0, 7) << 1);
}
- else if ((inst1 & 0xf800) == 0xe000) /* unconditional branch */
+ if (any == 0)
{
- nextpc = pc_val + (sbits (inst1, 0, 10) << 1);
+ xfree (buf);
+ return bpaddr;
}
- else if (thumb_insn_size (inst1) == 4) /* 32-bit instruction */
+
+ /* OK, the code bytes before this instruction contain at least one
+ halfword which resembles an IT instruction. We know that it's
+ Thumb code, but there are still two possibilities. Either the
+ halfword really is an IT instruction, or it is the second half of
+ a 32-bit Thumb instruction. The only way we can tell is to
+ scan forwards from a known instruction boundary. */
+ if (bpaddr - boundary > IT_SCAN_THRESHOLD)
{
- 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 = (gdb_byte *) xmalloc (new_len);
- memcpy (new_buf + bytes_to_read, buf, old_len);
- xfree (buf);
- if (target_read_memory (endaddr - new_len, new_buf, bytes_to_read) != 0)
- {
- xfree (new_buf);
- return NULL;
- }
- return new_buf;
-}
-
-/* An IT block is at most the 2-byte IT instruction followed by
- four 4-byte instructions. The furthest back we must search to
- find an IT block that affects the current instruction is thus
- 2 + 3 * 4 == 14 bytes. */
-#define MAX_IT_BLOCK_PREFIX 14
-
-/* Use a quick scan if there are more than this many bytes of
- code. */
-#define IT_SCAN_THRESHOLD 32
-
-/* Adjust a breakpoint's address to move breakpoints out of IT blocks.
- A breakpoint in an IT block may not be hit, depending on the
- condition flags. */
-static CORE_ADDR
-arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
-{
- gdb_byte *buf;
- char map_type;
- CORE_ADDR boundary, func_start;
- int buf_len;
- enum bfd_endian order = gdbarch_byte_order_for_code (gdbarch);
- int i, any, last_it, last_it_count;
-
- /* If we are using BKPT breakpoints, none of this is necessary. */
- if (gdbarch_tdep (gdbarch)->thumb2_breakpoint == NULL)
- return bpaddr;
-
- /* ARM mode does not have this problem. */
- if (!arm_pc_is_thumb (gdbarch, bpaddr))
- return bpaddr;
-
- /* We are setting a breakpoint in Thumb code that could potentially
- contain an IT block. The first step is to find how much Thumb
- code there is; we do not need to read outside of known Thumb
- sequences. */
- map_type = arm_find_mapping_symbol (bpaddr, &boundary);
- if (map_type == 0)
- /* Thumb-2 code must have mapping symbols to have a chance. */
- return bpaddr;
-
- bpaddr = gdbarch_addr_bits_remove (gdbarch, bpaddr);
-
- if (find_pc_partial_function (bpaddr, NULL, &func_start, NULL)
- && func_start > boundary)
- boundary = func_start;
-
- /* Search for a candidate IT instruction. We have to do some fancy
- footwork to distinguish a real IT instruction from the second
- half of a 32-bit instruction, but there is no need for that if
- there's no candidate. */
- buf_len = min (bpaddr - boundary, MAX_IT_BLOCK_PREFIX);
- if (buf_len == 0)
- /* No room for an IT instruction. */
- return bpaddr;
-
- buf = (gdb_byte *) xmalloc (buf_len);
- if (target_read_memory (bpaddr - buf_len, buf, buf_len) != 0)
- return bpaddr;
- any = 0;
- for (i = 0; i < buf_len; i += 2)
- {
- unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
- if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
- {
- any = 1;
- break;
- }
- }
- if (any == 0)
- {
- xfree (buf);
- return bpaddr;
- }
-
- /* OK, the code bytes before this instruction contain at least one
- halfword which resembles an IT instruction. We know that it's
- Thumb code, but there are still two possibilities. Either the
- halfword really is an IT instruction, or it is the second half of
- a 32-bit Thumb instruction. The only way we can tell is to
- scan forwards from a known instruction boundary. */
- if (bpaddr - boundary > IT_SCAN_THRESHOLD)
- {
- int definite;
+ int definite;
/* There's a lot of code before this instruction. Start with an
optimistic search; it's easy to recognize halfwords that can
@@ -7291,6 +6106,116 @@ thumb2_copy_block_xfer (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
return 0;
}
+/* Initialize arm_gdb_get_next_pcs_stor. */
+void
+arm_gdb_get_next_pcs_ctor (struct arm_gdb_get_next_pcs *self,
+ struct arm_get_next_pcs_ops *ops,
+ int byte_order,
+ int byte_order_for_code,
+ int is_thumb,
+ int arm_apcs_32,
+ const gdb_byte *arm_thumb2_breakpoint,
+ struct frame_info *frame,
+ struct gdbarch *gdbarch)
+{
+ arm_get_next_pcs_ctor ((struct arm_get_next_pcs *) self,
+ ops,
+ byte_order,
+ byte_order_for_code,
+ is_thumb,
+ arm_apcs_32,
+ arm_thumb2_breakpoint);
+
+ self->frame = frame;
+ self->gdbarch = gdbarch;
+}
+
+/* Wrapper over read_memory_unsigned_integer for use in arm_get_next_pcs.
+ This is used to avoid a dependency on BFD's bfd_endian enum. */
+
+ULONGEST
+arm_get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr, int len,
+ int byte_order)
+{
+ return read_memory_unsigned_integer (memaddr, len, byte_order);
+}
+
+/* Wrapper over gdbarch_addr_bits_remove for use in arm_get_next_pcs. */
+
+CORE_ADDR
+arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *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 arm_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 syscall_next_pc for use in get_next_pcs. */
+
+CORE_ADDR
+arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc)
+{
+ struct arm_gdb_get_next_pcs *next_pcs = (struct arm_gdb_get_next_pcs *) self;
+ struct gdbarch_tdep *tdep;
+
+ tdep = gdbarch_tdep (next_pcs->gdbarch);
+ if (tdep->syscall_next_pc != NULL)
+ return tdep->syscall_next_pc (next_pcs->frame);
+
+ 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_ctx;
+ CORE_ADDR pc;
+ int i;
+ VEC (CORE_ADDR) *next_pcs = NULL;
+
+ arm_gdb_get_next_pcs_ctor (&next_pcs_ctx,
+ &arm_get_next_pcs_ops,
+ gdbarch_byte_order (gdbarch),
+ gdbarch_byte_order_for_code (gdbarch),
+ arm_frame_is_thumb (frame),
+ arm_apcs_32,
+ gdbarch_tdep (gdbarch)->thumb2_breakpoint,
+ frame,
+ gdbarch);
+
+ next_pcs = arm_get_next_pcs ((struct arm_get_next_pcs *) &next_pcs,
+ get_frame_pc (frame));
+
+ for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++)
+ {
+ arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
+ }
+
+ VEC_free (CORE_ADDR, next_pcs);
+
+ 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 9b8447b..f3a13a4 100644
--- a/gdb/arm-tdep.h
+++ b/gdb/arm-tdep.h
@@ -23,6 +23,9 @@
struct gdbarch;
struct regset;
struct address_space;
+struct get_next_pcs;
+struct arm_get_next_pcs;
+struct gdb_get_next_pcs;
#include "arch/arm.h"
@@ -221,6 +224,17 @@ struct displaced_step_closure
struct displaced_step_closure *);
};
+/* Context for a get_next_pcs call on ARM in GDB. */
+struct arm_gdb_get_next_pcs
+{
+ /* Common context for gdb/gdbserver. */
+ struct arm_get_next_pcs base;
+ /* Frame information. */
+ struct frame_info *frame;
+ /* Architecture dependent information. */
+ struct gdbarch *gdbarch;
+};
+
/* Values for the WRITE_PC argument to displaced_write_reg. If the register
write may write to the PC, specifies the way the CPSR T bit, etc. is
modified by the instruction. */
@@ -250,11 +264,37 @@ 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);
+
+ULONGEST arm_get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
+ int len,
+ int byte_order);
+
+void arm_gdb_get_next_pcs_ctor (struct arm_gdb_get_next_pcs *self,
+ struct arm_get_next_pcs_ops *ops,
+ int byte_order,
+ int byte_order_for_code,
+ int is_thumb,
+ int arm_apcs_32,
+ const gdb_byte *arm_thumb2_breakpoint,
+ struct frame_info *frame,
+ struct gdbarch *gdbarch);
+
+CORE_ADDR
+arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
+ CORE_ADDR val);
+
+ULONGEST
+arm_get_next_pcs_collect_register_unsigned (struct arm_get_next_pcs *self,
+ int n);
+
+CORE_ADDR
+arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc);
+
+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 3abd89d..d3add93 100644
--- a/gdb/arm-wince-tdep.c
+++ b/gdb/arm-wince-tdep.c
@@ -25,6 +25,7 @@
#include "frame.h"
#include "arch/arm.h"
+#include "arch/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "windows-tdep.h"
diff --git a/gdb/armbsd-tdep.c b/gdb/armbsd-tdep.c
index 1b9bad7..b19048e 100644
--- a/gdb/armbsd-tdep.c
+++ b/gdb/armbsd-tdep.c
@@ -22,6 +22,7 @@
#include "regcache.h"
#include "regset.h"
+#include "arch/arm-get-next-pcs.h"
#include "arm-tdep.h"
/* Core file support. */
diff --git a/gdb/armnbsd-tdep.c b/gdb/armnbsd-tdep.c
index 14eceaa..c67b226 100644
--- a/gdb/armnbsd-tdep.c
+++ b/gdb/armnbsd-tdep.c
@@ -21,6 +21,7 @@
#include "osabi.h"
#include "arch/arm.h"
+#include "arch/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "solib-svr4.h"
diff --git a/gdb/armobsd-tdep.c b/gdb/armobsd-tdep.c
index 58bcc5d..883c63f 100644
--- a/gdb/armobsd-tdep.c
+++ b/gdb/armobsd-tdep.c
@@ -23,6 +23,7 @@
#include "tramp-frame.h"
#include "obsd-tdep.h"
+#include "arch/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "solib-svr4.h"
diff --git a/gdb/common/gdb_vecs.h b/gdb/common/gdb_vecs.h
index b3f56f8..a08d416 100644
--- a/gdb/common/gdb_vecs.h
+++ b/gdb/common/gdb_vecs.h
@@ -31,6 +31,8 @@ DEF_VEC_P (const_char_ptr);
DEF_VEC_I (int);
+DEF_VEC_I (CORE_ADDR);
+
extern void free_char_ptr_vec (VEC (char_ptr) *char_ptr_vec);
extern struct cleanup *
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index dfbb4d1..eb56e7e 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -44,7 +44,7 @@ aarch64*-*-elf)
aarch64*-*-linux*)
# Target: AArch64 linux
gdb_target_obs="aarch64-tdep.o aarch64-linux-tdep.o aarch64-insn.o \
- arm.o arm-tdep.o arm-linux-tdep.o \
+ arm.o arm-get-next-pcs.o arm-tdep.o arm-linux-tdep.o \
glibc-tdep.o linux-tdep.o solib-svr4.o \
symfile-mem.o linux-record.o"
build_gdbserver=yes
@@ -84,31 +84,34 @@ am33_2.0*-*-linux*)
arm*-wince-pe | arm*-*-mingw32ce*)
# Target: ARM based machine running Windows CE (win32)
- gdb_target_obs="arm.o arm-tdep.o arm-wince-tdep.o windows-tdep.o"
+ gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o arm-wince-tdep.o \
+ windows-tdep.o"
build_gdbserver=yes
;;
arm*-*-linux*)
# Target: ARM based machine running GNU/Linux
- gdb_target_obs="arm.o arm-tdep.o arm-linux-tdep.o glibc-tdep.o \
+ gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o \
+ arm-linux-tdep.o glibc-tdep.o \
solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o"
build_gdbserver=yes
;;
arm*-*-netbsd* | arm*-*-knetbsd*-gnu)
# Target: NetBSD/arm
- gdb_target_obs="arm.o arm-tdep.o armnbsd-tdep.o solib-svr4.o"
+ gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o armnbsd-tdep.o \
+ solib-svr4.o"
;;
arm*-*-openbsd*)
# Target: OpenBSD/arm
- gdb_target_obs="arm.o arm-tdep.o armbsd-tdep.o armobsd-tdep.o \
- obsd-tdep.o solib-svr4.o"
+ gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o armbsd-tdep.o \
+ armobsd-tdep.o obsd-tdep.o solib-svr4.o"
;;
arm*-*-symbianelf*)
# Target: SymbianOS/arm
- gdb_target_obs="arm.o arm-tdep.o arm-symbian-tdep.o"
+ gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o arm-symbian-tdep.o"
;;
arm*-*-*)
# Target: ARM embedded system
- gdb_target_obs="arm.o arm-tdep.o"
+ gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o"
gdb_sim=../sim/arm/libsim.a
;;
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index f18243b..22034fe 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -181,7 +181,7 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
$(srcdir)/common/common-exceptions.c $(srcdir)/symbol.c \
$(srcdir)/common/btrace-common.c \
$(srcdir)/common/fileio.c $(srcdir)/nat/linux-namespaces.c \
- $(srcdir)/arch/arm.c
+ $(srcdir)/arch/arm.c $(srcdir)/arch/arm-get-next-pcs.c
DEPFILES = @GDBSERVER_DEPFILES@
@@ -589,6 +589,9 @@ fileio.o: ../common/fileio.c
arm.o: ../arch/arm.c
$(COMPILE) $<
$(POSTCOMPILE)
+arm-get-next-pcs.o: ../arch/arm-get-next-pcs.c
+ $(COMPILE) $<
+ $(POSTCOMPILE)
# Native object files rules from ../nat
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index e854110..838dedd 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -71,6 +71,7 @@ case "${target}" in
srv_tgtobj="$srv_linux_obj linux-arm-low.o"
srv_tgtobj="$srv_tgtobj linux-aarch32-low.o"
srv_tgtobj="${srv_tgtobj} arm.o"
+ srv_tgtobj="${srv_tgtobj} arm-get-next-pcs.o"
srv_xmlfiles="arm-with-iwmmxt.xml"
srv_xmlfiles="${srv_xmlfiles} arm-with-vfpv2.xml"
srv_xmlfiles="${srv_xmlfiles} arm-with-vfpv3.xml"
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 78a4c8f..bb2a1b8 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -31,6 +31,7 @@
#include <signal.h>
#include "arch/arm.h"
+#include "arch/arm-get-next-pcs.h"
/* Defined in auto-generated files. */
void init_registers_arm (void);
@@ -126,6 +127,15 @@ struct arch_lwp_info
CORE_ADDR stopped_data_address;
};
+/* Context for a get_next_pcs call on ARM in GDBServer. */
+struct arm_gdbserver_get_next_pcs
+{
+ /* Common context for gdb/gdbserver. */
+ struct arm_get_next_pcs base;
+ /* The cache for registry values. */
+ struct regcache *regcache;
+};
+
/* These are in <asm/elf.h> in current kernels. */
#define HWCAP_VFP 64
#define HWCAP_IWMMXT 512
@@ -146,6 +156,29 @@ static int arm_regmap[] = {
64
};
+/* Forward declarations needed for get_next_pcs ops. */
+static ULONGEST
+get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
+ int len,
+ int byte_order);
+
+static ULONGEST
+get_next_pcs_collect_register_unsigned (struct arm_get_next_pcs *self, int n);
+
+static CORE_ADDR
+get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, CORE_ADDR val);
+
+static CORE_ADDR
+get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc);
+
+/* get_next_pcs operations. */
+static struct arm_get_next_pcs_ops get_next_pcs_ops = {
+ get_next_pcs_read_memory_unsigned_integer,
+ get_next_pcs_collect_register_unsigned,
+ get_next_pcs_syscall_next_pc,
+ get_next_pcs_addr_bits_remove
+};
+
static int
arm_cannot_store_register (int regno)
{
@@ -208,6 +241,13 @@ arm_fill_vfpregset (struct regcache *regcache, void *buf)
arm_fill_vfpregset_num (regcache, buf, num);
}
+/* Wrapper of UNMAKE_THUMB_ADDR for get_next_pcs. */
+static CORE_ADDR
+get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, CORE_ADDR val)
+{
+ return UNMAKE_THUMB_ADDR (val);
+}
+
static void
arm_store_vfpregset (struct regcache *regcache, const void *buf)
{
@@ -317,6 +357,39 @@ arm_breakpoint_at (CORE_ADDR where)
return 0;
}
+/* Wrapper of collect_register for get_next_pcs. */
+
+static ULONGEST
+get_next_pcs_collect_register_unsigned (struct arm_get_next_pcs *self, int n)
+{
+ struct arm_gdbserver_get_next_pcs *next_pcs
+ = (struct arm_gdbserver_get_next_pcs *) self;
+ ULONGEST res = 0;
+ int size = register_size (next_pcs->regcache->tdesc, n);
+
+ if (size > (int) sizeof (ULONGEST))
+ error (_("That operation is not available on integers of more than"
+ "%d bytes."),
+ (int) sizeof (ULONGEST));
+
+ collect_register (next_pcs->regcache, n, &res);
+ return res;
+}
+
+/* Read memory from the inferiror.
+ BYTE_ORDER is ignored and there to keep compatiblity with GDB's
+ read_memory_unsigned_integer. */
+static ULONGEST
+get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
+ int len,
+ int byte_order)
+{
+ ULONGEST res;
+
+ (*the_target->read_memory) (memaddr, (unsigned char *) &res, len);
+ return res;
+}
+
/* Fetch the thread-local storage pointer for libthread_db. */
ps_err_e
@@ -801,6 +874,52 @@ arm_prepare_to_resume (struct lwp_info *lwp)
}
}
+/* When PC is at a syscall instruction, return the PC of the next
+ instruction to be executed. */
+static CORE_ADDR
+get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc)
+{
+ CORE_ADDR return_addr = 0;
+ int is_thumb = self->is_thumb;
+ ULONGEST svc_number = 0;
+
+ if (is_thumb)
+ {
+ svc_number = self->ops->collect_register_unsigned (self, 7);
+ return_addr = pc + 2;
+ }
+ else
+ {
+ unsigned long this_instr = self->ops->read_memory_unsigned_integer
+ ((CORE_ADDR) pc, 4, self->byte_order_for_code);
+
+ unsigned long svc_operand = (0x00ffffff & this_instr);
+ if (svc_operand) /* OABI. */
+ {
+ svc_number = svc_operand - 0x900000;
+ }
+ else /* EABI. */
+ {
+ svc_number = self->ops->collect_register_unsigned (self, 7);
+ }
+
+ return_addr = pc + 4;
+ }
+
+ /* Is this a sigreturn or rt_sigreturn syscall?
+ If so it is currently not handeled. */
+ if (svc_number == 119 || svc_number == 173)
+ {
+ if (debug_threads)
+ debug_printf ("Unhandled sigreturn or rt_sigreturn syscall\n");
+ }
+
+ /* Addresses for calling Thumb functions have the bit 0 set. */
+ if (is_thumb)
+ return_addr = MAKE_THUMB_ADDR (return_addr);
+
+ return return_addr;
+}
static int
arm_get_hwcap (unsigned long *valp)
@@ -890,6 +1009,53 @@ arm_arch_setup (void)
have_ptrace_getregset = 0;
}
+/* Initialize arm_gdbserver_get_next_pcs. */
+
+static void
+arm_gdbserver_get_next_pcs_ctor (struct arm_gdbserver_get_next_pcs *self,
+ struct arm_get_next_pcs_ops *ops,
+ int byte_order,
+ int byte_order_for_code,
+ int is_thumb,
+ int arm_apcs_32,
+ const gdb_byte *arm_thumb2_breakpoint,
+ struct regcache *regcache)
+{
+ arm_get_next_pcs_ctor ((struct arm_get_next_pcs *) self,
+ ops,
+ byte_order,
+ byte_order_for_code,
+ is_thumb,
+ arm_apcs_32,
+ arm_thumb2_breakpoint);
+
+ self->regcache = regcache;
+}
+
+/* Fetch the next possible PCs after the current instruction executes. */
+
+static VEC (CORE_ADDR) *
+arm_gdbserver_get_next_pcs (CORE_ADDR pc, struct regcache *regcache)
+{
+ struct arm_gdbserver_get_next_pcs next_pcs_ctx;
+ VEC (CORE_ADDR) *next_pcs = NULL;
+
+ arm_gdbserver_get_next_pcs_ctor (&next_pcs_ctx,
+ &get_next_pcs_ops,
+ /* Byte order is ignored assumed as host. */
+ 0,
+ 0,
+ arm_is_thumb_mode (),
+ /* 32-bit apcs supported only. */
+ 1,
+ (const gdb_byte *) &thumb2_breakpoint,
+ regcache);
+
+ next_pcs = arm_get_next_pcs ((struct arm_get_next_pcs *) &next_pcs_ctx, pc);
+
+ return next_pcs;
+}
+
/* Support for hardware single step. */
static int
@@ -1032,7 +1198,7 @@ struct linux_target_ops the_low_target = {
arm_set_pc,
arm_breakpoint_kind_from_pc,
arm_sw_breakpoint_from_kind,
- NULL, /* get_next_pcs */
+ arm_gdbserver_get_next_pcs,
0,
arm_breakpoint_at,
arm_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index ee8d88f..d36acfe 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -124,8 +124,6 @@ struct process_info_private
struct lwp_info;
-DEF_VEC_I (CORE_ADDR);
-
struct linux_target_ops
{
/* Architecture-specific setup. */
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 96ad4fa..9a4189f 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -123,6 +123,7 @@ extern void discard_queued_stop_replies (ptid_t ptid);
#include "utils.h"
#include "debug.h"
+#include "gdb_vecs.h"
/* Maximum number of bytes to read/write at once. The value here
is chosen to fill up a packet (the headers account for the 32). */
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 37c8c93..b522726 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1613,8 +1613,6 @@ void iterate_over_symtabs (const char *name,
void *data),
void *data);
-DEF_VEC_I (CORE_ADDR);
-
VEC (CORE_ADDR) *find_pcs_for_symtab_line (struct symtab *symtab, int line,
struct linetable_entry **best_entry);
--
2.6.3
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH v3 10/10] Enable conditional breakpoints for targets that support software single step in GDBServer.
2015-11-23 14:14 [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
` (8 preceding siblings ...)
2015-11-23 14:15 ` [PATCH v3 08/10] Support software single step on ARM in GDBServer Antoine Tremblay
@ 2015-11-23 14:15 ` Antoine Tremblay
2015-11-26 10:25 ` Yao Qi
2015-11-27 9:27 ` [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM " Yao Qi
10 siblings, 1 reply; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-23 14:15 UTC (permalink / raw)
To: gdb-patches; +Cc: Antoine Tremblay
This patch enables support for conditional breakpoints if the target supports
software single step.
This was disabled before as the implementations of software single step were too
simple as discussed in
https://sourceware.org/ml/gdb-patches/2015-04/msg01110.html.
Since these issues are now fixed support can be added back.
No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }
gdb/gdbserver/ChangeLog:
* server.c (handle_query): Call target_supports_software_single_step.
---
gdb/gdbserver/server.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index fd2804b..2216ab8 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2211,13 +2211,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
strcat (own_buf, ";tracenz+");
}
- if (target_supports_hardware_single_step ())
+ if (target_supports_hardware_single_step ()
+ || target_supports_software_single_step () )
{
- /* Support target-side breakpoint conditions and commands.
- GDBserver needs to step over the breakpoint if the condition
- is false. GDBserver software single step is too simple, so
- disable conditional breakpoints if the target doesn't have
- hardware single step. */
strcat (own_buf, ";ConditionalBreakpoints+");
}
strcat (own_buf, ";BreakpointCommands+");
--
2.6.3
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 04/10] Remove support for thread events without PTRACE_EVENT_CLONE in GDBServer.
2015-11-23 14:14 ` [PATCH v3 04/10] Remove support for thread events without PTRACE_EVENT_CLONE in GDBServer Antoine Tremblay
@ 2015-11-25 16:48 ` Yao Qi
2015-11-25 17:42 ` Antoine Tremblay
0 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2015-11-25 16:48 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
> ---
> gdb/gdbserver/linux-low.c | 2 +-
> gdb/gdbserver/linux-low.h | 2 +-
> gdb/gdbserver/thread-db.c | 153 ++--------------------------------------------
> 3 files changed, 6 insertions(+), 151 deletions(-)
>
> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index f793a78..ce9cc2e 100644
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -5515,7 +5515,7 @@ linux_look_up_symbols (void)
> /* If the kernel supports tracing clones, then we don't need to
> use the magic thread event breakpoint to learn about
> threads. */
Nit: we no longer need this comment above. We can remove it too.
> - thread_db_init (!linux_supports_traceclone ());
> + thread_db_init ();
Patch looks good to me.
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 07/10] Share some ARM target dependant code from GDB with GDBServer.
2015-11-23 14:14 ` [PATCH v3 07/10] Share some ARM target dependant code from GDB with GDBServer Antoine Tremblay
@ 2015-11-25 17:01 ` Yao Qi
2015-11-26 10:38 ` Yao Qi
1 sibling, 0 replies; 37+ messages in thread
From: Yao Qi @ 2015-11-25 17:01 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
> gdb/ChangeLog:
>
> * arch/arm.c (bitcount): Move from arm-tdep.c.
> (condition_true): Likewise.
> * arch/arm.h (Instruction Definitions): Move form arm-tdep.h.
> (condition_true): Move defenition from arm-tdep.h.
> (bitcount): Likewise.
> * arm-tdep.c (condition_true): Move to arch/arm.c.
> (bitcount): Likewise.
> * arm-tdep.h (Instruction Definitions): Move to arch/arm.h.
> * arm-wince-tdep.c: Include arch/arm.h.
> * armnbsd-tdep.c: Likewise.
Looks good to me.
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 04/10] Remove support for thread events without PTRACE_EVENT_CLONE in GDBServer.
2015-11-25 16:48 ` Yao Qi
@ 2015-11-25 17:42 ` Antoine Tremblay
0 siblings, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-25 17:42 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 11/25/2015 11:48 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> ---
>> gdb/gdbserver/linux-low.c | 2 +-
>> gdb/gdbserver/linux-low.h | 2 +-
>> gdb/gdbserver/thread-db.c | 153 ++--------------------------------------------
>> 3 files changed, 6 insertions(+), 151 deletions(-)
>>
>> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
>> index f793a78..ce9cc2e 100644
>> --- a/gdb/gdbserver/linux-low.c
>> +++ b/gdb/gdbserver/linux-low.c
>> @@ -5515,7 +5515,7 @@ linux_look_up_symbols (void)
>> /* If the kernel supports tracing clones, then we don't need to
>> use the magic thread event breakpoint to learn about
>> threads. */
>
> Nit: we no longer need this comment above. We can remove it too.
>
Indeed, fixed.
>> - thread_db_init (!linux_supports_traceclone ());
>> + thread_db_init ();
>
> Patch looks good to me.
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 10/10] Enable conditional breakpoints for targets that support software single step in GDBServer.
2015-11-23 14:15 ` [PATCH v3 10/10] Enable conditional breakpoints for targets that support software single step " Antoine Tremblay
@ 2015-11-26 10:25 ` Yao Qi
2015-11-26 15:34 ` Antoine Tremblay
0 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2015-11-26 10:25 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
> No regressions, tested on ubuntu 14.04 ARMv7 and x86.
> With gdbserver-{native,extended} / { -marm -mthumb }
There should be more PASS in the test result, what are they?
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 06/10] Replace breakpoint_reinsert_addr by get_next_pcs operation in GDBServer.
2015-11-23 14:14 ` [PATCH v3 06/10] Replace breakpoint_reinsert_addr by get_next_pcs operation " Antoine Tremblay
@ 2015-11-26 10:30 ` Yao Qi
2015-11-26 13:48 ` Antoine Tremblay
2015-11-26 10:50 ` Pedro Alves
1 sibling, 1 reply; 37+ messages in thread
From: Yao Qi @ 2015-11-26 10:30 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
> gdb/gdbserver/ChangeLog:
> * linux-aarch64-low.c (struct linux_target_ops): Rename
> breakpoint_reinsert_addr to get_next_pcs.
Nit: the change is to the_low_target, not "struct linux_target_ops", so
it should be
* linux-aarch64-low.c (the_low_target): ....
> * linux-arm-low.c (struct linux_target_ops): Likewise.
> * linux-bfin-low.c (struct linux_target_ops): Likewise.
> * linux-cris-low.c (struct linux_target_ops): Likewise.
> * linux-crisv32-low.c (struct linux_target_ops): Likewise.
> * linux-low.c (can_software_single_step): Likewise.
> (install_software_single_step_breakpoints): New function.
> (start_step_over): Use install_software_single_step_breakpoints.
> * linux-low.h: New CORE_ADDR vector.
> (struct linux_target_ops) Rename breakpoint_reinsert_addr to
> get_next_pcs.
> * linux-mips-low.c (struct linux_target_ops): Likewise.
> * linux-nios2-low.c (struct linux_target_ops): Likewise.
> * linux-sparc-low.c (struct linux_target_ops): Likewise.
Patch is good to me.
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 07/10] Share some ARM target dependant code from GDB with GDBServer.
2015-11-23 14:14 ` [PATCH v3 07/10] Share some ARM target dependant code from GDB with GDBServer Antoine Tremblay
2015-11-25 17:01 ` Yao Qi
@ 2015-11-26 10:38 ` Yao Qi
2015-11-26 13:56 ` Antoine Tremblay
1 sibling, 1 reply; 37+ messages in thread
From: Yao Qi @ 2015-11-26 10:38 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
> +/* See arm.h. */
> +
> +int
> +condition_true (unsigned long cond, unsigned long status_reg)
We need "arm_" prefix for this function. Otherwise, patch is OK.
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 08/10] Support software single step on ARM in GDBServer.
2015-11-23 14:15 ` [PATCH v3 08/10] Support software single step on ARM in GDBServer Antoine Tremblay
@ 2015-11-26 10:49 ` Pedro Alves
2015-11-26 13:35 ` Antoine Tremblay
2015-11-26 12:48 ` Yao Qi
1 sibling, 1 reply; 37+ messages in thread
From: Pedro Alves @ 2015-11-26 10:49 UTC (permalink / raw)
To: Antoine Tremblay, gdb-patches
On 11/23/2015 02:12 PM, Antoine Tremblay wrote:
> In this v3:
>
> * The common arm_get_next_pcs call has been refactored the same way like
> so : VEC (CORE_ADDR) *arm_get_next_pcs (struct arm_get_next_pcs *ctx,
> CORE_ADDR pc);
>
> * Use ctor functions to construct gdb|gdbserver_get_next_pcs context.
>
> * Some style fixes.
>
> ---
>
> This patch teaches GDBserver how to software single step on ARM
> linux by sharing code with GDB.
>
> The arm_get_next_pcs function in GDB is now shared with GDBServer. So that
> GDBServer can use the function to return the possible addresses of the next PC.
>
> A proper shared context was also needed so that we could share the code, this
> context is described as this data structure (expressed as a class
> hierarchy):
>
> struct arch_get_next_pcs
> struct arch_(gdb|gdbserver)_get_next_pcs
>
> Where arch can by replaced by arm for this patch. This structure should be
> flexible enough to accomodate any arch that would need a get_next_pcs
> context.
(accommodate)
>
> Limitations :
>
> GDBServer can't step over a sigreturn or rt_sigreturn syscall since this would
> require the DWARF information to identify the caller PC. This situation
> only prints a warning for the moment.
I wonder whether this ends up being a regression? E.g., if you
put a breakpoint with a condition that evals false, on top of such an
instruction?
I wonder whether this ends up being a regression, or whether it only trigger
on new features, like tracepoints? I'm thinking that it may be a
regression for the case of a conditional breakpoint with a conditional
evaluated on the target?
Other than the warning, what happens? Does gdbserver lose control of
the inferior?
> + /* Insert a breakpoint right after the end of the atomic sequence. */
> + breaks[0] = loc;
> +
> + /* Check for duplicated breakpoints. Check also for a breakpoint
> + placed (branch instruction's destination) anywhere in sequence. */
> + if (last_breakpoint
> + && (breaks[1] == breaks[0]
> + || (breaks[1] >= pc && breaks[1] < loc)))
> + last_breakpoint = 0;
> +
> + /* Adds the breakpoints to the list to be inserted. */
> + for (index = 0; index <= last_breakpoint; index++)
> + {
> + VEC_safe_push (CORE_ADDR, *next_pcs, MAKE_THUMB_ADDR (breaks[index]));
> + }
No braces.
> +
> + return 1;
> +}
> +
> + else if (itstate & 0x0f)
> + {
> + /* We are in a conditional block. Check the condition. */
> + int cond = itstate >> 4;
> +
> + if (! condition_true (cond, status))
> + /* Advance to the next instruction. All the 32-bit
> + instructions share a common prefix. */
> + VEC_safe_push (CORE_ADDR, *next_pcs,
> + MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1)));
Here there should be braces around the comment and the VEC call.
> +
> + return *next_pcs;
> +
> + /* Otherwise, handle the instruction normally. */
> + }
> @@ -912,27 +922,44 @@ 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;
> + struct arm_gdb_get_next_pcs next_pcs_ctx;
> + CORE_ADDR pc;
> + int i;
> + VEC (CORE_ADDR) *next_pcs = NULL;
>
> /* If the target does have hardware single step, GDB doesn't have
> to bother software single step. */
> if (target_can_do_single_step () == 1)
> return 0;
>
> - next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
> + arm_gdb_get_next_pcs_ctor (&next_pcs_ctx,
> + &arm_linux_get_next_pcs_ops,
> + gdbarch_byte_order (gdbarch),
> + gdbarch_byte_order_for_code (gdbarch),
> + arm_frame_is_thumb (frame),
> + arm_apcs_32,
> + gdbarch_tdep (gdbarch)->thumb2_breakpoint,
> + frame,
> + gdbarch);
>
> - /* 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 = arm_get_next_pcs ((struct arm_get_next_pcs *) &next_pcs_ctx,
> + get_frame_pc (frame));
> +
> + for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++)
> + {
> + /* The Linux kernel offers some user-mode helpers in a high page. We can
> + not read this page (as of 2.6.23), and even if we could then we
> + couldn't set breakpoints in it, and even if we could then the atomic
> + operations would fail when interrupted. They are all called as
> + functions and return to the address in LR, so step to there
> + instead. */
> + if (pc > 0xffff0000)
> + pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
> +
> + arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
> + }
>
> - arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
> + VEC_free (CORE_ADDR, next_pcs);
This would be better freed with a cleanup:
VEC (CORE_ADDR) *next_pcs = NULL;
old_chain = make_cleanup (VEC_cleanup (CORE_ADDR), next_pcs);
...
do_cleanups (old_chain);
>
> return 1;
> }
> /* There's a lot of code before this instruction. Start with an
> optimistic search; it's easy to recognize halfwords that can
> @@ -7291,6 +6106,116 @@ thumb2_copy_block_xfer (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
> return 0;
> }
>
> +/* Initialize arm_gdb_get_next_pcs_stor. */
/* Initialize arm_gdb_get_next_pcs. */
> +void
> +arm_gdb_get_next_pcs_ctor (struct arm_gdb_get_next_pcs *self,
> + struct arm_get_next_pcs_ops *ops,
> + int byte_order,
> +/* 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_ctx;
> + CORE_ADDR pc;
> + int i;
> + VEC (CORE_ADDR) *next_pcs = NULL;
> +
> + arm_gdb_get_next_pcs_ctor (&next_pcs_ctx,
> + &arm_get_next_pcs_ops,
> + gdbarch_byte_order (gdbarch),
> + gdbarch_byte_order_for_code (gdbarch),
> + arm_frame_is_thumb (frame),
> + arm_apcs_32,
> + gdbarch_tdep (gdbarch)->thumb2_breakpoint,
> + frame,
> + gdbarch);
> +
> + next_pcs = arm_get_next_pcs ((struct arm_get_next_pcs *) &next_pcs,
This is wrong. Should be "(struct arm_get_next_pcs *) &next_pcs_ctx" instead.
I think this shows that it'd be good to run the series against the
testsuite on native ARM.
You could avoid these wrong casts by writting "&next_pcs_ctx.base" instead.
> + get_frame_pc (frame));
> +
> + for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++)
> + {
> + arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
> + }
No braces.
> +
> + VEC_free (CORE_ADDR, next_pcs);
Use a cleanup instead.
> +
> + 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 9b8447b..f3a13a4 100644
> --- a/gdb/arm-tdep.h
> +++ b/gdb/arm-tdep.h
> @@ -23,6 +23,9 @@
> struct gdbarch;
> struct regset;
> struct address_space;
> +struct get_next_pcs;
> +struct arm_get_next_pcs;
> +struct gdb_get_next_pcs;
>
> #include "arch/arm.h"
>
> @@ -221,6 +224,17 @@ struct displaced_step_closure
> struct displaced_step_closure *);
> };
>
> +/* Context for a get_next_pcs call on ARM in GDB. */
> +struct arm_gdb_get_next_pcs
> +{
> + /* Common context for gdb/gdbserver. */
> + struct arm_get_next_pcs base;
> + /* Frame information. */
> + struct frame_info *frame;
> + /* Architecture dependent information. */
> + struct gdbarch *gdbarch;
> +};
> +
> /* Values for the WRITE_PC argument to displaced_write_reg. If the register
> write may write to the PC, specifies the way the CPSR T bit, etc. is
> modified by the instruction. */
> @@ -250,11 +264,37 @@ 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);
> +
> +ULONGEST arm_get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
> + int len,
> + int byte_order);
> +
> +void arm_gdb_get_next_pcs_ctor (struct arm_gdb_get_next_pcs *self,
> + struct arm_get_next_pcs_ops *ops,
> + int byte_order,
> + int byte_order_for_code,
> + int is_thumb,
> + int arm_apcs_32,
> + const gdb_byte *arm_thumb2_breakpoint,
> + struct frame_info *frame,
> + struct gdbarch *gdbarch);
> +
> +CORE_ADDR
> +arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
See extension.h for how to indent these long declarations. The function
name should not be at column 0.
> + CORE_ADDR val);
> +
> +ULONGEST
> +arm_get_next_pcs_collect_register_unsigned (struct arm_get_next_pcs *self,
> + int n);
> +
> +CORE_ADDR
> +arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc);
> +
> +int arm_software_single_step (struct frame_info *frame);
> +
> +
> /* These are in <asm/elf.h> in current kernels. */
> #define HWCAP_VFP 64
> #define HWCAP_IWMMXT 512
> @@ -146,6 +156,29 @@ static int arm_regmap[] = {
> 64
> };
>
> +/* Forward declarations needed for get_next_pcs ops. */
> +static ULONGEST
> +get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
> + int len,
> + int byte_order);
> +
> +static ULONGEST
> +get_next_pcs_collect_register_unsigned (struct arm_get_next_pcs *self, int n);
> +
> +static CORE_ADDR
> +get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, CORE_ADDR val);
> +
> +static CORE_ADDR
> +get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc);
> +
Ditto.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 06/10] Replace breakpoint_reinsert_addr by get_next_pcs operation in GDBServer.
2015-11-23 14:14 ` [PATCH v3 06/10] Replace breakpoint_reinsert_addr by get_next_pcs operation " Antoine Tremblay
2015-11-26 10:30 ` Yao Qi
@ 2015-11-26 10:50 ` Pedro Alves
2015-11-26 13:50 ` Antoine Tremblay
1 sibling, 1 reply; 37+ messages in thread
From: Pedro Alves @ 2015-11-26 10:50 UTC (permalink / raw)
To: Antoine Tremblay, gdb-patches
On 11/23/2015 02:12 PM, Antoine Tremblay wrote:
> In this v3:
>
> * Changed get_next_pcs signature.
> * Remove common/get-next-pcs.h file.
> * CORE_ADDR is now in linux-low.h temporarely.
>
> ---
>
> This patch in preparation for software single step support on ARM. It refactors
> breakpoint_reinsert_addr into get_next_pcs so that multiple location can be
> returned.
>
> When software single stepping there can be multiple possible next addresses
> because we're stepping over a conditional branch instruction for example.
>
> The operation get_next_pcs handles that by returning a vector of all the
> possible next addresses.
>
> Software breakpoints are installed at each location returned.
>
Thanks for doing this. This interface does look cleaner to me, and a
more direct mapping C++ (so easier to clean up in the future).
>
> +/* Install breakpoints for software single stepping. */
> +
> +static void
> +install_software_single_step_breakpoints (struct lwp_info *lwp)
> +{
> + int i;
> + CORE_ADDR pc;
> + struct regcache *regcache = get_thread_regcache (current_thread, 1);
> + VEC (CORE_ADDR) *next_pcs = NULL;
I think this should be freed with a cleanup.
> +
> + pc = get_pc (lwp);
> + next_pcs = (*the_low_target.get_next_pcs) (pc, regcache);
> +
> + for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); ++i)
> + {
> + set_reinsert_breakpoint (pc);
> + }
Single-line statements don't get braces.
Otherwise LGTM.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 08/10] Support software single step on ARM in GDBServer.
2015-11-23 14:15 ` [PATCH v3 08/10] Support software single step on ARM in GDBServer Antoine Tremblay
2015-11-26 10:49 ` Pedro Alves
@ 2015-11-26 12:48 ` Yao Qi
2015-11-26 15:12 ` Antoine Tremblay
1 sibling, 1 reply; 37+ messages in thread
From: Yao Qi @ 2015-11-26 12:48 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
> Limitations :
>
> GDBServer can't step over a sigreturn or rt_sigreturn syscall since this would
> require the DWARF information to identify the caller PC. This situation
> only prints a warning for the moment.
They are using frame not DWARF to identify the PC, however, it is not
necessary. When GDB or GDBserver is doing software single step, it must
be in the inner-most frame, so we can get the caller PC by examining the
stack at a certain offset from SP (from regcache), like what are doing in
arm_linux_sigreturn_init and arm_linux_rt_sigreturn_init, rather than
replying frame unwinding. See my comments to arm_get_next_pcs_syscall_next_pc.
> +
> +/* Wrapper over syscall_next_pc for use in get_next_pcs. */
> +
> +CORE_ADDR
> +arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc)
> +{
> + struct arm_gdb_get_next_pcs *next_pcs = (struct arm_gdb_get_next_pcs *) self;
> + struct gdbarch_tdep *tdep;
> +
> + tdep = gdbarch_tdep (next_pcs->gdbarch);
> + if (tdep->syscall_next_pc != NULL)
> + return tdep->syscall_next_pc (next_pcs->frame);
> +
> + return 0;
> +}
> +
> +/* When PC is at a syscall instruction, return the PC of the next
> + instruction to be executed. */
> +static CORE_ADDR
> +get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc)
> +{
> + CORE_ADDR return_addr = 0;
> + int is_thumb = self->is_thumb;
> + ULONGEST svc_number = 0;
> +
> + if (is_thumb)
> + {
> + svc_number = self->ops->collect_register_unsigned (self, 7);
> + return_addr = pc + 2;
> + }
> + else
> + {
> + unsigned long this_instr = self->ops->read_memory_unsigned_integer
> + ((CORE_ADDR) pc, 4, self->byte_order_for_code);
> +
> + unsigned long svc_operand = (0x00ffffff & this_instr);
> + if (svc_operand) /* OABI. */
> + {
> + svc_number = svc_operand - 0x900000;
> + }
> + else /* EABI. */
> + {
> + svc_number = self->ops->collect_register_unsigned (self, 7);
> + }
> +
> + return_addr = pc + 4;
> + }
> +
> + /* Is this a sigreturn or rt_sigreturn syscall?
> + If so it is currently not handeled. */
> + if (svc_number == 119 || svc_number == 173)
> + {
> + if (debug_threads)
> + debug_printf ("Unhandled sigreturn or rt_sigreturn syscall\n");
We can still compute the caller pc by examining stack.
> + }
> +
> + /* Addresses for calling Thumb functions have the bit 0 set. */
> + if (is_thumb)
> + return_addr = MAKE_THUMB_ADDR (return_addr);
> +
> + return return_addr;
> +}
>
This leads me thinking more about current software single step code in
GDB. In GDB side, we are using frame to access registers, while in
GDBserver, we are using regcache. Why don't we use regcache in both
sides? so that the "struct arm_get_next_pcs_ops" can be simplified, for
example, field collect_register_unsigned may be no longer necessary.
In fact, software_single_step used to have regcache argument, added by
https://sourceware.org/ml/gdb-patches/2007-04/msg00218.html but we
changed to argument frame
https://sourceware.org/ml/gdb-patches/2007-04/msg00218.html IMO, it is
better to use regcache than frame. We have two options,
#1, switch from frame apis to regcache apis to access registers in arm
software single step. We can get regcache by get_current_regcache ().
#2, change argument of gdbarch method software_single_step from frame
to regcache, which means all its implementations need update, and
switch to regcache apis to access registers.
#2 is the right way to go in long term, and we really need to improve
software_single_step. Let me what do you think.
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 08/10] Support software single step on ARM in GDBServer.
2015-11-26 10:49 ` Pedro Alves
@ 2015-11-26 13:35 ` Antoine Tremblay
0 siblings, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-26 13:35 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 11/26/2015 05:49 AM, Pedro Alves wrote:
> On 11/23/2015 02:12 PM, Antoine Tremblay wrote:
>> In this v3:
>>
>> * The common arm_get_next_pcs call has been refactored the same way like
>> so : VEC (CORE_ADDR) *arm_get_next_pcs (struct arm_get_next_pcs *ctx,
>> CORE_ADDR pc);
>>
>> * Use ctor functions to construct gdb|gdbserver_get_next_pcs context.
>>
>> * Some style fixes.
>>
>> ---
>>
>> This patch teaches GDBserver how to software single step on ARM
>> linux by sharing code with GDB.
>>
>> The arm_get_next_pcs function in GDB is now shared with GDBServer. So that
>> GDBServer can use the function to return the possible addresses of the next PC.
>>
>> A proper shared context was also needed so that we could share the code, this
>> context is described as this data structure (expressed as a class
>> hierarchy):
>>
>> struct arch_get_next_pcs
>> struct arch_(gdb|gdbserver)_get_next_pcs
>>
>> Where arch can by replaced by arm for this patch. This structure should be
>> flexible enough to accomodate any arch that would need a get_next_pcs
>> context.
>
> (accommodate)
>
Fixed.
>>
>> Limitations :
>>
>> GDBServer can't step over a sigreturn or rt_sigreturn syscall since this would
>> require the DWARF information to identify the caller PC. This situation
>> only prints a warning for the moment.
>
> I wonder whether this ends up being a regression? E.g., if you
> put a breakpoint with a condition that evals false, on top of such an
> instruction?
Yes I think it could, I'll see if I can reproduce that.
>
> I wonder whether this ends up being a regression, or whether it only trigger
> on new features, like tracepoints?
That was my initial assessment but I think it could be a regression indeed.
I'm thinking that it may be a
> regression for the case of a conditional breakpoint with a conditional
> evaluated on the target?
>
> Other than the warning, what happens? Does gdbserver lose control of
> the inferior?
>
It will lose control, Yao has suggested a solution, I will look into it.
>> + /* Insert a breakpoint right after the end of the atomic sequence. */
>> + breaks[0] = loc;
>> +
>> + /* Check for duplicated breakpoints. Check also for a breakpoint
>> + placed (branch instruction's destination) anywhere in sequence. */
>> + if (last_breakpoint
>> + && (breaks[1] == breaks[0]
>> + || (breaks[1] >= pc && breaks[1] < loc)))
>> + last_breakpoint = 0;
>> +
>> + /* Adds the breakpoints to the list to be inserted. */
>> + for (index = 0; index <= last_breakpoint; index++)
>> + {
>> + VEC_safe_push (CORE_ADDR, *next_pcs, MAKE_THUMB_ADDR (breaks[index]));
>> + }
>
> No braces.
>
Fixed. and in the other place in the file too.
>> +
>> + return 1;
>> +}
>> +
>
>
>
>> + else if (itstate & 0x0f)
>> + {
>> + /* We are in a conditional block. Check the condition. */
>> + int cond = itstate >> 4;
>> +
>> + if (! condition_true (cond, status))
>> + /* Advance to the next instruction. All the 32-bit
>> + instructions share a common prefix. */
>> + VEC_safe_push (CORE_ADDR, *next_pcs,
>> + MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1)));
>
> Here there should be braces around the comment and the VEC call.
>
Ok.
>> +
>> + return *next_pcs;
>> +
>> + /* Otherwise, handle the instruction normally. */
>> + }
>
>
>> @@ -912,27 +922,44 @@ 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;
>> + struct arm_gdb_get_next_pcs next_pcs_ctx;
>> + CORE_ADDR pc;
>> + int i;
>> + VEC (CORE_ADDR) *next_pcs = NULL;
>>
>> /* If the target does have hardware single step, GDB doesn't have
>> to bother software single step. */
>> if (target_can_do_single_step () == 1)
>> return 0;
>>
>> - next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
>> + arm_gdb_get_next_pcs_ctor (&next_pcs_ctx,
>> + &arm_linux_get_next_pcs_ops,
>> + gdbarch_byte_order (gdbarch),
>> + gdbarch_byte_order_for_code (gdbarch),
>> + arm_frame_is_thumb (frame),
>> + arm_apcs_32,
>> + gdbarch_tdep (gdbarch)->thumb2_breakpoint,
>> + frame,
>> + gdbarch);
>>
>> - /* 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 = arm_get_next_pcs ((struct arm_get_next_pcs *) &next_pcs_ctx,
>> + get_frame_pc (frame));
>> +
>> + for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++)
>> + {
>> + /* The Linux kernel offers some user-mode helpers in a high page. We can
>> + not read this page (as of 2.6.23), and even if we could then we
>> + couldn't set breakpoints in it, and even if we could then the atomic
>> + operations would fail when interrupted. They are all called as
>> + functions and return to the address in LR, so step to there
>> + instead. */
>> + if (pc > 0xffff0000)
>> + pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
>> +
>> + arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
>> + }
>>
>> - arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
>> + VEC_free (CORE_ADDR, next_pcs);
>
> This would be better freed with a cleanup:
>
> VEC (CORE_ADDR) *next_pcs = NULL;
> old_chain = make_cleanup (VEC_cleanup (CORE_ADDR), next_pcs);
>
> ...
>
> do_cleanups (old_chain);
>
>>
>> return 1;
>> }
>
Ok fixed other occurrences too.
>
>> /* There's a lot of code before this instruction. Start with an
>> optimistic search; it's easy to recognize halfwords that can
>> @@ -7291,6 +6106,116 @@ thumb2_copy_block_xfer (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
>> return 0;
>> }
>>
>> +/* Initialize arm_gdb_get_next_pcs_stor. */
>
> /* Initialize arm_gdb_get_next_pcs. */
>
>
Done.
>> +void
>> +arm_gdb_get_next_pcs_ctor (struct arm_gdb_get_next_pcs *self,
>> + struct arm_get_next_pcs_ops *ops,
>> + int byte_order,
>
>
>
>> +/* 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_ctx;
>> + CORE_ADDR pc;
>> + int i;
>> + VEC (CORE_ADDR) *next_pcs = NULL;
>> +
>> + arm_gdb_get_next_pcs_ctor (&next_pcs_ctx,
>> + &arm_get_next_pcs_ops,
>> + gdbarch_byte_order (gdbarch),
>> + gdbarch_byte_order_for_code (gdbarch),
>> + arm_frame_is_thumb (frame),
>> + arm_apcs_32,
>> + gdbarch_tdep (gdbarch)->thumb2_breakpoint,
>> + frame,
>> + gdbarch);
>> +
>> + next_pcs = arm_get_next_pcs ((struct arm_get_next_pcs *) &next_pcs,
>
> This is wrong. Should be "(struct arm_get_next_pcs *) &next_pcs_ctx" instead.
>
Wow :( Indeed thx.
> I think this shows that it'd be good to run the series against the
> testsuite on native ARM.
I do run that all the time but on linux, it turns out I made the
misstake in arm-tdep.c but not in arm-linux-tdep.c so it did not show up
in my tests.
Thanks for seeing it!
>
> You could avoid these wrong casts by writting "&next_pcs_ctx.base" instead.
>
Indeed fixing like so all occurrences.
>> + get_frame_pc (frame));
>> +
>> + for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++)
>> + {
>> + arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
>> + }
>
> No braces.
>
Fixed.
>> +
>> + VEC_free (CORE_ADDR, next_pcs);
>
> Use a cleanup instead.
Fixed.
>
>> +
>> + 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 9b8447b..f3a13a4 100644
>> --- a/gdb/arm-tdep.h
>> +++ b/gdb/arm-tdep.h
>> @@ -23,6 +23,9 @@
>> struct gdbarch;
>> struct regset;
>> struct address_space;
>> +struct get_next_pcs;
>> +struct arm_get_next_pcs;
>> +struct gdb_get_next_pcs;
>>
>> #include "arch/arm.h"
>>
>> @@ -221,6 +224,17 @@ struct displaced_step_closure
>> struct displaced_step_closure *);
>> };
>>
>> +/* Context for a get_next_pcs call on ARM in GDB. */
>> +struct arm_gdb_get_next_pcs
>> +{
>> + /* Common context for gdb/gdbserver. */
>> + struct arm_get_next_pcs base;
>> + /* Frame information. */
>> + struct frame_info *frame;
>> + /* Architecture dependent information. */
>> + struct gdbarch *gdbarch;
>> +};
>> +
>> /* Values for the WRITE_PC argument to displaced_write_reg. If the register
>> write may write to the PC, specifies the way the CPSR T bit, etc. is
>> modified by the instruction. */
>> @@ -250,11 +264,37 @@ 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);
>> +
>> +ULONGEST arm_get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
>> + int len,
>> + int byte_order);
>> +
>> +void arm_gdb_get_next_pcs_ctor (struct arm_gdb_get_next_pcs *self,
>> + struct arm_get_next_pcs_ops *ops,
>> + int byte_order,
>> + int byte_order_for_code,
>> + int is_thumb,
>> + int arm_apcs_32,
>> + const gdb_byte *arm_thumb2_breakpoint,
>> + struct frame_info *frame,
>> + struct gdbarch *gdbarch);
>> +
>> +CORE_ADDR
>> +arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
>
> See extension.h for how to indent these long declarations. The function
> name should not be at column 0.
>
Ok, thanks for the example.
>> + CORE_ADDR val);
>> +
>> +ULONGEST
>> +arm_get_next_pcs_collect_register_unsigned (struct arm_get_next_pcs *self,
>> + int n);
>> +
>> +CORE_ADDR
>> +arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc);
>> +
>> +int arm_software_single_step (struct frame_info *frame);
>> +
>
>
>> +
>> /* These are in <asm/elf.h> in current kernels. */
>> #define HWCAP_VFP 64
>> #define HWCAP_IWMMXT 512
>> @@ -146,6 +156,29 @@ static int arm_regmap[] = {
>> 64
>> };
>>
>> +/* Forward declarations needed for get_next_pcs ops. */
>> +static ULONGEST
>> +get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
>> + int len,
>> + int byte_order);
>> +
>> +static ULONGEST
>> +get_next_pcs_collect_register_unsigned (struct arm_get_next_pcs *self, int n);
>> +
>> +static CORE_ADDR
>> +get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, CORE_ADDR val);
>> +
>> +static CORE_ADDR
>> +get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc);
>> +
>
> Ditto.
>
Done.
Note I also used a cleanup now in
install_software_single_step_breakpoint and removed the braces in the for.
Thank you,
Antoine
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 06/10] Replace breakpoint_reinsert_addr by get_next_pcs operation in GDBServer.
2015-11-26 10:30 ` Yao Qi
@ 2015-11-26 13:48 ` Antoine Tremblay
0 siblings, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-26 13:48 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 11/26/2015 05:29 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> gdb/gdbserver/ChangeLog:
>> * linux-aarch64-low.c (struct linux_target_ops): Rename
>> breakpoint_reinsert_addr to get_next_pcs.
>
> Nit: the change is to the_low_target, not "struct linux_target_ops", so
> it should be
>
Right indeed. fixed.
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 06/10] Replace breakpoint_reinsert_addr by get_next_pcs operation in GDBServer.
2015-11-26 10:50 ` Pedro Alves
@ 2015-11-26 13:50 ` Antoine Tremblay
0 siblings, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-26 13:50 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 11/26/2015 05:50 AM, Pedro Alves wrote:
> On 11/23/2015 02:12 PM, Antoine Tremblay wrote:
>> In this v3:
>>
>> * Changed get_next_pcs signature.
>> * Remove common/get-next-pcs.h file.
>> * CORE_ADDR is now in linux-low.h temporarely.
>>
>> ---
>>
>> This patch in preparation for software single step support on ARM. It refactors
>> breakpoint_reinsert_addr into get_next_pcs so that multiple location can be
>> returned.
>>
>> When software single stepping there can be multiple possible next addresses
>> because we're stepping over a conditional branch instruction for example.
>>
>> The operation get_next_pcs handles that by returning a vector of all the
>> possible next addresses.
>>
>> Software breakpoints are installed at each location returned.
>>
>
>
> Thanks for doing this. This interface does look cleaner to me, and a
> more direct mapping C++ (so easier to clean up in the future).
>
Indeed thanks for the idea!
>>
>> +/* Install breakpoints for software single stepping. */
>> +
>> +static void
>> +install_software_single_step_breakpoints (struct lwp_info *lwp)
>> +{
>> + int i;
>> + CORE_ADDR pc;
>> + struct regcache *regcache = get_thread_regcache (current_thread, 1);
>> + VEC (CORE_ADDR) *next_pcs = NULL;
>
> I think this should be freed with a cleanup.
>
>> +
>> + pc = get_pc (lwp);
>> + next_pcs = (*the_low_target.get_next_pcs) (pc, regcache);
>> +
>> + for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); ++i)
>> + {
>> + set_reinsert_breakpoint (pc);
>> + }
>
> Single-line statements don't get braces.
>
Done, learning the style :)
Thank you,
Antoine
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 07/10] Share some ARM target dependant code from GDB with GDBServer.
2015-11-26 10:38 ` Yao Qi
@ 2015-11-26 13:56 ` Antoine Tremblay
0 siblings, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-26 13:56 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 11/26/2015 05:38 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> +/* See arm.h. */
>> +
>> +int
>> +condition_true (unsigned long cond, unsigned long status_reg)
>
> We need "arm_" prefix for this function. Otherwise, patch is OK.
>
Ok in fact I guess I should add this prefix to bitcount too.
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 08/10] Support software single step on ARM in GDBServer.
2015-11-26 12:48 ` Yao Qi
@ 2015-11-26 15:12 ` Antoine Tremblay
2015-11-26 16:04 ` Yao Qi
0 siblings, 1 reply; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-26 15:12 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 11/26/2015 07:48 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> Limitations :
>>
>> GDBServer can't step over a sigreturn or rt_sigreturn syscall since this would
>> require the DWARF information to identify the caller PC. This situation
>> only prints a warning for the moment.
>
> They are using frame not DWARF to identify the PC, however, it is not
> necessary. When GDB or GDBserver is doing software single step, it must
> be in the inner-most frame, so we can get the caller PC by examining the
> stack at a certain offset from SP (from regcache), like what are doing in
> arm_linux_sigreturn_init and arm_linux_rt_sigreturn_init, rather than
> replying frame unwinding. See my comments to arm_get_next_pcs_syscall_next_pc.
>
Humm and frame can use the dwarf unwinder can it not ? frame is still
very confusing to me.
But I'm glad it's not the only option, I'll examine the stack and fix it.
>> +
>> +/* Wrapper over syscall_next_pc for use in get_next_pcs. */
>> +
>> +CORE_ADDR
>> +arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc)
>> +{
>> + struct arm_gdb_get_next_pcs *next_pcs = (struct arm_gdb_get_next_pcs *) self;
>> + struct gdbarch_tdep *tdep;
>> +
>> + tdep = gdbarch_tdep (next_pcs->gdbarch);
>> + if (tdep->syscall_next_pc != NULL)
>> + return tdep->syscall_next_pc (next_pcs->frame);
>> +
>> + return 0;
>> +}
>> +
>
>
>> +/* When PC is at a syscall instruction, return the PC of the next
>> + instruction to be executed. */
>> +static CORE_ADDR
>> +get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc)
>> +{
>> + CORE_ADDR return_addr = 0;
>> + int is_thumb = self->is_thumb;
>> + ULONGEST svc_number = 0;
>> +
>> + if (is_thumb)
>> + {
>> + svc_number = self->ops->collect_register_unsigned (self, 7);
>> + return_addr = pc + 2;
>> + }
>> + else
>> + {
>> + unsigned long this_instr = self->ops->read_memory_unsigned_integer
>> + ((CORE_ADDR) pc, 4, self->byte_order_for_code);
>> +
>> + unsigned long svc_operand = (0x00ffffff & this_instr);
>> + if (svc_operand) /* OABI. */
>> + {
>> + svc_number = svc_operand - 0x900000;
>> + }
>> + else /* EABI. */
>> + {
>> + svc_number = self->ops->collect_register_unsigned (self, 7);
>> + }
>> +
>> + return_addr = pc + 4;
>> + }
>> +
>> + /* Is this a sigreturn or rt_sigreturn syscall?
>> + If so it is currently not handeled. */
>> + if (svc_number == 119 || svc_number == 173)
>> + {
>> + if (debug_threads)
>> + debug_printf ("Unhandled sigreturn or rt_sigreturn syscall\n");
>
> We can still compute the caller pc by examining stack.
>
>> + }
>> +
>> + /* Addresses for calling Thumb functions have the bit 0 set. */
>> + if (is_thumb)
>> + return_addr = MAKE_THUMB_ADDR (return_addr);
>> +
>> + return return_addr;
>> +}
>>
>
> This leads me thinking more about current software single step code in
> GDB. In GDB side, we are using frame to access registers, while in
> GDBserver, we are using regcache. Why don't we use regcache in both
> sides? so that the "struct arm_get_next_pcs_ops" can be simplified, for
> example, field collect_register_unsigned may be no longer necessary.
>
> In fact, software_single_step used to have regcache argument, added by
> https://sourceware.org/ml/gdb-patches/2007-04/msg00218.html but we
> changed to argument frame
> https://sourceware.org/ml/gdb-patches/2007-04/msg00218.html
This is the same link as the previous one...
IMO, it is
> better to use regcache than frame. We have two options,
>
> #1, switch from frame apis to regcache apis to access registers in arm
> software single step. We can get regcache by get_current_regcache ().
> #2, change argument of gdbarch method software_single_step from frame
> to regcache, which means all its implementations need update, and
> switch to regcache apis to access registers.
>
> #2 is the right way to go in long term, and we really need to improve
> software_single_step. Let me what do you think.
>
Looking at the impacts of #2, I do not feel comfortable including these
changes in this patch set. I feel they would require a patch set of
their own.
However #1 seems like something possible I would start by this option if
that's fine with you ?
Also, I can still do the refactoring before this patch but it will
require more work since I'll have to diff the functions moved etc.. do
you feel it's required to do so or the refactoring could be done after
this patch ?
Thank you,
Antoine
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 10/10] Enable conditional breakpoints for targets that support software single step in GDBServer.
2015-11-26 10:25 ` Yao Qi
@ 2015-11-26 15:34 ` Antoine Tremblay
2015-12-03 9:50 ` Yao Qi
0 siblings, 1 reply; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-26 15:34 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 11/26/2015 05:25 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> No regressions, tested on ubuntu 14.04 ARMv7 and x86.
>> With gdbserver-{native,extended} / { -marm -mthumb }
>
> There should be more PASS in the test result, what are they?
>
(gdb) PASS: gdb.base/cond-eval-mode.exp: set breakpoint
condition-evaluation target
(gdb) PASS: gdb.base/cond-eval-mode.exp: probe for target remote
(gdb) PASS: gdb.base/cond-eval-mode.exp: set remote
conditional-breakpoints-packet off
(gdb) PASS: gdb.base/cond-eval-mode.exp: set breakpoint
condition-evaluation target, with support disabled
(gdb) PASS: gdb.base/cond-eval-mode.exp: set remote
conditional-breakpoints-packet on
(gdb) PASS: gdb.base/cond-eval-mode.exp: restore set breakpoint
condition-evaluation target
(gdb) PASS: gdb.base/cond-eval-mode.exp: break: break foo
(gdb) PASS: gdb.base/cond-eval-mode.exp: break: condition $bpnum
cond_global==0
(gdb) PASS: gdb.base/cond-eval-mode.exp: break: continue
(gdb) PASS: gdb.base/cond-eval-mode.exp: break: break foo
(gdb) PASS: gdb.base/cond-eval-mode.exp: break: condition $bpnum
cond_global==1
(gdb) PASS: gdb.base/cond-eval-mode.exp: break: b bar
(gdb) PASS: gdb.base/cond-eval-mode.exp: break: continue
(gdb) PASS: gdb.base/cond-eval-mode.exp: hbreak: hbreak foo
(gdb) PASS: gdb.base/cond-eval-mode.exp: hbreak: condition $bpnum
cond_global==0
(gdb) PASS: gdb.base/cond-eval-mode.exp: hbreak: continue
(gdb) PASS: gdb.base/cond-eval-mode.exp: hbreak: hbreak foo
(gdb) PASS: gdb.base/cond-eval-mode.exp: hbreak: condition $bpnum
cond_global==1
(gdb) PASS: gdb.base/cond-eval-mode.exp: hbreak: b bar
(gdb) PASS: gdb.base/cond-eval-mode.exp: hbreak: continue
(gdb) PASS: gdb.base/cond-eval-mode.exp: watch: watch global
(gdb) PASS: gdb.base/cond-eval-mode.exp: watch: condition $bpnum
cond_global==0
(gdb) PASS: gdb.base/cond-eval-mode.exp: watch: continue
(gdb) PASS: gdb.base/cond-eval-mode.exp: watch: watch global
(gdb) PASS: gdb.base/cond-eval-mode.exp: watch: condition $bpnum
cond_global==1
(gdb) PASS: gdb.base/cond-eval-mode.exp: watch: b bar
(gdb) PASS: gdb.base/cond-eval-mode.exp: watch: continue
(gdb) PASS: gdb.base/cond-eval-mode.exp: rwatch: rwatch global
(gdb) PASS: gdb.base/cond-eval-mode.exp: rwatch: condition $bpnum
cond_global==0
(gdb) PASS: gdb.base/cond-eval-mode.exp: rwatch: continue
(gdb) PASS: gdb.base/cond-eval-mode.exp: rwatch: rwatch global
(gdb) PASS: gdb.base/cond-eval-mode.exp: rwatch: condition $bpnum
cond_global==1
(gdb) PASS: gdb.base/cond-eval-mode.exp: rwatch: b bar
(gdb) PASS: gdb.base/cond-eval-mode.exp: rwatch: continue
(gdb) PASS: gdb.base/cond-eval-mode.exp: awatch: awatch global
(gdb) PASS: gdb.base/cond-eval-mode.exp: awatch: condition $bpnum
cond_global==0
(gdb) PASS: gdb.base/cond-eval-mode.exp: awatch: continue
(gdb) PASS: gdb.base/cond-eval-mode.exp: awatch: awatch global
(gdb) PASS: gdb.base/cond-eval-mode.exp: awatch: condition $bpnum
cond_global==1
(gdb) PASS: gdb.base/cond-eval-mode.exp: awatch: b bar
(gdb) PASS: gdb.base/cond-eval-mode.exp: awatch: continue
Should these be part of the commit log ?
as :
New tests passing :
PASS: gdb.base/cond-eval-mode.exp: set breakpoint condition-evaluation
target and related...
Maybe?
Thanks,
Antoine Tremblay
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 08/10] Support software single step on ARM in GDBServer.
2015-11-26 15:12 ` Antoine Tremblay
@ 2015-11-26 16:04 ` Yao Qi
2015-11-26 16:07 ` Antoine Tremblay
0 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2015-11-26 16:04 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches
On 26/11/15 15:11, Antoine Tremblay wrote:
> This is the same link as the previous one...
>
Oops, sorry, https://sourceware.org/ml/gdb-patches/2007-06/msg00087.html
> IMO, it is
>> better to use regcache than frame. We have two options,
>>
>> #1, switch from frame apis to regcache apis to access registers in arm
>> software single step. We can get regcache by get_current_regcache ().
>> #2, change argument of gdbarch method software_single_step from frame
>> to regcache, which means all its implementations need update, and
>> switch to regcache apis to access registers.
>>
>> #2 is the right way to go in long term, and we really need to improve
>> software_single_step. Let me what do you think.
>>
>
> Looking at the impacts of #2, I do not feel comfortable including these
> changes in this patch set. I feel they would require a patch set of
> their own.
>
> However #1 seems like something possible I would start by this option if
> that's fine with you ?
Yes.
>
> Also, I can still do the refactoring before this patch but it will
> require more work since I'll have to diff the functions moved etc.. do
> you feel it's required to do so or the refactoring could be done after
> this patch ?
I prefer doing the refactor first, and separately, because after this
refactor, your patch #8 will be simplified a lot. In this series, we
want to share the code on arm software single step, however, registers
are accessed through frame in GDB side, while through regcache in
GDBserver. In order to share code, we should unify them as much as we
can, that is, access registers through regcache as well in GDB side.
Then, we can move the code from arm-tdep.c to arch/ directory, to
support software single step in GDBserver.
--
Yao (é½å°§)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 08/10] Support software single step on ARM in GDBServer.
2015-11-26 16:04 ` Yao Qi
@ 2015-11-26 16:07 ` Antoine Tremblay
2015-11-27 13:45 ` Antoine Tremblay
0 siblings, 1 reply; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-26 16:07 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 11/26/2015 11:03 AM, Yao Qi wrote:
>
>
> On 26/11/15 15:11, Antoine Tremblay wrote:
>> This is the same link as the previous one...
>>
>
> Oops, sorry, https://sourceware.org/ml/gdb-patches/2007-06/msg00087.html
>
Thanks
>
>> IMO, it is
>>> better to use regcache than frame. We have two options,
>>>
>>> #1, switch from frame apis to regcache apis to access registers in arm
>>> software single step. We can get regcache by get_current_regcache
>>> ().
>>> #2, change argument of gdbarch method software_single_step from frame
>>> to regcache, which means all its implementations need update, and
>>> switch to regcache apis to access registers.
>>>
>>> #2 is the right way to go in long term, and we really need to improve
>>> software_single_step. Let me what do you think.
>>>
>>
>> Looking at the impacts of #2, I do not feel comfortable including these
>> changes in this patch set. I feel they would require a patch set of
>> their own.
>>
>> However #1 seems like something possible I would start by this option if
>> that's fine with you ?
>
> Yes.
>
>>
>> Also, I can still do the refactoring before this patch but it will
>> require more work since I'll have to diff the functions moved etc.. do
>> you feel it's required to do so or the refactoring could be done after
>> this patch ?
>
> I prefer doing the refactor first, and separately, because after this
> refactor, your patch #8 will be simplified a lot. In this series, we
> want to share the code on arm software single step, however, registers
> are accessed through frame in GDB side, while through regcache in
> GDBserver. In order to share code, we should unify them as much as we
> can, that is, access registers through regcache as well in GDB side.
> Then, we can move the code from arm-tdep.c to arch/ directory, to
> support software single step in GDBserver.
>
Yes ok I'll do it this way then.
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer.
2015-11-23 14:14 [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
` (9 preceding siblings ...)
2015-11-23 14:15 ` [PATCH v3 10/10] Enable conditional breakpoints for targets that support software single step " Antoine Tremblay
@ 2015-11-27 9:27 ` Yao Qi
2015-11-27 13:16 ` Antoine Tremblay
10 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2015-11-27 9:27 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
> Patches 1 and 2 fix general issues in the software single step control flow.
I think patches #1, #2 and #4 can go in now, since they are quite
general. Do you plan to write the GDB counterpart of patch #4? I mean
remove thread event breakpoint from GDB for linux.
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer.
2015-11-27 9:27 ` [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM " Yao Qi
@ 2015-11-27 13:16 ` Antoine Tremblay
2015-11-30 20:21 ` Antoine Tremblay
2015-12-01 9:33 ` Yao Qi
0 siblings, 2 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-27 13:16 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 11/27/2015 04:26 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> Patches 1 and 2 fix general issues in the software single step control flow.
>
> I think patches #1, #2 and #4 can go in now, since they are quite
> general.
Hi, yes I was thinking about pushing 1-2 also.
#4 however feels like it's part of a group with #3 and 5, together they
cleanup the whole situation as these reinsert_addr implementations are
directly related to the thread_event support that was removed. Otherwise
we leave some inconsistent dead code there...
So I could push [1-5] if that's ok with you ?
> Do you plan to write the GDB counterpart of patch #4? I mean
> remove thread event breakpoint from GDB for linux.
>
I do not has there is not need for it in GDB at this point.
Should I ? Seems like we would remove some support for a refactoring in
that case ?
Regards,
Antoine
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 08/10] Support software single step on ARM in GDBServer.
2015-11-26 16:07 ` Antoine Tremblay
@ 2015-11-27 13:45 ` Antoine Tremblay
2015-11-27 15:15 ` Yao Qi
0 siblings, 1 reply; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-27 13:45 UTC (permalink / raw)
To: gdb-patches
On 11/26/2015 11:07 AM, Antoine Tremblay wrote:
>
>
> On 11/26/2015 11:03 AM, Yao Qi wrote:
>>
>>
>> On 26/11/15 15:11, Antoine Tremblay wrote:
>>> This is the same link as the previous one...
>>>
>>
>> Oops, sorry, https://sourceware.org/ml/gdb-patches/2007-06/msg00087.html
>>
> Thanks
>
>>
>>> IMO, it is
>>>> better to use regcache than frame. We have two options,
>>>>
>>>> #1, switch from frame apis to regcache apis to access registers in
>>>> arm
>>>> software single step. We can get regcache by get_current_regcache
>>>> ().
>>
About this one, as we thought it would simplify the
collect_register_unsigned field.
It's unfortunate but it won't because GDB's collect_registers_unsigned
reads the registers and then calls extract_register_unsigned (in the
same call).
This function uses bfd enums for byte ordering and I can't use that in
GDBServer as discussed previously.
So I will not be able to directly share GDB's
collect_registers_unsigned, thus either collect_register_unsigned will
be replaced by 2 calls, one shared that fetches the register, and then a
call that extracts the integer as a different operation on GDB and
GDBserver or I will end up with the same collect_register_unsigned field
only it will be using regcache on GDBServer's side now.
And I don't think it's good to have it in 2 calls, so I will have the
same collect_register_unsigned_field...
Thus this refactoring would not simplify the patch and IMHO would create
some inconsistency why are we using regcache in some place for no
apparent gain while all the rest uses frame.
In light of this, I plan to keep it as is unless there's an objection ?
Regards,
Antoine
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 08/10] Support software single step on ARM in GDBServer.
2015-11-27 13:45 ` Antoine Tremblay
@ 2015-11-27 15:15 ` Yao Qi
2015-11-27 15:35 ` Antoine Tremblay
0 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2015-11-27 15:15 UTC (permalink / raw)
To: Antoine Tremblay, gdb-patches
On 27/11/15 13:45, Antoine Tremblay wrote:
>
> And I don't think it's good to have it in 2 calls, so I will have the
> same collect_register_unsigned_field...
If so, let us keep collect_register_unsigned field.
>
> Thus this refactoring would not simplify the patch and IMHO would create
> some inconsistency why are we using regcache in some place for no
> apparent gain while all the rest uses frame.
That is what I want to avoid... for the software single step routines
in two sides (GDB and GDBserver), I don't want to see that regcache is
used in one side while frame is used in the other side. regcache should
be used in both sides.
>
> In light of this, I plan to keep it as is unless there's an objection ?
Sorry, I don't like the patch as is. I plan to change gdbarch method
software_single_step to something like this,
F:VEC (CORE_ADDR) *:software_single_step:struct regcache *regcache:regcache
it returns a set of address on which GDB can insert software single step
breakpoint.
--
Yao (é½å°§)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 08/10] Support software single step on ARM in GDBServer.
2015-11-27 15:15 ` Yao Qi
@ 2015-11-27 15:35 ` Antoine Tremblay
0 siblings, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-27 15:35 UTC (permalink / raw)
To: Yao Qi, gdb-patches
On 11/27/2015 10:15 AM, Yao Qi wrote:
> On 27/11/15 13:45, Antoine Tremblay wrote:
>>
>> And I don't think it's good to have it in 2 calls, so I will have the
>> same collect_register_unsigned_field...
>
> If so, let us keep collect_register_unsigned field.
>>
>> Thus this refactoring would not simplify the patch and IMHO would create
>> some inconsistency why are we using regcache in some place for no
>> apparent gain while all the rest uses frame.
>
> That is what I want to avoid... for the software single step routines
> in two sides (GDB and GDBserver), I don't want to see that regcache is
> used in one side while frame is used in the other side. regcache should
> be used in both sides.
>
>>
>> In light of this, I plan to keep it as is unless there's an objection ?
>
> Sorry, I don't like the patch as is. I plan to change gdbarch method
> software_single_step to something like this,
>
> F:VEC (CORE_ADDR) *:software_single_step:struct regcache *regcache:regcache
>
> it returns a set of address on which GDB can insert software single step
> breakpoint.
>
Alright as long as it's understood that the fields will stay, I'll do it
like this.
Thanks,
Antoine
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer.
2015-11-27 13:16 ` Antoine Tremblay
@ 2015-11-30 20:21 ` Antoine Tremblay
2015-12-01 9:33 ` Yao Qi
1 sibling, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-11-30 20:21 UTC (permalink / raw)
To: GDB
On 11/27/2015 08:16 AM, Antoine Tremblay wrote:
> On 11/27/2015 04:26 AM, Yao Qi wrote:
>> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>>
>>> Patches 1 and 2 fix general issues in the software single step
>>> control flow.
>>
>> I think patches #1, #2 and #4 can go in now, since they are quite
>> general.
>
> Hi, yes I was thinking about pushing 1-2 also.
>
> #4 however feels like it's part of a group with #3 and 5, together they
> cleanup the whole situation as these reinsert_addr implementations are
> directly related to the thread_event support that was removed. Otherwise
> we leave some inconsistent dead code there...
>
> So I could push [1-5] if that's ok with you ?
>
>
Patches [1-5] are pushed in
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer.
2015-11-27 13:16 ` Antoine Tremblay
2015-11-30 20:21 ` Antoine Tremblay
@ 2015-12-01 9:33 ` Yao Qi
2015-12-01 13:00 ` Antoine Tremblay
1 sibling, 1 reply; 37+ messages in thread
From: Yao Qi @ 2015-12-01 9:33 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: Yao Qi, gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
> So I could push [1-5] if that's ok with you ?
>
>
Yes, please.
>> Do you plan to write the GDB counterpart of patch #4? I mean
>> remove thread event breakpoint from GDB for linux.
>>
>
> I do not has there is not need for it in GDB at this point.
>
I can't parse this sentence.
> Should I ? Seems like we would remove some support for a refactoring
> in that case ?
GDB and GDBserver should be consistent. We should remove thread event
breakpoint from GDB, but if you don't plan to do that, let us know.
Some one else can do that. I want to avoid duplicated work.
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer.
2015-12-01 9:33 ` Yao Qi
@ 2015-12-01 13:00 ` Antoine Tremblay
0 siblings, 0 replies; 37+ messages in thread
From: Antoine Tremblay @ 2015-12-01 13:00 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 12/01/2015 04:33 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> So I could push [1-5] if that's ok with you ?
>>
>>
>
> Yes, please.
>
Done.
>>> Do you plan to write the GDB counterpart of patch #4? I mean
>>> remove thread event breakpoint from GDB for linux.
>>>
>>
>> I do not has there is not need for it in GDB at this point.
>>
>
> I can't parse this sentence.
I meant , I did not plan to do it, has I saw no need.
>
>> Should I ? Seems like we would remove some support for a refactoring
>> in that case ?
>
> GDB and GDBserver should be consistent. We should remove thread event
> breakpoint from GDB, but if you don't plan to do that, let us know.
> Some one else can do that. I want to avoid duplicated work.
>
Ok then, I'll add it to my TODO list, after this patch set is in.
Regards,
Antoine
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH v3 10/10] Enable conditional breakpoints for targets that support software single step in GDBServer.
2015-11-26 15:34 ` Antoine Tremblay
@ 2015-12-03 9:50 ` Yao Qi
0 siblings, 0 replies; 37+ messages in thread
From: Yao Qi @ 2015-12-03 9:50 UTC (permalink / raw)
To: Antoine Tremblay; +Cc: Yao Qi, gdb-patches
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
Hi,
Sorry I missed this mail...
> New tests passing :
>
> PASS: gdb.base/cond-eval-mode.exp: set breakpoint condition-evaluation
> target and related...
>
> Maybe?
Yes, this can be added to the commit log in V4.
--
Yao (齐尧)
^ permalink raw reply [flat|nested] 37+ messages in thread
end of thread, other threads:[~2015-12-03 9:50 UTC | newest]
Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-23 14:14 [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM in GDBServer Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 09/10] Enable software single stepping for while-stepping actions " Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 02/10] Fix instruction skipping when using software single step " Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 05/10] Remove too simple breakpoint_reinsert_addr implementations Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 04/10] Remove support for thread events without PTRACE_EVENT_CLONE in GDBServer Antoine Tremblay
2015-11-25 16:48 ` Yao Qi
2015-11-25 17:42 ` Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 06/10] Replace breakpoint_reinsert_addr by get_next_pcs operation " Antoine Tremblay
2015-11-26 10:30 ` Yao Qi
2015-11-26 13:48 ` Antoine Tremblay
2015-11-26 10:50 ` Pedro Alves
2015-11-26 13:50 ` Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 01/10] Fix breakpoint size when stepping over a permanent breakpoint " Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 03/10] Refactor queries for hardware and software single stepping support " Antoine Tremblay
2015-11-23 14:14 ` [PATCH v3 07/10] Share some ARM target dependant code from GDB with GDBServer Antoine Tremblay
2015-11-25 17:01 ` Yao Qi
2015-11-26 10:38 ` Yao Qi
2015-11-26 13:56 ` Antoine Tremblay
2015-11-23 14:15 ` [PATCH v3 08/10] Support software single step on ARM in GDBServer Antoine Tremblay
2015-11-26 10:49 ` Pedro Alves
2015-11-26 13:35 ` Antoine Tremblay
2015-11-26 12:48 ` Yao Qi
2015-11-26 15:12 ` Antoine Tremblay
2015-11-26 16:04 ` Yao Qi
2015-11-26 16:07 ` Antoine Tremblay
2015-11-27 13:45 ` Antoine Tremblay
2015-11-27 15:15 ` Yao Qi
2015-11-27 15:35 ` Antoine Tremblay
2015-11-23 14:15 ` [PATCH v3 10/10] Enable conditional breakpoints for targets that support software single step " Antoine Tremblay
2015-11-26 10:25 ` Yao Qi
2015-11-26 15:34 ` Antoine Tremblay
2015-12-03 9:50 ` Yao Qi
2015-11-27 9:27 ` [PATCH v3 0/10] Support software single step and conditional breakpoints on ARM " Yao Qi
2015-11-27 13:16 ` Antoine Tremblay
2015-11-30 20:21 ` Antoine Tremblay
2015-12-01 9:33 ` Yao Qi
2015-12-01 13:00 ` Antoine Tremblay
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox