1 //===-- NativeRegisterContextWindows_i386.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 #if defined(__i386__) || defined(_M_IX86) 10 11 #include "NativeRegisterContextWindows_i386.h" 12 13 #include "NativeThreadWindows.h" 14 #include "Plugins/Process/Utility/RegisterContextWindows_i386.h" 15 #include "ProcessWindowsLog.h" 16 #include "lldb/Host/HostInfo.h" 17 #include "lldb/Host/HostThread.h" 18 #include "lldb/Host/windows/HostThreadWindows.h" 19 #include "lldb/Host/windows/windows.h" 20 21 #include "lldb/Utility/Log.h" 22 #include "lldb/Utility/RegisterValue.h" 23 #include "llvm/ADT/STLExtras.h" 24 25 using namespace lldb; 26 using namespace lldb_private; 27 28 #define REG_CONTEXT_SIZE sizeof(::CONTEXT) 29 30 namespace { 31 static const uint32_t g_gpr_regnums_i386[] = { 32 lldb_eax_i386, lldb_ebx_i386, lldb_ecx_i386, lldb_edx_i386, 33 lldb_edi_i386, lldb_esi_i386, lldb_ebp_i386, lldb_esp_i386, 34 lldb_eip_i386, lldb_eflags_i386, lldb_cs_i386, lldb_fs_i386, 35 lldb_gs_i386, lldb_ss_i386, lldb_ds_i386, lldb_es_i386, 36 LLDB_INVALID_REGNUM // Register sets must be terminated with this flag. 37 }; 38 39 static const RegisterSet g_reg_sets_i386[] = { 40 {"General Purpose Registers", "gpr", 41 llvm::array_lengthof(g_gpr_regnums_i386) - 1, g_gpr_regnums_i386}, 42 }; 43 44 enum { k_num_register_sets = 1 }; 45 46 } // namespace 47 48 static RegisterInfoInterface * 49 CreateRegisterInfoInterface(const ArchSpec &target_arch) { 50 assert((HostInfo::GetArchitecture().GetAddressByteSize() == 4) && 51 "Register setting path assumes this is a 32-bit host"); 52 return new RegisterContextWindows_i386(target_arch); 53 } 54 55 static Status GetThreadContextHelper(lldb::thread_t thread_handle, 56 PCONTEXT context_ptr, 57 const DWORD control_flag) { 58 Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); 59 Status error; 60 61 memset(context_ptr, 0, sizeof(::CONTEXT)); 62 context_ptr->ContextFlags = control_flag; 63 if (!::GetThreadContext(thread_handle, context_ptr)) { 64 error.SetError(GetLastError(), eErrorTypeWin32); 65 LLDB_LOG(log, "{0} GetThreadContext failed with error {1}", __FUNCTION__, 66 error); 67 return error; 68 } 69 return Status(); 70 } 71 72 static Status SetThreadContextHelper(lldb::thread_t thread_handle, 73 PCONTEXT context_ptr) { 74 Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); 75 Status error; 76 77 if (!::SetThreadContext(thread_handle, context_ptr)) { 78 error.SetError(GetLastError(), eErrorTypeWin32); 79 LLDB_LOG(log, "{0} SetThreadContext failed with error {1}", __FUNCTION__, 80 error); 81 return error; 82 } 83 return Status(); 84 } 85 86 std::unique_ptr<NativeRegisterContextWindows> 87 NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows( 88 const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { 89 return std::make_unique<NativeRegisterContextWindows_i386>(target_arch, 90 native_thread); 91 } 92 93 NativeRegisterContextWindows_i386::NativeRegisterContextWindows_i386( 94 const ArchSpec &target_arch, NativeThreadProtocol &native_thread) 95 : NativeRegisterContextWindows(native_thread, 96 CreateRegisterInfoInterface(target_arch)) {} 97 98 bool NativeRegisterContextWindows_i386::IsGPR(uint32_t reg_index) const { 99 return (reg_index < k_first_alias_i386); 100 } 101 102 bool NativeRegisterContextWindows_i386::IsDR(uint32_t reg_index) const { 103 return (reg_index >= lldb_dr0_i386 && reg_index <= lldb_dr7_i386); 104 } 105 106 uint32_t NativeRegisterContextWindows_i386::GetRegisterSetCount() const { 107 return k_num_register_sets; 108 } 109 110 const RegisterSet * 111 NativeRegisterContextWindows_i386::GetRegisterSet(uint32_t set_index) const { 112 if (set_index >= k_num_register_sets) 113 return nullptr; 114 return &g_reg_sets_i386[set_index]; 115 } 116 117 Status NativeRegisterContextWindows_i386::GPRRead(const uint32_t reg, 118 RegisterValue ®_value) { 119 ::CONTEXT tls_context; 120 DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; 121 Status error = 122 GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag); 123 if (error.Fail()) 124 return error; 125 126 switch (reg) { 127 case lldb_eax_i386: 128 reg_value.SetUInt32(tls_context.Eax); 129 break; 130 case lldb_ebx_i386: 131 reg_value.SetUInt32(tls_context.Ebx); 132 break; 133 case lldb_ecx_i386: 134 reg_value.SetUInt32(tls_context.Ecx); 135 break; 136 case lldb_edx_i386: 137 reg_value.SetUInt32(tls_context.Edx); 138 break; 139 case lldb_edi_i386: 140 reg_value.SetUInt32(tls_context.Edi); 141 break; 142 case lldb_esi_i386: 143 reg_value.SetUInt32(tls_context.Esi); 144 break; 145 case lldb_ebp_i386: 146 reg_value.SetUInt32(tls_context.Ebp); 147 break; 148 case lldb_esp_i386: 149 reg_value.SetUInt32(tls_context.Esp); 150 break; 151 case lldb_eip_i386: 152 reg_value.SetUInt32(tls_context.Eip); 153 break; 154 case lldb_eflags_i386: 155 reg_value.SetUInt32(tls_context.EFlags); 156 break; 157 case lldb_cs_i386: 158 reg_value.SetUInt32(tls_context.SegCs); 159 break; 160 case lldb_fs_i386: 161 reg_value.SetUInt32(tls_context.SegFs); 162 break; 163 case lldb_gs_i386: 164 reg_value.SetUInt32(tls_context.SegGs); 165 break; 166 case lldb_ss_i386: 167 reg_value.SetUInt32(tls_context.SegSs); 168 break; 169 case lldb_ds_i386: 170 reg_value.SetUInt32(tls_context.SegDs); 171 break; 172 case lldb_es_i386: 173 reg_value.SetUInt32(tls_context.SegEs); 174 break; 175 } 176 177 return error; 178 } 179 180 Status 181 NativeRegisterContextWindows_i386::GPRWrite(const uint32_t reg, 182 const RegisterValue ®_value) { 183 ::CONTEXT tls_context; 184 DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; 185 auto thread_handle = GetThreadHandle(); 186 Status error = 187 GetThreadContextHelper(thread_handle, &tls_context, context_flag); 188 if (error.Fail()) 189 return error; 190 191 switch (reg) { 192 case lldb_eax_i386: 193 tls_context.Eax = reg_value.GetAsUInt32(); 194 break; 195 case lldb_ebx_i386: 196 tls_context.Ebx = reg_value.GetAsUInt32(); 197 break; 198 case lldb_ecx_i386: 199 tls_context.Ecx = reg_value.GetAsUInt32(); 200 break; 201 case lldb_edx_i386: 202 tls_context.Edx = reg_value.GetAsUInt32(); 203 break; 204 case lldb_edi_i386: 205 tls_context.Edi = reg_value.GetAsUInt32(); 206 break; 207 case lldb_esi_i386: 208 tls_context.Esi = reg_value.GetAsUInt32(); 209 break; 210 case lldb_ebp_i386: 211 tls_context.Ebp = reg_value.GetAsUInt32(); 212 break; 213 case lldb_esp_i386: 214 tls_context.Esp = reg_value.GetAsUInt32(); 215 break; 216 case lldb_eip_i386: 217 tls_context.Eip = reg_value.GetAsUInt32(); 218 break; 219 case lldb_eflags_i386: 220 tls_context.EFlags = reg_value.GetAsUInt32(); 221 break; 222 case lldb_cs_i386: 223 tls_context.SegCs = reg_value.GetAsUInt32(); 224 break; 225 case lldb_fs_i386: 226 tls_context.SegFs = reg_value.GetAsUInt32(); 227 break; 228 case lldb_gs_i386: 229 tls_context.SegGs = reg_value.GetAsUInt32(); 230 break; 231 case lldb_ss_i386: 232 tls_context.SegSs = reg_value.GetAsUInt32(); 233 break; 234 case lldb_ds_i386: 235 tls_context.SegDs = reg_value.GetAsUInt32(); 236 break; 237 case lldb_es_i386: 238 tls_context.SegEs = reg_value.GetAsUInt32(); 239 break; 240 } 241 242 return SetThreadContextHelper(thread_handle, &tls_context); 243 } 244 245 Status NativeRegisterContextWindows_i386::DRRead(const uint32_t reg, 246 RegisterValue ®_value) { 247 ::CONTEXT tls_context; 248 DWORD context_flag = CONTEXT_DEBUG_REGISTERS; 249 Status error = 250 GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag); 251 if (error.Fail()) 252 return error; 253 254 switch (reg) { 255 case lldb_dr0_i386: 256 reg_value.SetUInt32(tls_context.Dr0); 257 break; 258 case lldb_dr1_i386: 259 reg_value.SetUInt32(tls_context.Dr1); 260 break; 261 case lldb_dr2_i386: 262 reg_value.SetUInt32(tls_context.Dr2); 263 break; 264 case lldb_dr3_i386: 265 reg_value.SetUInt32(tls_context.Dr3); 266 break; 267 case lldb_dr4_i386: 268 return Status("register DR4 is obsolete"); 269 case lldb_dr5_i386: 270 return Status("register DR5 is obsolete"); 271 case lldb_dr6_i386: 272 reg_value.SetUInt32(tls_context.Dr6); 273 break; 274 case lldb_dr7_i386: 275 reg_value.SetUInt32(tls_context.Dr7); 276 break; 277 } 278 279 return {}; 280 } 281 282 Status 283 NativeRegisterContextWindows_i386::DRWrite(const uint32_t reg, 284 const RegisterValue ®_value) { 285 ::CONTEXT tls_context; 286 DWORD context_flag = CONTEXT_DEBUG_REGISTERS; 287 auto thread_handle = GetThreadHandle(); 288 Status error = 289 GetThreadContextHelper(thread_handle, &tls_context, context_flag); 290 if (error.Fail()) 291 return error; 292 293 switch (reg) { 294 case lldb_dr0_i386: 295 tls_context.Dr0 = reg_value.GetAsUInt32(); 296 break; 297 case lldb_dr1_i386: 298 tls_context.Dr1 = reg_value.GetAsUInt32(); 299 break; 300 case lldb_dr2_i386: 301 tls_context.Dr2 = reg_value.GetAsUInt32(); 302 break; 303 case lldb_dr3_i386: 304 tls_context.Dr3 = reg_value.GetAsUInt32(); 305 break; 306 case lldb_dr4_i386: 307 return Status("register DR4 is obsolete"); 308 case lldb_dr5_i386: 309 return Status("register DR5 is obsolete"); 310 case lldb_dr6_i386: 311 tls_context.Dr6 = reg_value.GetAsUInt32(); 312 break; 313 case lldb_dr7_i386: 314 tls_context.Dr7 = reg_value.GetAsUInt32(); 315 break; 316 } 317 318 return SetThreadContextHelper(thread_handle, &tls_context); 319 } 320 321 Status 322 NativeRegisterContextWindows_i386::ReadRegister(const RegisterInfo *reg_info, 323 RegisterValue ®_value) { 324 Status error; 325 326 if (!reg_info) { 327 error.SetErrorString("reg_info NULL"); 328 return error; 329 } 330 331 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; 332 if (reg == LLDB_INVALID_REGNUM) { 333 // This is likely an internal register for lldb use only and should not be 334 // directly queried. 335 error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " 336 "register, cannot read directly", 337 reg_info->name); 338 return error; 339 } 340 341 if (IsGPR(reg)) 342 return GPRRead(reg, reg_value); 343 344 if (IsDR(reg)) 345 return DRRead(reg, reg_value); 346 347 return Status("unimplemented"); 348 } 349 350 Status NativeRegisterContextWindows_i386::WriteRegister( 351 const RegisterInfo *reg_info, const RegisterValue ®_value) { 352 Status error; 353 354 if (!reg_info) { 355 error.SetErrorString("reg_info NULL"); 356 return error; 357 } 358 359 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; 360 if (reg == LLDB_INVALID_REGNUM) { 361 // This is likely an internal register for lldb use only and should not be 362 // directly written. 363 error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " 364 "register, cannot write directly", 365 reg_info->name); 366 return error; 367 } 368 369 if (IsGPR(reg)) 370 return GPRWrite(reg, reg_value); 371 372 if (IsDR(reg)) 373 return DRWrite(reg, reg_value); 374 375 return Status("unimplemented"); 376 } 377 378 Status NativeRegisterContextWindows_i386::ReadAllRegisterValues( 379 lldb::DataBufferSP &data_sp) { 380 const size_t data_size = REG_CONTEXT_SIZE; 381 data_sp = std::make_shared<DataBufferHeap>(data_size, 0); 382 ::CONTEXT tls_context; 383 Status error = 384 GetThreadContextHelper(GetThreadHandle(), &tls_context, CONTEXT_ALL); 385 if (error.Fail()) 386 return error; 387 388 uint8_t *dst = data_sp->GetBytes(); 389 ::memcpy(dst, &tls_context, data_size); 390 return error; 391 } 392 393 Status NativeRegisterContextWindows_i386::WriteAllRegisterValues( 394 const lldb::DataBufferSP &data_sp) { 395 Status error; 396 const size_t data_size = REG_CONTEXT_SIZE; 397 if (!data_sp) { 398 error.SetErrorStringWithFormat( 399 "NativeRegisterContextWindows_i386::%s invalid data_sp provided", 400 __FUNCTION__); 401 return error; 402 } 403 404 if (data_sp->GetByteSize() != data_size) { 405 error.SetErrorStringWithFormatv( 406 "data_sp contained mismatched data size, expected {0}, actual {1}", 407 data_size, data_sp->GetByteSize()); 408 return error; 409 } 410 411 ::CONTEXT tls_context; 412 memcpy(&tls_context, data_sp->GetBytes(), data_size); 413 return SetThreadContextHelper(GetThreadHandle(), &tls_context); 414 } 415 416 Status NativeRegisterContextWindows_i386::IsWatchpointHit(uint32_t wp_index, 417 bool &is_hit) { 418 is_hit = false; 419 420 if (wp_index >= NumSupportedHardwareWatchpoints()) 421 return Status("watchpoint index out of range"); 422 423 RegisterValue reg_value; 424 Status error = DRRead(lldb_dr6_i386, reg_value); 425 if (error.Fail()) 426 return error; 427 428 is_hit = reg_value.GetAsUInt32() & (1 << wp_index); 429 430 return {}; 431 } 432 433 Status NativeRegisterContextWindows_i386::GetWatchpointHitIndex( 434 uint32_t &wp_index, lldb::addr_t trap_addr) { 435 wp_index = LLDB_INVALID_INDEX32; 436 437 for (uint32_t i = 0; i < NumSupportedHardwareWatchpoints(); i++) { 438 bool is_hit; 439 Status error = IsWatchpointHit(i, is_hit); 440 if (error.Fail()) 441 return error; 442 443 if (is_hit) { 444 wp_index = i; 445 return {}; 446 } 447 } 448 449 return {}; 450 } 451 452 Status NativeRegisterContextWindows_i386::IsWatchpointVacant(uint32_t wp_index, 453 bool &is_vacant) { 454 is_vacant = false; 455 456 if (wp_index >= NumSupportedHardwareWatchpoints()) 457 return Status("Watchpoint index out of range"); 458 459 RegisterValue reg_value; 460 Status error = DRRead(lldb_dr7_i386, reg_value); 461 if (error.Fail()) 462 return error; 463 464 is_vacant = !(reg_value.GetAsUInt32() & (1 << (2 * wp_index))); 465 466 return error; 467 } 468 469 bool NativeRegisterContextWindows_i386::ClearHardwareWatchpoint( 470 uint32_t wp_index) { 471 if (wp_index >= NumSupportedHardwareWatchpoints()) 472 return false; 473 474 // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0, 1, 2, or 3 of 475 // the debug status register (DR6) 476 477 RegisterValue reg_value; 478 Status error = DRRead(lldb_dr6_i386, reg_value); 479 if (error.Fail()) 480 return false; 481 482 uint32_t bit_mask = 1 << wp_index; 483 uint32_t status_bits = reg_value.GetAsUInt32() & ~bit_mask; 484 error = DRWrite(lldb_dr6_i386, RegisterValue(status_bits)); 485 if (error.Fail()) 486 return false; 487 488 // for watchpoints 0, 1, 2, or 3, respectively, clear bits {0-1,16-19}, 489 // {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} of the debug control register 490 // (DR7) 491 492 error = DRRead(lldb_dr7_i386, reg_value); 493 if (error.Fail()) 494 return false; 495 496 bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); 497 uint32_t control_bits = reg_value.GetAsUInt32() & ~bit_mask; 498 return DRWrite(lldb_dr7_i386, RegisterValue(control_bits)).Success(); 499 } 500 501 Status NativeRegisterContextWindows_i386::ClearAllHardwareWatchpoints() { 502 RegisterValue reg_value; 503 504 // clear bits {0-4} of the debug status register (DR6) 505 506 Status error = DRRead(lldb_dr6_i386, reg_value); 507 if (error.Fail()) 508 return error; 509 510 uint32_t status_bits = reg_value.GetAsUInt32() & ~0xF; 511 error = DRWrite(lldb_dr6_i386, RegisterValue(status_bits)); 512 if (error.Fail()) 513 return error; 514 515 // clear bits {0-7,16-31} of the debug control register (DR7) 516 517 error = DRRead(lldb_dr7_i386, reg_value); 518 if (error.Fail()) 519 return error; 520 521 uint32_t control_bits = reg_value.GetAsUInt32() & ~0xFFFF00FF; 522 return DRWrite(lldb_dr7_i386, RegisterValue(control_bits)); 523 } 524 525 uint32_t NativeRegisterContextWindows_i386::SetHardwareWatchpoint( 526 lldb::addr_t addr, size_t size, uint32_t watch_flags) { 527 switch (size) { 528 case 1: 529 case 2: 530 case 4: 531 break; 532 default: 533 return LLDB_INVALID_INDEX32; 534 } 535 536 if (watch_flags == 0x2) 537 watch_flags = 0x3; 538 539 if (watch_flags != 0x1 && watch_flags != 0x3) 540 return LLDB_INVALID_INDEX32; 541 542 for (uint32_t wp_index = 0; wp_index < NumSupportedHardwareWatchpoints(); 543 ++wp_index) { 544 bool is_vacant; 545 if (IsWatchpointVacant(wp_index, is_vacant).Fail()) 546 return LLDB_INVALID_INDEX32; 547 548 if (is_vacant) { 549 if (!ClearHardwareWatchpoint(wp_index)) 550 return LLDB_INVALID_INDEX32; 551 552 if (ApplyHardwareBreakpoint(wp_index, addr, size, watch_flags).Fail()) 553 return LLDB_INVALID_INDEX32; 554 555 return wp_index; 556 } 557 } 558 return LLDB_INVALID_INDEX32; 559 } 560 561 Status NativeRegisterContextWindows_i386::ApplyHardwareBreakpoint( 562 uint32_t wp_index, lldb::addr_t addr, size_t size, uint32_t flags) { 563 RegisterValue reg_value; 564 auto error = DRRead(lldb_dr7_i386, reg_value); 565 if (error.Fail()) 566 return error; 567 568 // for watchpoints 0, 1, 2, or 3, respectively, set bits 1, 3, 5, or 7 569 uint32_t enable_bit = 1 << (2 * wp_index); 570 571 // set bits 16-17, 20-21, 24-25, or 28-29 572 // with 0b01 for write, and 0b11 for read/write 573 uint32_t rw_bits = flags << (16 + 4 * wp_index); 574 575 // set bits 18-19, 22-23, 26-27, or 30-31 576 // with 0b00, 0b01, 0b10, or 0b11 577 // for 1, 2, 8 (if supported), or 4 bytes, respectively 578 uint32_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); 579 580 uint32_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); 581 582 uint32_t control_bits = reg_value.GetAsUInt32() & ~bit_mask; 583 control_bits |= enable_bit | rw_bits | size_bits; 584 585 error = DRWrite(lldb_dr7_i386, RegisterValue(control_bits)); 586 if (error.Fail()) 587 return error; 588 589 error = DRWrite(lldb_dr0_i386 + wp_index, RegisterValue(addr)); 590 if (error.Fail()) 591 return error; 592 593 return {}; 594 } 595 596 lldb::addr_t 597 NativeRegisterContextWindows_i386::GetWatchpointAddress(uint32_t wp_index) { 598 if (wp_index >= NumSupportedHardwareWatchpoints()) 599 return LLDB_INVALID_ADDRESS; 600 601 RegisterValue reg_value; 602 if (DRRead(lldb_dr0_i386 + wp_index, reg_value).Fail()) 603 return LLDB_INVALID_ADDRESS; 604 605 return reg_value.GetAsUInt32(); 606 } 607 608 uint32_t NativeRegisterContextWindows_i386::NumSupportedHardwareWatchpoints() { 609 return 4; 610 } 611 612 #endif 613