GDB (xrefs)
Loading...
Searching...
No Matches
aarch64-linux-hw-point.c
Go to the documentation of this file.
1/* Copyright (C) 2009-2023 Free Software Foundation, Inc.
2 Contributed by ARM Ltd.
3
4 This file is part of GDB.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18
19#include "gdbsupport/common-defs.h"
20#include "gdbsupport/break-common.h"
21#include "gdbsupport/common-regcache.h"
22#include "nat/linux-nat.h"
24
25#include <sys/uio.h>
26
27/* The order in which <sys/ptrace.h> and <asm/ptrace.h> are included
28 can be important. <sys/ptrace.h> often declares various PTRACE_*
29 enums. <asm/ptrace.h> often defines preprocessor constants for
30 these very same symbols. When that's the case, build errors will
31 result when <asm/ptrace.h> is included before <sys/ptrace.h>. */
32#include <sys/ptrace.h>
33#include <asm/ptrace.h>
34
35#include <elf.h>
36
37/* See aarch64-linux-hw-point.h */
38
40
41/* Helper for aarch64_notify_debug_reg_change. Records the
42 information about the change of one hardware breakpoint/watchpoint
43 setting for the thread LWP.
44 N.B. The actual updating of hardware debug registers is not
45 carried out until the moment the thread is resumed. */
46
47static int
49 unsigned int idx)
50{
51 int tid = ptid_of_lwp (lwp).lwp ();
52 struct arch_lwp_info *info = lwp_arch_private_info (lwp);
53 dr_changed_t *dr_changed_ptr;
54 dr_changed_t dr_changed;
55
56 if (info == NULL)
57 {
58 info = XCNEW (struct arch_lwp_info);
59 lwp_set_arch_private_info (lwp, info);
60 }
61
62 if (show_debug_regs)
63 {
64 debug_printf ("debug_reg_change_callback: \n\tOn entry:\n");
65 debug_printf ("\ttid%d, dr_changed_bp=0x%s, "
66 "dr_changed_wp=0x%s\n", tid,
67 phex (info->dr_changed_bp, 8),
68 phex (info->dr_changed_wp, 8));
69 }
70
71 dr_changed_ptr = is_watchpoint ? &info->dr_changed_wp
72 : &info->dr_changed_bp;
73 dr_changed = *dr_changed_ptr;
74
75 gdb_assert (idx >= 0
78
79 /* The actual update is done later just before resuming the lwp,
80 we just mark that one register pair needs updating. */
81 DR_MARK_N_CHANGED (dr_changed, idx);
82 *dr_changed_ptr = dr_changed;
83
84 /* If the lwp isn't stopped, force it to momentarily pause, so
85 we can update its debug registers. */
86 if (!lwp_is_stopped (lwp))
87 linux_stop_lwp (lwp);
88
89 if (show_debug_regs)
90 {
91 debug_printf ("\tOn exit:\n\ttid%d, dr_changed_bp=0x%s, "
92 "dr_changed_wp=0x%s\n", tid,
93 phex (info->dr_changed_bp, 8),
94 phex (info->dr_changed_wp, 8));
95 }
96
97 return 0;
98}
99
100/* Notify each thread that their IDXth breakpoint/watchpoint register
101 pair needs to be updated. The message will be recorded in each
102 thread's arch-specific data area, the actual updating will be done
103 when the thread is resumed. */
104
105void
107 int is_watchpoint, unsigned int idx)
108{
109 ptid_t pid_ptid = ptid_t (ptid.pid ());
110
111 iterate_over_lwps (pid_ptid, [=] (struct lwp_info *info)
112 {
113 return debug_reg_change_callback (info,
115 idx);
116 });
117}
118
119/* Reconfigure STATE to be compatible with Linux kernels with the PR
120 external/20207 bug. This is called when
121 KERNEL_SUPPORTS_ANY_CONTIGUOUS_RANGE transitions to false. Note we
122 don't try to support combining watchpoints with matching (and thus
123 shared) masks, as it's too late when we get here. On buggy
124 kernels, GDB will try to first setup the perfect matching ranges,
125 which will run out of registers before this function can merge
126 them. It doesn't look like worth the effort to improve that, given
127 eventually buggy kernels will be phased out. */
128
129static void
131{
132 for (int i = 0; i < aarch64_num_wp_regs; ++i)
133 if ((state->dr_ctrl_wp[i] & 1) != 0)
134 {
135 gdb_assert (state->dr_ref_count_wp[i] != 0);
136 uint8_t mask_orig = (state->dr_ctrl_wp[i] >> 5) & 0xff;
137 gdb_assert (mask_orig != 0);
138 static const uint8_t old_valid[] = { 0x01, 0x03, 0x0f, 0xff };
139 uint8_t mask = 0;
140 for (const uint8_t old_mask : old_valid)
141 if (mask_orig <= old_mask)
142 {
143 mask = old_mask;
144 break;
145 }
146 gdb_assert (mask != 0);
147
148 /* No update needed for this watchpoint? */
149 if (mask == mask_orig)
150 continue;
151 state->dr_ctrl_wp[i] |= mask << 5;
152 state->dr_addr_wp[i]
153 = align_down (state->dr_addr_wp[i], AARCH64_HWP_ALIGNMENT);
154
155 /* Try to match duplicate entries. */
156 for (int j = 0; j < i; ++j)
157 if ((state->dr_ctrl_wp[j] & 1) != 0
158 && state->dr_addr_wp[j] == state->dr_addr_wp[i]
159 && state->dr_addr_orig_wp[j] == state->dr_addr_orig_wp[i]
160 && state->dr_ctrl_wp[j] == state->dr_ctrl_wp[i])
161 {
162 state->dr_ref_count_wp[j] += state->dr_ref_count_wp[i];
163 state->dr_ref_count_wp[i] = 0;
164 state->dr_addr_wp[i] = 0;
165 state->dr_addr_orig_wp[i] = 0;
166 state->dr_ctrl_wp[i] &= ~1;
167 break;
168 }
169
171 1 /* is_watchpoint */, i);
172 }
173}
174
175/* Call ptrace to set the thread TID's hardware breakpoint/watchpoint
176 registers with data from *STATE. */
177
178void
180 int tid, int watchpoint)
181{
182 int i, count;
183 struct iovec iov;
184 struct user_hwdebug_state regs;
185 const CORE_ADDR *addr;
186 const unsigned int *ctrl;
187
188 memset (&regs, 0, sizeof (regs));
189 iov.iov_base = &regs;
191 addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
192 ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
193 if (count == 0)
194 return;
195 iov.iov_len = (offsetof (struct user_hwdebug_state, dbg_regs)
196 + count * sizeof (regs.dbg_regs[0]));
197
198 for (i = 0; i < count; i++)
199 {
200 regs.dbg_regs[i].addr = addr[i];
201 regs.dbg_regs[i].ctrl = ctrl[i];
202 }
203
204 if (ptrace (PTRACE_SETREGSET, tid,
205 watchpoint ? NT_ARM_HW_WATCH : NT_ARM_HW_BREAK,
206 (void *) &iov))
207 {
208 /* Handle Linux kernels with the PR external/20207 bug. */
209 if (watchpoint && errno == EINVAL
211 {
215 return;
216 }
217 error (_("Unexpected error setting hardware debug registers"));
218 }
219}
220
221/* Return true if debug arch level is compatible for hw watchpoints
222 and breakpoints. */
223
224static bool
225compatible_debug_arch (unsigned int debug_arch)
226{
227 if (debug_arch == AARCH64_DEBUG_ARCH_V8)
228 return true;
229 if (debug_arch == AARCH64_DEBUG_ARCH_V8_1)
230 return true;
231 if (debug_arch == AARCH64_DEBUG_ARCH_V8_2)
232 return true;
233 if (debug_arch == AARCH64_DEBUG_ARCH_V8_4)
234 return true;
235 if (debug_arch == AARCH64_DEBUG_ARCH_V8_8)
236 return true;
237 if (debug_arch == AARCH64_DEBUG_ARCH_V8_9)
238 return true;
239
240 return false;
241}
242
243/* Get the hardware debug register capacity information from the
244 process represented by TID. */
245
246void
248{
249 struct iovec iov;
250 struct user_hwdebug_state dreg_state;
251
252 iov.iov_base = &dreg_state;
253 iov.iov_len = sizeof (dreg_state);
254
255 /* Get hardware watchpoint register info. */
256 if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_HW_WATCH, &iov) == 0
257 && compatible_debug_arch (AARCH64_DEBUG_ARCH (dreg_state.dbg_info)))
258 {
259 aarch64_num_wp_regs = AARCH64_DEBUG_NUM_SLOTS (dreg_state.dbg_info);
261 {
262 warning (_("Unexpected number of hardware watchpoint registers"
263 " reported by ptrace, got %d, expected %d."),
266 }
267 }
268 else
269 {
270 warning (_("Unable to determine the number of hardware watchpoints"
271 " available."));
273 }
274
275 /* Get hardware breakpoint register info. */
276 if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_HW_BREAK, &iov) == 0
277 && compatible_debug_arch (AARCH64_DEBUG_ARCH (dreg_state.dbg_info)))
278 {
279 aarch64_num_bp_regs = AARCH64_DEBUG_NUM_SLOTS (dreg_state.dbg_info);
281 {
282 warning (_("Unexpected number of hardware breakpoint registers"
283 " reported by ptrace, got %d, expected %d."),
286 }
287 }
288 else
289 {
290 warning (_("Unable to determine the number of hardware breakpoints"
291 " available."));
293 }
294}
int aarch64_num_bp_regs
int aarch64_num_wp_regs
#define AARCH64_HBP_MAX_NUM
#define AARCH64_DEBUG_ARCH_V8_1
#define AARCH64_DEBUG_ARCH_V8_4
#define AARCH64_DEBUG_ARCH_V8
#define AARCH64_HWP_MAX_NUM
#define AARCH64_HWP_ALIGNMENT
#define AARCH64_DEBUG_ARCH_V8_9
#define AARCH64_DEBUG_ARCH_V8_2
#define AARCH64_DEBUG_ARCH_V8_8
void aarch64_linux_get_debug_reg_capacity(int tid)
static bool compatible_debug_arch(unsigned int debug_arch)
static void aarch64_downgrade_regs(struct aarch64_debug_reg_state *state)
void aarch64_linux_set_debug_regs(struct aarch64_debug_reg_state *state, int tid, int watchpoint)
static int debug_reg_change_callback(struct lwp_info *lwp, int is_watchpoint, unsigned int idx)
void aarch64_notify_debug_reg_change(ptid_t ptid, int is_watchpoint, unsigned int idx)
bool kernel_supports_any_contiguous_range
#define AARCH64_DEBUG_ARCH(x)
ULONGEST dr_changed_t
#define AARCH64_DEBUG_NUM_SLOTS(x)
#define DR_MARK_N_CHANGED(x, n)
bool is_watchpoint(const struct breakpoint *bpt)
#define ptrace(request, pid, addr, data)
Definition gdb_ptrace.h:141
ptid_t ptid_of_lwp(struct lwp_info *lwp)
Definition linux-nat.c:277
void linux_stop_lwp(struct lwp_info *lwp)
Definition linux-nat.c:2204
ptid_t current_lwp_ptid(void)
Definition linux-nat.c:4488
void lwp_set_arch_private_info(struct lwp_info *lwp, struct arch_lwp_info *info)
Definition linux-nat.c:285
struct arch_lwp_info * lwp_arch_private_info(struct lwp_info *lwp)
Definition linux-nat.c:294
struct lwp_info * iterate_over_lwps(ptid_t filter, gdb::function_view< iterate_over_lwps_ftype > callback)
Definition linux-nat.c:860
int lwp_is_stopped(struct lwp_info *lwp)
Definition linux-nat.c:302
#define PTRACE_SETREGSET
#define PTRACE_GETREGSET
unsigned int dr_ref_count_wp[AARCH64_HWP_MAX_NUM]
CORE_ADDR dr_addr_bp[AARCH64_HBP_MAX_NUM]
unsigned int dr_ctrl_wp[AARCH64_HWP_MAX_NUM]
CORE_ADDR dr_addr_wp[AARCH64_HWP_MAX_NUM]
unsigned int dr_ctrl_bp[AARCH64_HBP_MAX_NUM]
CORE_ADDR dr_addr_orig_wp[AARCH64_HWP_MAX_NUM]