Linux native: Use TRAP_BRKPT/TRAP_HWBPT
This patch adjusts the native Linux target backend to tell the core whether a trap was caused by a breakpoint. It teaches the target to get that information out of the si_code of the SIGTRAP siginfo. Tested on x86-64 Fedora 20, s390 RHEL 7, and PPC64 Fedora 18. An earlier version was tested on ARM Fedora 21. gdb/ChangeLog: 2015-03-04 Pedro Alves <palves@redhat.com> * linux-nat.c (save_sigtrap): Check for breakpoints before checking watchpoints. (status_callback) [USE_SIGTRAP_SIGINFO]: Don't check whether a breakpoint is inserted if relying on SIGTRAP's siginfo.si_code. (check_stopped_by_breakpoint) [USE_SIGTRAP_SIGINFO]: Decide whether a breakpoint triggered based on the SIGTRAP's siginfo.si_code. (linux_nat_stopped_by_sw_breakpoint) (linux_nat_supports_stopped_by_sw_breakpoint) (linux_nat_stopped_by_hw_breakpoint) (linux_nat_supports_stopped_by_hw_breakpoint): New functions. (linux_nat_wait_1): Don't re-increment the PC if relying on SIGTRAP's siginfo->si_code. (linux_nat_add_target): Install new target methods. * linux-thread-db.c (check_event): Don't account for breakpoint PC offset if the target already adjusted the PC. * nat/linux-ptrace.h (USE_SIGTRAP_SIGINFO): New. (GDB_ARCH_TRAP_BRKPT): New. (TRAP_HWBKPT): Define if not already defined.
This commit is contained in:
parent
f7e6eed528
commit
faf09f0119
4 changed files with 180 additions and 7 deletions
|
@ -1,3 +1,24 @@
|
||||||
|
2015-03-04 Pedro Alves <palves@redhat.com>
|
||||||
|
|
||||||
|
* linux-nat.c (save_sigtrap): Check for breakpoints before
|
||||||
|
checking watchpoints.
|
||||||
|
(status_callback) [USE_SIGTRAP_SIGINFO]: Don't check whether a
|
||||||
|
breakpoint is inserted if relying on SIGTRAP's siginfo.si_code.
|
||||||
|
(check_stopped_by_breakpoint) [USE_SIGTRAP_SIGINFO]: Decide whether
|
||||||
|
a breakpoint triggered based on the SIGTRAP's siginfo.si_code.
|
||||||
|
(linux_nat_stopped_by_sw_breakpoint)
|
||||||
|
(linux_nat_supports_stopped_by_sw_breakpoint)
|
||||||
|
(linux_nat_stopped_by_hw_breakpoint)
|
||||||
|
(linux_nat_supports_stopped_by_hw_breakpoint): New functions.
|
||||||
|
(linux_nat_wait_1): Don't re-increment the PC if relying on
|
||||||
|
SIGTRAP's siginfo->si_code.
|
||||||
|
(linux_nat_add_target): Install new target methods.
|
||||||
|
* linux-thread-db.c (check_event): Don't account for breakpoint PC
|
||||||
|
offset if the target already adjusted the PC.
|
||||||
|
* nat/linux-ptrace.h (USE_SIGTRAP_SIGINFO): New.
|
||||||
|
(GDB_ARCH_TRAP_BRKPT): New.
|
||||||
|
(TRAP_HWBKPT): Define if not already defined.
|
||||||
|
|
||||||
2015-03-04 Pedro Alves <palves@redhat.com>
|
2015-03-04 Pedro Alves <palves@redhat.com>
|
||||||
|
|
||||||
* NEWS: Mention the new "swbreak" and "hwbreak" stop reasons.
|
* NEWS: Mention the new "swbreak" and "hwbreak" stop reasons.
|
||||||
|
|
109
gdb/linux-nat.c
109
gdb/linux-nat.c
|
@ -2399,11 +2399,19 @@ save_sigtrap (struct lwp_info *lp)
|
||||||
gdb_assert (lp->stop_reason == TARGET_STOPPED_BY_NO_REASON);
|
gdb_assert (lp->stop_reason == TARGET_STOPPED_BY_NO_REASON);
|
||||||
gdb_assert (lp->status != 0);
|
gdb_assert (lp->status != 0);
|
||||||
|
|
||||||
if (check_stopped_by_watchpoint (lp))
|
/* Check first if this was a SW/HW breakpoint before checking
|
||||||
return;
|
watchpoints, because at least s390 can't tell the data address of
|
||||||
|
hardware watchpoint hits, and the kernel returns
|
||||||
|
stopped-by-watchpoint as long as there's a watchpoint set. */
|
||||||
if (linux_nat_status_is_event (lp->status))
|
if (linux_nat_status_is_event (lp->status))
|
||||||
check_stopped_by_breakpoint (lp);
|
check_stopped_by_breakpoint (lp);
|
||||||
|
|
||||||
|
/* Note that TRAP_HWBKPT can indicate either a hardware breakpoint
|
||||||
|
or hardware watchpoint. Check which is which if we got
|
||||||
|
TARGET_STOPPED_BY_HW_BREAKPOINT. */
|
||||||
|
if (lp->stop_reason == TARGET_STOPPED_BY_NO_REASON
|
||||||
|
|| lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT)
|
||||||
|
check_stopped_by_watchpoint (lp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns true if the LWP had stopped for a watchpoint. */
|
/* Returns true if the LWP had stopped for a watchpoint. */
|
||||||
|
@ -2557,6 +2565,8 @@ status_callback (struct lwp_info *lp, void *data)
|
||||||
paddress (target_gdbarch (), pc));
|
paddress (target_gdbarch (), pc));
|
||||||
discard = 1;
|
discard = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !USE_SIGTRAP_SIGINFO
|
||||||
else if (!breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
|
else if (!breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
|
||||||
{
|
{
|
||||||
if (debug_linux_nat)
|
if (debug_linux_nat)
|
||||||
|
@ -2567,6 +2577,7 @@ status_callback (struct lwp_info *lp, void *data)
|
||||||
|
|
||||||
discard = 1;
|
discard = 1;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (discard)
|
if (discard)
|
||||||
{
|
{
|
||||||
|
@ -2669,10 +2680,49 @@ check_stopped_by_breakpoint (struct lwp_info *lp)
|
||||||
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
||||||
CORE_ADDR pc;
|
CORE_ADDR pc;
|
||||||
CORE_ADDR sw_bp_pc;
|
CORE_ADDR sw_bp_pc;
|
||||||
|
#if USE_SIGTRAP_SIGINFO
|
||||||
|
siginfo_t siginfo;
|
||||||
|
#endif
|
||||||
|
|
||||||
pc = regcache_read_pc (regcache);
|
pc = regcache_read_pc (regcache);
|
||||||
sw_bp_pc = pc - target_decr_pc_after_break (gdbarch);
|
sw_bp_pc = pc - target_decr_pc_after_break (gdbarch);
|
||||||
|
|
||||||
|
#if USE_SIGTRAP_SIGINFO
|
||||||
|
if (linux_nat_get_siginfo (lp->ptid, &siginfo))
|
||||||
|
{
|
||||||
|
if (siginfo.si_signo == SIGTRAP)
|
||||||
|
{
|
||||||
|
if (siginfo.si_code == GDB_ARCH_TRAP_BRKPT)
|
||||||
|
{
|
||||||
|
if (debug_linux_nat)
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"CSBB: Push back software "
|
||||||
|
"breakpoint for %s\n",
|
||||||
|
target_pid_to_str (lp->ptid));
|
||||||
|
|
||||||
|
/* Back up the PC if necessary. */
|
||||||
|
if (pc != sw_bp_pc)
|
||||||
|
regcache_write_pc (regcache, sw_bp_pc);
|
||||||
|
|
||||||
|
lp->stop_pc = sw_bp_pc;
|
||||||
|
lp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (siginfo.si_code == TRAP_HWBKPT)
|
||||||
|
{
|
||||||
|
if (debug_linux_nat)
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"CSBB: Push back hardware "
|
||||||
|
"breakpoint/watchpoint for %s\n",
|
||||||
|
target_pid_to_str (lp->ptid));
|
||||||
|
|
||||||
|
lp->stop_pc = pc;
|
||||||
|
lp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
if ((!lp->step || lp->stop_pc == sw_bp_pc)
|
if ((!lp->step || lp->stop_pc == sw_bp_pc)
|
||||||
&& software_breakpoint_inserted_here_p (get_regcache_aspace (regcache),
|
&& software_breakpoint_inserted_here_p (get_regcache_aspace (regcache),
|
||||||
sw_bp_pc))
|
sw_bp_pc))
|
||||||
|
@ -2704,10 +2754,53 @@ check_stopped_by_breakpoint (struct lwp_info *lp)
|
||||||
lp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
|
lp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Returns true if the LWP had stopped for a software breakpoint. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
linux_nat_stopped_by_sw_breakpoint (struct target_ops *ops)
|
||||||
|
{
|
||||||
|
struct lwp_info *lp = find_lwp_pid (inferior_ptid);
|
||||||
|
|
||||||
|
gdb_assert (lp != NULL);
|
||||||
|
|
||||||
|
return lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement the supports_stopped_by_sw_breakpoint method. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
linux_nat_supports_stopped_by_sw_breakpoint (struct target_ops *ops)
|
||||||
|
{
|
||||||
|
return USE_SIGTRAP_SIGINFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns true if the LWP had stopped for a hardware
|
||||||
|
breakpoint/watchpoint. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
linux_nat_stopped_by_hw_breakpoint (struct target_ops *ops)
|
||||||
|
{
|
||||||
|
struct lwp_info *lp = find_lwp_pid (inferior_ptid);
|
||||||
|
|
||||||
|
gdb_assert (lp != NULL);
|
||||||
|
|
||||||
|
return lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement the supports_stopped_by_hw_breakpoint method. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
linux_nat_supports_stopped_by_hw_breakpoint (struct target_ops *ops)
|
||||||
|
{
|
||||||
|
return USE_SIGTRAP_SIGINFO;
|
||||||
|
}
|
||||||
|
|
||||||
/* Select one LWP out of those that have events pending. */
|
/* Select one LWP out of those that have events pending. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -3360,8 +3453,10 @@ linux_nat_wait_1 (struct target_ops *ops,
|
||||||
gdb_assert (lp != NULL);
|
gdb_assert (lp != NULL);
|
||||||
|
|
||||||
/* Now that we've selected our final event LWP, un-adjust its PC if
|
/* Now that we've selected our final event LWP, un-adjust its PC if
|
||||||
it was a software breakpoint. */
|
it was a software breakpoint, and we can't reliably support the
|
||||||
if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT)
|
"stopped by software breakpoint" stop reason. */
|
||||||
|
if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
|
||||||
|
&& !USE_SIGTRAP_SIGINFO)
|
||||||
{
|
{
|
||||||
struct regcache *regcache = get_thread_regcache (lp->ptid);
|
struct regcache *regcache = get_thread_regcache (lp->ptid);
|
||||||
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
||||||
|
@ -4651,6 +4746,10 @@ linux_nat_add_target (struct target_ops *t)
|
||||||
t->to_thread_address_space = linux_nat_thread_address_space;
|
t->to_thread_address_space = linux_nat_thread_address_space;
|
||||||
t->to_stopped_by_watchpoint = linux_nat_stopped_by_watchpoint;
|
t->to_stopped_by_watchpoint = linux_nat_stopped_by_watchpoint;
|
||||||
t->to_stopped_data_address = linux_nat_stopped_data_address;
|
t->to_stopped_data_address = linux_nat_stopped_data_address;
|
||||||
|
t->to_stopped_by_sw_breakpoint = linux_nat_stopped_by_sw_breakpoint;
|
||||||
|
t->to_supports_stopped_by_sw_breakpoint = linux_nat_supports_stopped_by_sw_breakpoint;
|
||||||
|
t->to_stopped_by_hw_breakpoint = linux_nat_stopped_by_hw_breakpoint;
|
||||||
|
t->to_supports_stopped_by_hw_breakpoint = linux_nat_supports_stopped_by_hw_breakpoint;
|
||||||
|
|
||||||
t->to_can_async_p = linux_nat_can_async_p;
|
t->to_can_async_p = linux_nat_can_async_p;
|
||||||
t->to_is_async_p = linux_nat_is_async_p;
|
t->to_is_async_p = linux_nat_is_async_p;
|
||||||
|
|
|
@ -1437,8 +1437,10 @@ check_event (ptid_t ptid)
|
||||||
info = get_thread_db_info (ptid_get_pid (ptid));
|
info = get_thread_db_info (ptid_get_pid (ptid));
|
||||||
|
|
||||||
/* Bail out early if we're not at a thread event breakpoint. */
|
/* Bail out early if we're not at a thread event breakpoint. */
|
||||||
stop_pc = regcache_read_pc (regcache)
|
stop_pc = regcache_read_pc (regcache);
|
||||||
- target_decr_pc_after_break (gdbarch);
|
if (!target_supports_stopped_by_sw_breakpoint ())
|
||||||
|
stop_pc -= target_decr_pc_after_break (gdbarch);
|
||||||
|
|
||||||
if (stop_pc != info->td_create_bp_addr
|
if (stop_pc != info->td_create_bp_addr
|
||||||
&& stop_pc != info->td_death_bp_addr)
|
&& stop_pc != info->td_death_bp_addr)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -88,6 +88,57 @@ struct buffer;
|
||||||
#define __WALL 0x40000000 /* Wait for any child. */
|
#define __WALL 0x40000000 /* Wait for any child. */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* True if whether a breakpoint/watchpoint triggered can be determined
|
||||||
|
from the si_code of SIGTRAP's siginfo_t (TRAP_BRKPT/TRAP_HWBKPT).
|
||||||
|
That is, if the kernel can tell us whether the thread executed a
|
||||||
|
software breakpoint, we trust it. The kernel will be determining
|
||||||
|
that from the hardware (e.g., from which exception was raised in
|
||||||
|
the CPU). Relying on whether a breakpoint is planted in memory at
|
||||||
|
the time the SIGTRAP is processed to determine whether the thread
|
||||||
|
stopped for a software breakpoint can be too late. E.g., the
|
||||||
|
breakpoint could have been removed since. Or the thread could have
|
||||||
|
stepped an instruction the size of a breakpoint instruction, and
|
||||||
|
before the stop is processed a breakpoint is inserted at its
|
||||||
|
address. Getting these wrong is disastrous on decr_pc_after_break
|
||||||
|
architectures. The moribund location mechanism helps with that
|
||||||
|
somewhat but it is an heuristic, and can well fail. Getting that
|
||||||
|
information out of the kernel and ultimately out of the CPU is the
|
||||||
|
way to go. That said, some architecture may get the si_code wrong,
|
||||||
|
and as such we're leaving fallback code in place. We'll remove
|
||||||
|
this after a while if no problem is reported. */
|
||||||
|
#define USE_SIGTRAP_SIGINFO 1
|
||||||
|
|
||||||
|
/* The x86 kernel gets some of the si_code values backwards, like
|
||||||
|
this:
|
||||||
|
|
||||||
|
| what | si_code |
|
||||||
|
|------------------------------------------+------------|
|
||||||
|
| software breakpoints (int3) | SI_KERNEL |
|
||||||
|
| single-steps | TRAP_TRACE |
|
||||||
|
| single-stepping a syscall | TRAP_BRKPT |
|
||||||
|
| user sent SIGTRAP | 0 |
|
||||||
|
| exec SIGTRAP (when no PTRACE_EVENT_EXEC) | 0 |
|
||||||
|
| hardware breakpoints/watchpoints | TRAP_HWBPT |
|
||||||
|
|
||||||
|
That is, it reports SI_KERNEL for software breakpoints (and only
|
||||||
|
for those), and TRAP_BRKPT for single-stepping a syscall... If the
|
||||||
|
kernel is ever fixed, we'll just have to detect it like we detect
|
||||||
|
optional ptrace features: by forking and debugging ourselves,
|
||||||
|
running to a breakpoint and checking what comes out of
|
||||||
|
siginfo->si_code.
|
||||||
|
|
||||||
|
The generic Linux target code should use GDB_ARCH_TRAP_BRKPT
|
||||||
|
instead of TRAP_BRKPT to abstract out this x86 peculiarity. */
|
||||||
|
#if defined __i386__ || defined __x86_64__
|
||||||
|
# define GDB_ARCH_TRAP_BRKPT SI_KERNEL
|
||||||
|
#else
|
||||||
|
# define GDB_ARCH_TRAP_BRKPT TRAP_BRKPT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TRAP_HWBKPT
|
||||||
|
# define TRAP_HWBKPT 4
|
||||||
|
#endif
|
||||||
|
|
||||||
extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
|
extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
|
||||||
|
|
||||||
/* Find all possible reasons we could have failed to attach to PTID
|
/* Find all possible reasons we could have failed to attach to PTID
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue