Coverage Report

Created: 2022-01-22 13:19

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.cpp
Line
Count
Source (jump to first uncovered line)
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>
14
static const T *TypedRead(const DataExtractor &data_extractor, offset_t &offset,
15
39
                          offset_t size = sizeof(T)) {
16
39
  return static_cast<const T *>(data_extractor.GetData(&offset, size));
17
39
}
PECallFrameInfo.cpp:llvm::Win64EH::UnwindInfo const* TypedRead<llvm::Win64EH::UnwindInfo>(lldb_private::DataExtractor const&, unsigned long long&, unsigned long long)
Line
Count
Source
15
4
                          offset_t size = sizeof(T)) {
16
4
  return static_cast<const T *>(data_extractor.GetData(&offset, size));
17
4
}
PECallFrameInfo.cpp:llvm::Win64EH::UnwindCode const* TypedRead<llvm::Win64EH::UnwindCode>(lldb_private::DataExtractor const&, unsigned long long&, unsigned long long)
Line
Count
Source
15
28
                          offset_t size = sizeof(T)) {
16
28
  return static_cast<const T *>(data_extractor.GetData(&offset, size));
17
28
}
PECallFrameInfo.cpp:llvm::Win64EH::RuntimeFunction const* TypedRead<llvm::Win64EH::RuntimeFunction>(lldb_private::DataExtractor const&, unsigned long long&, unsigned long long)
Line
Count
Source
15
7
                          offset_t size = sizeof(T)) {
16
7
  return static_cast<const T *>(data_extractor.GetData(&offset, size));
17
7
}
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();
40
3
  bool IsError() const { return m_error; }
41
42
3
  const UnwindInfo *GetUnwindInfo() const { return m_unwind_info; }
43
28
  const UnwindCode *GetUnwindCode() const { return m_unwind_code; }
44
23
  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
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
3
      m_chained(false) {}
