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