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 
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;
31 };
32 
33 using EHProgram = std::vector<EHInstruction>;
34 
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:
48 
49  bool m_error;
50 
53  const UnwindInfo *m_unwind_info;
54 
57  const UnwindCode *m_unwind_code;
58 
59  bool m_chained;
60 };
61 
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 
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) {
78 
79  offset_t offset = 0;
81  TypedRead<UnwindInfo>(m_unwind_info_data, offset, UNWIND_INFO_SIZE);
82  if (!m_unwind_info) {
83  m_error = true;
84  break;
85  }
86 
88  m_unwind_info_rva + UNWIND_INFO_SIZE,
89  m_unwind_info->NumCodes * sizeof(UnwindCode));
91  }
92 
95  TypedRead<UnwindCode>(m_unwind_code_data, m_unwind_code_offset);
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 
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 
146 };
147 
149  uint32_t unwind_info_rva)
150  : m_iterator(object_file, unwind_info_rva) {}
151 
153  while (m_iterator.GetNext())
155  return false;
156 
157  if (m_iterator.IsError())
158  return false;
159 
160  Finalize();
161 
162  return true;
163 }
164 
166  static uint32_t machine_to_lldb_register[] = {
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 
179  static uint32_t xmm_to_lldb_register[] = {
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(
206 
207  return true;
208  }
209  case UOP_AllocLarge: {
210  uint32_t fo;
211  if (!ParseBigOrScaledFrameOffset(fo, operation_info, 8))
212  return false;
213 
215  LLDB_INVALID_REGNUM, fo});
216 
217  return true;
218  }
219  case UOP_AllocSmall: {
220  m_program.emplace_back(
222  static_cast<uint32_t>(operation_info) * 8 + 8});
223  return true;
224  }
225  case UOP_SetFPReg: {
227  if (m_iterator.GetUnwindInfo()->getFrameRegister())
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{
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(
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(
278 
279  return true;
280  }
281  case UOP_PushMachFrame: {
282  if (operation_info)
284  LLDB_INVALID_REGNUM, 8});
286  lldb_rip_x86_64, 8});
288  lldb_cs_x86_64, 8});
290  lldb_rflags_x86_64, 8});
292  lldb_rsp_x86_64, 8});
294  lldb_ss_x86_64, 8});
295 
296  return true;
297  }
298  default:
299  return false;
300  }
301 }
302 
304  for (const EHInstruction &i : m_program)
305  if (i.reg == lldb_rip_x86_64)
306  return;
307 
308  m_program.emplace_back(
310 }
311 
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 
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 
343  if (!m_iterator.GetNext())
344  return false;
345 
346  result = m_iterator.GetUnwindCode()->FrameOffset;
347 
348  return true;
349 }
350 
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) {
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) {
399  row->SetRegisterLocationToAtCFAPlusOffset(
400  it->reg, rsp_frame_offset - cfa_frame_offset, false);
401  rsp_frame_offset += it->frame_offset;
402  break;
404  rsp_frame_offset += it->frame_offset;
405  break;
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 
421  int32_t result = 0;
422 
423  for (EHProgram::const_iterator it = m_begin; it != m_end; ++it) {
424  switch (it->type) {
427  result += it->frame_offset;
428  break;
429  default:
430  break;
431  }
432  }
433 
434  return result;
435 }
436 
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 
445  range.Clear();
446 
447  const RuntimeFunction *runtime_function =
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 
461  UnwindPlan &unwind_plan) {
462  return GetUnwindPlan(AddressRange(addr, 1), unwind_plan);
463 }
464 
466  UnwindPlan &unwind_plan) {
467  unwind_plan.Clear();
468 
469  unwind_plan.SetSourceName("PE EH info");
471  unwind_plan.SetRegisterKind(eRegisterKindLLDB);
472 
473  const RuntimeFunction *runtime_function =
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 
501  m_object_file.GetAddress(runtime_function->StartAddress),
502  runtime_function->EndAddress - runtime_function->StartAddress));
504 
505  return true;
506 }
507 
509  const AddressRange &range) const {
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 }
int32_t GetCFAFrameOffset() const
uint32_t frame_offset
An data extractor class.
Definition: DataExtractor.h:48
bool GetAddressRange(lldb_private::Address addr, lldb_private::AddressRange &range) override
std::vector< EHInstruction > EHProgram
void SetSourceName(const char *)
Definition: UnwindPlan.cpp:546
A class that represents a running process on the host machine.
lldb::addr_t GetByteSize() const
Get accessor for the byte size of this range.
Definition: AddressRange.h:222
const void * GetData(lldb::offset_t *offset_ptr, lldb::offset_t length) const
Extract length bytes from *offset_ptr.
UnwindCodesIterator m_iterator
const UnwindCode * GetUnwindCode() const
EHProgramRange(EHProgram::const_iterator begin, EHProgram::const_iterator end)
const UnwindInfo * m_unwind_info
static uint32_t ConvertMachineToLLDBRegister(uint8_t machine_reg)
lldb_private::DataExtractor ReadImageDataByRVA(uint32_t rva, size_t size)
bool ParseBigFrameOffset(uint32_t &result)
bool ParseBigOrScaledFrameOffset(uint32_t &result, bool big, uint32_t scale)
static uint32_t ConvertXMMToLLDBRegister(uint8_t xmm_reg)
const EHProgram & GetProgram() const
DataExtractor m_unwind_code_data
const UnwindCode * m_unwind_code
const llvm::Win64EH::RuntimeFunction * FindRuntimeFunctionIntersectsWithRange(const lldb_private::AddressRange &range) const
void SetPlanValidAddressRange(const AddressRange &range)
Definition: UnwindPlan.cpp:426
uint32_t GetRVA(const lldb_private::Address &addr) const
#define UINT32_MAX
Definition: lldb-defines.h:31
const UnwindInfo * GetUnwindInfo() const
lldb&#39;s internal register numbers
uint64_t offset_t
Definition: lldb-types.h:87
void AppendRow(const RowSP &row_sp)
Definition: UnwindPlan.cpp:357
static const T * TypedRead(const DataExtractor &data_extractor, offset_t &offset, offset_t size=sizeof(T))
PECallFrameInfo(ObjectFilePECOFF &object_file, uint32_t exception_dir_rva, uint32_t exception_dir_size)
ObjectFilePECOFF & m_object_file
EHProgram::const_iterator m_end
void SetRegisterKind(lldb::RegisterKind kind)
Definition: UnwindPlan.h:420
A section + offset based address class.
Definition: Address.h:59
EHProgramBuilder(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva)
bool ProcessUnwindCode(UnwindCode code)
EHProgram::const_iterator m_begin
std::unique_ptr< UnwindPlan::Row > BuildUnwindPlanRow() const
uint64_t GetByteSize() const
Get the number of bytes contained in this object.
void SetUnwindPlanValidAtAllInstructions(lldb_private::LazyBool valid_at_all_insn)
Definition: UnwindPlan.h:473
lldb_private::Address GetAddress(uint32_t rva)
ObjectFilePECOFF & m_object_file
uint64_t addr_t
Definition: lldb-types.h:83
lldb_private::DataExtractor m_exception_dir
bool GetUnwindPlan(const lldb_private::Address &addr, lldb_private::UnwindPlan &unwind_plan) override
Definition: SBAddress.h:15
DataExtractor m_unwind_info_data
Address & GetBaseAddress()
Get accessor for the base address of the range.
Definition: AddressRange.h:210
UnwindCodesIterator(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva)
bool ParseFrameOffset(uint32_t &result)
A section + offset based address range class.
Definition: AddressRange.h:25
void Clear()
Clear the object&#39;s state.
void SetSourcedFromCompiler(lldb_private::LazyBool from_compiler)
Definition: UnwindPlan.h:461
void SetByteSize(lldb::addr_t byte_size)
Set accessor for the byte size of this range.
Definition: AddressRange.h:238
#define LLDB_INVALID_REGNUM
Definition: lldb-defines.h:90