68
69
31
bool UnwindCodesIterator::GetNext() {
70
31
  static constexpr int UNWIND_INFO_SIZE = 4;
71
72
31
  m_error = false;
73
31
  m_unwind_code = nullptr;
74
32
  while (!m_unwind_code) {
75
32
    if (!m_unwind_info) {
76
4
      m_unwind_info_data =
77
4
          m_object_file.ReadImageDataByRVA(m_unwind_info_rva, UNWIND_INFO_SIZE);
78
79
4
      offset_t offset = 0;
80
4
      m_unwind_info =
81
4
          TypedRead<UnwindInfo>(m_unwind_info_data, offset, UNWIND_INFO_SIZE);
82
4
      if (!m_unwind_info) {
83
0
        m_error = true;
84
0
        break;
85
0
      }
86
87
4
      m_unwind_code_data = m_object_file.ReadImageDataByRVA(
88
4
          m_unwind_info_rva + UNWIND_INFO_SIZE,
89
4
          m_unwind_info->NumCodes * sizeof(UnwindCode));
90
4
      m_unwind_code_offset = 0;
91
4
    }
92
93
32
    if (m_unwind_code_offset < m_unwind_code_data.GetByteSize()) {
94
28
      m_unwind_code =
95
28
          TypedRead<UnwindCode>(m_unwind_code_data, m_unwind_code_offset);
96
28
      m_error = !m_unwind_code;
97
28
      break;
98
28
    }
99
100
4
    if (!(m_unwind_info->getFlags() & UNW_ChainInfo))
101
3
      break;
102
103
1
    uint32_t runtime_function_rva =
104
1
        m_unwind_info_rva + UNWIND_INFO_SIZE +
105
1
        ((m_unwind_info->NumCodes + 1) & ~1) * sizeof(UnwindCode);
106
1
    DataExtractor runtime_function_data = m_object_file.ReadImageDataByRVA(
107
1
        runtime_function_rva, sizeof(RuntimeFunction));
108
109
1
    offset_t offset = 0;
110
1
    const auto *runtime_function =
111
1
        TypedRead<RuntimeFunction>(runtime_function_data, offset);
112
1
    if (!runtime_function) {
113
0
      m_error = true;
114
0
      break;
115
0
    }
116
117
1
    m_unwind_info_rva = runtime_function->UnwindInfoOffset;
118
1
    m_unwind_info = nullptr;
119
1
    m_chained = true;
120
1
  }
121
122
31
  return !!m_unwind_code;
123
31
}
124
125
class EHProgramBuilder {
126
public:
127
  EHProgramBuilder(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva);
128
129
  bool Build();
130
131
52
  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
148
EHProgramBuilder::EHProgramBuilder(ObjectFilePECOFF &object_file,
149
                                   uint32_t unwind_info_rva)
150
3
    : m_iterator(object_file, unwind_info_rva) {}
151
152
3
bool EHProgramBuilder::Build() {
153
26
  while (m_iterator.GetNext())
154
23
    if (!ProcessUnwindCode(*m_iterator.GetUnwindCode()))
155
0
      return false;
156
157
3
  if (m_iterator.IsError())
158
0
    return false;
159
160
3
  Finalize();
161
162
3
  return true;
163
3
}
164
165
20
uint32_t EHProgramBuilder::ConvertMachineToLLDBRegister(uint8_t machine_reg) {
166
20
  static uint32_t machine_to_lldb_register[] = {
167
20
      lldb_rax_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64, lldb_rbx_x86_64,
168
20
      lldb_rsp_x86_64, lldb_rbp_x86_64, lldb_rsi_x86_64, lldb_rdi_x86_64,
169
20
      lldb_r8_x86_64,  lldb_r9_x86_64,  lldb_r10_x86_64, lldb_r11_x86_64,
170
20
      lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64};
171
172
20
  if (machine_reg >= llvm::array_lengthof(machine_to_lldb_register))
173
0
    return LLDB_INVALID_REGNUM;
174
175
20
  return machine_to_lldb_register[machine_reg];
176
20
}
177
178
0
uint32_t EHProgramBuilder::ConvertXMMToLLDBRegister(uint8_t xmm_reg) {
179
0
  static uint32_t xmm_to_lldb_register[] = {
180
0
      lldb_xmm0_x86_64,  lldb_xmm1_x86_64,  lldb_xmm2_x86_64,
181
0
      lldb_xmm3_x86_64,  lldb_xmm4_x86_64,  lldb_xmm5_x86_64,
182
0
      lldb_xmm6_x86_64,  lldb_xmm7_x86_64,  lldb_xmm8_x86_64,
183
0
      lldb_xmm9_x86_64,  lldb_xmm10_x86_64, lldb_xmm11_x86_64,
184
0
      lldb_xmm12_x86_64, lldb_xmm13_x86_64, lldb_xmm14_x86_64,
185
0
      lldb_xmm15_x86_64};
186
187
0
  if (xmm_reg >= llvm::array_lengthof(xmm_to_lldb_register))
188
0
    return LLDB_INVALID_REGNUM;
189
190
0
  return xmm_to_lldb_register[xmm_reg];
191
0
}
192
193
23
bool EHProgramBuilder::ProcessUnwindCode(UnwindCode code) {
194
23
  uint8_t o = m_iterator.IsChained() ? 
06
:
code.u.CodeOffset17
;
195
23
  uint8_t unwind_operation = code.getUnwindOp();
196
23
  uint8_t operation_info = code.getOpInfo();
197
198
23
  switch (unwind_operation) {
199
15
  case UOP_PushNonVol: {
200
15
    uint32_t r = ConvertMachineToLLDBRegister(operation_info);
201
15
    if (r == LLDB_INVALID_REGNUM)
202
0
      return false;
203
204
15
    m_program.emplace_back(
205
15
        EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, r, 8});
206
207
15
    return true;
208
15
  }
209
1
  case UOP_AllocLarge: {
210
1
    uint32_t fo;
211
1
    if (!ParseBigOrScaledFrameOffset(fo, operation_info, 8))
212
0
      return false;
213
214
1
    m_program.emplace_back(EHInstruction{o, EHInstruction::Type::ALLOCATE,
215
1
                                         LLDB_INVALID_REGNUM, fo});
216
217
1
    return true;
218
1
  }
219
2
  case UOP_AllocSmall: {
220
2
    m_program.emplace_back(
221
2
        EHInstruction{o, EHInstruction::Type::ALLOCATE, LLDB_INVALID_REGNUM,
222
2
                      static_cast<uint32_t>(operation_info) * 8 + 8});
223
2
    return true;
224
1
  }
225
1
  case UOP_SetFPReg: {
226
1
    uint32_t fpr = LLDB_INVALID_REGNUM;
227
1
    if (m_iterator.GetUnwindInfo()->getFrameRegister())
228
1
      fpr = ConvertMachineToLLDBRegister(
229
1
          m_iterator.GetUnwindInfo()->getFrameRegister());
230
1
    if (fpr == LLDB_INVALID_REGNUM)
231
0
      return false;
232
233
1
    uint32_t fpro =
234
1
        static_cast<uint32_t>(m_iterator.GetUnwindInfo()->getFrameOffset()) *
235
1
        16;
236
237
1
    m_program.emplace_back(EHInstruction{
238
1
        o, EHInstruction::Type::SET_FRAME_POINTER_REGISTER, fpr, fpro});
239
240
1
    return true;
241
1
  }
242
4
  case UOP_SaveNonVol:
243
4
  case UOP_SaveNonVolBig: {
244
4
    uint32_t r = ConvertMachineToLLDBRegister(operation_info);
245
4
    if (r == LLDB_INVALID_REGNUM)
246
0
      return false;
247
248
4
    uint32_t fo;
249
4
    if (!ParseBigOrScaledFrameOffset(fo, unwind_operation == UOP_SaveNonVolBig,
250
4
                                     8))
251
0
      return false;
252
253
4
    m_program.emplace_back(
254
4
        EHInstruction{o, EHInstruction::Type::SAVE_REGISTER, r, fo});
255
256
4
    return true;
257
4
  }
258
0
  case UOP_Epilog: {
259
0
    return m_iterator.GetNext();
260
4
  }
261
0
  case UOP_SpareCode: {
262
    // ReSharper disable once CppIdenticalOperandsInBinaryExpression
263
0
    return m_iterator.GetNext() && m_iterator.GetNext();
264
4
  }
265
0
  case UOP_SaveXMM128:
266
0
  case UOP_SaveXMM128Big: {
267
0
    uint32_t r = ConvertXMMToLLDBRegister(operation_info);
268
0
    if (r == LLDB_INVALID_REGNUM)
269
0
      return false;
270
271
0
    uint32_t fo;
272
0
    if (!ParseBigOrScaledFrameOffset(fo, unwind_operation == UOP_SaveXMM128Big,
273
0
                                     16))
274
0
      return false;
275
276
0
    m_program.emplace_back(
277
0
        EHInstruction{o, EHInstruction::Type::SAVE_REGISTER, r, fo});
278
279
0
    return true;
280
0
  }
281
0
  case UOP_PushMachFrame: {
282
0
    if (operation_info)
283
0
      m_program.emplace_back(EHInstruction{o, EHInstruction::Type::ALLOCATE,
284
0
                                           LLDB_INVALID_REGNUM, 8});
285
0
    m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,
286
0
                                         lldb_rip_x86_64, 8});
287
0
    m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,
288
0
                                         lldb_cs_x86_64, 8});
