1 #include "PECallFrameInfo.h"
3 #include "ObjectFilePECOFF.h"
5 #include "Plugins/Process/Utility/lldb-x86-register-enums.h"
6 #include "lldb/Symbol/UnwindPlan.h"
7 #include "llvm/Support/Win64EH.h"
10 using namespace lldb_private
;
11 using namespace llvm::Win64EH
;
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
));
19 struct EHInstruction
{
23 SET_FRAME_POINTER_REGISTER
,
30 uint32_t frame_offset
;
33 using EHProgram
= std::vector
<EHInstruction
>;
35 class UnwindCodesIterator
{
37 UnwindCodesIterator(ObjectFilePECOFF
&object_file
, uint32_t unwind_info_rva
);
40 bool IsError() const { return m_error
; }
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
; }
47 ObjectFilePECOFF
&m_object_file
;
51 uint32_t m_unwind_info_rva
;
52 DataExtractor m_unwind_info_data
;
53 const UnwindInfo
*m_unwind_info
= nullptr;
55 DataExtractor m_unwind_code_data
;
56 offset_t m_unwind_code_offset
;
57 const UnwindCode
*m_unwind_code
= nullptr;
59 bool m_chained
= false;
62 UnwindCodesIterator::UnwindCodesIterator(ObjectFilePECOFF
&object_file
,
63 uint32_t unwind_info_rva
)
64 : m_object_file(object_file
),
65 m_unwind_info_rva(unwind_info_rva
), m_unwind_code_offset
{} {}
67 bool UnwindCodesIterator::GetNext() {
68 static constexpr int UNWIND_INFO_SIZE
= 4;
71 m_unwind_code
= nullptr;
72 while (!m_unwind_code
) {
75 m_object_file
.ReadImageDataByRVA(m_unwind_info_rva
, UNWIND_INFO_SIZE
);
79 TypedRead
<UnwindInfo
>(m_unwind_info_data
, offset
, UNWIND_INFO_SIZE
);
85 m_unwind_code_data
= m_object_file
.ReadImageDataByRVA(
86 m_unwind_info_rva
+ UNWIND_INFO_SIZE
,
87 m_unwind_info
->NumCodes
* sizeof(UnwindCode
));
88 m_unwind_code_offset
= 0;
91 if (m_unwind_code_offset
< m_unwind_code_data
.GetByteSize()) {
93 TypedRead
<UnwindCode
>(m_unwind_code_data
, m_unwind_code_offset
);
94 m_error
= !m_unwind_code
;
98 if (!(m_unwind_info
->getFlags() & UNW_ChainInfo
))
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
));
108 const auto *runtime_function
=
109 TypedRead
<RuntimeFunction
>(runtime_function_data
, offset
);
110 if (!runtime_function
) {
115 m_unwind_info_rva
= runtime_function
->UnwindInfoOffset
;
116 m_unwind_info
= nullptr;
120 return !!m_unwind_code
;
123 class EHProgramBuilder
{
125 EHProgramBuilder(ObjectFilePECOFF
&object_file
, uint32_t unwind_info_rva
);
129 const EHProgram
&GetProgram() const { return m_program
; }
132 static uint32_t ConvertMachineToLLDBRegister(uint8_t machine_reg
);
133 static uint32_t ConvertXMMToLLDBRegister(uint8_t xmm_reg
);
135 bool ProcessUnwindCode(UnwindCode code
);
138 bool ParseBigOrScaledFrameOffset(uint32_t &result
, bool big
, uint32_t scale
);
139 bool ParseBigFrameOffset(uint32_t &result
);
140 bool ParseFrameOffset(uint32_t &result
);
142 UnwindCodesIterator m_iterator
;
146 EHProgramBuilder::EHProgramBuilder(ObjectFilePECOFF
&object_file
,
147 uint32_t unwind_info_rva
)
148 : m_iterator(object_file
, unwind_info_rva
) {}
150 bool EHProgramBuilder::Build() {
151 while (m_iterator
.GetNext())
152 if (!ProcessUnwindCode(*m_iterator
.GetUnwindCode()))
155 if (m_iterator
.IsError())
163 uint32_t EHProgramBuilder::ConvertMachineToLLDBRegister(uint8_t machine_reg
) {
164 static uint32_t machine_to_lldb_register
[] = {
165 lldb_rax_x86_64
, lldb_rcx_x86_64
, lldb_rdx_x86_64
, lldb_rbx_x86_64
,
166 lldb_rsp_x86_64
, lldb_rbp_x86_64
, lldb_rsi_x86_64
, lldb_rdi_x86_64
,
167 lldb_r8_x86_64
, lldb_r9_x86_64
, lldb_r10_x86_64
, lldb_r11_x86_64
,
168 lldb_r12_x86_64
, lldb_r13_x86_64
, lldb_r14_x86_64
, lldb_r15_x86_64
};
170 if (machine_reg
>= std::size(machine_to_lldb_register
))
171 return LLDB_INVALID_REGNUM
;
173 return machine_to_lldb_register
[machine_reg
];
176 uint32_t EHProgramBuilder::ConvertXMMToLLDBRegister(uint8_t xmm_reg
) {
177 static uint32_t xmm_to_lldb_register
[] = {
178 lldb_xmm0_x86_64
, lldb_xmm1_x86_64
, lldb_xmm2_x86_64
,
179 lldb_xmm3_x86_64
, lldb_xmm4_x86_64
, lldb_xmm5_x86_64
,
180 lldb_xmm6_x86_64
, lldb_xmm7_x86_64
, lldb_xmm8_x86_64
,
181 lldb_xmm9_x86_64
, lldb_xmm10_x86_64
, lldb_xmm11_x86_64
,
182 lldb_xmm12_x86_64
, lldb_xmm13_x86_64
, lldb_xmm14_x86_64
,
185 if (xmm_reg
>= std::size(xmm_to_lldb_register
))
186 return LLDB_INVALID_REGNUM
;
188 return xmm_to_lldb_register
[xmm_reg
];
191 bool EHProgramBuilder::ProcessUnwindCode(UnwindCode code
) {
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();
196 switch (unwind_operation
) {
197 case UOP_PushNonVol
: {
198 uint32_t r
= ConvertMachineToLLDBRegister(operation_info
);
199 if (r
== LLDB_INVALID_REGNUM
)
202 m_program
.emplace_back(
203 EHInstruction
{o
, EHInstruction::Type::PUSH_REGISTER
, r
, 8});
207 case UOP_AllocLarge
: {
209 if (!ParseBigOrScaledFrameOffset(fo
, operation_info
, 8))
212 m_program
.emplace_back(EHInstruction
{o
, EHInstruction::Type::ALLOCATE
,
213 LLDB_INVALID_REGNUM
, fo
});
217 case UOP_AllocSmall
: {
218 m_program
.emplace_back(
219 EHInstruction
{o
, EHInstruction::Type::ALLOCATE
, LLDB_INVALID_REGNUM
,
220 static_cast<uint32_t>(operation_info
) * 8 + 8});
224 uint32_t fpr
= LLDB_INVALID_REGNUM
;
225 if (m_iterator
.GetUnwindInfo()->getFrameRegister())
226 fpr
= ConvertMachineToLLDBRegister(
227 m_iterator
.GetUnwindInfo()->getFrameRegister());
228 if (fpr
== LLDB_INVALID_REGNUM
)
232 static_cast<uint32_t>(m_iterator
.GetUnwindInfo()->getFrameOffset()) *
235 m_program
.emplace_back(EHInstruction
{
236 o
, EHInstruction::Type::SET_FRAME_POINTER_REGISTER
, fpr
, fpro
});
241 case UOP_SaveNonVolBig
: {
242 uint32_t r
= ConvertMachineToLLDBRegister(operation_info
);
243 if (r
== LLDB_INVALID_REGNUM
)
247 if (!ParseBigOrScaledFrameOffset(fo
, unwind_operation
== UOP_SaveNonVolBig
,
251 m_program
.emplace_back(
252 EHInstruction
{o
, EHInstruction::Type::SAVE_REGISTER
, r
, fo
});
257 return m_iterator
.GetNext();
259 case UOP_SpareCode
: {
260 // ReSharper disable once CppIdenticalOperandsInBinaryExpression
261 return m_iterator
.GetNext() && m_iterator
.GetNext();
264 case UOP_SaveXMM128Big
: {
265 uint32_t r
= ConvertXMMToLLDBRegister(operation_info
);
266 if (r
== LLDB_INVALID_REGNUM
)
270 if (!ParseBigOrScaledFrameOffset(fo
, unwind_operation
== UOP_SaveXMM128Big
,
274 m_program
.emplace_back(
275 EHInstruction
{o
, EHInstruction::Type::SAVE_REGISTER
, r
, fo
});
279 case UOP_PushMachFrame
: {
281 m_program
.emplace_back(EHInstruction
{o
, EHInstruction::Type::ALLOCATE
,
282 LLDB_INVALID_REGNUM
, 8});
283 m_program
.emplace_back(EHInstruction
{o
, EHInstruction::Type::PUSH_REGISTER
,
284 lldb_rip_x86_64
, 8});
285 m_program
.emplace_back(EHInstruction
{o
, EHInstruction::Type::PUSH_REGISTER
,
287 m_program
.emplace_back(EHInstruction
{o
, EHInstruction::Type::PUSH_REGISTER
,
288 lldb_rflags_x86_64
, 8});
289 m_program
.emplace_back(EHInstruction
{o
, EHInstruction::Type::PUSH_REGISTER
,
290 lldb_rsp_x86_64
, 8});
291 m_program
.emplace_back(EHInstruction
{o
, EHInstruction::Type::PUSH_REGISTER
,
301 void EHProgramBuilder::Finalize() {
302 for (const EHInstruction
&i
: m_program
)
303 if (i
.reg
== lldb_rip_x86_64
)
306 m_program
.emplace_back(
307 EHInstruction
{0, EHInstruction::Type::PUSH_REGISTER
, lldb_rip_x86_64
, 8});
310 bool EHProgramBuilder::ParseBigOrScaledFrameOffset(uint32_t &result
, bool big
,
313 if (!ParseBigFrameOffset(result
))
316 if (!ParseFrameOffset(result
))
325 bool EHProgramBuilder::ParseBigFrameOffset(uint32_t &result
) {
326 if (!m_iterator
.GetNext())
329 result
= m_iterator
.GetUnwindCode()->FrameOffset
;
331 if (!m_iterator
.GetNext())
334 result
+= static_cast<uint32_t>(m_iterator
.GetUnwindCode()->FrameOffset
)
340 bool EHProgramBuilder::ParseFrameOffset(uint32_t &result
) {
341 if (!m_iterator
.GetNext())
344 result
= m_iterator
.GetUnwindCode()->FrameOffset
;
349 class EHProgramRange
{
351 EHProgramRange(EHProgram::const_iterator begin
,
352 EHProgram::const_iterator end
);
354 std::unique_ptr
<UnwindPlan::Row
> BuildUnwindPlanRow() const;
357 int32_t GetCFAFrameOffset() const;
359 EHProgram::const_iterator m_begin
;
360 EHProgram::const_iterator m_end
;
363 EHProgramRange::EHProgramRange(EHProgram::const_iterator begin
,
364 EHProgram::const_iterator end
)
365 : m_begin(begin
), m_end(end
) {}
367 std::unique_ptr
<UnwindPlan::Row
> EHProgramRange::BuildUnwindPlanRow() const {
368 std::unique_ptr
<UnwindPlan::Row
> row
= std::make_unique
<UnwindPlan::Row
>();
370 if (m_begin
!= m_end
)
371 row
->SetOffset(m_begin
->offset
);
373 int32_t cfa_frame_offset
= GetCFAFrameOffset();
375 bool frame_pointer_found
= false;
376 for (EHProgram::const_iterator it
= m_begin
; it
!= m_end
; ++it
) {
378 case EHInstruction::Type::SET_FRAME_POINTER_REGISTER
:
379 row
->GetCFAValue().SetIsRegisterPlusOffset(it
->reg
, cfa_frame_offset
-
381 frame_pointer_found
= true;
386 if (frame_pointer_found
)
389 if (!frame_pointer_found
)
390 row
->GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64
,
393 int32_t rsp_frame_offset
= 0;
394 for (EHProgram::const_iterator it
= m_begin
; it
!= m_end
; ++it
) {
396 case EHInstruction::Type::PUSH_REGISTER
:
397 row
->SetRegisterLocationToAtCFAPlusOffset(
398 it
->reg
, rsp_frame_offset
- cfa_frame_offset
, false);
399 rsp_frame_offset
+= it
->frame_offset
;
401 case EHInstruction::Type::ALLOCATE
:
402 rsp_frame_offset
+= it
->frame_offset
;
404 case EHInstruction::Type::SAVE_REGISTER
:
405 row
->SetRegisterLocationToAtCFAPlusOffset(
406 it
->reg
, it
->frame_offset
- cfa_frame_offset
, false);
413 row
->SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64
, 0, false);
418 int32_t EHProgramRange::GetCFAFrameOffset() const {
421 for (EHProgram::const_iterator it
= m_begin
; it
!= m_end
; ++it
) {
423 case EHInstruction::Type::PUSH_REGISTER
:
424 case EHInstruction::Type::ALLOCATE
:
425 result
+= it
->frame_offset
;
435 PECallFrameInfo::PECallFrameInfo(ObjectFilePECOFF
&object_file
,
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
)) {}
442 bool PECallFrameInfo::GetAddressRange(Address addr
, AddressRange
&range
) {
445 const RuntimeFunction
*runtime_function
=
446 FindRuntimeFunctionIntersectsWithRange(AddressRange(addr
, 1));
447 if (!runtime_function
)
450 range
.GetBaseAddress() =
451 m_object_file
.GetAddress(runtime_function
->StartAddress
);
452 range
.SetByteSize(runtime_function
->EndAddress
-
453 runtime_function
->StartAddress
);
458 bool PECallFrameInfo::GetUnwindPlan(const Address
&addr
,
459 UnwindPlan
&unwind_plan
) {
460 return GetUnwindPlan(AddressRange(addr
, 1), unwind_plan
);
463 bool PECallFrameInfo::GetUnwindPlan(const AddressRange
&range
,
464 UnwindPlan
&unwind_plan
) {
467 unwind_plan
.SetSourceName("PE EH info");
468 unwind_plan
.SetSourcedFromCompiler(eLazyBoolYes
);
469 unwind_plan
.SetRegisterKind(eRegisterKindLLDB
);
471 const RuntimeFunction
*runtime_function
=
472 FindRuntimeFunctionIntersectsWithRange(range
);
473 if (!runtime_function
)
476 EHProgramBuilder
builder(m_object_file
, runtime_function
->UnwindInfoOffset
);
477 if (!builder
.Build())
480 std::vector
<UnwindPlan::RowSP
> rows
;
482 uint32_t last_offset
= UINT32_MAX
;
483 for (auto it
= builder
.GetProgram().begin(); it
!= builder
.GetProgram().end();
485 if (it
->offset
== last_offset
)
488 EHProgramRange program_range
=
489 EHProgramRange(it
, builder
.GetProgram().end());
490 rows
.push_back(program_range
.BuildUnwindPlanRow());
492 last_offset
= it
->offset
;
495 for (auto it
= rows
.rbegin(); it
!= rows
.rend(); ++it
)
496 unwind_plan
.AppendRow(*it
);
498 unwind_plan
.SetPlanValidAddressRange(AddressRange(
499 m_object_file
.GetAddress(runtime_function
->StartAddress
),
500 runtime_function
->EndAddress
- runtime_function
->StartAddress
));
501 unwind_plan
.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo
);
506 const RuntimeFunction
*PECallFrameInfo::FindRuntimeFunctionIntersectsWithRange(
507 const AddressRange
&range
) const {
508 uint32_t rva
= m_object_file
.GetRVA(range
.GetBaseAddress());
509 addr_t size
= range
.GetByteSize();
512 uint32_t end
= m_exception_dir
.GetByteSize() / sizeof(RuntimeFunction
);
513 while (begin
< end
) {
514 uint32_t curr
= (begin
+ end
) / 2;
516 offset_t offset
= curr
* sizeof(RuntimeFunction
);
517 const auto *runtime_function
=
518 TypedRead
<RuntimeFunction
>(m_exception_dir
, offset
);
519 if (!runtime_function
)
522 if (runtime_function
->StartAddress
< rva
+ size
&&
523 runtime_function
->EndAddress
> rva
)
524 return runtime_function
;
526 if (runtime_function
->StartAddress
>= rva
+ size
)
529 if (runtime_function
->EndAddress
<= rva
)