[memprof] Move YAML traits to MemProf.h (NFC) (#118668)
[llvm-project.git] / lldb / source / Plugins / ObjectFile / PECOFF / PECallFrameInfo.cpp
blob9bd48f2b9f60f4fd01802b4a7c55803fd13844aa
1 #include "PECallFrameInfo.h"
3 #include "ObjectFilePECOFF.h"
5 #include "Plugins/Process/Utility/lldb-x86-register-enums.h"
6 #include "lldb/Symbol/UnwindPlan.h"
7 #include "llvm/Support/Win64EH.h"
9 using namespace lldb;
10 using namespace lldb_private;
11 using namespace llvm::Win64EH;
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));
19 struct EHInstruction {
20 enum class Type {
21 PUSH_REGISTER,
22 ALLOCATE,
23 SET_FRAME_POINTER_REGISTER,
24 SAVE_REGISTER
27 uint8_t offset;
28 Type type;
29 uint32_t reg;
30 uint32_t frame_offset;
33 using EHProgram = std::vector<EHInstruction>;
35 class UnwindCodesIterator {
36 public:
37 UnwindCodesIterator(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva);
39 bool GetNext();
40 bool IsError() const { return m_error; }
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; }
46 private:
47 ObjectFilePECOFF &m_object_file;
49 bool m_error = false;
51 uint32_t m_unwind_info_rva;
52 DataExtractor m_unwind_info_data;
53 const UnwindInfo *m_unwind_info = nullptr;
55 DataExtractor m_unwind_code_data;
56 offset_t m_unwind_code_offset;
57 const UnwindCode *m_unwind_code = nullptr;
59 bool m_chained = false;
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{} {}
67 bool UnwindCodesIterator::GetNext() {
68 static constexpr int UNWIND_INFO_SIZE = 4;
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);
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;
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;
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;
98 if (!(m_unwind_info->getFlags() & UNW_ChainInfo))
99 break;
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));
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;
115 m_unwind_info_rva = runtime_function->UnwindInfoOffset;
116 m_unwind_info = nullptr;
117 m_chained = true;
120 return !!m_unwind_code;
123 class EHProgramBuilder {
124 public:
125 EHProgramBuilder(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva);
127 bool Build();
129 const EHProgram &GetProgram() const { return m_program; }
131 private:
132 static uint32_t ConvertMachineToLLDBRegister(uint8_t machine_reg);
133 static uint32_t ConvertXMMToLLDBRegister(uint8_t xmm_reg);
135 bool ProcessUnwindCode(UnwindCode code);
136 void Finalize();
138 bool ParseBigOrScaledFrameOffset(uint32_t &result, bool big, uint32_t scale);
139 bool ParseBigFrameOffset(uint32_t &result);
140 bool ParseFrameOffset(uint32_t &result);
142 UnwindCodesIterator m_iterator;
143 EHProgram m_program;
146 EHProgramBuilder::EHProgramBuilder(ObjectFilePECOFF &object_file,
147 uint32_t unwind_info_rva)
148 : m_iterator(object_file, unwind_info_rva) {}
150 bool EHProgramBuilder::Build() {
151 while (m_iterator.GetNext())
152 if (!ProcessUnwindCode(*m_iterator.GetUnwindCode()))
153 return false;
155 if (m_iterator.IsError())
156 return false;
158 Finalize();
160 return true;
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};
170 if (machine_reg >= std::size(machine_to_lldb_register))
171 return LLDB_INVALID_REGNUM;
173 return machine_to_lldb_register[machine_reg];
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};
185 if (xmm_reg >= std::size(xmm_to_lldb_register))
186 return LLDB_INVALID_REGNUM;
188 return xmm_to_lldb_register[xmm_reg];
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();
196 switch (unwind_operation) {
197 case UOP_PushNonVol: {
198 uint32_t r = ConvertMachineToLLDBRegister(operation_info);
199 if (r == LLDB_INVALID_REGNUM)
200 return false;
202 m_program.emplace_back(
203 EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, r, 8});
205 return true;
207 case UOP_AllocLarge: {
208 uint32_t fo;
209 if (!ParseBigOrScaledFrameOffset(fo, operation_info, 8))
210 return false;
212 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::ALLOCATE,
213 LLDB_INVALID_REGNUM, fo});
215 return true;
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;
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;
231 uint32_t fpro =
232 static_cast<uint32_t>(m_iterator.GetUnwindInfo()->getFrameOffset()) *
235 m_program.emplace_back(EHInstruction{
236 o, EHInstruction::Type::SET_FRAME_POINTER_REGISTER, fpr, fpro});
238 return true;
240 case UOP_SaveNonVol:
241 case UOP_SaveNonVolBig: {
242 uint32_t r = ConvertMachineToLLDBRegister(operation_info);
243 if (r == LLDB_INVALID_REGNUM)
244 return false;
246 uint32_t fo;
247 if (!ParseBigOrScaledFrameOffset(fo, unwind_operation == UOP_SaveNonVolBig,
249 return false;
251 m_program.emplace_back(
252 EHInstruction{o, EHInstruction::Type::SAVE_REGISTER, r, fo});
254 return true;
256 case UOP_Epilog: {
257 return m_iterator.GetNext();
259 case UOP_SpareCode: {
260 // ReSharper disable once CppIdenticalOperandsInBinaryExpression
261 return m_iterator.GetNext() && m_iterator.GetNext();
263 case UOP_SaveXMM128:
264 case UOP_SaveXMM128Big: {
265 uint32_t r = ConvertXMMToLLDBRegister(operation_info);
266 if (r == LLDB_INVALID_REGNUM)
267 return false;
269 uint32_t fo;
270 if (!ParseBigOrScaledFrameOffset(fo, unwind_operation == UOP_SaveXMM128Big,
271 16))
272 return false;
274 m_program.emplace_back(
275 EHInstruction{o, EHInstruction::Type::SAVE_REGISTER, r, fo});
277 return true;
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});
294 return true;
296 default:
297 return false;
301 void EHProgramBuilder::Finalize() {
302 for (const EHInstruction &i : m_program)
303 if (i.reg == lldb_rip_x86_64)
304 return;
306 m_program.emplace_back(
307 EHInstruction{0, EHInstruction::Type::PUSH_REGISTER, lldb_rip_x86_64, 8});
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;
319 result *= scale;
322 return true;
325 bool EHProgramBuilder::ParseBigFrameOffset(uint32_t &result) {
326 if (!m_iterator.GetNext())
327 return false;
329 result = m_iterator.GetUnwindCode()->FrameOffset;
331 if (!m_iterator.GetNext())
332 return false;
334 result += static_cast<uint32_t>(m_iterator.GetUnwindCode()->FrameOffset)
335 << 16;
337 return true;
340 bool EHProgramBuilder::ParseFrameOffset(uint32_t &result) {
341 if (!m_iterator.GetNext())
342 return false;
344 result = m_iterator.GetUnwindCode()->FrameOffset;
346 return true;
349 class EHProgramRange {
350 public:
351 EHProgramRange(EHProgram::const_iterator begin,
352 EHProgram::const_iterator end);
354 std::unique_ptr<UnwindPlan::Row> BuildUnwindPlanRow() const;
356 private:
357 int32_t GetCFAFrameOffset() const;
359 EHProgram::const_iterator m_begin;
360 EHProgram::const_iterator m_end;
363 EHProgramRange::EHProgramRange(EHProgram::const_iterator begin,
364 EHProgram::const_iterator end)
365 : m_begin(begin), m_end(end) {}
367 std::unique_ptr<UnwindPlan::Row> EHProgramRange::BuildUnwindPlanRow() const {
368 std::unique_ptr<UnwindPlan::Row> row = std::make_unique<UnwindPlan::Row>();
370 if (m_begin != m_end)
371 row->SetOffset(m_begin->offset);
373 int32_t cfa_frame_offset = GetCFAFrameOffset();
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;
386 if (frame_pointer_found)
387 break;
389 if (!frame_pointer_found)
390 row->GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64,
391 cfa_frame_offset);
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;
413 row->SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, false);
415 return row;
418 int32_t EHProgramRange::GetCFAFrameOffset() const {
419 int32_t result = 0;
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;
432 return result;
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)) {}
442 bool PECallFrameInfo::GetAddressRange(Address addr, AddressRange &range) {
443 range.Clear();
445 const RuntimeFunction *runtime_function =
446 FindRuntimeFunctionIntersectsWithRange(AddressRange(addr, 1));
447 if (!runtime_function)
448 return false;
450 range.GetBaseAddress() =
451 m_object_file.GetAddress(runtime_function->StartAddress);
452 range.SetByteSize(runtime_function->EndAddress -
453 runtime_function->StartAddress);
455 return true;
458 bool PECallFrameInfo::GetUnwindPlan(const Address &addr,
459 UnwindPlan &unwind_plan) {
460 return GetUnwindPlan(AddressRange(addr, 1), unwind_plan);
463 bool PECallFrameInfo::GetUnwindPlan(const AddressRange &range,
464 UnwindPlan &unwind_plan) {
465 unwind_plan.Clear();
467 unwind_plan.SetSourceName("PE EH info");
468 unwind_plan.SetSourcedFromCompiler(eLazyBoolYes);
469 unwind_plan.SetRegisterKind(eRegisterKindLLDB);
471 const RuntimeFunction *runtime_function =
472 FindRuntimeFunctionIntersectsWithRange(range);
473 if (!runtime_function)
474 return false;
476 EHProgramBuilder builder(m_object_file, runtime_function->UnwindInfoOffset);
477 if (!builder.Build())
478 return false;
480 std::vector<UnwindPlan::RowSP> rows;
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;
488 EHProgramRange program_range =
489 EHProgramRange(it, builder.GetProgram().end());
490 rows.push_back(program_range.BuildUnwindPlanRow());
492 last_offset = it->offset;
495 for (auto it = rows.rbegin(); it != rows.rend(); ++it)
496 unwind_plan.AppendRow(*it);
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);
503 return true;
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();
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;
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;
522 if (runtime_function->StartAddress < rva + size &&
523 runtime_function->EndAddress > rva)
524 return runtime_function;
526 if (runtime_function->StartAddress >= rva + size)
527 end = curr;
529 if (runtime_function->EndAddress <= rva)
530 begin = curr + 1;
533 return nullptr;