289
0
    m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,
290
0
                                         lldb_rflags_x86_64, 8});
291
0
    m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,
292
0
                                         lldb_rsp_x86_64, 8});
293
0
    m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,
294
0
                                         lldb_ss_x86_64, 8});
295
296
0
    return true;
297
0
  }
298
0
  default:
299
0
    return false;
300
23
  }
301
23
}
302
303
3
void EHProgramBuilder::Finalize() {
304
3
  for (const EHInstruction &i : m_program)
305
23
    if (i.reg == lldb_rip_x86_64)
306
0
      return;
307
308
3
  m_program.emplace_back(
309
3
      EHInstruction{0, EHInstruction::Type::PUSH_REGISTER, lldb_rip_x86_64, 8});
310
3
}
311
312
bool EHProgramBuilder::ParseBigOrScaledFrameOffset(uint32_t &result, bool big,
313
5
                                                   uint32_t scale) {
314
5
  if (big) {
315
0
    if (!ParseBigFrameOffset(result))
316
0
      return false;
317
5
  } else {
318
5
    if (!ParseFrameOffset(result))
319
0
      return false;
320
321
5
    result *= scale;
322
5
  }
323
324
5
  return true;
325
5
}
326
327
0
bool EHProgramBuilder::ParseBigFrameOffset(uint32_t &result) {
328
0
  if (!m_iterator.GetNext())
329
0
    return false;
330
331
0
  result = m_iterator.GetUnwindCode()->FrameOffset;
332
333
0
  if (!m_iterator.GetNext())
334
0
    return false;
335
336
0
  result += static_cast<uint32_t>(m_iterator.GetUnwindCode()->FrameOffset)
337
0
            << 16;
338
339
0
  return true;
340
0
}
341
342
5
bool EHProgramBuilder::ParseFrameOffset(uint32_t &result) {
343
5
  if (!m_iterator.GetNext())
344
0
    return false;
345
346
5
  result = m_iterator.GetUnwindCode()->FrameOffset;
347
348
5
  return true;
349
5
}
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
365
EHProgramRange::EHProgramRange(EHProgram::const_iterator begin,
366
                               EHProgram::const_iterator end)
