Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
96380 views
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
15
using namespace lldb_private;
16
17
// E (bit 0), used to enable breakpoint/watchpoint
18
constexpr uint32_t g_enable_bit = 1;
19
// PAC (bits 2:1): 0b10
20
constexpr uint32_t g_pac_bits = (2 << 1);
21
22
// Returns appropriate control register bits for the specified size
23
static 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
32
uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareBreakpoints() {
33
Log *log = GetLog(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
44
uint32_t
45
NativeRegisterContextDBReg_arm64::SetHardwareBreakpoint(lldb::addr_t addr,
46
size_t size) {
47
Log *log = GetLog(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(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(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
106
bool NativeRegisterContextDBReg_arm64::ClearHardwareBreakpoint(
107
uint32_t hw_idx) {
108
Log *log = GetLog(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(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
146
Status NativeRegisterContextDBReg_arm64::GetHardwareBreakHitIndex(
147
uint32_t &bp_index, lldb::addr_t trap_addr) {
148
Log *log = GetLog(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
167
Status NativeRegisterContextDBReg_arm64::ClearAllHardwareBreakpoints() {
168
Log *log = GetLog(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(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(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
202
bool 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
209
uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareWatchpoints() {
210
Log *log = GetLog(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
221
uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint(
222
lldb::addr_t addr, size_t size, uint32_t watch_flags) {
223
Log *log = GetLog(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(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(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
314
bool NativeRegisterContextDBReg_arm64::ClearHardwareWatchpoint(
315
uint32_t wp_index) {
316
Log *log = GetLog(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(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
355
Status 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(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(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
386
uint32_t
387
NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) {
388
Log *log = GetLog(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
405
bool NativeRegisterContextDBReg_arm64::WatchpointIsEnabled(uint32_t wp_index) {
406
Log *log = GetLog(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
415
Status NativeRegisterContextDBReg_arm64::GetWatchpointHitIndex(
416
uint32_t &wp_index, lldb::addr_t trap_addr) {
417
Log *log = GetLog(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(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
446
lldb::addr_t
447
NativeRegisterContextDBReg_arm64::GetWatchpointAddress(uint32_t wp_index) {
448
Log *log = GetLog(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
459
lldb::addr_t
460
NativeRegisterContextDBReg_arm64::GetWatchpointHitAddress(uint32_t wp_index) {
461
Log *log = GetLog(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
472