aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
Diffstat (limited to 'gdb')
-rw-r--r--gdb/loongarch-linux-nat.c74
-rw-r--r--gdb/nat/loongarch-hw-point.c37
-rw-r--r--gdb/nat/loongarch-hw-point.h11
-rw-r--r--gdb/nat/loongarch-linux-hw-point.c51
-rw-r--r--gdb/nat/loongarch-linux-hw-point.h1
-rw-r--r--gdb/nat/loongarch-linux.c21
6 files changed, 172 insertions, 23 deletions
diff --git a/gdb/loongarch-linux-nat.c b/gdb/loongarch-linux-nat.c
index 9f57600..8736287 100644
--- a/gdb/loongarch-linux-nat.c
+++ b/gdb/loongarch-linux-nat.c
@@ -54,6 +54,11 @@ public:
bool stopped_by_watchpoint () override;
bool stopped_data_address (CORE_ADDR *) override;
+ int insert_hw_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt) override;
+ int remove_hw_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt) override;
+
/* Override the GNU/Linux inferior startup hook. */
void post_startup_inferior (ptid_t) override;
@@ -489,7 +494,8 @@ loongarch_linux_nat_target::can_use_hw_breakpoint (enum bptype type, int cnt,
}
else if (type == bp_hardware_breakpoint)
{
- return 0;
+ if (loongarch_num_bp_regs == 0)
+ return 0;
}
else
gdb_assert_not_reached ("unexpected breakpoint type");
@@ -624,6 +630,72 @@ loongarch_linux_nat_target::stopped_by_watchpoint ()
return stopped_data_address (&addr);
}
+/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address.
+ Return 0 on success, -1 on failure. */
+
+int
+loongarch_linux_nat_target::insert_hw_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt)
+{
+ int ret;
+ CORE_ADDR addr = bp_tgt->placed_address = bp_tgt->reqstd_address;
+ int len;
+ const enum target_hw_bp_type type = hw_execute;
+ struct loongarch_debug_reg_state *state
+ = loongarch_get_debug_reg_state (inferior_ptid.pid ());
+
+ gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
+
+ if (show_debug_regs)
+ gdb_printf (gdb_stdlog,
+ "insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
+ (unsigned long) addr, len);
+
+ ret = loongarch_handle_breakpoint (type, addr, len, 1 /* is_insert */,
+ inferior_ptid, state);
+
+ if (show_debug_regs)
+ {
+ loongarch_show_debug_reg_state (state,
+ "insert_hw_breakpoint", addr, len, type);
+ }
+
+ return ret;
+}
+
+/* Remove a hardware-assisted breakpoint at BP_TGT->placed_address.
+ Return 0 on success, -1 on failure. */
+
+int
+loongarch_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt)
+{
+ int ret;
+ CORE_ADDR addr = bp_tgt->placed_address;
+ int len = 4;
+ const enum target_hw_bp_type type = hw_execute;
+ struct loongarch_debug_reg_state *state
+ = loongarch_get_debug_reg_state (inferior_ptid.pid ());
+
+ gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
+
+ if (show_debug_regs)
+ gdb_printf (gdb_stdlog,
+ "remove_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
+ (unsigned long) addr, len);
+
+ ret = loongarch_handle_breakpoint (type, addr, len, 0 /* is_insert */,
+ inferior_ptid, state);
+
+ if (show_debug_regs)
+ {
+ loongarch_show_debug_reg_state (state,
+ "remove_hw_watchpoint", addr, len, type);
+ }
+
+ return ret;
+}
+
/* Implement the virtual inf_ptrace_target::post_startup_inferior method. */
void
diff --git a/gdb/nat/loongarch-hw-point.c b/gdb/nat/loongarch-hw-point.c
index 44af61a..089f3bd 100644
--- a/gdb/nat/loongarch-hw-point.c
+++ b/gdb/nat/loongarch-hw-point.c
@@ -27,6 +27,7 @@
/* Number of hardware breakpoints/watchpoints the target supports.
They are initialized with values obtained via ptrace. */
+int loongarch_num_bp_regs;
int loongarch_num_wp_regs;
/* Given the hardware breakpoint or watchpoint type TYPE and its
@@ -112,7 +113,10 @@ loongarch_dr_state_insert_one_point (ptid_t ptid,
}
else
{
- return -1;
+ num_regs = loongarch_num_bp_regs;
+ dr_addr_p = state->dr_addr_bp;
+ dr_ctrl_p = state->dr_ctrl_bp;
+ dr_ref_count = state->dr_ref_count_bp;
}
ctrl = loongarch_point_encode_ctrl_reg (type, len);
@@ -184,7 +188,10 @@ loongarch_dr_state_remove_one_point (ptid_t ptid,
}
else
{
- return -1;
+ num_regs = loongarch_num_bp_regs;
+ dr_addr_p = state->dr_addr_bp;
+ dr_ctrl_p = state->dr_ctrl_bp;
+ dr_ref_count = state->dr_ref_count_bp;
}
ctrl = loongarch_point_encode_ctrl_reg (type, len);
@@ -215,6 +222,20 @@ loongarch_dr_state_remove_one_point (ptid_t ptid,
}
int
+loongarch_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
+ int len, int is_insert, ptid_t ptid,
+ struct loongarch_debug_reg_state *state)
+{
+ if (is_insert)
+ return loongarch_dr_state_insert_one_point (ptid, state, type, addr,
+ len, -1);
+ else
+ return loongarch_dr_state_remove_one_point (ptid, state, type, addr,
+ len, -1);
+}
+
+
+int
loongarch_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
int len, int is_insert, ptid_t ptid,
struct loongarch_debug_reg_state *state)
@@ -234,12 +255,12 @@ bool
loongarch_any_set_debug_regs_state (loongarch_debug_reg_state *state,
bool watchpoint)
{
- int count = watchpoint ? loongarch_num_wp_regs : 0;
+ int count = watchpoint ? loongarch_num_wp_regs : loongarch_num_bp_regs;
if (count == 0)
return false;
- const CORE_ADDR *addr = watchpoint ? state->dr_addr_wp : 0;
- const unsigned int *ctrl = watchpoint ? state->dr_ctrl_wp : 0;
+ const CORE_ADDR *addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
+ const unsigned int *ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
for (int i = 0; i < count; i++)
if (addr[i] != 0 || ctrl[i] != 0)
@@ -268,6 +289,12 @@ loongarch_show_debug_reg_state (struct loongarch_debug_reg_state *state,
: "??unknown??"))));
debug_printf (":\n");
+ debug_printf ("\tBREAKPOINTs:\n");
+ for (i = 0; i < loongarch_num_bp_regs; i++)
+ debug_printf ("\tBP%d: addr=%s, ctrl=0x%08x, ref.count=%d\n",
+ i, core_addr_to_string_nz (state->dr_addr_bp[i]),
+ state->dr_ctrl_bp[i], state->dr_ref_count_bp[i]);
+
debug_printf ("\tWATCHPOINTs:\n");
for (i = 0; i < loongarch_num_wp_regs; i++)
debug_printf ("\tWP%d: addr=%s, ctrl=0x%08x, ref.count=%d\n",
diff --git a/gdb/nat/loongarch-hw-point.h b/gdb/nat/loongarch-hw-point.h
index 86e5aa3..fd79285 100644
--- a/gdb/nat/loongarch-hw-point.h
+++ b/gdb/nat/loongarch-hw-point.h
@@ -33,6 +33,7 @@
Neither of these values may exceed the width of dr_changed_t
measured in bits. */
+#define LOONGARCH_HBP_MAX_NUM 8
#define LOONGARCH_HWP_MAX_NUM 8
@@ -53,12 +54,18 @@
struct loongarch_debug_reg_state
{
+ /* hardware breakpoint */
+ CORE_ADDR dr_addr_bp[LOONGARCH_HBP_MAX_NUM];
+ unsigned int dr_ctrl_bp[LOONGARCH_HBP_MAX_NUM];
+ unsigned int dr_ref_count_bp[LOONGARCH_HBP_MAX_NUM];
+
/* hardware watchpoint */
CORE_ADDR dr_addr_wp[LOONGARCH_HWP_MAX_NUM];
unsigned int dr_ctrl_wp[LOONGARCH_HWP_MAX_NUM];
unsigned int dr_ref_count_wp[LOONGARCH_HWP_MAX_NUM];
};
+extern int loongarch_num_bp_regs;
extern int loongarch_num_wp_regs;
/* Invoked when IDXth breakpoint/watchpoint register pair needs to be
@@ -68,6 +75,10 @@ void loongarch_notify_debug_reg_change (ptid_t ptid, int is_watchpoint,
unsigned int idx);
+int loongarch_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
+ int len, int is_insert, ptid_t ptid,
+ struct loongarch_debug_reg_state *state);
+
int loongarch_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
int len, int is_insert, ptid_t ptid,
struct loongarch_debug_reg_state *state);
diff --git a/gdb/nat/loongarch-linux-hw-point.c b/gdb/nat/loongarch-linux-hw-point.c
index cced5a3..fbc80fd 100644
--- a/gdb/nat/loongarch-linux-hw-point.c
+++ b/gdb/nat/loongarch-linux-hw-point.c
@@ -62,9 +62,6 @@ loongarch_dr_change_callback (struct lwp_info *lwp, int is_watchpoint,
dr_changed_t *dr_changed_ptr;
dr_changed_t dr_changed;
- if (!is_watchpoint)
- return -1;
-
if (info == NULL)
{
info = XCNEW (struct arch_lwp_info);
@@ -74,14 +71,19 @@ loongarch_dr_change_callback (struct lwp_info *lwp, int is_watchpoint,
if (show_debug_regs)
{
debug_printf ("loongarch_dr_change_callback: \n\tOn entry:\n");
- debug_printf ("\ttid%d, dr_changed_wp=0x%s\n",
- tid, phex (info->dr_changed_wp, 8));
+ debug_printf ("\ttid%d, dr_changed_bp=0x%s, "
+ "dr_changed_wp=0x%s\n", tid,
+ phex (info->dr_changed_bp, 8),
+ phex (info->dr_changed_wp, 8));
}
- dr_changed_ptr = &info->dr_changed_wp;
+ dr_changed_ptr = is_watchpoint ? &info->dr_changed_wp
+ : &info->dr_changed_bp;
dr_changed = *dr_changed_ptr;
- gdb_assert (idx >= 0 && idx <= loongarch_num_wp_regs);
+ gdb_assert (idx >= 0
+ && (idx <= (is_watchpoint ? loongarch_num_wp_regs
+ : loongarch_num_bp_regs)));
/* The actual update is done later just before resuming the lwp,
we just mark that one register pair needs updating. */
@@ -95,8 +97,10 @@ loongarch_dr_change_callback (struct lwp_info *lwp, int is_watchpoint,
if (show_debug_regs)
{
- debug_printf ("\tOn exit:\n\ttid%d, dr_changed_wp=0x%s\n",
- tid, phex (info->dr_changed_wp, 8));
+ debug_printf ("\tOn exit:\n\ttid%d, dr_changed_bp=0x%s, "
+ "dr_changed_wp=0x%s\n", tid,
+ phex (info->dr_changed_bp, 8),
+ phex (info->dr_changed_wp, 8));
}
return 0;
@@ -136,9 +140,9 @@ loongarch_linux_set_debug_regs (struct loongarch_debug_reg_state *state,
memset (&regs, 0, sizeof (regs));
iov.iov_base = &regs;
- count = watchpoint ? loongarch_num_wp_regs : 0;
- addr = watchpoint ? state->dr_addr_wp : 0;
- ctrl = watchpoint ? state->dr_ctrl_wp : 0;
+ count = watchpoint ? loongarch_num_wp_regs : loongarch_num_bp_regs;
+ addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
+ ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
if (count == 0)
return;
@@ -151,7 +155,9 @@ loongarch_linux_set_debug_regs (struct loongarch_debug_reg_state *state,
regs.dbg_regs[i].ctrl = ctrl[i];
}
- if (ptrace(PTRACE_SETREGSET, tid, NT_LOONGARCH_HW_WATCH, (void *) &iov))
+ if (ptrace(PTRACE_SETREGSET, tid,
+ watchpoint ? NT_LOONGARCH_HW_WATCH : NT_LOONGARCH_HW_BREAK,
+ (void *) &iov))
{
if (errno == EINVAL)
error (_("Invalid argument setting hardware debug registers"));
@@ -194,6 +200,25 @@ loongarch_linux_get_debug_reg_capacity (int tid)
loongarch_num_wp_regs = 0;
}
+ /* Get hardware breakpoint register info. */
+ result = ptrace (PTRACE_GETREGSET, tid, NT_LOONGARCH_HW_BREAK, &iov);
+ if ( result == 0)
+ {
+ loongarch_num_bp_regs = LOONGARCH_DEBUG_NUM_SLOTS (dreg_state.dbg_info);
+ if (loongarch_num_bp_regs > LOONGARCH_HBP_MAX_NUM)
+ {
+ warning (_("Unexpected number of hardware breakpoint registers"
+ " reported by ptrace, got %d, expected %d."),
+ loongarch_num_bp_regs, LOONGARCH_HBP_MAX_NUM);
+ loongarch_num_bp_regs = LOONGARCH_HBP_MAX_NUM;
+ }
+ }
+ else
+ {
+ warning (_("Unable to determine the number of hardware breakpoints"
+ " available."));
+ loongarch_num_bp_regs = 0;
+ }
}
/* Return the debug register state for process PID. If no existing
diff --git a/gdb/nat/loongarch-linux-hw-point.h b/gdb/nat/loongarch-linux-hw-point.h
index 4086907..8d96443 100644
--- a/gdb/nat/loongarch-linux-hw-point.h
+++ b/gdb/nat/loongarch-linux-hw-point.h
@@ -93,6 +93,7 @@ struct arch_lwp_info
/* When bit N is 1, it indicates the Nth hardware breakpoint or
watchpoint register pair needs to be updated when the thread is
resumed; see loongarch_linux_prepare_to_resume. */
+ dr_changed_t dr_changed_bp;
dr_changed_t dr_changed_wp;
};
diff --git a/gdb/nat/loongarch-linux.c b/gdb/nat/loongarch-linux.c
index 088d0fc..03a0aaf 100644
--- a/gdb/nat/loongarch-linux.c
+++ b/gdb/nat/loongarch-linux.c
@@ -43,7 +43,8 @@ loongarch_linux_prepare_to_resume (struct lwp_info *lwp)
if (info == NULL)
return;
- if (DR_HAS_CHANGED (info->dr_changed_wp))
+ if (DR_HAS_CHANGED (info->dr_changed_bp)
+ || DR_HAS_CHANGED (info->dr_changed_wp))
{
ptid_t ptid = ptid_of_lwp (lwp);
int tid = ptid.lwp ();
@@ -53,9 +54,19 @@ loongarch_linux_prepare_to_resume (struct lwp_info *lwp)
if (show_debug_regs)
debug_printf ("prepare_to_resume thread %d\n", tid);
- loongarch_linux_set_debug_regs (state, tid, 1);
- DR_CLEAR_CHANGED (info->dr_changed_wp);
-
+ /* Watchpoints. */
+ if (DR_HAS_CHANGED (info->dr_changed_wp))
+ {
+ loongarch_linux_set_debug_regs (state, tid, 1);
+ DR_CLEAR_CHANGED (info->dr_changed_wp);
+ }
+
+ /* Breakpoints. */
+ if (DR_HAS_CHANGED (info->dr_changed_bp))
+ {
+ loongarch_linux_set_debug_regs (state, tid, 0);
+ DR_CLEAR_CHANGED (info->dr_changed_bp);
+ }
}
}
@@ -72,6 +83,8 @@ loongarch_linux_new_thread (struct lwp_info *lwp)
/* If there are hardware breakpoints/watchpoints in the process then mark that
all the hardware breakpoint/watchpoint register pairs for this thread need
to be initialized (with data from arch_process_info.debug_reg_state). */
+ if (loongarch_any_set_debug_regs_state (state, false))
+ DR_MARK_ALL_CHANGED (info->dr_changed_bp, loongarch_num_bp_regs);
if (loongarch_any_set_debug_regs_state (state, true))
DR_MARK_ALL_CHANGED (info->dr_changed_wp, loongarch_num_wp_regs);