367
20
    : m_begin(begin), m_end(end) {}
368
369
20
std::unique_ptr<UnwindPlan::Row> EHProgramRange::BuildUnwindPlanRow() const {
370
20
  std::unique_ptr<UnwindPlan::Row> row = std::make_unique<UnwindPlan::Row>();
371
372
20
  if (m_begin != m_end)
373
20
    row->SetOffset(m_begin->offset);
374
375
20
  int32_t cfa_frame_offset = GetCFAFrameOffset();
376
377
20
  bool frame_pointer_found = false;
378
97
  for (EHProgram::const_iterator it = m_begin; it != m_end; 
++it77
) {
379
81
    switch (it->type) {
380
4
    case EHInstruction::Type::SET_FRAME_POINTER_REGISTER:
381
4
      row->GetCFAValue().SetIsRegisterPlusOffset(it->reg, cfa_frame_offset -
382
4
                                                              it->frame_offset);
383
4
      frame_pointer_found = true;
384
4
      break;
385
77
    default:
386
77
      break;
387
81
    }
388
81
    if (frame_pointer_found)
389
4
      break;
390
81
  }
391
20
  if (!frame_pointer_found)
392
16
    row->GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64,
393
16
                                               cfa_frame_offset);
394
395
20
  int32_t rsp_frame_offset = 0;
396
129
  for (EHProgram::const_iterator it = m_begin; it != m_end; 
++it109
) {
397
109
    switch (it->type) {
398
90
    case EHInstruction::Type::PUSH_REGISTER:
399
90
      row->SetRegisterLocationToAtCFAPlusOffset(
400
90
          it->reg, rsp_frame_offset - cfa_frame_offset, false);
401
90
      rsp_frame_offset += it->frame_offset;
402
90
      break;
403
8
    case EHInstruction::Type::ALLOCATE:
404
8
      rsp_frame_offset += it->frame_offset;
405
8
      break;
406
7
    case EHInstruction::Type::SAVE_REGISTER:
407
7
      row->SetRegisterLocationToAtCFAPlusOffset(
408
7
          it->reg, it->frame_offset - cfa_frame_offset, false);
409
7
      break;
410
4
    default:
411
4
      break;
412
109
    }
413
109
  }
414
415
20
  row->SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, false);
416
417
20
  return row;
418
20
}
419
420
20
int32_t EHProgramRange::GetCFAFrameOffset() const {
421
20
  int32_t result = 0;
422
423
129
  for (EHProgram::const_iterator it = m_begin; it != m_end; 
++it109
) {
424
109
    switch (it->type) {
425
90
    case EHInstruction::Type::PUSH_REGISTER:
426
98
    case EHInstruction::Type::ALLOCATE:
427
98
      result += it->frame_offset;
428
98
      break;
429
11
    default:
430
11
      break;
431
109
    }
432
109
  }
433
434
20
  return result;
435
20
}
436
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
3
                                                      exception_dir_size)) {}
