1 #include "PECallFrameInfo.h" 2 3 #include "ObjectFilePECOFF.h" 4 5 #include "Plugins/Process/Utility/lldb-x86-register-enums.h" 6 #include "lldb/Symbol/UnwindPlan.h" 7 #include "llvm/Support/Win64EH.h" 8 9 using namespace lldb; 10 using namespace lldb_private; 11 using namespace llvm::Win64EH; 12 13 template <typename T> 14 static const T *TypedRead(const DataExtractor &data_extractor, offset_t &offset, 15 offset_t size = sizeof(T)) { 16 return static_cast<const T *>(data_extractor.GetData(&offset, size)); 17 } 18 19 struct EHInstruction { 20 enum class Type { 21 PUSH_REGISTER, 22 ALLOCATE, 23 SET_FRAME_POINTER_REGISTER, 24 SAVE_REGISTER 25 }; 26 27 uint8_t offset; 28 Type type; 29 uint32_t reg; 30 uint32_t frame_offset; 31 }; 32 33 using EHProgram = std::vector<EHInstruction>; 34 35 class UnwindCodesIterator { 36 public: 37 UnwindCodesIterator(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva); 38 39 bool GetNext(); 40 bool IsError() const { return m_error; } 41 42 const UnwindInfo *GetUnwindInfo() const { return m_unwind_info; } 43 const UnwindCode *GetUnwindCode() const { return m_unwind_code; } 44 bool IsChained() const { return m_chained; } 45 46 private: 47 ObjectFilePECOFF &m_object_file; 48 49 bool m_error; 50 51 uint32_t m_unwind_info_rva; 52 DataExtractor m_unwind_info_data; 53 const UnwindInfo *m_unwind_info; 54 55 DataExtractor m_unwind_code_data; 56 offset_t m_unwind_code_offset; 57 const UnwindCode *m_unwind_code; 58 59 bool m_chained; 60 }; 61 62 UnwindCodesIterator::UnwindCodesIterator(ObjectFilePECOFF &object_file, 63 uint32_t unwind_info_rva) 64 : m_object_file(object_file), m_error(false), 65 m_unwind_info_rva(unwind_info_rva), 66 m_unwind_info(nullptr), m_unwind_code_offset{}, m_unwind_code(nullptr), 67 m_chained(false) {} 68 69 bool UnwindCodesIterator::GetNext() { 70 static constexpr int UNWIND_INFO_SIZE = 4; 71 72 m_error = false; 73 m_unwind_code = nullptr; 74 while (!m_unwind_code) { 75 if (!m_unwind_info) { 76 m_unwind_info_data = 77 m_object_file.ReadImageDataByRVA(m_unwind_info_rva, UNWIND_INFO_SIZE); 78 79 offset_t offset = 0; 80 m_unwind_info = 81 TypedRead<UnwindInfo>(m_unwind_info_data, offset, UNWIND_INFO_SIZE); 82 if (!m_unwind_info) { 83 m_error = true; 84 break; 85 } 86 87 m_unwind_code_data = m_object_file.ReadImageDataByRVA( 88 m_unwind_info_rva + UNWIND_INFO_SIZE, 89 m_unwind_info->NumCodes * sizeof(UnwindCode)); 90 m_unwind_code_offset = 0; 91 } 92 93 if (m_unwind_code_offset < m_unwind_code_data.GetByteSize()) { 94 m_unwind_code = 95 TypedRead<UnwindCode>(m_unwind_code_data, m_unwind_code_offset); 96 m_error = !m_unwind_code; 97 break; 98 } 99 100 if (!(m_unwind_info->getFlags() & UNW_ChainInfo)) 101 break; 102 103 uint32_t runtime_function_rva = 104 m_unwind_info_rva + UNWIND_INFO_SIZE + 105 ((m_unwind_info->NumCodes + 1) & ~1) * sizeof(UnwindCode); 106 DataExtractor runtime_function_data = m_object_file.ReadImageDataByRVA( 107 runtime_function_rva, sizeof(RuntimeFunction)); 108 109 offset_t offset = 0; 110 const auto *runtime_function = 111 TypedRead<RuntimeFunction>(runtime_function_data, offset); 112 if (!runtime_function) { 113 m_error = true; 114 break; 115 } 116 117 m_unwind_info_rva = runtime_function->UnwindInfoOffset; 118 m_unwind_info = nullptr; 119 m_chained = true; 120 } 121 122 return !!m_unwind_code; 123 } 124 125 class EHProgramBuilder { 126 public: 127 EHProgramBuilder(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva); 128 129 bool Build(); 130 131 const EHProgram &GetProgram() const { return m_program; } 132 133 private: 134 static uint32_t ConvertMachineToLLDBRegister(uint8_t machine_reg); 135 static uint32_t ConvertXMMToLLDBRegister(uint8_t xmm_reg); 136 137 bool ProcessUnwindCode(UnwindCode code); 138 void Finalize(); 139 140 bool ParseBigOrScaledFrameOffset(uint32_t &result, bool big, uint32_t scale); 141 bool ParseBigFrameOffset(uint32_t &result); 142 bool ParseFrameOffset(uint32_t &result); 143 144 UnwindCodesIterator m_iterator; 145 EHProgram m_program; 146 }; 147 148 EHProgramBuilder::EHProgramBuilder(ObjectFilePECOFF &object_file, 149 uint32_t unwind_info_rva) 150 : m_iterator(object_file, unwind_info_rva) {} 151 152 bool EHProgramBuilder::Build() { 153 while (m_iterator.GetNext()) 154 if (!ProcessUnwindCode(*m_iterator.GetUnwindCode())) 155 return false; 156 157 if (m_iterator.IsError()) 158 return false; 159 160 Finalize(); 161 162 return true; 163 } 164 165 uint32_t EHProgramBuilder::ConvertMachineToLLDBRegister(uint8_t machine_reg) { 166 static uint32_t machine_to_lldb_register[] = { 167 lldb_rax_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64, lldb_rbx_x86_64, 168 lldb_rsp_x86_64, lldb_rbp_x86_64, lldb_rsi_x86_64, lldb_rdi_x86_64, 169 lldb_r8_x86_64, lldb_r9_x86_64, lldb_r10_x86_64, lldb_r11_x86_64, 170 lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64}; 171 172 if (machine_reg >= llvm::array_lengthof(machine_to_lldb_register)) 173 return LLDB_INVALID_REGNUM; 174 175 return machine_to_lldb_register[machine_reg]; 176 } 177 178 uint32_t EHProgramBuilder::ConvertXMMToLLDBRegister(uint8_t xmm_reg) { 179 static uint32_t xmm_to_lldb_register[] = { 180 lldb_xmm0_x86_64, lldb_xmm1_x86_64, lldb_xmm2_x86_64, 181 lldb_xmm3_x86_64, lldb_xmm4_x86_64, lldb_xmm5_x86_64, 182 lldb_xmm6_x86_64, lldb_xmm7_x86_64, lldb_xmm8_x86_64, 183 lldb_xmm9_x86_64, lldb_xmm10_x86_64, lldb_xmm11_x86_64, 184 lldb_xmm12_x86_64, lldb_xmm13_x86_64, lldb_xmm14_x86_64, 185 lldb_xmm15_x86_64}; 186 187 if (xmm_reg >= llvm::array_lengthof(xmm_to_lldb_register)) 188 return LLDB_INVALID_REGNUM; 189 190 return xmm_to_lldb_register[xmm_reg]; 191 } 192 193 bool EHProgramBuilder::ProcessUnwindCode(UnwindCode code) { 194 uint8_t o = m_iterator.IsChained() ? 0 : code.u.CodeOffset; 195 uint8_t unwind_operation = code.getUnwindOp(); 196 uint8_t operation_info = code.getOpInfo(); 197 198 switch (unwind_operation) { 199 case UOP_PushNonVol: { 200 uint32_t r = ConvertMachineToLLDBRegister(operation_info); 201 if (r == LLDB_INVALID_REGNUM) 202 return false; 203 204 m_program.emplace_back( 205 EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, r, 8}); 206 207 return true; 208 } 209 case UOP_AllocLarge: { 210 uint32_t fo; 211 if (!ParseBigOrScaledFrameOffset(fo, operation_info, 8)) 212 return false; 213 214 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::ALLOCATE, 215 LLDB_INVALID_REGNUM, fo}); 216 217 return true; 218 } 219 case UOP_AllocSmall: { 220 m_program.emplace_back( 221 EHInstruction{o, EHInstruction::Type::ALLOCATE, LLDB_INVALID_REGNUM, 222 static_cast<uint32_t>(operation_info) * 8 + 8}); 223 return true; 224 } 225 case UOP_SetFPReg: { 226 uint32_t fpr = LLDB_INVALID_REGNUM; 227 if (m_iterator.GetUnwindInfo()->getFrameRegister()) 228 fpr = ConvertMachineToLLDBRegister( 229 m_iterator.GetUnwindInfo()->getFrameRegister()); 230 if (fpr == LLDB_INVALID_REGNUM) 231 return false; 232 233 uint32_t fpro = 234 static_cast<uint32_t>(m_iterator.GetUnwindInfo()->getFrameOffset()) * 235 16; 236 237 m_program.emplace_back(EHInstruction{ 238 o, EHInstruction::Type::SET_FRAME_POINTER_REGISTER, fpr, fpro}); 239 240 return true; 241 } 242 case UOP_SaveNonVol: 243 case UOP_SaveNonVolBig: { 244 uint32_t r = ConvertMachineToLLDBRegister(operation_info); 245 if (r == LLDB_INVALID_REGNUM) 246 return false; 247 248 uint32_t fo; 249 if (!ParseBigOrScaledFrameOffset(fo, unwind_operation == UOP_SaveNonVolBig, 250 8)) 251 return false; 252 253 m_program.emplace_back( 254 EHInstruction{o, EHInstruction::Type::SAVE_REGISTER, r, fo}); 255 256 return true; 257 } 258 case UOP_Epilog: { 259 return m_iterator.GetNext(); 260 } 261 case UOP_SpareCode: { 262 // ReSharper disable once CppIdenticalOperandsInBinaryExpression 263 return m_iterator.GetNext() && m_iterator.GetNext(); 264 } 265 case UOP_SaveXMM128: 266 case UOP_SaveXMM128Big: { 267 uint32_t r = ConvertXMMToLLDBRegister(operation_info); 268 if (r == LLDB_INVALID_REGNUM) 269 return false; 270 271 uint32_t fo; 272 if (!ParseBigOrScaledFrameOffset(fo, unwind_operation == UOP_SaveXMM128Big, 273 16)) 274 return false; 275 276 m_program.emplace_back( 277 EHInstruction{o, EHInstruction::Type::SAVE_REGISTER, r, fo}); 278 279 return true; 280 } 281 case UOP_PushMachFrame: { 282 if (operation_info) 283 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::ALLOCATE, 284 LLDB_INVALID_REGNUM, 8}); 285 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, 286 lldb_rip_x86_64, 8}); 287 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, 288 lldb_cs_x86_64, 8}); 289 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, 290 lldb_rflags_x86_64, 8}); 291 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, 292 lldb_rsp_x86_64, 8}); 293 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, 294 lldb_ss_x86_64, 8}); 295 296 return true; 297 } 298 default: 299 return false; 300 } 301 } 302 303 void EHProgramBuilder::Finalize() { 304 for (const EHInstruction &i : m_program) 305 if (i.reg == lldb_rip_x86_64) 306 return; 307 308 m_program.emplace_back( 309 EHInstruction{0, EHInstruction::Type::PUSH_REGISTER, lldb_rip_x86_64, 8}); 310 } 311 312 bool EHProgramBuilder::ParseBigOrScaledFrameOffset(uint32_t &result, bool big, 313 uint32_t scale) { 314 if (big) { 315 if (!ParseBigFrameOffset(result)) 316 return false; 317 } else { 318 if (!ParseFrameOffset(result)) 319 return false; 320 321 result *= scale; 322 } 323 324 return true; 325 } 326 327 bool EHProgramBuilder::ParseBigFrameOffset(uint32_t &result) { 328 if (!m_iterator.GetNext()) 329 return false; 330 331 result = m_iterator.GetUnwindCode()->FrameOffset; 332 333 if (!m_iterator.GetNext()) 334 return false; 335 336 result += static_cast<uint32_t>(m_iterator.GetUnwindCode()->FrameOffset) 337 << 16; 338 339 return true; 340 } 341 342 bool EHProgramBuilder::ParseFrameOffset(uint32_t &result) { 343 if (!m_iterator.GetNext()) 344 return false; 345 346 result = m_iterator.GetUnwindCode()->FrameOffset; 347 348 return true; 349 } 350 351 class EHProgramRange { 352 public: 353 EHProgramRange(EHProgram::const_iterator begin, 354 EHProgram::const_iterator end); 355 356 std::unique_ptr<UnwindPlan::Row> BuildUnwindPlanRow() const; 357 358 private: 359 int32_t GetCFAFrameOffset() const; 360 361 EHProgram::const_iterator m_begin; 362 EHProgram::const_iterator m_end; 363 }; 364 365 EHProgramRange::EHProgramRange(EHProgram::const_iterator begin, 366 EHProgram::const_iterator end) 367 : m_begin(begin), m_end(end) {} 368 369 std::unique_ptr<UnwindPlan::Row> EHProgramRange::BuildUnwindPlanRow() const { 370 std::unique_ptr<UnwindPlan::Row> row = std::make_unique<UnwindPlan::Row>(); 371 372 if (m_begin != m_end) 373 row->SetOffset(m_begin->offset); 374 375 int32_t cfa_frame_offset = GetCFAFrameOffset(); 376 377 bool frame_pointer_found = false; 378 for (EHProgram::const_iterator it = m_begin; it != m_end; ++it) { 379 switch (it->type) { 380 case EHInstruction::Type::SET_FRAME_POINTER_REGISTER: 381 row->GetCFAValue().SetIsRegisterPlusOffset(it->reg, cfa_frame_offset - 382 it->frame_offset); 383 frame_pointer_found = true; 384 break; 385 default: 386 break; 387 } 388 if (frame_pointer_found) 389 break; 390 } 391 if (!frame_pointer_found) 392 row->GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 393 cfa_frame_offset); 394 395 int32_t rsp_frame_offset = 0; 396 for (EHProgram::const_iterator it = m_begin; it != m_end; ++it) { 397 switch (it->type) { 398 case EHInstruction::Type::PUSH_REGISTER: 399 row->SetRegisterLocationToAtCFAPlusOffset( 400 it->reg, rsp_frame_offset - cfa_frame_offset, false); 401 rsp_frame_offset += it->frame_offset; 402 break; 403 case EHInstruction::Type::ALLOCATE: 404 rsp_frame_offset += it->frame_offset; 405 break; 406 case EHInstruction::Type::SAVE_REGISTER: 407 row->SetRegisterLocationToAtCFAPlusOffset( 408 it->reg, it->frame_offset - cfa_frame_offset, false); 409 break; 410 default: 411 break; 412 } 413 } 414 415 row->SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, false); 416 417 return row; 418 } 419 420 int32_t EHProgramRange::GetCFAFrameOffset() const { 421 int32_t result = 0; 422 423 for (EHProgram::const_iterator it = m_begin; it != m_end; ++it) { 424 switch (it->type) { 425 case EHInstruction::Type::PUSH_REGISTER: 426 case EHInstruction::Type::ALLOCATE: 427 result += it->frame_offset; 428 break; 429 default: 430 break; 431 } 432 } 433 434 return result; 435 } 436 437 PECallFrameInfo::PECallFrameInfo(ObjectFilePECOFF &object_file, 438 uint32_t exception_dir_rva, 439 uint32_t exception_dir_size) 440 : m_object_file(object_file), 441 m_exception_dir(object_file.ReadImageDataByRVA(exception_dir_rva, 442 exception_dir_size)) {} 443 444 bool PECallFrameInfo::GetAddressRange(Address addr, AddressRange &range) { 445 range.Clear(); 446 447 const RuntimeFunction *runtime_function = 448 FindRuntimeFunctionIntersectsWithRange(AddressRange(addr, 1)); 449 if (!runtime_function) 450 return false; 451 452 range.GetBaseAddress() = 453 m_object_file.GetAddress(runtime_function->StartAddress); 454 range.SetByteSize(runtime_function->EndAddress - 455 runtime_function->StartAddress); 456 457 return true; 458 } 459 460 bool PECallFrameInfo::GetUnwindPlan(const Address &addr, 461 UnwindPlan &unwind_plan) { 462 return GetUnwindPlan(AddressRange(addr, 1), unwind_plan); 463 } 464 465 bool PECallFrameInfo::GetUnwindPlan(const AddressRange &range, 466 UnwindPlan &unwind_plan) { 467 unwind_plan.Clear(); 468 469 unwind_plan.SetSourceName("PE EH info"); 470 unwind_plan.SetSourcedFromCompiler(eLazyBoolYes); 471 unwind_plan.SetRegisterKind(eRegisterKindLLDB); 472 473 const RuntimeFunction *runtime_function = 474 FindRuntimeFunctionIntersectsWithRange(range); 475 if (!runtime_function) 476 return false; 477 478 EHProgramBuilder builder(m_object_file, runtime_function->UnwindInfoOffset); 479 if (!builder.Build()) 480 return false; 481 482 std::vector<UnwindPlan::RowSP> rows; 483 484 uint32_t last_offset = UINT32_MAX; 485 for (auto it = builder.GetProgram().begin(); it != builder.GetProgram().end(); 486 ++it) { 487 if (it->offset == last_offset) 488 continue; 489 490 EHProgramRange program_range = 491 EHProgramRange(it, builder.GetProgram().end()); 492 rows.push_back(program_range.BuildUnwindPlanRow()); 493 494 last_offset = it->offset; 495 } 496 497 for (auto it = rows.rbegin(); it != rows.rend(); ++it) 498 unwind_plan.AppendRow(*it); 499 500 unwind_plan.SetPlanValidAddressRange(AddressRange( 501 m_object_file.GetAddress(runtime_function->StartAddress), 502 runtime_function->EndAddress - runtime_function->StartAddress)); 503 unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); 504 505 return true; 506 } 507 508 const RuntimeFunction *PECallFrameInfo::FindRuntimeFunctionIntersectsWithRange( 509 const AddressRange &range) const { 510 uint32_t rva = m_object_file.GetRVA(range.GetBaseAddress()); 511 addr_t size = range.GetByteSize(); 512 513 uint32_t begin = 0; 514 uint32_t end = m_exception_dir.GetByteSize() / sizeof(RuntimeFunction); 515 while (begin < end) { 516 uint32_t curr = (begin + end) / 2; 517 518 offset_t offset = curr * sizeof(RuntimeFunction); 519 const auto *runtime_function = 520 TypedRead<RuntimeFunction>(m_exception_dir, offset); 521 if (!runtime_function) 522 break; 523 524 if (runtime_function->StartAddress < rva + size && 525 runtime_function->EndAddress > rva) 526 return runtime_function; 527 528 if (runtime_function->StartAddress >= rva + size) 529 end = curr; 530 531 if (runtime_function->EndAddress <= rva) 532 begin = curr + 1; 533 } 534 535 return nullptr; 536 } 537