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
20 enum class Type {
25 };
26
27 uint8_t offset;
29 uint32_t reg;
30 uint32_t frame_offset;
31};
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) {
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
86 m_unwind_info_rva + UNWIND_INFO_SIZE,
87 m_unwind_info->NumCodes * sizeof(UnwindCode));
89 }
90
93 TypedRead<UnwindCode>(m_unwind_code_data, m_unwind_code_offset);
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())
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
354 std::unique_ptr<UnwindPlan::Row> BuildUnwindPlanRow() const;
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
367std::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) {
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) {
397 row->SetRegisterLocationToAtCFAPlusOffset(
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;
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
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
459 UnwindPlan &unwind_plan) {
460 return GetUnwindPlan(AddressRange(addr, 1), unwind_plan);
461}
462
464 UnwindPlan &unwind_plan) {
465 unwind_plan.Clear();
466
467 unwind_plan.SetSourceName("PE EH info");
470
471 const RuntimeFunction *runtime_function =
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
499 m_object_file.GetAddress(runtime_function->StartAddress),
500 runtime_function->EndAddress - runtime_function->StartAddress));
502
503 return true;
504}
505
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}
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)
std::unique_ptr< UnwindPlan::Row > BuildUnwindPlanRow() const
EHProgram::const_iterator m_end
EHProgram::const_iterator m_begin
int32_t GetCFAFrameOffset() const
lldb_private::DataExtractor ReadImageDataByRVA(uint32_t rva, size_t size)
lldb_private::Address GetAddress(uint32_t rva)
uint32_t GetRVA(const lldb_private::Address &addr) const
bool GetAddressRange(lldb_private::Address addr, lldb_private::AddressRange &range) override
ObjectFilePECOFF & m_object_file
bool GetUnwindPlan(const lldb_private::Address &addr, lldb_private::UnwindPlan &unwind_plan) 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.
Definition: AddressRange.h:25
Address & GetBaseAddress()
Get accessor for the base address of the range.
Definition: AddressRange.h:211
void Clear()
Clear the object's state.
void SetByteSize(lldb::addr_t byte_size)
Set accessor for the byte size of this range.
Definition: AddressRange.h:239
lldb::addr_t GetByteSize() const
Get accessor for the byte size of this range.
Definition: AddressRange.h:223
A section + offset based address class.
Definition: Address.h:62
An data extractor class.
Definition: DataExtractor.h:48
const void * GetData(lldb::offset_t *offset_ptr, lldb::offset_t length) const
Extract length bytes from *offset_ptr.
uint64_t GetByteSize() const
Get the number of bytes contained in this object.
void SetRegisterKind(lldb::RegisterKind kind)
Definition: UnwindPlan.h:452
void SetPlanValidAddressRange(const AddressRange &range)
Definition: UnwindPlan.cpp:448
void AppendRow(const RowSP &row_sp)
Definition: UnwindPlan.cpp:379
void SetSourcedFromCompiler(lldb_private::LazyBool from_compiler)
Definition: UnwindPlan.h:493
void SetSourceName(const char *)
Definition: UnwindPlan.cpp:581
void SetUnwindPlanValidAtAllInstructions(lldb_private::LazyBool valid_at_all_insn)
Definition: UnwindPlan.h:505
#define UINT32_MAX
Definition: lldb-defines.h:19
#define LLDB_INVALID_REGNUM
Definition: lldb-defines.h:87
A class that represents a running process on the host machine.
Definition: SBAddress.h:15
uint64_t offset_t
Definition: lldb-types.h:85
uint64_t addr_t
Definition: lldb-types.h:80
@ eRegisterKindLLDB
lldb's internal register numbers
uint32_t frame_offset