1//===-- NativeRegisterContextDBReg_arm64.cpp ------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "NativeRegisterContextDBReg_arm64.h"
10
11#include "lldb/Utility/LLDBLog.h"
12#include "lldb/Utility/Log.h"
13#include "lldb/Utility/RegisterValue.h"
14
15using namespace lldb_private;
16
17// E (bit 0), used to enable breakpoint/watchpoint
18constexpr uint32_t g_enable_bit = 1;
19// PAC (bits 2:1): 0b10
20constexpr uint32_t g_pac_bits = (2 << 1);
21
22// Returns appropriate control register bits for the specified size
23static constexpr inline uint64_t GetSizeBits(int size) {
24 // BAS (bits 12:5) hold a bit-mask of addresses to watch
25 // e.g. 0b00000001 means 1 byte at address
26 // 0b00000011 means 2 bytes (addr..addr+1)
27 // ...
28 // 0b11111111 means 8 bytes (addr..addr+7)
29 return ((1 << size) - 1) << 5;
30}
31
32uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareBreakpoints() {
33 Log *log = GetLog(mask: LLDBLog::Breakpoints);
34 llvm::Error error = ReadHardwareDebugInfo();
35 if (error) {
36 LLDB_LOG_ERROR(log, std::move(error),
37 "failed to read debug registers: {0}");
38 return 0;
39 }
40
41 return m_max_hbp_supported;
42}
43
44uint32_t
45NativeRegisterContextDBReg_arm64::SetHardwareBreakpoint(lldb::addr_t addr,
46 size_t size) {
47 Log *log = GetLog(mask: LLDBLog::Breakpoints);
48 LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size);
49
50 // Read hardware breakpoint and watchpoint information.
51 llvm::Error error = ReadHardwareDebugInfo();
52 if (error) {
53 LLDB_LOG_ERROR(
54 log, std::move(error),
55 "unable to set breakpoint: failed to read debug registers: {0}");
56 return LLDB_INVALID_INDEX32;
57 }
58
59 uint32_t control_value = 0, bp_index = 0;
60
61 // Check if size has a valid hardware breakpoint length.
62 if (size != 4)
63 return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware
64 // breakpoint
65
66 // Check 4-byte alignment for hardware breakpoint target address.
67 if (addr & 0x03)
68 return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned.
69
70 // Setup control value
71 control_value = g_enable_bit | g_pac_bits | GetSizeBits(size);
72
73 // Iterate over stored breakpoints and find a free bp_index
74 bp_index = LLDB_INVALID_INDEX32;
75 for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
76 if (!BreakpointIsEnabled(bp_index: i))
77 bp_index = i; // Mark last free slot
78 else if (m_hbp_regs[i].address == addr)
79 return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints.
80 }
81
82 if (bp_index == LLDB_INVALID_INDEX32)
83 return LLDB_INVALID_INDEX32;
84
85 // Update breakpoint in local cache
86 m_hbp_regs[bp_index].real_addr = addr;
87 m_hbp_regs[bp_index].address = addr;
88 m_hbp_regs[bp_index].control = control_value;
89
90 // PTRACE call to set corresponding hardware breakpoint register.
91 error = WriteHardwareDebugRegs(hwbType: eDREGTypeBREAK);
92
93 if (error) {
94 m_hbp_regs[bp_index].address = 0;
95 m_hbp_regs[bp_index].control &= ~1;
96
97 LLDB_LOG_ERROR(
98 log, std::move(error),
99 "unable to set breakpoint: failed to write debug registers: {0}");
100 return LLDB_INVALID_INDEX32;
101 }
102
103 return bp_index;
104}
105
106bool NativeRegisterContextDBReg_arm64::ClearHardwareBreakpoint(
107 uint32_t hw_idx) {
108 Log *log = GetLog(mask: LLDBLog::Breakpoints);
109 LLDB_LOG(log, "hw_idx: {0}", hw_idx);
110
111 // Read hardware breakpoint and watchpoint information.
112 llvm::Error error = ReadHardwareDebugInfo();
113 if (error) {
114 LLDB_LOG_ERROR(
115 log, std::move(error),
116 "unable to clear breakpoint: failed to read debug registers: {0}");
117 return false;
118 }
119
120 if (hw_idx >= m_max_hbp_supported)
121 return false;
122
123 // Create a backup we can revert to in case of failure.
124 lldb::addr_t tempAddr = m_hbp_regs[hw_idx].address;
125 uint32_t tempControl = m_hbp_regs[hw_idx].control;
126
127 m_hbp_regs[hw_idx].control &= ~g_enable_bit;
128 m_hbp_regs[hw_idx].address = 0;
129
130 // PTRACE call to clear corresponding hardware breakpoint register.
131 error = WriteHardwareDebugRegs(hwbType: eDREGTypeBREAK);
132
133 if (error) {
134 m_hbp_regs[hw_idx].control = tempControl;
135 m_hbp_regs[hw_idx].address = tempAddr;
136
137 LLDB_LOG_ERROR(
138 log, std::move(error),
139 "unable to clear breakpoint: failed to write debug registers: {0}");
140 return false;
141 }
142
143 return true;
144}
145
146Status NativeRegisterContextDBReg_arm64::GetHardwareBreakHitIndex(
147 uint32_t &bp_index, lldb::addr_t trap_addr) {
148 Log *log = GetLog(mask: LLDBLog::Breakpoints);
149
150 LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__);
151
152 lldb::addr_t break_addr;
153
154 for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) {
155 break_addr = m_hbp_regs[bp_index].address;
156
157 if (BreakpointIsEnabled(bp_index) && trap_addr == break_addr) {
158 m_hbp_regs[bp_index].hit_addr = trap_addr;
159 return Status();
160 }
161 }
162
163 bp_index = LLDB_INVALID_INDEX32;
164 return Status();
165}
166
167Status NativeRegisterContextDBReg_arm64::ClearAllHardwareBreakpoints() {
168 Log *log = GetLog(mask: LLDBLog::Breakpoints);
169
170 LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__);
171
172 // Read hardware breakpoint and watchpoint information.
173 llvm::Error error = ReadHardwareDebugInfo();
174 if (error)
175 return Status(std::move(error));
176
177 for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
178 if (BreakpointIsEnabled(bp_index: i)) {
179 // Create a backup we can revert to in case of failure.
180 lldb::addr_t tempAddr = m_hbp_regs[i].address;
181 uint32_t tempControl = m_hbp_regs[i].control;
182
183 // Clear watchpoints in local cache
184 m_hbp_regs[i].control &= ~g_enable_bit;
185 m_hbp_regs[i].address = 0;
186
187 // Ptrace call to update hardware debug registers
188 error = WriteHardwareDebugRegs(hwbType: eDREGTypeBREAK);
189
190 if (error) {
191 m_hbp_regs[i].control = tempControl;
192 m_hbp_regs[i].address = tempAddr;
193
194 return Status(std::move(error));
195 }
196 }
197 }
198
199 return Status();
200}
201
202bool NativeRegisterContextDBReg_arm64::BreakpointIsEnabled(uint32_t bp_index) {
203 if ((m_hbp_regs[bp_index].control & g_enable_bit) != 0)
204 return true;
205 else
206 return false;
207}
208
209uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareWatchpoints() {
210 Log *log = GetLog(mask: LLDBLog::Watchpoints);
211 llvm::Error error = ReadHardwareDebugInfo();
212 if (error) {
213 LLDB_LOG_ERROR(log, std::move(error),
214 "failed to read debug registers: {0}");
215 return 0;
216 }
217
218 return m_max_hwp_supported;
219}
220
221uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint(
222 lldb::addr_t addr, size_t size, uint32_t watch_flags) {
223 Log *log = GetLog(mask: LLDBLog::Watchpoints);
224 LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size,
225 watch_flags);
226
227 // Read hardware breakpoint and watchpoint information.
228 llvm::Error error = ReadHardwareDebugInfo();
229 if (error) {
230 LLDB_LOG_ERROR(
231 log, std::move(error),
232 "unable to set watchpoint: failed to read debug registers: {0}");
233 return LLDB_INVALID_INDEX32;
234 }
235
236 uint32_t control_value = 0, wp_index = 0;
237 lldb::addr_t real_addr = addr;
238
239 // Check if we are setting watchpoint other than read/write/access Also
240 // update watchpoint flag to match AArch64 write-read bit configuration.
241 switch (watch_flags) {
242 case 1:
243 watch_flags = 2;
244 break;
245 case 2:
246 watch_flags = 1;
247 break;
248 case 3:
249 break;
250 default:
251 return LLDB_INVALID_INDEX32;
252 }
253
254 // Check if size has a valid hardware watchpoint length.
255 if (size != 1 && size != 2 && size != 4 && size != 8)
256 return LLDB_INVALID_INDEX32;
257
258 // Check 8-byte alignment for hardware watchpoint target address. Below is a
259 // hack to recalculate address and size in order to make sure we can watch
260 // non 8-byte aligned addresses as well.
261 if (addr & 0x07) {
262 uint8_t watch_mask = (addr & 0x07) + size;
263
264 if (watch_mask > 0x08)
265 return LLDB_INVALID_INDEX32;
266 else if (watch_mask <= 0x02)
267 size = 2;
268 else if (watch_mask <= 0x04)
269 size = 4;
270 else
271 size = 8;
272
273 addr = addr & (~0x07);
274 }
275
276 // Setup control value
277 control_value = g_enable_bit | g_pac_bits | GetSizeBits(size);
278 control_value |= watch_flags << 3;
279
280 // Iterate over stored watchpoints and find a free wp_index
281 wp_index = LLDB_INVALID_INDEX32;
282 for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
283 if (!WatchpointIsEnabled(wp_index: i))
284 wp_index = i; // Mark last free slot
285 else if (m_hwp_regs[i].address == addr) {
286 return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints.
287 }
288 }
289
290 if (wp_index == LLDB_INVALID_INDEX32)
291 return LLDB_INVALID_INDEX32;
292
293 // Update watchpoint in local cache
294 m_hwp_regs[wp_index].real_addr = real_addr;
295 m_hwp_regs[wp_index].address = addr;
296 m_hwp_regs[wp_index].control = control_value;
297
298 // PTRACE call to set corresponding watchpoint register.
299 error = WriteHardwareDebugRegs(hwbType: eDREGTypeWATCH);
300
301 if (error) {
302 m_hwp_regs[wp_index].address = 0;
303 m_hwp_regs[wp_index].control &= ~g_enable_bit;
304
305 LLDB_LOG_ERROR(
306 log, std::move(error),
307 "unable to set watchpoint: failed to write debug registers: {0}");
308 return LLDB_INVALID_INDEX32;
309 }
310
311 return wp_index;
312}
313
314bool NativeRegisterContextDBReg_arm64::ClearHardwareWatchpoint(
315 uint32_t wp_index) {
316 Log *log = GetLog(mask: LLDBLog::Watchpoints);
317 LLDB_LOG(log, "wp_index: {0}", wp_index);
318
319 // Read hardware breakpoint and watchpoint information.
320 llvm::Error error = ReadHardwareDebugInfo();
321 if (error) {
322 LLDB_LOG_ERROR(
323 log, std::move(error),
324 "unable to clear watchpoint: failed to read debug registers: {0}");
325 return false;
326 }
327
328 if (wp_index >= m_max_hwp_supported)
329 return false;
330
331 // Create a backup we can revert to in case of failure.
332 lldb::addr_t tempAddr = m_hwp_regs[wp_index].address;
333 uint32_t tempControl = m_hwp_regs[wp_index].control;
334
335 // Update watchpoint in local cache
336 m_hwp_regs[wp_index].control &= ~g_enable_bit;
337 m_hwp_regs[wp_index].address = 0;
338
339 // Ptrace call to update hardware debug registers
340 error = WriteHardwareDebugRegs(hwbType: eDREGTypeWATCH);
341
342 if (error) {
343 m_hwp_regs[wp_index].control = tempControl;
344 m_hwp_regs[wp_index].address = tempAddr;
345
346 LLDB_LOG_ERROR(
347 log, std::move(error),
348 "unable to clear watchpoint: failed to write debug registers: {0}");
349 return false;
350 }
351
352 return true;
353}
354
355Status NativeRegisterContextDBReg_arm64::ClearAllHardwareWatchpoints() {
356 // Read hardware breakpoint and watchpoint information.
357 llvm::Error error = ReadHardwareDebugInfo();
358 if (error)
359 return Status(std::move(error));
360
361 for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
362 if (WatchpointIsEnabled(wp_index: i)) {
363 // Create a backup we can revert to in case of failure.
364 lldb::addr_t tempAddr = m_hwp_regs[i].address;
365 uint32_t tempControl = m_hwp_regs[i].control;
366
367 // Clear watchpoints in local cache
368 m_hwp_regs[i].control &= ~g_enable_bit;
369 m_hwp_regs[i].address = 0;
370
371 // Ptrace call to update hardware debug registers
372 error = WriteHardwareDebugRegs(hwbType: eDREGTypeWATCH);
373
374 if (error) {
375 m_hwp_regs[i].control = tempControl;
376 m_hwp_regs[i].address = tempAddr;
377
378 return Status(std::move(error));
379 }
380 }
381 }
382
383 return Status();
384}
385
386uint32_t
387NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) {
388 Log *log = GetLog(mask: LLDBLog::Watchpoints);
389 LLDB_LOG(log, "wp_index: {0}", wp_index);
390
391 switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) {
392 case 0x01:
393 return 1;
394 case 0x03:
395 return 2;
396 case 0x0f:
397 return 4;
398 case 0xff:
399 return 8;
400 default:
401 return 0;
402 }
403}
404
405bool NativeRegisterContextDBReg_arm64::WatchpointIsEnabled(uint32_t wp_index) {
406 Log *log = GetLog(mask: LLDBLog::Watchpoints);
407 LLDB_LOG(log, "wp_index: {0}", wp_index);
408
409 if ((m_hwp_regs[wp_index].control & g_enable_bit) != 0)
410 return true;
411 else
412 return false;
413}
414
415Status NativeRegisterContextDBReg_arm64::GetWatchpointHitIndex(
416 uint32_t &wp_index, lldb::addr_t trap_addr) {
417 Log *log = GetLog(mask: LLDBLog::Watchpoints);
418 LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr);
419
420 // Read hardware breakpoint and watchpoint information.
421 llvm::Error error = ReadHardwareDebugInfo();
422 if (error)
423 return Status(std::move(error));
424
425 // Mask off ignored bits from watchpoint trap address.
426 trap_addr = FixWatchpointHitAddress(hit_addr: trap_addr);
427
428 uint32_t watch_size;
429 lldb::addr_t watch_addr;
430
431 for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) {
432 watch_size = GetWatchpointSize(wp_index);
433 watch_addr = m_hwp_regs[wp_index].address;
434
435 if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr &&
436 trap_addr < watch_addr + watch_size) {
437 m_hwp_regs[wp_index].hit_addr = trap_addr;
438 return Status();
439 }
440 }
441
442 wp_index = LLDB_INVALID_INDEX32;
443 return Status();
444}
445
446lldb::addr_t
447NativeRegisterContextDBReg_arm64::GetWatchpointAddress(uint32_t wp_index) {
448 Log *log = GetLog(mask: LLDBLog::Watchpoints);
449 LLDB_LOG(log, "wp_index: {0}", wp_index);
450
451 if (wp_index >= m_max_hwp_supported)
452 return LLDB_INVALID_ADDRESS;
453
454 if (WatchpointIsEnabled(wp_index))
455 return m_hwp_regs[wp_index].real_addr;
456 return LLDB_INVALID_ADDRESS;
457}
458
459lldb::addr_t
460NativeRegisterContextDBReg_arm64::GetWatchpointHitAddress(uint32_t wp_index) {
461 Log *log = GetLog(mask: LLDBLog::Watchpoints);
462 LLDB_LOG(log, "wp_index: {0}", wp_index);
463
464 if (wp_index >= m_max_hwp_supported)
465 return LLDB_INVALID_ADDRESS;
466
467 if (WatchpointIsEnabled(wp_index))
468 return m_hwp_regs[wp_index].hit_addr;
469 return LLDB_INVALID_ADDRESS;
470}
471

source code of lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp