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