Unexecuted instantiation: PECallFrameInfo::PECallFrameInfo(ObjectFilePECOFF&, unsigned int, unsigned int)
PECallFrameInfo::PECallFrameInfo(ObjectFilePECOFF&, unsigned int, unsigned int)
Line
Count
Source
442
3
                                                      exception_dir_size)) {}
443
444
0
bool PECallFrameInfo::GetAddressRange(Address addr, AddressRange &range) {
445
0
  range.Clear();
446
447
0
  const RuntimeFunction *runtime_function =
448
0
      FindRuntimeFunctionIntersectsWithRange(AddressRange(addr, 1));
449
0
  if (!runtime_function)
450
0
    return false;
451
452
0
  range.GetBaseAddress() =
453
0
      m_object_file.GetAddress(runtime_function->StartAddress);
454
0
  range.SetByteSize(runtime_function->EndAddress -
455
0
                    runtime_function->StartAddress);
456
457
0
  return true;
458
0
}
459
460
bool PECallFrameInfo::GetUnwindPlan(const Address &addr,
461
3
                                    UnwindPlan &unwind_plan) {
462
3
  return GetUnwindPlan(AddressRange(addr, 1), unwind_plan);
463
3
}
464
465
bool PECallFrameInfo::GetUnwindPlan(const AddressRange &range,
466
3
                                    UnwindPlan &unwind_plan) {
467
3
  unwind_plan.Clear();
468
469
3
  unwind_plan.SetSourceName("PE EH info");
470
3
  unwind_plan.SetSourcedFromCompiler(eLazyBoolYes);
471
3
  unwind_plan.SetRegisterKind(eRegisterKindLLDB);
472
473
3
  const RuntimeFunction *runtime_function =
474
3
      FindRuntimeFunctionIntersectsWithRange(range);
475
3
  if (!runtime_function)
476
0
    return false;
477
478
3
  EHProgramBuilder builder(m_object_file, runtime_function->UnwindInfoOffset);
479
3
  if (!builder.Build())
480
0
    return false;
481
482
3
  std::vector<UnwindPlan::RowSP> rows;
483
484
3
  uint32_t last_offset = UINT32_MAX;
485
29
  for (auto it = builder.GetProgram().begin(); it != builder.GetProgram().end();
486
26
       ++it) {
487
26
    if (it->offset == last_offset)
488
6
      continue;
489
490
20
    EHProgramRange program_range =
491
20
        EHProgramRange(it, builder.GetProgram().end());
492
20
    rows.push_back(program_range.BuildUnwindPlanRow());
493
494
20
    last_offset = it->offset;
495
20
  }
496
497
23
  for (auto it = rows.rbegin(); it != rows.rend(); 
++it20
)
498
20
    unwind_plan.AppendRow(*it);
499
500
3
  unwind_plan.SetPlanValidAddressRange(AddressRange(
501
3
      m_object_file.GetAddress(runtime_function->StartAddress),
502
3
      runtime_function->EndAddress - runtime_function->StartAddress));
503
3
  unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
504
505
3
  return true;
506
3
}
507
508
const RuntimeFunction *PECallFrameInfo::FindRuntimeFunctionIntersectsWithRange(
509
3
    const AddressRange &range) const {
510
3
  uint32_t rva = m_object_file.GetRVA(range.GetBaseAddress());
511
3
  addr_t size = range.GetByteSize();
512
513
3
  uint32_t begin = 0;
514
3
  uint32_t end = m_exception_dir.GetByteSize() / sizeof(RuntimeFunction);
515
6
  while (begin < end) {
516
6
    uint32_t curr = (begin + end) / 2;
517
518
6
    offset_t offset = curr * sizeof(RuntimeFunction);
519
6
    const auto *runtime_function =
520
6
        TypedRead<RuntimeFunction>(m_exception_dir, offset);
521
6
    if (!runtime_function)
522
0
      break;
523
524
6
    if (runtime_function->StartAddress < rva + size &&
525
6
        
runtime_function->EndAddress > rva5
)
526
3
      return runtime_function;
527
528
3
    if (runtime_function->StartAddress >= rva + size)
529
1
      end = curr;
530
531
3
    if (runtime_function->EndAddress <= rva)
532
2
      begin = curr + 1;
533
3
  }
534
535
0
  return nullptr;
536
3
}