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>
TypedRead(const DataExtractor & data_extractor,offset_t & offset,offset_t size=sizeof (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();
IsError() const40 bool IsError() const { return m_error; }
41
GetUnwindInfo() const42 const UnwindInfo *GetUnwindInfo() const { return m_unwind_info; }
GetUnwindCode() const43 const UnwindCode *GetUnwindCode() const { return m_unwind_code; }
IsChained() const44 bool IsChained() const { return m_chained; }
45
46 private:
47 ObjectFilePECOFF &m_object_file;
48
49 bool m_error;
50
51 uint32_t m_unwind_info_rva;
52 DataExtractor m_unwind_info_data;
53 const UnwindInfo *m_unwind_info;
54
55 DataExtractor m_unwind_code_data;
56 offset_t m_unwind_code_offset;
57 const UnwindCode *m_unwind_code;
58
59 bool m_chained;
60 };
61
UnwindCodesIterator(ObjectFilePECOFF & object_file,uint32_t unwind_info_rva)62 UnwindCodesIterator::UnwindCodesIterator(ObjectFilePECOFF &object_file,
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
GetNext()69 bool UnwindCodesIterator::GetNext() {
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) {
76 m_unwind_info_data =
77 m_object_file.ReadImageDataByRVA(m_unwind_info_rva, UNWIND_INFO_SIZE);
78
79 offset_t offset = 0;
80 m_unwind_info =
81 TypedRead<UnwindInfo>(m_unwind_info_data, offset, UNWIND_INFO_SIZE);
82 if (!m_unwind_info) {
83 m_error = true;
84 break;
85 }
86
87 m_unwind_code_data = m_object_file.ReadImageDataByRVA(
88 m_unwind_info_rva + UNWIND_INFO_SIZE,
89 m_unwind_info->NumCodes * sizeof(UnwindCode));
90 m_unwind_code_offset = 0;
91 }
92
93 if (m_unwind_code_offset < m_unwind_code_data.GetByteSize()) {
94 m_unwind_code =
95 TypedRead<UnwindCode>(m_unwind_code_data, m_unwind_code_offset);
96 m_error = !m_unwind_code;
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
125 class EHProgramBuilder {
126 public:
127 EHProgramBuilder(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva);
128
129 bool Build();
130
GetProgram() const131 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
144 UnwindCodesIterator m_iterator;
145 EHProgram m_program;
146 };
147
EHProgramBuilder(ObjectFilePECOFF & object_file,uint32_t unwind_info_rva)148 EHProgramBuilder::EHProgramBuilder(ObjectFilePECOFF &object_file,
149 uint32_t unwind_info_rva)
150 : m_iterator(object_file, unwind_info_rva) {}
151
Build()152 bool EHProgramBuilder::Build() {
153 while (m_iterator.GetNext())
154 if (!ProcessUnwindCode(*m_iterator.GetUnwindCode()))
155 return false;
156
157 if (m_iterator.IsError())
158 return false;
159
160 Finalize();
161
162 return true;
163 }
164
ConvertMachineToLLDBRegister(uint8_t machine_reg)165 uint32_t EHProgramBuilder::ConvertMachineToLLDBRegister(uint8_t machine_reg) {
166 static uint32_t machine_to_lldb_register[] = {
167 lldb_rax_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64, lldb_rbx_x86_64,
168 lldb_rsp_x86_64, lldb_rbp_x86_64, lldb_rsi_x86_64, lldb_rdi_x86_64,
169 lldb_r8_x86_64, lldb_r9_x86_64, lldb_r10_x86_64, lldb_r11_x86_64,
170 lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64};
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
ConvertXMMToLLDBRegister(uint8_t xmm_reg)178 uint32_t EHProgramBuilder::ConvertXMMToLLDBRegister(uint8_t xmm_reg) {
179 static uint32_t xmm_to_lldb_register[] = {
180 lldb_xmm0_x86_64, lldb_xmm1_x86_64, lldb_xmm2_x86_64,
181 lldb_xmm3_x86_64, lldb_xmm4_x86_64, lldb_xmm5_x86_64,
182 lldb_xmm6_x86_64, lldb_xmm7_x86_64, lldb_xmm8_x86_64,
183 lldb_xmm9_x86_64, lldb_xmm10_x86_64, lldb_xmm11_x86_64,
184 lldb_xmm12_x86_64, lldb_xmm13_x86_64, lldb_xmm14_x86_64,
185 lldb_xmm15_x86_64};
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
ProcessUnwindCode(UnwindCode code)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(
205 EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, r, 8});
206
207 return true;
208 }
209 case UOP_AllocLarge: {
210 uint32_t fo;
211 if (!ParseBigOrScaledFrameOffset(fo, operation_info, 8))
212 return false;
213
214 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::ALLOCATE,
215 LLDB_INVALID_REGNUM, fo});
216
217 return true;
218 }
219 case UOP_AllocSmall: {
220 m_program.emplace_back(
221 EHInstruction{o, EHInstruction::Type::ALLOCATE, LLDB_INVALID_REGNUM,
222 static_cast<uint32_t>(operation_info) * 8 + 8});
223 return true;
224 }
225 case UOP_SetFPReg: {
226 uint32_t fpr = LLDB_INVALID_REGNUM;
227 if (m_iterator.GetUnwindInfo()->getFrameRegister())
228 fpr = ConvertMachineToLLDBRegister(
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{
238 o, EHInstruction::Type::SET_FRAME_POINTER_REGISTER, fpr, fpro});
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(
254 EHInstruction{o, EHInstruction::Type::SAVE_REGISTER, r, fo});
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(
277 EHInstruction{o, EHInstruction::Type::SAVE_REGISTER, r, fo});
278
279 return true;
280 }
281 case UOP_PushMachFrame: {
282 if (operation_info)
283 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::ALLOCATE,
284 LLDB_INVALID_REGNUM, 8});
285 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,
286 lldb_rip_x86_64, 8});
287 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,
288 lldb_cs_x86_64, 8});
289 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,
290 lldb_rflags_x86_64, 8});
291 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,
292 lldb_rsp_x86_64, 8});
293 m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,
294 lldb_ss_x86_64, 8});
295
296 return true;
297 }
298 default:
299 return false;
300 }
301 }
302
Finalize()303 void EHProgramBuilder::Finalize() {
304 for (const EHInstruction &i : m_program)
305 if (i.reg == lldb_rip_x86_64)
306 return;
307
308 m_program.emplace_back(
309 EHInstruction{0, EHInstruction::Type::PUSH_REGISTER, lldb_rip_x86_64, 8});
310 }
311
ParseBigOrScaledFrameOffset(uint32_t & result,bool big,uint32_t scale)312 bool EHProgramBuilder::ParseBigOrScaledFrameOffset(uint32_t &result, bool big,
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
ParseBigFrameOffset(uint32_t & result)327 bool EHProgramBuilder::ParseBigFrameOffset(uint32_t &result) {
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
ParseFrameOffset(uint32_t & result)342 bool EHProgramBuilder::ParseFrameOffset(uint32_t &result) {
343 if (!m_iterator.GetNext())
344 return false;
345
346 result = m_iterator.GetUnwindCode()->FrameOffset;
347
348 return true;
349 }
350
351 class EHProgramRange {
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
EHProgramRange(EHProgram::const_iterator begin,EHProgram::const_iterator end)365 EHProgramRange::EHProgramRange(EHProgram::const_iterator begin,
366 EHProgram::const_iterator end)
367 : m_begin(begin), m_end(end) {}
368
BuildUnwindPlanRow() const369 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) {
380 case EHInstruction::Type::SET_FRAME_POINTER_REGISTER:
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) {
398 case EHInstruction::Type::PUSH_REGISTER:
399 row->SetRegisterLocationToAtCFAPlusOffset(
400 it->reg, rsp_frame_offset - cfa_frame_offset, false);
401 rsp_frame_offset += it->frame_offset;
402 break;
403 case EHInstruction::Type::ALLOCATE:
404 rsp_frame_offset += it->frame_offset;
405 break;
406 case EHInstruction::Type::SAVE_REGISTER:
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
GetCFAFrameOffset() const420 int32_t EHProgramRange::GetCFAFrameOffset() const {
421 int32_t result = 0;
422
423 for (EHProgram::const_iterator it = m_begin; it != m_end; ++it) {
424 switch (it->type) {
425 case EHInstruction::Type::PUSH_REGISTER:
426 case EHInstruction::Type::ALLOCATE:
427 result += it->frame_offset;
428 break;
429 default:
430 break;
431 }
432 }
433
434 return result;
435 }
436
PECallFrameInfo(ObjectFilePECOFF & object_file,uint32_t exception_dir_rva,uint32_t exception_dir_size)437 PECallFrameInfo::PECallFrameInfo(ObjectFilePECOFF &object_file,
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
GetAddressRange(Address addr,AddressRange & range)444 bool PECallFrameInfo::GetAddressRange(Address addr, AddressRange &range) {
445 range.Clear();
446
447 const RuntimeFunction *runtime_function =
448 FindRuntimeFunctionIntersectsWithRange(AddressRange(addr, 1));
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
GetUnwindPlan(const Address & addr,UnwindPlan & unwind_plan)460 bool PECallFrameInfo::GetUnwindPlan(const Address &addr,
461 UnwindPlan &unwind_plan) {
462 return GetUnwindPlan(AddressRange(addr, 1), unwind_plan);
463 }
464
GetUnwindPlan(const AddressRange & range,UnwindPlan & unwind_plan)465 bool PECallFrameInfo::GetUnwindPlan(const AddressRange &range,
466 UnwindPlan &unwind_plan) {
467 unwind_plan.Clear();
468
469 unwind_plan.SetSourceName("PE EH info");
470 unwind_plan.SetSourcedFromCompiler(eLazyBoolYes);
471 unwind_plan.SetRegisterKind(eRegisterKindLLDB);
472
473 const RuntimeFunction *runtime_function =
474 FindRuntimeFunctionIntersectsWithRange(range);
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
500 unwind_plan.SetPlanValidAddressRange(AddressRange(
501 m_object_file.GetAddress(runtime_function->StartAddress),
502 runtime_function->EndAddress - runtime_function->StartAddress));
503 unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
504
505 return true;
506 }
507
FindRuntimeFunctionIntersectsWithRange(const AddressRange & range) const508 const RuntimeFunction *PECallFrameInfo::FindRuntimeFunctionIntersectsWithRange(
509 const AddressRange &range) const {
510 uint32_t rva = m_object_file.GetRVA(range.GetBaseAddress());
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 }
537