LLDB mainline
PECallFrameInfo.cpp
Go to the documentation of this file.
1#include "PECallFrameInfo.h"
2
3#include "ObjectFilePECOFF.h"
4
7#include "llvm/Support/Win64EH.h"
8
9using namespace lldb;
10using namespace lldb_private;
11using namespace llvm::Win64EH;
12
13template <typename T>
14static 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
32
33using EHProgram = std::vector<EHInstruction>;
34
36public:
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
46private:
48
49 bool m_error = false;
50
53 const UnwindInfo *m_unwind_info = nullptr;
54
57 const UnwindCode *m_unwind_code = nullptr;
58
59 bool m_chained = false;
60};
61
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
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) {
75 m_object_file.ReadImageDataByRVA(m_unwind_info_rva, UNWIND_INFO_SIZE);
76
77 offset_t offset = 0;
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));
89 }
90
91 if (m_unwind_code_offset < m_unwind_code_data.GetByteSize()) {
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
124public:
125 EHProgramBuilder(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva);
126
127 bool Build();
128
129 const EHProgram &GetProgram() const { return m_program; }
130
131private:
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
144};
145
147 uint32_t unwind_info_rva)
148 : m_iterator(object_file, unwind_info_rva) {}
149
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
164 static uint32_t machine_to_lldb_register[] = {
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
177 static uint32_t xmm_to_lldb_register[] = {
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
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(
204
205 return true;
206 }
207 case UOP_AllocLarge: {
208 uint32_t fo;
209 if (!ParseBigOrScaledFrameOffset(fo, operation_info, 8))
210 return false;
211
214
215 return true;
216 }
217 case UOP_AllocSmall: {
218 m_program.emplace_back(
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())
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{
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(
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(
276
277 return true;
278 }
279 case UOP_PushMachFrame: {
280 if (operation_info)
284 lldb_rip_x86_64, 8});
286 lldb_cs_x86_64, 8});
290 lldb_rsp_x86_64, 8});
292 lldb_ss_x86_64, 8});
293
294 return true;
295 }
296 default:
297 return false;
298 }
299}
300
302 for (const EHInstruction &i : m_program)
303 if (i.reg == lldb_rip_x86_64)
304 return;
305
306 m_program.emplace_back(
308}
309
310bool 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
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
341 if (!m_iterator.GetNext())
342 return false;
343
344 result = m_iterator.GetUnwindCode()->FrameOffset;
345
346 return true;
347}
348
350public:
351 EHProgramRange(EHProgram::const_iterator begin,
352 EHProgram::const_iterator end);
353
355
356private:
357 int32_t GetCFAFrameOffset() const;
358
359 EHProgram::const_iterator m_begin;
360 EHProgram::const_iterator m_end;
361};
362
363EHProgramRange::EHProgramRange(EHProgram::const_iterator begin,
364 EHProgram::const_iterator end)
365 : m_begin(begin), m_end(end) {}
366
368 UnwindPlan::Row 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) {
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)
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) {
398 it->reg, rsp_frame_offset - cfa_frame_offset, false);
399 rsp_frame_offset += it->frame_offset;
400 break;
402 rsp_frame_offset += it->frame_offset;
403 break;
406 it->reg, it->frame_offset - cfa_frame_offset, false);
407 break;
408 default:
409 break;
410 }
411 }
412
414
415 return row;
416}
417
419 int32_t result = 0;
420
421 for (EHProgram::const_iterator it = m_begin; it != m_end; ++it) {
422 switch (it->type) {
425 result += it->frame_offset;
426 break;
427 default:
428 break;
429 }
430 }
431
432 return result;
433}
434
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
443 range.Clear();
444
445 const RuntimeFunction *runtime_function =
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
458std::unique_ptr<UnwindPlan> PECallFrameInfo::GetUnwindPlan(
459 llvm::ArrayRef<lldb_private::AddressRange> ranges,
460 const lldb_private::Address &addr) {
461 // Only continuous functions are supported.
462 if (ranges.size() != 1)
463 return nullptr;
464 const AddressRange &range = ranges[0];
465
466 const RuntimeFunction *runtime_function =
468 if (!runtime_function)
469 return nullptr;
470
471 auto plan_up = std::make_unique<UnwindPlan>(eRegisterKindLLDB);
472 plan_up->SetSourceName("PE EH info");
473 plan_up->SetSourcedFromCompiler(eLazyBoolYes);
474
475 EHProgramBuilder builder(m_object_file, runtime_function->UnwindInfoOffset);
476 if (!builder.Build())
477 return nullptr;
478
479 std::vector<UnwindPlan::Row> rows;
480
481 uint32_t last_offset = UINT32_MAX;
482 for (auto it = builder.GetProgram().begin(); it != builder.GetProgram().end();
483 ++it) {
484 if (it->offset == last_offset)
485 continue;
486
487 EHProgramRange program_range =
488 EHProgramRange(it, builder.GetProgram().end());
489 rows.push_back(program_range.BuildUnwindPlanRow());
490
491 last_offset = it->offset;
492 }
493
494 for (auto it = rows.rbegin(); it != rows.rend(); ++it)
495 plan_up->AppendRow(std::move(*it));
496
497 plan_up->SetPlanValidAddressRanges({AddressRange(
498 m_object_file.GetAddress(runtime_function->StartAddress),
499 runtime_function->EndAddress - runtime_function->StartAddress)});
500 plan_up->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
501
502 return plan_up;
503}
504
506 const AddressRange &range) const {
507 uint32_t rva = m_object_file.GetRVA(range.GetBaseAddress());
508 addr_t size = range.GetByteSize();
509
510 uint32_t begin = 0;
511 uint32_t end = m_exception_dir.GetByteSize() / sizeof(RuntimeFunction);
512 while (begin < end) {
513 uint32_t curr = (begin + end) / 2;
514
515 offset_t offset = curr * sizeof(RuntimeFunction);
516 const auto *runtime_function =
518 if (!runtime_function)
519 break;
520
521 if (runtime_function->StartAddress < rva + size &&
522 runtime_function->EndAddress > rva)
523 return runtime_function;
524
525 if (runtime_function->StartAddress >= rva + size)
526 end = curr;
527
528 if (runtime_function->EndAddress <= rva)
529 begin = curr + 1;
530 }
531
532 return nullptr;
533}
std::vector< EHInstruction > EHProgram
static const T * TypedRead(const DataExtractor &data_extractor, offset_t &offset, offset_t size=sizeof(T))
static uint32_t ConvertXMMToLLDBRegister(uint8_t xmm_reg)
const EHProgram & GetProgram() const
static uint32_t ConvertMachineToLLDBRegister(uint8_t machine_reg)
bool ProcessUnwindCode(UnwindCode code)
EHProgramBuilder(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva)
UnwindCodesIterator m_iterator
bool ParseFrameOffset(uint32_t &result)
bool ParseBigOrScaledFrameOffset(uint32_t &result, bool big, uint32_t scale)
bool ParseBigFrameOffset(uint32_t &result)
EHProgramRange(EHProgram::const_iterator begin, EHProgram::const_iterator end)
UnwindPlan::Row BuildUnwindPlanRow() const
EHProgram::const_iterator m_end
EHProgram::const_iterator m_begin
int32_t GetCFAFrameOffset() const
bool GetAddressRange(lldb_private::Address addr, lldb_private::AddressRange &range) override
ObjectFilePECOFF & m_object_file
std::unique_ptr< lldb_private::UnwindPlan > GetUnwindPlan(const lldb_private::Address &addr) override
const llvm::Win64EH::RuntimeFunction * FindRuntimeFunctionIntersectsWithRange(const lldb_private::AddressRange &range) const
PECallFrameInfo(ObjectFilePECOFF &object_file, uint32_t exception_dir_rva, uint32_t exception_dir_size)
lldb_private::DataExtractor m_exception_dir
DataExtractor m_unwind_info_data
ObjectFilePECOFF & m_object_file
const UnwindInfo * GetUnwindInfo() const
DataExtractor m_unwind_code_data
const UnwindInfo * m_unwind_info
UnwindCodesIterator(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva)
const UnwindCode * m_unwind_code
const UnwindCode * GetUnwindCode() const
A section + offset based address range class.
Address & GetBaseAddress()
Get accessor for the base address of the range.
void Clear()
Clear the object's state.
void SetByteSize(lldb::addr_t byte_size)
Set accessor for the byte size of this range.
lldb::addr_t GetByteSize() const
Get accessor for the byte size of this range.
A section + offset based address class.
Definition Address.h:62
An data extractor class.
const void * GetData(lldb::offset_t *offset_ptr, lldb::offset_t length) const
Extract length bytes from *offset_ptr.
void SetIsRegisterPlusOffset(uint32_t reg_num, int32_t offset)
Definition UnwindPlan.h:240
bool SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num, int32_t offset, bool can_replace)
void SetOffset(int64_t offset)
Definition UnwindPlan.h:361
bool SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num, int32_t offset, bool can_replace)
const FAValue & GetCFAValue() const
Definition UnwindPlan.h:365
#define UINT32_MAX
#define LLDB_INVALID_REGNUM
A class that represents a running process on the host machine.
uint64_t offset_t
Definition lldb-types.h:85
uint64_t addr_t
Definition lldb-types.h:80
@ eRegisterKindLLDB
lldb's internal register numbers