Add support for hardware breakpoints/watchpoints on FreeBSD/Aarch64.
[binutils-gdb.git] / gdb / aarch64-fbsd-nat.c
1 /* Native-dependent code for FreeBSD/aarch64.
2
3 Copyright (C) 2017-2022 Free Software Foundation, Inc.
4
5 This file is part of GDB.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include "defs.h"
21 #include "arch-utils.h"
22 #include "inferior.h"
23 #include "regcache.h"
24 #include "target.h"
25 #include "nat/aarch64-hw-point.h"
26
27 #include <sys/param.h>
28 #include <sys/ptrace.h>
29 #include <machine/armreg.h>
30 #include <machine/reg.h>
31
32 #include "fbsd-nat.h"
33 #include "aarch64-fbsd-tdep.h"
34 #include "aarch64-nat.h"
35 #include "inf-ptrace.h"
36
37 #if __FreeBSD_version >= 1400005
38 #define HAVE_DBREG
39
40 #include <unordered_set>
41 #endif
42
43 #ifdef HAVE_DBREG
44 struct aarch64_fbsd_nat_target final
45 : public aarch64_nat_target<fbsd_nat_target>
46 #else
47 struct aarch64_fbsd_nat_target final : public fbsd_nat_target
48 #endif
49 {
50 void fetch_registers (struct regcache *, int) override;
51 void store_registers (struct regcache *, int) override;
52
53 #ifdef HAVE_DBREG
54 /* Hardware breakpoints and watchpoints. */
55 bool stopped_by_watchpoint () override;
56 bool stopped_data_address (CORE_ADDR *) override;
57 bool stopped_by_hw_breakpoint () override;
58 bool supports_stopped_by_hw_breakpoint () override;
59
60 void post_startup_inferior (ptid_t) override;
61 void post_attach (int pid) override;
62
63 void low_new_fork (ptid_t parent, pid_t child) override;
64 void low_delete_thread (thread_info *) override;
65 void low_prepare_to_resume (thread_info *) override;
66
67 private:
68 void probe_debug_regs (int pid);
69 static bool debug_regs_probed;
70 #endif
71 };
72
73 static aarch64_fbsd_nat_target the_aarch64_fbsd_nat_target;
74 bool aarch64_fbsd_nat_target::debug_regs_probed;
75
76 /* Fetch register REGNUM from the inferior. If REGNUM is -1, do this
77 for all registers. */
78
79 void
80 aarch64_fbsd_nat_target::fetch_registers (struct regcache *regcache,
81 int regnum)
82 {
83 fetch_register_set<struct reg> (regcache, regnum, PT_GETREGS,
84 &aarch64_fbsd_gregset);
85 fetch_register_set<struct fpreg> (regcache, regnum, PT_GETFPREGS,
86 &aarch64_fbsd_fpregset);
87 }
88
89 /* Store register REGNUM back into the inferior. If REGNUM is -1, do
90 this for all registers. */
91
92 void
93 aarch64_fbsd_nat_target::store_registers (struct regcache *regcache,
94 int regnum)
95 {
96 store_register_set<struct reg> (regcache, regnum, PT_GETREGS, PT_SETREGS,
97 &aarch64_fbsd_gregset);
98 store_register_set<struct fpreg> (regcache, regnum, PT_GETFPREGS,
99 PT_SETFPREGS, &aarch64_fbsd_fpregset);
100 }
101
102 #ifdef HAVE_DBREG
103 /* Set of threads which need to update debug registers on next resume. */
104
105 static std::unordered_set<lwpid_t> aarch64_debug_pending_threads;
106
107 /* Implement the "stopped_data_address" target_ops method. */
108
109 bool
110 aarch64_fbsd_nat_target::stopped_data_address (CORE_ADDR *addr_p)
111 {
112 siginfo_t siginfo;
113 struct aarch64_debug_reg_state *state;
114
115 if (!fbsd_nat_get_siginfo (inferior_ptid, &siginfo))
116 return false;
117
118 /* This must be a hardware breakpoint. */
119 if (siginfo.si_signo != SIGTRAP
120 || siginfo.si_code != TRAP_TRACE
121 || siginfo.si_trapno != EXCP_WATCHPT_EL0)
122 return false;
123
124 const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
125
126 /* Check if the address matches any watched address. */
127 state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
128 return aarch64_stopped_data_address (state, addr_trap, addr_p);
129 }
130
131 /* Implement the "stopped_by_watchpoint" target_ops method. */
132
133 bool
134 aarch64_fbsd_nat_target::stopped_by_watchpoint ()
135 {
136 CORE_ADDR addr;
137
138 return stopped_data_address (&addr);
139 }
140
141 /* Implement the "stopped_by_hw_breakpoint" target_ops method. */
142
143 bool
144 aarch64_fbsd_nat_target::stopped_by_hw_breakpoint ()
145 {
146 siginfo_t siginfo;
147 struct aarch64_debug_reg_state *state;
148
149 if (!fbsd_nat_get_siginfo (inferior_ptid, &siginfo))
150 return false;
151
152 /* This must be a hardware breakpoint. */
153 if (siginfo.si_signo != SIGTRAP
154 || siginfo.si_code != TRAP_TRACE
155 || siginfo.si_trapno != EXCP_WATCHPT_EL0)
156 return false;
157
158 return !stopped_by_watchpoint();
159 }
160
161 /* Implement the "supports_stopped_by_hw_breakpoint" target_ops method. */
162
163 bool
164 aarch64_fbsd_nat_target::supports_stopped_by_hw_breakpoint ()
165 {
166 return true;
167 }
168
169 /* Fetch the hardware debug register capability information. */
170
171 void
172 aarch64_fbsd_nat_target::probe_debug_regs (int pid)
173 {
174 if (!debug_regs_probed)
175 {
176 struct dbreg reg;
177
178 debug_regs_probed = true;
179 aarch64_num_bp_regs = 0;
180 aarch64_num_wp_regs = 0;
181
182 if (ptrace(PT_GETDBREGS, pid, (PTRACE_TYPE_ARG3) &reg, 0) == 0)
183 {
184 switch (reg.db_debug_ver)
185 {
186 case AARCH64_DEBUG_ARCH_V8:
187 case AARCH64_DEBUG_ARCH_V8_1:
188 case AARCH64_DEBUG_ARCH_V8_2:
189 case AARCH64_DEBUG_ARCH_V8_4:
190 break;
191 default:
192 return;
193 }
194
195 aarch64_num_bp_regs = reg.db_nbkpts;
196 if (aarch64_num_bp_regs > AARCH64_HBP_MAX_NUM)
197 {
198 warning (_("Unexpected number of hardware breakpoint registers"
199 " reported by ptrace, got %d, expected %d."),
200 aarch64_num_bp_regs, AARCH64_HBP_MAX_NUM);
201 aarch64_num_bp_regs = AARCH64_HBP_MAX_NUM;
202 }
203 aarch64_num_wp_regs = reg.db_nwtpts;
204 if (aarch64_num_wp_regs > AARCH64_HWP_MAX_NUM)
205 {
206 warning (_("Unexpected number of hardware watchpoint registers"
207 " reported by ptrace, got %d, expected %d."),
208 aarch64_num_wp_regs, AARCH64_HWP_MAX_NUM);
209 aarch64_num_wp_regs = AARCH64_HWP_MAX_NUM;
210 }
211 }
212 }
213 }
214
215 /* Implement the virtual inf_ptrace_target::post_startup_inferior method. */
216
217 void
218 aarch64_fbsd_nat_target::post_startup_inferior (ptid_t ptid)
219 {
220 aarch64_remove_debug_reg_state (ptid.pid ());
221 probe_debug_regs (ptid.pid ());
222 fbsd_nat_target::post_startup_inferior (ptid);
223 }
224
225 /* Implement the "post_attach" target_ops method. */
226
227 void
228 aarch64_fbsd_nat_target::post_attach (int pid)
229 {
230 aarch64_remove_debug_reg_state (pid);
231 probe_debug_regs (pid);
232 fbsd_nat_target::post_attach (pid);
233 }
234
235 /* Implement the virtual fbsd_nat_target::low_new_fork method. */
236
237 void
238 aarch64_fbsd_nat_target::low_new_fork (ptid_t parent, pid_t child)
239 {
240 struct aarch64_debug_reg_state *parent_state, *child_state;
241
242 /* If there is no parent state, no watchpoints nor breakpoints have
243 been set, so there is nothing to do. */
244 parent_state = aarch64_lookup_debug_reg_state (parent.pid ());
245 if (parent_state == nullptr)
246 return;
247
248 /* The kernel clears debug registers in the new child process after
249 fork, but GDB core assumes the child inherits the watchpoints/hw
250 breakpoints of the parent, and will remove them all from the
251 forked off process. Copy the debug registers mirrors into the
252 new process so that all breakpoints and watchpoints can be
253 removed together. */
254
255 child_state = aarch64_get_debug_reg_state (child);
256 *child_state = *parent_state;
257 }
258
259 /* Mark debug register state "dirty" for all threads belonging to the
260 current inferior. */
261
262 void
263 aarch64_notify_debug_reg_change (ptid_t ptid,
264 int is_watchpoint, unsigned int idx)
265 {
266 for (thread_info *tp : current_inferior ()->non_exited_threads ())
267 {
268 if (tp->ptid.lwp_p ())
269 aarch64_debug_pending_threads.emplace (tp->ptid.lwp ());
270 }
271 }
272
273 /* Implement the virtual fbsd_nat_target::low_delete_thread method. */
274
275 void
276 aarch64_fbsd_nat_target::low_delete_thread (thread_info *tp)
277 {
278 gdb_assert(tp->ptid.lwp_p ());
279 aarch64_debug_pending_threads.erase (tp->ptid.lwp ());
280 }
281
282 /* Implement the virtual fbsd_nat_target::low_prepare_to_resume method. */
283
284 void
285 aarch64_fbsd_nat_target::low_prepare_to_resume (thread_info *tp)
286 {
287 gdb_assert(tp->ptid.lwp_p ());
288
289 if (aarch64_debug_pending_threads.erase (tp->ptid.lwp ()) == 0)
290 return;
291
292 struct aarch64_debug_reg_state *state =
293 aarch64_lookup_debug_reg_state (tp->ptid.pid ());
294 gdb_assert(state != nullptr);
295
296 struct dbreg reg;
297 memset (&reg, 0, sizeof(reg));
298 for (int i = 0; i < aarch64_num_bp_regs; i++)
299 {
300 reg.db_breakregs[i].dbr_addr = state->dr_addr_bp[i];
301 reg.db_breakregs[i].dbr_ctrl = state->dr_ctrl_bp[i];
302 }
303 for (int i = 0; i < aarch64_num_wp_regs; i++)
304 {
305 reg.db_watchregs[i].dbw_addr = state->dr_addr_wp[i];
306 reg.db_watchregs[i].dbw_ctrl = state->dr_ctrl_wp[i];
307 }
308 if (ptrace(PT_SETDBREGS, tp->ptid.lwp (), (PTRACE_TYPE_ARG3) &reg, 0) != 0)
309 error (_("Failed to set hardware debug registers"));
310 }
311 #else
312 /* A stub that should never be called. */
313 void
314 aarch64_notify_debug_reg_change (ptid_t ptid,
315 int is_watchpoint, unsigned int idx)
316 {
317 gdb_assert (true);
318 }
319 #endif
320
321 void _initialize_aarch64_fbsd_nat ();
322 void
323 _initialize_aarch64_fbsd_nat ()
324 {
325 #ifdef HAVE_DBREG
326 aarch64_initialize_hw_point ();
327 #endif
328 add_inf_child_target (&the_aarch64_fbsd_nat_target);
329 }