From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id IfNFJAsT12k3Sw4AWB0awg (envelope-from ) for ; Wed, 08 Apr 2026 22:46:35 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.a=rsa-sha256 header.s=mxsw2412 header.b=egNDmFek; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 8E6451E0BC; Wed, 08 Apr 2026 22:46:35 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 04CAD1E08C for ; Wed, 08 Apr 2026 22:46:33 -0400 (EDT) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id C5CCA4BA2E1F for ; Thu, 9 Apr 2026 02:46:31 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C5CCA4BA2E1F Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.a=rsa-sha256 header.s=mxsw2412 header.b=egNDmFek Received: from smtpbg151.qq.com (smtpbg151.qq.com [18.169.211.239]) by sourceware.org (Postfix) with ESMTPS id B00CB4BA2E26 for ; Thu, 9 Apr 2026 02:45:56 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B00CB4BA2E26 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: sourceware.org; spf=none smtp.mailfrom=linux.spacemit.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org B00CB4BA2E26 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=18.169.211.239 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1775702757; cv=none; b=Jf8QK9wNvKgrNo+0jU9IOczD/Kr4Wzb0mZ2Swyo46mcrOpqg+KgEn8KtH1XwL2WDViMwo250a6tevRVspDeu18ijYvn0oYOQQMJVCHoQ5RMUtl9wHVeM+TAxqc2TTn5xbAdHvEr0DEGWz/ptLyxxykE1fsTy2dGtyavSZ1VgZkE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1775702757; c=relaxed/simple; bh=NjThWkfe3JmIoAG21xTRc2AJxRCNA7O+jm3qlG6W+80=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=nllngC4+nG9i4gCbuZzZ7wLtMWZn4SDQjapakSnAR4PnaHFD/ttLSbrUurXD4+KZ1l4NaQFgNLvU7j2MTUoufyD8bqNPNiNYBFLwU5Xjss8dKfdjfh9uysKtyYC+oDiM/MdOi5GCNgObTlGQhoxBGTRCxb0txnOKv+B+9j5O1V8= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B00CB4BA2E26 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1775702753; bh=XZDjt53+GQWJxmEJNEYoHNkl8lhHA+FUyDRhVQ8w2cY=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=egNDmFekpjJWm5gnlfzLlYGwPjgn20o1HpmRl5DlxDRCh3nanOBAeaZTiTjuCO4MY zJi50wm3rGIWA+229yNUFYbFxxDBF4+pkvl8rj2dZOBQfLEYnqc9PB9wGOllOezkXS depC36XtLgrsMW7oeU40Dr6gtoGpAA3piImRrNno= X-QQ-mid: esmtpsz11t1775702749t69a9e273 X-QQ-Originating-IP: nYbaAVb7sd5HvEdWx+f7i6HOVacO6djAb6txJNkI/ZA= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Thu, 09 Apr 2026 10:45:48 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 2273576026107624090 EX-QQ-RecipientCnt: 2 From: Zane Leung To: gdb-patches@sourceware.org Cc: zhuangqiubin@linux.spacemit.com Subject: [PATCH v2 1/2] gdb: riscv: Add support for hardware breakpoints/watchpoints Date: Thu, 9 Apr 2026 10:45:45 +0800 Message-Id: <20260409024546.350958-2-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260409024546.350958-1-liangzhen@linux.spacemit.com> References: <20260409024546.350958-1-liangzhen@linux.spacemit.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-QQ-SENDSIZE: 520 Feedback-ID: esmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: NR3PJyWXADsZXdxc6Zzz/vTGFsb/KRk8b54vcZrBCMpCbns1vPxNGP4L NQ7uQjW5bYDvRm+oj1ZA4PXxIuMHPipFpB1ZjYX+/mbCZr24aILt9/asQ7GN9s/G4PhPySj EUO+FKXIvUgWXB/U51ZLh/JSC/qxI1GZiH7Lo0PM7No0JACL9lo+YNUNJk9MmteBSsJeCCq d8N3TQ+kOl6IYFJ3goZSTilREhc/1nwEZ/NRejPnyxx9AUxONIXvJJr58X4ozcM63fuaVDM Kb9/CSY+c7baXKg47OeT/uYDwrJz+BDvNe5LTeFM126sxgrGr2D/2lmlaa+f9n4EF/pNdt4 gbBGPESEa2DRlHSvHDTRlnHZsTso4MM9BqhbStZjY9uQz694AMGexjC1RXL4Zqr5xVg2Y6T 4wTCi42bvXgwyZPkSTUN4oza6tqop6yZl4NxtPAXH7lPhNrJca89uhtsJ4cHfISpgbesGwm /vaD6KdfTiLb0dzDOLiVaCal+0IJXfPMtaHEfNxzogKuvgvhKTyuMBGIzUDW+uYHzsvJTRs z0cclbghtEGaCt4PjT2EsTvYi2kbTH+ZdlU1Zum4Ygra4xZGa2vN881tqGsSA5cX9cIx5Vp qf44stAbkspPJX96nlfiwVIVdB3nz7Ubj6F+a31ADWyc9rzhlrvdHHMmBATPfEE60+RdYiT Xg59HJaj5YybGJIQualInVmTrFtYDAYNrl3HQQoV0ep1PLEcDWUwnHJI6RKkhN9dyvTKco3 JL8kkqpj9v0/mVp76XQmH5nyMnnEyUBqiJjRRDNbk6HyV7vgXAXl5UF7LyVCHuWJYCsMRgO whZuG8dSnvLTP+EhUlfgCkPQDbJSoEoKx72KXfcXK3E0nIKcrFrGk5U1gpyevkB/oUDAblc 8CPWrqQHxt9Q95pHhpqnagccy42EhUsP3NZsoU5haGq4J9wpsUHMetcNDn5UJVmZX34KdWw XwWvYr48FuvNbhnVGkEzttSo2dZ4BNm4fDMze6FDRdYMY4KAchJbqXsHeN2cleCFOgYKtSX yD6oKUIlGI53S3uJWqg+9vYkuscAKMH07ZotqFEE2AFjiqfQc8ORFmv03cE8v0C5UZhx4nV eeNTHQblF7Y1ib9SbqjbcOkmvYbCqtKfSNS637HK6TK X-QQ-XMRINFO: M/715EihBoGS47X28/vv4NpnfpeBLnr4Qg== X-QQ-RECHKSPAM: 0 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org From: liangzhen Implement hardware breakpoint and watchpoint support for RISC-V architecture. --- gdb/Makefile.in | 3 + gdb/configure.nat | 3 +- gdb/nat/riscv-hw-point.c | 244 ++++++++++++++++++++++++ gdb/nat/riscv-hw-point.h | 89 +++++++++ gdb/nat/riscv-linux-hw-point.c | 192 +++++++++++++++++++ gdb/nat/riscv-linux-hw-point.h | 122 ++++++++++++ gdb/nat/riscv-linux.c | 82 ++++++++ gdb/nat/riscv-linux.h | 39 ++++ gdb/riscv-linux-nat.c | 333 +++++++++++++++++++++++++++++++++ include/elf/common.h | 2 + 10 files changed, 1108 insertions(+), 1 deletion(-) create mode 100644 gdb/nat/riscv-hw-point.c create mode 100644 gdb/nat/riscv-hw-point.h create mode 100644 gdb/nat/riscv-linux-hw-point.c create mode 100644 gdb/nat/riscv-linux-hw-point.h create mode 100644 gdb/nat/riscv-linux.c create mode 100644 gdb/nat/riscv-linux.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 2488d789580..8998c3fdfbf 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1571,6 +1571,9 @@ HFILES_NO_SRCDIR = \ nat/linux-procfs.h \ nat/linux-ptrace.h \ nat/linux-waitpid.h \ + nat/riscv-hw-point.h \ + nat/riscv-linux.h \ + nat/riscv-linux-hw-point.h \ nat/loongarch-hw-point.h \ nat/loongarch-linux.h \ nat/loongarch-linux-hw-point.h \ diff --git a/gdb/configure.nat b/gdb/configure.nat index 38dd4179511..aee8541f486 100644 --- a/gdb/configure.nat +++ b/gdb/configure.nat @@ -325,7 +325,8 @@ case ${gdb_host} in riscv*) # Host: RISC-V, running Linux NATDEPFILES="${NATDEPFILES} riscv-linux-nat.o \ - nat/riscv-linux-tdesc.o" + nat/riscv-linux-tdesc.o nat/riscv-hw-point.o \ + nat/riscv-linux.o nat/riscv-linux-hw-point.o" ;; s390) # Host: S390, running Linux diff --git a/gdb/nat/riscv-hw-point.c b/gdb/nat/riscv-hw-point.c new file mode 100644 index 00000000000..8c13895c668 --- /dev/null +++ b/gdb/nat/riscv-hw-point.c @@ -0,0 +1,244 @@ +/* Copyright (C) 2026 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 . */ + +#include "gdbsupport/break-common.h" +#include "gdbsupport/common-regcache.h" +#include "riscv-hw-point.h" +#include "riscv-linux-hw-point.h" + +/* Number of hardware breakpoints/watchpoints the target supports. + They are initialized with values obtained via ptrace. */ + +int riscv_num_hwbp_regs; + +/* Record the insertion of one breakpoint/watchpoint, as represented by + ADDR, TYPE and LEN, in the process' arch-specific data area *STATE. + Return TRUE if the operation succeeds. */ + +static bool +riscv_dr_state_insert_one_point (ptid_t ptid, + struct riscv_debug_reg_state *state, + enum target_hw_bp_type type, CORE_ADDR addr, + int len) +{ + int i, idx, num_regs; + unsigned int *dr_type_p, *dr_len_p, *dr_ref_count; + CORE_ADDR *dr_addr_p; + + /* Set up state pointers. */ + num_regs = riscv_num_hwbp_regs; + dr_addr_p = state->dr_addr_hwbp; + dr_type_p = state->dr_type_hwbp; + dr_len_p = state->dr_len_hwbp; + dr_ref_count = state->dr_ref_count_hwbp; + + /* Find an existing or free register in our cache. */ + idx = -1; + for (i = 0; i < num_regs; ++i) + { + if (dr_ref_count[i] == 0) + { + idx = i; + /* no break; continue hunting for an exising one. */ + } + else if (dr_addr_p[i] == addr && dr_type_p[i] == type + && dr_len_p[i] == len) + { + idx = i; + break; + } + } + + /* No space. */ + if (idx == -1) + return false; + + /* Update our cache. */ + if (dr_ref_count[idx] == 0) + { + /* new entry */ + dr_addr_p[idx] = addr; + dr_type_p[idx] = type; + dr_len_p[idx] = len; + dr_ref_count[idx] = 1; + + /* Notify the change. */ + riscv_notify_debug_reg_change (ptid, idx); + } + else + { + /* existing entry */ + dr_ref_count[idx]++; + } + + return true; +} + +/* Record the removal of one breakpoint/watchpoint, as represented by + ADDR, TYPE and LEN, in the process' arch-specific data area *STATE. + Return TRUE if the operation succeeds. */ + +static bool +riscv_dr_state_remove_one_point (ptid_t ptid, + struct riscv_debug_reg_state *state, + enum target_hw_bp_type type, CORE_ADDR addr, + int len) +{ + int i, num_regs; + unsigned int *dr_type_p, *dr_len_p, *dr_ref_count; + CORE_ADDR *dr_addr_p; + + /* Set up state pointers. */ + num_regs = riscv_num_hwbp_regs; + dr_addr_p = state->dr_addr_hwbp; + dr_type_p = state->dr_type_hwbp; + dr_len_p = state->dr_len_hwbp; + dr_ref_count = state->dr_ref_count_hwbp; + + /* Find the entry that matches the ADDR, TYPE and LEN. */ + for (i = 0; i < num_regs; ++i) + if (dr_addr_p[i] == addr && dr_type_p[i] == type && + dr_len_p[i] == len) + { + gdb_assert (dr_ref_count[i] != 0); + break; + } + + /* Not found. */ + if (i == num_regs) + return false; + + /* Clear our cache. */ + if (--dr_ref_count[i] == 0) + { + dr_addr_p[i] = 0; + dr_type_p[i] = 0; + dr_len_p[i] = 0; + + /* Notify the change. */ + riscv_notify_debug_reg_change (ptid, i); + } + + return true; +} + +/* Handle insertion or removal of a hardware breakpoint/watchpoint. + If IS_INSERT is 1, insert the point; otherwise remove it. + Return TRUE if the operation succeeds. */ + +bool +riscv_handle_point (enum target_hw_bp_type type, CORE_ADDR addr, + int len, int is_insert, ptid_t ptid, + struct riscv_debug_reg_state *state) +{ + if (is_insert) + return riscv_dr_state_insert_one_point (ptid, state, type, addr, len); + else + return riscv_dr_state_remove_one_point (ptid, state, type, addr, len); +} + +/* Return TRUE if there are any hardware breakpoints/watchpoints. */ + +bool +riscv_any_set_debug_regs_state (riscv_debug_reg_state *state) +{ + int count = riscv_num_hwbp_regs; + if (count == 0) + return false; + + const CORE_ADDR *addr = state->dr_addr_hwbp; + const unsigned int *len = state->dr_len_hwbp; + + for (int i = 0; i < count; i++) + if (addr[i] != 0 || len[i] != 0) + return true; + + return false; +} + +/* Print the values of the cached breakpoint/watchpoint registers. */ + +void +riscv_show_debug_reg_state (struct riscv_debug_reg_state *state, + const char *func, CORE_ADDR addr, + int len, enum target_hw_bp_type type) +{ + debug_printf ("%s", func); + if (addr != 0 || len != 0) + debug_printf (" (addr=0x%08lx, len=%d, type=%s)", + (unsigned long) addr, len, + type == hw_write ? "hw-write-watchpoint" + : (type == hw_read ? "hw-read-watchpoint" + : (type == hw_access ? "hw-access-watchpoint" + : (type == hw_execute ? "hw-breakpoint" + : "??unknown??")))); + debug_printf (":\n"); + + debug_printf ("\tHWPOINTs:\n"); + for (int i = 0; i < riscv_num_hwbp_regs; i++) + debug_printf ("\t%cP%d: addr=%s, len=%d, type=%s, ref.count=%d\n", + state->dr_type_hwbp[i] == hw_execute ? 'B' : 'W', + i, core_addr_to_string_nz (state->dr_addr_hwbp[i]), + state->dr_len_hwbp[i], + type == hw_write ? "hw-write-watchpoint" + : (type == hw_read ? "hw-read-watchpoint" + : (type == hw_access ? "hw-access-watchpoint" + : (type == hw_execute ? "hw-breakpoint" + : "??unknown??"))), + state->dr_ref_count_hwbp[i]); +} + +/* Return true if we can watch a memory region that starts address + ADDR and whose length is LEN in bytes. */ + +int +riscv_region_ok_for_watchpoint (CORE_ADDR addr, int len) +{ + /* Can not set watchpoints for zero or negative lengths. */ + if (len <= 0) + return 0; + + /* Must have hardware watchpoint debug register(s). */ + if (riscv_num_hwbp_regs == 0) + return 0; + + return 1; +} + +/* Helper for the "stopped_data_address/low_stopped_data_address" target + method. Returns TRUE if a hardware watchpoint trap at ADDR_TRAP matches + a set watchpoint. The address of the matched watchpoint is returned in + *ADDR_P. */ + +bool +riscv_stopped_data_address (const struct riscv_debug_reg_state *state, + CORE_ADDR addr_trap, CORE_ADDR *addr_p) +{ + for (int i = 0; i < riscv_num_hwbp_regs; i++) + { + const CORE_ADDR addr = state->dr_addr_hwbp[i]; + if (state->dr_ref_count_hwbp[i] + && addr_trap == addr + && state->dr_type_hwbp[i] != hw_execute) + { + *addr_p = addr; + return true; + } + } + + return false; +} diff --git a/gdb/nat/riscv-hw-point.h b/gdb/nat/riscv-hw-point.h new file mode 100644 index 00000000000..0a6d3795d64 --- /dev/null +++ b/gdb/nat/riscv-hw-point.h @@ -0,0 +1,89 @@ +/* Copyright (C) 2026 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 . */ + +#ifndef GDB_NAT_RISCV_HW_POINT_H +#define GDB_NAT_RISCV_HW_POINT_H + +/* Macro definitions, data structures, and code for the hardware + breakpoint and hardware watchpoint support follow. We use the + following abbreviations throughout the code: + + hw - hardware + bp - breakpoint + wp - watchpoint */ + +/* Maximum number of hardware breakpoint + watchpoint registers. */ + +#define RISCV_HWBP_MAX_NUM 16 + + +/* Structure for managing the hardware breakpoint/watchpoint resources. + DR_ADDR_* stores the address, DR_CTRL_* stores the control register + content, and DR_REF_COUNT_* counts the numbers of references to the + corresponding hwbp, by which way the limited hardware resources are + not wasted on duplicated bp/wp settings (though so far gdb has done + a good job by not sending duplicated bp/wp requests). */ + +struct riscv_debug_reg_state +{ + /* hardware/hardware breakpoint */ + CORE_ADDR dr_addr_hwbp[RISCV_HWBP_MAX_NUM]; + unsigned int dr_type_hwbp[RISCV_HWBP_MAX_NUM]; + unsigned int dr_len_hwbp[RISCV_HWBP_MAX_NUM]; + unsigned int dr_ref_count_hwbp[RISCV_HWBP_MAX_NUM]; +}; + +extern int riscv_num_hwbp_regs; + +/* Invoked when IDXth breakpoint/watchpoint register pair needs to be + updated. */ + +void riscv_notify_debug_reg_change (ptid_t ptid, unsigned int idx); + + +/* Handle insertion or removal of a hardware breakpoint/watchpoint. + If IS_INSERT is 1, insert the point; otherwise remove it. + Return TRUE if the operation succeeds. */ + +bool riscv_handle_point (enum target_hw_bp_type type, CORE_ADDR addr, + int len, int is_insert, ptid_t ptid, + struct riscv_debug_reg_state *state); + +/* Return TRUE if there are any hardware breakpoints/watchpoints. */ + +bool riscv_any_set_debug_regs_state (riscv_debug_reg_state *state); + +/* Print the values of the cached breakpoint/watchpoint registers. */ + +void riscv_show_debug_reg_state (struct riscv_debug_reg_state *state, + const char *func, CORE_ADDR addr, + int len, enum target_hw_bp_type type); + +/* Return true if we can watch a memory region that starts address + ADDR and whose length is LEN in bytes. */ + +int riscv_region_ok_for_watchpoint (CORE_ADDR addr, int len); + +/* Helper for the "stopped_data_address/low_stopped_data_address" target + method. Returns TRUE if a hardware watchpoint trap at ADDR_TRAP matches + a set watchpoint. The address of the matched watchpoint is returned in + *ADDR_P. */ + +bool riscv_stopped_data_address (const struct riscv_debug_reg_state *state, + CORE_ADDR addr_trap, CORE_ADDR *addr_p); + +#endif /* GDB_NAT_RISCV_HW_POINT_H */ diff --git a/gdb/nat/riscv-linux-hw-point.c b/gdb/nat/riscv-linux-hw-point.c new file mode 100644 index 00000000000..e7f3865bb66 --- /dev/null +++ b/gdb/nat/riscv-linux-hw-point.c @@ -0,0 +1,192 @@ +/* Copyright (C) 2026 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 . */ + +#include "gdbsupport/break-common.h" +#include "gdbsupport/common-regcache.h" +#include "nat/linux-nat.h" +#include "riscv-linux-hw-point.h" + +#include + +/* The order in which and are included + can be important. often declares various PTRACE_* + enums. often defines preprocessor constants for + these very same symbols. When that's the case, build errors will + result when is included before . */ + +#include +#include + +#include "elf/common.h" + +/* Helper for riscv_notify_debug_reg_change. Records the + information about the change of one hardware breakpoint/watchpoint + setting for the thread LWP. + N.B. The actual updating of hardware debug registers is not + carried out until the moment the thread is resumed. */ + +static int +riscv_dr_change_callback (struct lwp_info *lwp, unsigned int idx) +{ + int tid = ptid_of_lwp (lwp).lwp (); + struct arch_lwp_info *info = lwp_arch_private_info (lwp); + dr_changed_t *dr_changed_ptr; + dr_changed_t dr_changed; + + if (info == NULL) + { + info = XCNEW (struct arch_lwp_info); + lwp_set_arch_private_info (lwp, info); + } + + if (show_debug_regs) + { + debug_printf ("riscv_dr_change_callback: \n\tOn entry:\n"); + debug_printf ("\ttid%d, dr_changed_hwbp=0x%s\n", tid, + phex (info->dr_changed_hwbp, 8)); + } + + dr_changed_ptr = &info->dr_changed_hwbp; + dr_changed = *dr_changed_ptr; + + gdb_assert (idx >= 0 && idx <= riscv_num_hwbp_regs); + + /* The actual update is done later just before resuming the lwp, + we just mark that one register pair needs updating. */ + DR_MARK_N_CHANGED (dr_changed, idx); + *dr_changed_ptr = dr_changed; + + /* If the lwp isn't stopped, force it to momentarily pause, so + we can update its debug registers. */ + if (!lwp_is_stopped (lwp)) + linux_stop_lwp (lwp); + + if (show_debug_regs) + { + debug_printf ("\tOn exit:\n\ttid%d, dr_changed_hwbp=0x%s\n", tid, + phex (info->dr_changed_hwbp, 8)); + } + + return 0; +} + +/* Notify each thread that their IDXth breakpoint/watchpoint register + pair needs to be updated. The message will be recorded in each + thread's arch-specific data area, the actual updating will be done + when the thread is resumed. */ + +void +riscv_notify_debug_reg_change (ptid_t ptid, unsigned int idx) +{ + ptid_t pid_ptid = ptid_t (ptid.pid ()); + + iterate_over_lwps (pid_ptid, [=] (struct lwp_info *info) + { + return riscv_dr_change_callback (info, + idx); + }); +} + +/* Convert GDB hardware breakpoint/watchpoint type to RISC-V hardware type. */ + +static unsigned int +riscv_hwbp_type (unsigned int type) +{ + switch (type) + { + case hw_execute: + return RISCV_HW_EXECUTE; + case hw_read: + return RISCV_HW_READ; + case hw_write: + return RISCV_HW_WRITE; + case hw_access: + return RISCV_HW_ACCESS; + } + + gdb_assert_not_reached ("unexpected hwbp type"); +} + +/* Call ptrace to set the thread TID's hardware breakpoint/watchpoint + registers with data from *STATE. */ + +void +riscv_linux_set_debug_regs (struct riscv_debug_reg_state *state, + int tid) +{ + int i, count; + struct iovec iov; + struct riscv_hwdebug_state regs; + + memset (®s, 0, sizeof (regs)); + iov.iov_base = ®s; + count = riscv_num_hwbp_regs; + + if (count == 0) + return; + + iov.iov_len = (offsetof (struct riscv_hwdebug_state, dbg_regs) + + count * sizeof (regs.dbg_regs[0])); + for (i = 0; i < count; i++) + { + regs.dbg_regs[i].addr = state->dr_addr_hwbp[i]; + regs.dbg_regs[i].type = riscv_hwbp_type(state->dr_type_hwbp[i]); + regs.dbg_regs[i].len = state->dr_len_hwbp[i]; + } + + if (ptrace(PTRACE_SETREGSET, tid, NT_RISCV_HW_BREAK, (void *) &iov)) + { + if (errno == EINVAL) + error (_("Invalid argument setting hardware debug registers")); + else + error (_("Unexpected error setting hardware debug registers")); + } +} + +/* Get the hardware debug register capacity information from the + process represented by TID. */ + +void +riscv_linux_get_debug_reg_capacity (int tid) +{ + struct iovec iov; + struct riscv_hwdebug_state dbg_state; + int result; + iov.iov_base = &dbg_state; + iov.iov_len = sizeof (dbg_state); + + /* Get hardware breakpoint/watchpoint register info. */ + result = ptrace (PTRACE_GETREGSET, tid, NT_RISCV_HW_BREAK, &iov); + + if (result == 0) + { + riscv_num_hwbp_regs = RISCV_DEBUG_NUM_SLOTS (dbg_state.dbg_info); + if (riscv_num_hwbp_regs > RISCV_HWBP_MAX_NUM) + { + warning (_("Unexpected number of hardware breakpoint/watchpoint" + " registers reported by ptrace, got %d, expected %d."), + riscv_num_hwbp_regs, RISCV_HWBP_MAX_NUM); + riscv_num_hwbp_regs = RISCV_HWBP_MAX_NUM; + } + } + else + { + warning (_("Unable to determine the number of hardware" + " breakpoints/watchpoints available.")); + riscv_num_hwbp_regs = 0; + } +} diff --git a/gdb/nat/riscv-linux-hw-point.h b/gdb/nat/riscv-linux-hw-point.h new file mode 100644 index 00000000000..0f0f8f79577 --- /dev/null +++ b/gdb/nat/riscv-linux-hw-point.h @@ -0,0 +1,122 @@ +/* Copyright (C) 2026 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 . */ + +#ifndef GDB_NAT_RISCV_LINUX_HW_POINT_H +#define GDB_NAT_RISCV_LINUX_HW_POINT_H + +#include "gdbsupport/break-common.h" /* For enum target_hw_bp_type. */ + +#include "nat/riscv-hw-point.h" + +struct riscv_hwdebug_state { + uint64_t dbg_info; + struct { + uint64_t addr; + uint64_t type; + uint64_t len; + } dbg_regs[16]; +}; + +enum riscv_hwbp_type { + RISCV_HW_EXECUTE = 0, + RISCV_HW_READ = 1, + RISCV_HW_WRITE = 2, + RISCV_HW_ACCESS = 3, +}; + +/* Macros to extract fields from the hardware debug information word. */ +#define RISCV_DEBUG_NUM_SLOTS(x) ((x) & 0xffff) + +/* Each bit of a variable of this type is used to indicate whether a + hardware breakpoint or watchpoint setting has been changed since + the last update. + + Bit N corresponds to the Nth hardware breakpoint or watchpoint + setting which is managed in riscv_debug_reg_state, where N is + valid between 0 and the total number of the hardware breakpoint or + watchpoint debug registers minus 1. + + When bit N is 1, the corresponding breakpoint or watchpoint setting + has changed, and therefore the corresponding hardware debug + register needs to be updated via the ptrace interface. + + In the per-thread arch-specific data area, we define two such + variables for per-thread hardware breakpoint and watchpoint + settings respectively. + + This type is part of the mechanism which helps reduce the number of + ptrace calls to the kernel, i.e. avoid asking the kernel to write + to the debug registers with unchanged values. */ + +typedef ULONGEST dr_changed_t; + +/* Set each of the lower M bits of X to 1; assert X is wide enough. */ + +#define DR_MARK_ALL_CHANGED(x, m) \ + do \ + { \ + gdb_assert (sizeof ((x)) * 8 >= (m)); \ + (x) = (((dr_changed_t)1 << (m)) - 1); \ + } while (0) + +#define DR_MARK_N_CHANGED(x, n) \ + do \ + { \ + (x) |= ((dr_changed_t)1 << (n)); \ + } while (0) + +#define DR_CLEAR_CHANGED(x) \ + do \ + { \ + (x) = 0; \ + } while (0) + +#define DR_HAS_CHANGED(x) ((x) != 0) +#define DR_N_HAS_CHANGED(x, n) ((x) & ((dr_changed_t)1 << (n))) + +/* Per-thread arch-specific data we want to keep. */ + +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 riscv_linux_prepare_to_resume. */ + dr_changed_t dr_changed_hwbp; +}; + +/* Call ptrace to set the thread TID's hardware breakpoint/watchpoint + registers with data from *STATE. */ + +void riscv_linux_set_debug_regs (struct riscv_debug_reg_state *state, + int tid); + +/* Get the hardware debug register capacity information from the + process represented by TID. */ + +void riscv_linux_get_debug_reg_capacity (int tid); + +/* Return the debug register state for process PID. If no existing + state is found for this process, return nullptr. */ + +struct riscv_debug_reg_state *riscv_lookup_debug_reg_state (pid_t pid); + +/* Return the debug register state for process PID. If no existing + state is found for this process, create new state. */ + +struct riscv_debug_reg_state *riscv_get_debug_reg_state (pid_t pid); + +#endif /* GDB_NAT_RISCV_LINUX_HW_POINT_H */ diff --git a/gdb/nat/riscv-linux.c b/gdb/nat/riscv-linux.c new file mode 100644 index 00000000000..65183fd3dbd --- /dev/null +++ b/gdb/nat/riscv-linux.c @@ -0,0 +1,82 @@ +/* Copyright (C) 2026 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 . */ + +#include "gdbsupport/break-common.h" +#include "nat/linux-nat.h" +#include "nat/riscv-linux-hw-point.h" +#include "nat/riscv-linux.h" + +#include "elf/common.h" +#include "nat/gdb_ptrace.h" +#include +#include + +/* Called when resuming a thread LWP. + The hardware debug registers are updated when there is any change. */ + +void +riscv_linux_prepare_to_resume (struct lwp_info *lwp) +{ + struct arch_lwp_info *info = lwp_arch_private_info (lwp); + + /* NULL means this is the main thread still going through the shell, + or, no watchpoint has been set yet. In that case, there's + nothing to do. */ + if (info == NULL) + return; + + if (DR_HAS_CHANGED (info->dr_changed_hwbp)) + { + ptid_t ptid = ptid_of_lwp (lwp); + int tid = ptid.lwp (); + struct riscv_debug_reg_state *state + = riscv_get_debug_reg_state (ptid.pid ()); + + if (show_debug_regs) + debug_printf ("prepare_to_resume thread %d\n", tid); + + riscv_linux_set_debug_regs (state, tid); + DR_CLEAR_CHANGED (info->dr_changed_hwbp); + } +} + +/* Function to call when a new thread is detected. */ + +void +riscv_linux_new_thread (struct lwp_info *lwp) +{ + ptid_t ptid = ptid_of_lwp (lwp); + struct riscv_debug_reg_state *state + = riscv_get_debug_reg_state (ptid.pid ()); + struct arch_lwp_info *info = XCNEW (struct arch_lwp_info); + + /* 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 (riscv_any_set_debug_regs_state (state)) + DR_MARK_ALL_CHANGED (info->dr_changed_hwbp, riscv_num_hwbp_regs); + + lwp_set_arch_private_info (lwp, info); +} + +/* See nat/riscv-linux.h. */ + +void +riscv_linux_delete_thread (struct arch_lwp_info *arch_lwp) +{ + xfree (arch_lwp); +} diff --git a/gdb/nat/riscv-linux.h b/gdb/nat/riscv-linux.h new file mode 100644 index 00000000000..b147ba93319 --- /dev/null +++ b/gdb/nat/riscv-linux.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2026 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 . */ + +#ifndef GDB_NAT_RISCV_LINUX_H +#define GDB_NAT_RISCV_LINUX_H + +#include + +/* Defines ps_err_e, struct ps_prochandle. */ +#include "gdb_proc_service.h" + +/* Called when resuming a thread LWP. + The hardware debug registers are updated when there is any change. */ + +void riscv_linux_prepare_to_resume (struct lwp_info *lwp); + +/* Function to call when a new thread is detected. */ + +void riscv_linux_new_thread (struct lwp_info *lwp); + +/* Function to call when a thread is being deleted. */ + +void riscv_linux_delete_thread (struct arch_lwp_info *arch_lwp); + +#endif /* GDB_NAT_RISCV_LINUX_H */ diff --git a/gdb/riscv-linux-nat.c b/gdb/riscv-linux-nat.c index e1e81ad50c4..77b8e868590 100644 --- a/gdb/riscv-linux-nat.c +++ b/gdb/riscv-linux-nat.c @@ -22,9 +22,13 @@ #include "riscv-tdep.h" #include "inferior.h" +#include "cli/cli-cmds.h" #include "elf/common.h" #include "nat/riscv-linux-tdesc.h" +#include "nat/riscv-hw-point.h" +#include "nat/riscv-linux.h" +#include "nat/riscv-linux-hw-point.h" #include @@ -33,6 +37,44 @@ # define NFPREG 33 #endif +/* Hash table storing per-process data. We don't bind this to a + per-inferior registry because of targets like GNU/Linux that + need to keep track of processes that aren't bound to any inferior + (e.g., fork children, checkpoints). */ + +static std::unordered_map + riscv_debug_process_state; + +/* Return the debug register state for process PID. If no existing + state is found for this process, return nullptr. */ + +struct riscv_debug_reg_state * +riscv_lookup_debug_reg_state (pid_t pid) +{ + auto it = riscv_debug_process_state.find (pid); + if (it != riscv_debug_process_state.end ()) + return &it->second; + + return nullptr; +} + +/* Return the debug register state for process PID. If no existing + state is found for this process, create new state. */ + +struct riscv_debug_reg_state * +riscv_get_debug_reg_state (pid_t pid) +{ + return &riscv_debug_process_state[pid]; +} + +/* Remove any existing per-process debug state for process PID. */ + +static void +riscv_remove_debug_reg_state (pid_t pid) +{ + riscv_debug_process_state.erase (pid); +} + /* RISC-V Linux native additions to the default linux support. */ class riscv_linux_nat_target final : public linux_nat_target @@ -42,6 +84,37 @@ class riscv_linux_nat_target final : public linux_nat_target void fetch_registers (struct regcache *regcache, int regnum) override; void store_registers (struct regcache *regcache, int regnum) override; + /* Add our hardware breakpoint and watchpoint implementation. */ + int can_use_hw_breakpoint (enum bptype type, int cnt, int othertype) override; + int region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) 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; + int insert_watchpoint (CORE_ADDR addr, int len, enum target_hw_bp_type type, + struct expression *cond) override; + int remove_watchpoint (CORE_ADDR addr, int len, enum target_hw_bp_type type, + struct expression *cond) override; + bool stopped_by_watchpoint () override; + std::vector stopped_data_addresses () override; + + /* Override the GNU/Linux inferior startup hook. */ + void post_startup_inferior (ptid_t ptid) override; + + /* Override the GNU/Linux post attach hook. */ + void post_attach (int pid) override; + + /* These three defer to common nat/ code. */ + void low_new_thread (struct lwp_info *lp) override + { riscv_linux_new_thread (lp); } + void low_delete_thread (struct arch_lwp_info *lp) override + { riscv_linux_delete_thread (lp); } + void low_prepare_to_resume (struct lwp_info *lp) override + { riscv_linux_prepare_to_resume (lp); } + + void low_new_fork (struct lwp_info *parent, pid_t child_pid) override; + void low_forget_process (pid_t pid) override; + /* Read suitable target description. */ const struct target_desc *read_description () override; }; @@ -327,6 +400,251 @@ riscv_linux_nat_target::store_registers (struct regcache *regcache, int regnum) now. */ } +/* Check if nat target can use hardware breakpoints/watchpoints. */ + +int +riscv_linux_nat_target::can_use_hw_breakpoint (enum bptype type, int cnt, + int othertype) +{ + switch (type) + { + case bp_hardware_breakpoint: + case bp_hardware_watchpoint: + case bp_read_watchpoint: + case bp_access_watchpoint: + case bp_watchpoint: + if (riscv_num_hwbp_regs == 0) + return 0; + break; + default: + gdb_assert_not_reached ("unexpected breakpoint type"); + } + return 1; +} + +int +riscv_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, + int len) +{ + return riscv_region_ok_for_watchpoint (addr, len); +} + +/* Insert a hardware breakpoint at BP_TGT->placed_address. + Return 0 if point is inserted, -1 if not. */ + +int +riscv_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 riscv_debug_reg_state *state + = riscv_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 = riscv_handle_point (type, addr, len, 1 /* is_insert */, + inferior_ptid, state) ? 0 : -1; + + if (show_debug_regs) + { + riscv_show_debug_reg_state (state, + "insert_hw_breakpoint", addr, len, type); + } + + return ret; +} + +/* Remove a hardware breakpoint at BP_TGT->placed_address. + Return 0 if point is removeed, -1 if not. */ + +int +riscv_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch, + struct bp_target_info *bp_tgt) +{ + int ret, len; + CORE_ADDR addr = bp_tgt->placed_address; + const enum target_hw_bp_type type = hw_execute; + struct riscv_debug_reg_state *state + = riscv_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 = riscv_handle_point (type, addr, len, 0 /* is_insert */, + inferior_ptid, state) ? 0 : -1; + + if (show_debug_regs) + { + riscv_show_debug_reg_state (state, + "remove_hw_watchpoint", addr, len, type); + } + + return ret; +} + +/* Insert a hardware watchpoint of type TYPE on region starting at address ADDR + with LEN legth. Return 0 if point is inserted, -1 if not. */ + +int +riscv_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len, + enum target_hw_bp_type type, + struct expression *cond) +{ + int ret; + struct riscv_debug_reg_state *state + = riscv_get_debug_reg_state (inferior_ptid.pid ()); + + if (show_debug_regs) + gdb_printf (gdb_stdlog, + "insert_watchpoint on entry (addr=0x%08lx, len=%d)\n", + (unsigned long) addr, len); + + gdb_assert (type != hw_execute); + + ret = riscv_handle_point (type, addr, len, 1 /* is_insert */, + inferior_ptid, state) ? 0 : -1; + + if (show_debug_regs) + { + riscv_show_debug_reg_state (state, + "insert_watchpoint", addr, len, type); + } + + return ret; +} + +/* Remove a hardware watchpoint of type TYPE on region starting at address ADDR + with LEN legth. Return 0 if point is removed, -1 if not. */ + +int +riscv_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len, + enum target_hw_bp_type type, + struct expression *cond) +{ + int ret; + struct riscv_debug_reg_state *state + = riscv_get_debug_reg_state (inferior_ptid.pid ()); + + if (show_debug_regs) + gdb_printf (gdb_stdlog, + "remove_watchpoint on entry (addr=0x%08lx, len=%d)\n", + (unsigned long) addr, len); + + gdb_assert (type != hw_execute); + + ret = riscv_handle_point (type, addr, len, 0 /* is_insert */, + inferior_ptid, state) ? 0 : -1; + + if (show_debug_regs) + { + riscv_show_debug_reg_state (state, + "remove_watchpoint", addr, len, type); + } + + return ret; +} + +/* Implement the "stopped_data_addresses" target_ops method. */ + +std::vector +riscv_linux_nat_target::stopped_data_addresses () +{ + siginfo_t siginfo; + struct riscv_debug_reg_state *state; + + if (!linux_nat_get_siginfo (inferior_ptid, &siginfo)) + return {}; + + /* This must be a hardware breakpoint. */ + if (siginfo.si_signo != SIGTRAP || (siginfo.si_code & 0xffff) != TRAP_HWBKPT) + return {}; + + state = riscv_get_debug_reg_state (inferior_ptid.pid ()); + + CORE_ADDR addr; + if (riscv_stopped_data_address (state, (CORE_ADDR) siginfo.si_addr, + &addr)) + return { addr }; + + return {}; +} + +/* Implement the "stopped_by_watchpoint" target_ops method. */ + +bool +riscv_linux_nat_target::stopped_by_watchpoint () +{ + return !stopped_data_addresses ().empty (); +} + +/* Implement the virtual inf_ptrace_target::post_startup_inferior method. */ + +void +riscv_linux_nat_target::post_startup_inferior (ptid_t ptid) +{ + low_forget_process (ptid.pid ()); + riscv_linux_get_debug_reg_capacity (ptid.pid ()); + linux_nat_target::post_startup_inferior (ptid); +} + +/* Implement the "post_attach" target_ops method. */ + +void +riscv_linux_nat_target::post_attach (int pid) +{ + low_forget_process (pid); + riscv_linux_get_debug_reg_capacity (pid); + linux_nat_target::post_attach (pid); +} + +/* linux_nat_new_fork hook. */ + +void +riscv_linux_nat_target::low_new_fork (struct lwp_info *parent, + pid_t child_pid) +{ + pid_t parent_pid; + struct riscv_debug_reg_state *parent_state; + struct riscv_debug_reg_state *child_state; + + /* NULL means no watchpoint has ever been set in the parent. In + that case, there's nothing to do. */ + if (parent->arch_private == NULL) + return; + + /* GDB core assumes the child inherits the watchpoints/hw + breakpoints of the parent, and will remove them all from the + forked off process. Copy the debug registers mirrors into the + new process so that all breakpoints and watchpoints can be + removed together. */ + + parent_pid = parent->ptid.pid (); + parent_state = riscv_get_debug_reg_state (parent_pid); + child_state = riscv_get_debug_reg_state (child_pid); + *child_state = *parent_state; +} + +/* Called whenever GDB is no longer debugging process PID. It deletes + data structures that keep track of debug register state. */ + +void +riscv_linux_nat_target::low_forget_process (pid_t pid) +{ + riscv_remove_debug_reg_state (pid); +} + /* Initialize RISC-V Linux native support. */ INIT_GDB_FILE (riscv_linux_nat) @@ -334,4 +652,19 @@ INIT_GDB_FILE (riscv_linux_nat) /* Register the target. */ linux_target = &the_riscv_linux_nat_target; add_inf_child_target (&the_riscv_linux_nat_target); + + /* Add a maintenance command to enable printing the RISC-V internal + debug registers mirror variables. */ + add_setshow_boolean_cmd ("show-debug-regs", class_maintenance, + &show_debug_regs, _("\ +Set whether to show the RISC-V debug registers state."), _("\ +Show whether to show the RISC-V debug registers state."), _("\ +Use \"on\" to enable, \"off\" to disable.\n\ +If enabled, the debug registers values are shown when GDB inserts\n\ +or removes a hardware breakpoint or watchpoint, and when the inferior\n\ +triggers a breakpoint or watchpoint."), + NULL, + NULL, + &maintenance_set_cmdlist, + &maintenance_show_cmdlist); } diff --git a/include/elf/common.h b/include/elf/common.h index 1ae68221a89..5d27dcb14e0 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -781,6 +781,8 @@ /* note name must be "LINUX". */ #define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */ /* note name must be "LINUX". */ +#define NT_RISCV_HW_BREAK 0x903 /* RISC-V hardware breakpoint registers */ + /* note name must be "LINUX". */ #define NT_SIGINFO 0x53494749 /* Fields of siginfo_t. */ #define NT_FILE 0x46494c45 /* Description of mapped files. */ -- 2.34.1