1 //===-- UnwindAssemblyInstEmulation.cpp -----------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "UnwindAssemblyInstEmulation.h"
10 
11 #include "lldb/Core/Address.h"
12 #include "lldb/Core/Disassembler.h"
13 #include "lldb/Core/DumpDataExtractor.h"
14 #include "lldb/Core/DumpRegisterValue.h"
15 #include "lldb/Core/FormatEntity.h"
16 #include "lldb/Core/PluginManager.h"
17 #include "lldb/Target/ExecutionContext.h"
18 #include "lldb/Target/Process.h"
19 #include "lldb/Target/Target.h"
20 #include "lldb/Target/Thread.h"
21 #include "lldb/Utility/ArchSpec.h"
22 #include "lldb/Utility/DataBufferHeap.h"
23 #include "lldb/Utility/DataExtractor.h"
24 #include "lldb/Utility/LLDBLog.h"
25 #include "lldb/Utility/Log.h"
26 #include "lldb/Utility/Status.h"
27 #include "lldb/Utility/StreamString.h"
28 
29 using namespace lldb;
30 using namespace lldb_private;
31 
LLDB_PLUGIN_DEFINE(UnwindAssemblyInstEmulation)32 LLDB_PLUGIN_DEFINE(UnwindAssemblyInstEmulation)
33 
34 //  UnwindAssemblyInstEmulation method definitions
35 
36 bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
37     AddressRange &range, Thread &thread, UnwindPlan &unwind_plan) {
38   std::vector<uint8_t> function_text(range.GetByteSize());
39   ProcessSP process_sp(thread.GetProcess());
40   if (process_sp) {
41     Status error;
42     const bool force_live_memory = true;
43     if (process_sp->GetTarget().ReadMemory(
44             range.GetBaseAddress(), function_text.data(), range.GetByteSize(),
45             error, force_live_memory) != range.GetByteSize()) {
46       return false;
47     }
48   }
49   return GetNonCallSiteUnwindPlanFromAssembly(
50       range, function_text.data(), function_text.size(), unwind_plan);
51 }
52 
GetNonCallSiteUnwindPlanFromAssembly(AddressRange & range,uint8_t * opcode_data,size_t opcode_size,UnwindPlan & unwind_plan)53 bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
54     AddressRange &range, uint8_t *opcode_data, size_t opcode_size,
55     UnwindPlan &unwind_plan) {
56   if (opcode_data == nullptr || opcode_size == 0)
57     return false;
58 
59   if (range.GetByteSize() == 0 || !range.GetBaseAddress().IsValid() ||
60       !m_inst_emulator_up)
61     return false;
62 
63   // The instruction emulation subclass setup the unwind plan for the first
64   // instruction.
65   m_inst_emulator_up->CreateFunctionEntryUnwind(unwind_plan);
66 
67   // CreateFunctionEntryUnwind should have created the first row. If it doesn't,
68   // then we are done.
69   if (unwind_plan.GetRowCount() == 0)
70     return false;
71 
72   const bool prefer_file_cache = true;
73   DisassemblerSP disasm_sp(Disassembler::DisassembleBytes(
74       m_arch, nullptr, nullptr, nullptr, nullptr, range.GetBaseAddress(),
75       opcode_data, opcode_size, 99999, prefer_file_cache));
76 
77   if (!disasm_sp)
78     return false;
79 
80   Log *log = GetLog(LLDBLog::Unwind);
81 
82   m_range_ptr = &range;
83   m_unwind_plan_ptr = &unwind_plan;
84 
85   const uint32_t addr_byte_size = m_arch.GetAddressByteSize();
86   const bool show_address = true;
87   const bool show_bytes = true;
88   const bool show_control_flow_kind = false;
89 
90   m_state.cfa_reg_info = *m_inst_emulator_up->GetRegisterInfo(
91       unwind_plan.GetRegisterKind(), unwind_plan.GetInitialCFARegister());
92   m_state.fp_is_cfa = false;
93   m_state.register_values.clear();
94 
95   m_pushed_regs.clear();
96 
97   // Initialize the CFA with a known value. In the 32 bit case it will be
98   // 0x80000000, and in the 64 bit case 0x8000000000000000. We use the address
99   // byte size to be safe for any future address sizes
100   m_initial_sp = (1ull << ((addr_byte_size * 8) - 1));
101   RegisterValue cfa_reg_value;
102   cfa_reg_value.SetUInt(m_initial_sp, m_state.cfa_reg_info.byte_size);
103   SetRegisterValue(m_state.cfa_reg_info, cfa_reg_value);
104 
105   const InstructionList &inst_list = disasm_sp->GetInstructionList();
106   const size_t num_instructions = inst_list.GetSize();
107 
108   if (num_instructions > 0) {
109     Instruction *inst = inst_list.GetInstructionAtIndex(0).get();
110     const lldb::addr_t base_addr = inst->GetAddress().GetFileAddress();
111 
112     // Map for storing the unwind state at a given offset. When we see a forward
113     // branch we add a new entry to this map with the actual unwind plan row and
114     // register context for the target address of the branch as the current data
115     // have to be valid for the target address of the branch too if we are in
116     // the same function.
117     std::map<lldb::addr_t, UnwindState> saved_unwind_states;
118 
119     // Make a copy of the current instruction Row and save it in m_state so
120     // we can add updates as we process the instructions.
121     m_state.row = *unwind_plan.GetLastRow();
122 
123     // Add the initial state to the save list with offset 0.
124     auto condition_block_start_state =
125         saved_unwind_states.emplace(0, m_state).first;
126 
127     // The architecture dependent condition code of the last processed
128     // instruction.
129     EmulateInstruction::InstructionCondition last_condition =
130         EmulateInstruction::UnconditionalCondition;
131 
132     for (size_t idx = 0; idx < num_instructions; ++idx) {
133       m_curr_row_modified = false;
134       m_forward_branch_offset = 0;
135 
136       inst = inst_list.GetInstructionAtIndex(idx).get();
137       if (!inst)
138         continue;
139 
140       lldb::addr_t current_offset =
141           inst->GetAddress().GetFileAddress() - base_addr;
142       auto it = saved_unwind_states.upper_bound(current_offset);
143       assert(it != saved_unwind_states.begin() &&
144              "Unwind row for the function entry missing");
145       --it; // Move it to the row corresponding to the current offset
146 
147       // If the offset of m_curr_row don't match with the offset we see in
148       // saved_unwind_states then we have to update current unwind state to
149       // the saved values. It is happening after we processed an epilogue and a
150       // return to caller instruction.
151       if (it->second.row.GetOffset() != m_state.row.GetOffset())
152         m_state = it->second;
153 
154       m_inst_emulator_up->SetInstruction(inst->GetOpcode(), inst->GetAddress(),
155                                          nullptr);
156 
157       if (last_condition != m_inst_emulator_up->GetInstructionCondition()) {
158         // If the last instruction was conditional with a different condition
159         // than the current condition then restore the state.
160         if (last_condition != EmulateInstruction::UnconditionalCondition) {
161           m_state = condition_block_start_state->second;
162           m_state.row.SetOffset(current_offset);
163           // The last instruction might already created a row for this offset
164           // and we want to overwrite it.
165           saved_unwind_states.insert_or_assign(current_offset, m_state);
166         }
167 
168         // We are starting a new conditional block at the actual offset
169         condition_block_start_state = it;
170       }
171 
172       if (log && log->GetVerbose()) {
173         StreamString strm;
174         lldb_private::FormatEntity::Entry format;
175         FormatEntity::Parse("${frame.pc}: ", format);
176         inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize(), show_address,
177                    show_bytes, show_control_flow_kind, nullptr, nullptr,
178                    nullptr, &format, 0);
179         log->PutString(strm.GetString());
180       }
181 
182       last_condition = m_inst_emulator_up->GetInstructionCondition();
183 
184       m_inst_emulator_up->EvaluateInstruction(
185           eEmulateInstructionOptionIgnoreConditions);
186 
187       // If the current instruction is a branch forward then save the current
188       // CFI information for the offset where we are branching.
189       if (m_forward_branch_offset != 0 &&
190           range.ContainsFileAddress(inst->GetAddress().GetFileAddress() +
191                                     m_forward_branch_offset)) {
192         if (auto [it, inserted] = saved_unwind_states.emplace(
193                 current_offset + m_forward_branch_offset, m_state);
194             inserted)
195           it->second.row.SetOffset(current_offset + m_forward_branch_offset);
196       }
197 
198       // Were there any changes to the CFI while evaluating this instruction?
199       if (m_curr_row_modified) {
200         // Save the modified row if we don't already have a CFI row in the
201         // current address
202         if (saved_unwind_states.count(current_offset +
203                                       inst->GetOpcode().GetByteSize()) == 0) {
204           m_state.row.SetOffset(current_offset +
205                                 inst->GetOpcode().GetByteSize());
206           saved_unwind_states.emplace(
207               current_offset + inst->GetOpcode().GetByteSize(), m_state);
208         }
209       }
210     }
211     for (auto &[_, state] : saved_unwind_states) {
212       unwind_plan.InsertRow(std::move(state.row),
213                             /*replace_existing=*/true);
214     }
215   }
216 
217   if (log && log->GetVerbose()) {
218     StreamString strm;
219     lldb::addr_t base_addr = range.GetBaseAddress().GetFileAddress();
220     strm.Printf("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):",
221                 base_addr, base_addr + range.GetByteSize());
222     unwind_plan.Dump(strm, nullptr, base_addr);
223     log->PutString(strm.GetString());
224   }
225   return unwind_plan.GetRowCount() > 0;
226 }
227 
AugmentUnwindPlanFromCallSite(AddressRange & func,Thread & thread,UnwindPlan & unwind_plan)228 bool UnwindAssemblyInstEmulation::AugmentUnwindPlanFromCallSite(
229     AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {
230   return false;
231 }
232 
GetFastUnwindPlan(AddressRange & func,Thread & thread,UnwindPlan & unwind_plan)233 bool UnwindAssemblyInstEmulation::GetFastUnwindPlan(AddressRange &func,
234                                                     Thread &thread,
235                                                     UnwindPlan &unwind_plan) {
236   return false;
237 }
238 
FirstNonPrologueInsn(AddressRange & func,const ExecutionContext & exe_ctx,Address & first_non_prologue_insn)239 bool UnwindAssemblyInstEmulation::FirstNonPrologueInsn(
240     AddressRange &func, const ExecutionContext &exe_ctx,
241     Address &first_non_prologue_insn) {
242   return false;
243 }
244 
245 UnwindAssembly *
CreateInstance(const ArchSpec & arch)246 UnwindAssemblyInstEmulation::CreateInstance(const ArchSpec &arch) {
247   std::unique_ptr<EmulateInstruction> inst_emulator_up(
248       EmulateInstruction::FindPlugin(arch, eInstructionTypePrologueEpilogue,
249                                      nullptr));
250   // Make sure that all prologue instructions are handled
251   if (inst_emulator_up)
252     return new UnwindAssemblyInstEmulation(arch, inst_emulator_up.release());
253   return nullptr;
254 }
255 
Initialize()256 void UnwindAssemblyInstEmulation::Initialize() {
257   PluginManager::RegisterPlugin(GetPluginNameStatic(),
258                                 GetPluginDescriptionStatic(), CreateInstance);
259 }
260 
Terminate()261 void UnwindAssemblyInstEmulation::Terminate() {
262   PluginManager::UnregisterPlugin(CreateInstance);
263 }
264 
GetPluginDescriptionStatic()265 llvm::StringRef UnwindAssemblyInstEmulation::GetPluginDescriptionStatic() {
266   return "Instruction emulation based unwind information.";
267 }
268 
MakeRegisterKindValuePair(const RegisterInfo & reg_info)269 uint64_t UnwindAssemblyInstEmulation::MakeRegisterKindValuePair(
270     const RegisterInfo &reg_info) {
271   lldb::RegisterKind reg_kind;
272   uint32_t reg_num;
273   if (EmulateInstruction::GetBestRegisterKindAndNumber(&reg_info, reg_kind,
274                                                        reg_num))
275     return (uint64_t)reg_kind << 24 | reg_num;
276   return 0ull;
277 }
278 
SetRegisterValue(const RegisterInfo & reg_info,const RegisterValue & reg_value)279 void UnwindAssemblyInstEmulation::SetRegisterValue(
280     const RegisterInfo &reg_info, const RegisterValue &reg_value) {
281   m_state.register_values[MakeRegisterKindValuePair(reg_info)] = reg_value;
282 }
283 
GetRegisterValue(const RegisterInfo & reg_info,RegisterValue & reg_value)284 bool UnwindAssemblyInstEmulation::GetRegisterValue(const RegisterInfo &reg_info,
285                                                    RegisterValue &reg_value) {
286   const uint64_t reg_id = MakeRegisterKindValuePair(reg_info);
287   RegisterValueMap::const_iterator pos = m_state.register_values.find(reg_id);
288   if (pos != m_state.register_values.end()) {
289     reg_value = pos->second;
290     return true; // We had a real value that comes from an opcode that wrote
291                  // to it...
292   }
293   // We are making up a value that is recognizable...
294   reg_value.SetUInt(reg_id, reg_info.byte_size);
295   return false;
296 }
297 
ReadMemory(EmulateInstruction * instruction,void * baton,const EmulateInstruction::Context & context,lldb::addr_t addr,void * dst,size_t dst_len)298 size_t UnwindAssemblyInstEmulation::ReadMemory(
299     EmulateInstruction *instruction, void *baton,
300     const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst,
301     size_t dst_len) {
302   Log *log = GetLog(LLDBLog::Unwind);
303 
304   if (log && log->GetVerbose()) {
305     StreamString strm;
306     strm.Printf(
307         "UnwindAssemblyInstEmulation::ReadMemory    (addr = 0x%16.16" PRIx64
308         ", dst = %p, dst_len = %" PRIu64 ", context = ",
309         addr, dst, (uint64_t)dst_len);
310     context.Dump(strm, instruction);
311     log->PutString(strm.GetString());
312   }
313   memset(dst, 0, dst_len);
314   return dst_len;
315 }
316 
WriteMemory(EmulateInstruction * instruction,void * baton,const EmulateInstruction::Context & context,lldb::addr_t addr,const void * dst,size_t dst_len)317 size_t UnwindAssemblyInstEmulation::WriteMemory(
318     EmulateInstruction *instruction, void *baton,
319     const EmulateInstruction::Context &context, lldb::addr_t addr,
320     const void *dst, size_t dst_len) {
321   if (baton && dst && dst_len)
322     return ((UnwindAssemblyInstEmulation *)baton)
323         ->WriteMemory(instruction, context, addr, dst, dst_len);
324   return 0;
325 }
326 
WriteMemory(EmulateInstruction * instruction,const EmulateInstruction::Context & context,lldb::addr_t addr,const void * dst,size_t dst_len)327 size_t UnwindAssemblyInstEmulation::WriteMemory(
328     EmulateInstruction *instruction, const EmulateInstruction::Context &context,
329     lldb::addr_t addr, const void *dst, size_t dst_len) {
330   DataExtractor data(dst, dst_len,
331                      instruction->GetArchitecture().GetByteOrder(),
332                      instruction->GetArchitecture().GetAddressByteSize());
333 
334   Log *log = GetLog(LLDBLog::Unwind);
335 
336   if (log && log->GetVerbose()) {
337     StreamString strm;
338 
339     strm.PutCString("UnwindAssemblyInstEmulation::WriteMemory   (");
340     DumpDataExtractor(data, &strm, 0, eFormatBytes, 1, dst_len, UINT32_MAX,
341                       addr, 0, 0);
342     strm.PutCString(", context = ");
343     context.Dump(strm, instruction);
344     log->PutString(strm.GetString());
345   }
346 
347   switch (context.type) {
348   default:
349   case EmulateInstruction::eContextInvalid:
350   case EmulateInstruction::eContextReadOpcode:
351   case EmulateInstruction::eContextImmediate:
352   case EmulateInstruction::eContextAdjustBaseRegister:
353   case EmulateInstruction::eContextRegisterPlusOffset:
354   case EmulateInstruction::eContextAdjustPC:
355   case EmulateInstruction::eContextRegisterStore:
356   case EmulateInstruction::eContextRegisterLoad:
357   case EmulateInstruction::eContextRelativeBranchImmediate:
358   case EmulateInstruction::eContextAbsoluteBranchRegister:
359   case EmulateInstruction::eContextSupervisorCall:
360   case EmulateInstruction::eContextTableBranchReadMemory:
361   case EmulateInstruction::eContextWriteRegisterRandomBits:
362   case EmulateInstruction::eContextWriteMemoryRandomBits:
363   case EmulateInstruction::eContextArithmetic:
364   case EmulateInstruction::eContextAdvancePC:
365   case EmulateInstruction::eContextReturnFromException:
366   case EmulateInstruction::eContextPopRegisterOffStack:
367   case EmulateInstruction::eContextAdjustStackPointer:
368     break;
369 
370   case EmulateInstruction::eContextPushRegisterOnStack: {
371     uint32_t reg_num = LLDB_INVALID_REGNUM;
372     uint32_t generic_regnum = LLDB_INVALID_REGNUM;
373     assert(context.GetInfoType() ==
374                EmulateInstruction::eInfoTypeRegisterToRegisterPlusOffset &&
375            "unhandled case, add code to handle this!");
376     const uint32_t unwind_reg_kind = m_unwind_plan_ptr->GetRegisterKind();
377     reg_num = context.info.RegisterToRegisterPlusOffset.data_reg
378                   .kinds[unwind_reg_kind];
379     generic_regnum = context.info.RegisterToRegisterPlusOffset.data_reg
380                          .kinds[eRegisterKindGeneric];
381 
382     if (reg_num != LLDB_INVALID_REGNUM &&
383         generic_regnum != LLDB_REGNUM_GENERIC_SP) {
384       if (m_pushed_regs.try_emplace(reg_num, addr).second) {
385         const int32_t offset = addr - m_initial_sp;
386         m_state.row.SetRegisterLocationToAtCFAPlusOffset(reg_num, offset,
387                                                          /*can_replace=*/true);
388         m_curr_row_modified = true;
389       }
390     }
391   } break;
392   }
393 
394   return dst_len;
395 }
396 
ReadRegister(EmulateInstruction * instruction,void * baton,const RegisterInfo * reg_info,RegisterValue & reg_value)397 bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction,
398                                                void *baton,
399                                                const RegisterInfo *reg_info,
400                                                RegisterValue &reg_value) {
401 
402   if (baton && reg_info)
403     return ((UnwindAssemblyInstEmulation *)baton)
404         ->ReadRegister(instruction, reg_info, reg_value);
405   return false;
406 }
ReadRegister(EmulateInstruction * instruction,const RegisterInfo * reg_info,RegisterValue & reg_value)407 bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction,
408                                                const RegisterInfo *reg_info,
409                                                RegisterValue &reg_value) {
410   bool synthetic = GetRegisterValue(*reg_info, reg_value);
411 
412   Log *log = GetLog(LLDBLog::Unwind);
413 
414   if (log && log->GetVerbose()) {
415 
416     StreamString strm;
417     strm.Printf("UnwindAssemblyInstEmulation::ReadRegister  (name = \"%s\") => "
418                 "synthetic_value = %i, value = ",
419                 reg_info->name, synthetic);
420     DumpRegisterValue(reg_value, strm, *reg_info, false, false, eFormatDefault);
421     log->PutString(strm.GetString());
422   }
423   return true;
424 }
425 
WriteRegister(EmulateInstruction * instruction,void * baton,const EmulateInstruction::Context & context,const RegisterInfo * reg_info,const RegisterValue & reg_value)426 bool UnwindAssemblyInstEmulation::WriteRegister(
427     EmulateInstruction *instruction, void *baton,
428     const EmulateInstruction::Context &context, const RegisterInfo *reg_info,
429     const RegisterValue &reg_value) {
430   if (baton && reg_info)
431     return ((UnwindAssemblyInstEmulation *)baton)
432         ->WriteRegister(instruction, context, reg_info, reg_value);
433   return false;
434 }
WriteRegister(EmulateInstruction * instruction,const EmulateInstruction::Context & context,const RegisterInfo * reg_info,const RegisterValue & reg_value)435 bool UnwindAssemblyInstEmulation::WriteRegister(
436     EmulateInstruction *instruction, const EmulateInstruction::Context &context,
437     const RegisterInfo *reg_info, const RegisterValue &reg_value) {
438   Log *log = GetLog(LLDBLog::Unwind);
439 
440   if (log && log->GetVerbose()) {
441 
442     StreamString strm;
443     strm.Printf(
444         "UnwindAssemblyInstEmulation::WriteRegister (name = \"%s\", value = ",
445         reg_info->name);
446     DumpRegisterValue(reg_value, strm, *reg_info, false, false, eFormatDefault);
447     strm.PutCString(", context = ");
448     context.Dump(strm, instruction);
449     log->PutString(strm.GetString());
450   }
451 
452   SetRegisterValue(*reg_info, reg_value);
453 
454   switch (context.type) {
455   case EmulateInstruction::eContextInvalid:
456   case EmulateInstruction::eContextReadOpcode:
457   case EmulateInstruction::eContextImmediate:
458   case EmulateInstruction::eContextAdjustBaseRegister:
459   case EmulateInstruction::eContextRegisterPlusOffset:
460   case EmulateInstruction::eContextAdjustPC:
461   case EmulateInstruction::eContextRegisterStore:
462   case EmulateInstruction::eContextSupervisorCall:
463   case EmulateInstruction::eContextTableBranchReadMemory:
464   case EmulateInstruction::eContextWriteRegisterRandomBits:
465   case EmulateInstruction::eContextWriteMemoryRandomBits:
466   case EmulateInstruction::eContextAdvancePC:
467   case EmulateInstruction::eContextReturnFromException:
468   case EmulateInstruction::eContextPushRegisterOnStack:
469   case EmulateInstruction::eContextRegisterLoad:
470     //            {
471     //                const uint32_t reg_num =
472     //                reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
473     //                if (reg_num != LLDB_INVALID_REGNUM)
474     //                {
475     //                    const bool can_replace_only_if_unspecified = true;
476     //
477     //                    m_curr_row.SetRegisterLocationToUndefined (reg_num,
478     //                                                               can_replace_only_if_unspecified,
479     //                                                               can_replace_only_if_unspecified);
480     //                    m_curr_row_modified = true;
481     //                }
482     //            }
483     break;
484 
485   case EmulateInstruction::eContextArithmetic: {
486     // If we adjusted the current frame pointer by a constant then adjust the
487     // CFA offset
488     // with the same amount.
489     lldb::RegisterKind kind = m_unwind_plan_ptr->GetRegisterKind();
490     if (m_state.fp_is_cfa &&
491         reg_info->kinds[kind] == m_state.cfa_reg_info.kinds[kind] &&
492         context.GetInfoType() ==
493             EmulateInstruction::eInfoTypeRegisterPlusOffset &&
494         context.info.RegisterPlusOffset.reg.kinds[kind] ==
495             m_state.cfa_reg_info.kinds[kind]) {
496       const int64_t offset = context.info.RegisterPlusOffset.signed_offset;
497       m_state.row.GetCFAValue().IncOffset(-1 * offset);
498       m_curr_row_modified = true;
499     }
500   } break;
501 
502   case EmulateInstruction::eContextAbsoluteBranchRegister:
503   case EmulateInstruction::eContextRelativeBranchImmediate: {
504     if (context.GetInfoType() == EmulateInstruction::eInfoTypeISAAndImmediate &&
505         context.info.ISAAndImmediate.unsigned_data32 > 0) {
506       m_forward_branch_offset =
507           context.info.ISAAndImmediateSigned.signed_data32;
508     } else if (context.GetInfoType() ==
509                    EmulateInstruction::eInfoTypeISAAndImmediateSigned &&
510                context.info.ISAAndImmediateSigned.signed_data32 > 0) {
511       m_forward_branch_offset = context.info.ISAAndImmediate.unsigned_data32;
512     } else if (context.GetInfoType() ==
513                    EmulateInstruction::eInfoTypeImmediate &&
514                context.info.unsigned_immediate > 0) {
515       m_forward_branch_offset = context.info.unsigned_immediate;
516     } else if (context.GetInfoType() ==
517                    EmulateInstruction::eInfoTypeImmediateSigned &&
518                context.info.signed_immediate > 0) {
519       m_forward_branch_offset = context.info.signed_immediate;
520     }
521   } break;
522 
523   case EmulateInstruction::eContextPopRegisterOffStack: {
524     const uint32_t reg_num =
525         reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
526     const uint32_t generic_regnum = reg_info->kinds[eRegisterKindGeneric];
527     if (reg_num != LLDB_INVALID_REGNUM &&
528         generic_regnum != LLDB_REGNUM_GENERIC_SP) {
529       switch (context.GetInfoType()) {
530       case EmulateInstruction::eInfoTypeAddress:
531         if (auto it = m_pushed_regs.find(reg_num);
532             it != m_pushed_regs.end() && context.info.address == it->second) {
533           m_state.row.SetRegisterLocationToSame(reg_num,
534                                                 false /*must_replace*/);
535           m_curr_row_modified = true;
536 
537           // FP has been restored to its original value, we are back
538           // to using SP to calculate the CFA.
539           if (m_state.fp_is_cfa) {
540             m_state.fp_is_cfa = false;
541             lldb::RegisterKind sp_reg_kind = eRegisterKindGeneric;
542             uint32_t sp_reg_num = LLDB_REGNUM_GENERIC_SP;
543             RegisterInfo sp_reg_info =
544                 *m_inst_emulator_up->GetRegisterInfo(sp_reg_kind, sp_reg_num);
545             RegisterValue sp_reg_val;
546             if (GetRegisterValue(sp_reg_info, sp_reg_val)) {
547               m_state.cfa_reg_info = sp_reg_info;
548               const uint32_t cfa_reg_num =
549                   sp_reg_info.kinds[m_unwind_plan_ptr->GetRegisterKind()];
550               assert(cfa_reg_num != LLDB_INVALID_REGNUM);
551               m_state.row.GetCFAValue().SetIsRegisterPlusOffset(
552                   cfa_reg_num, m_initial_sp - sp_reg_val.GetAsUInt64());
553             }
554           }
555         }
556         break;
557       case EmulateInstruction::eInfoTypeISA:
558         assert(
559             (generic_regnum == LLDB_REGNUM_GENERIC_PC ||
560              generic_regnum == LLDB_REGNUM_GENERIC_FLAGS) &&
561             "eInfoTypeISA used for popping a register other the PC/FLAGS");
562         if (generic_regnum != LLDB_REGNUM_GENERIC_FLAGS) {
563           m_state.row.SetRegisterLocationToSame(reg_num,
564                                                 false /*must_replace*/);
565           m_curr_row_modified = true;
566         }
567         break;
568       default:
569         assert(false && "unhandled case, add code to handle this!");
570         break;
571       }
572     }
573   } break;
574 
575   case EmulateInstruction::eContextSetFramePointer:
576     if (!m_state.fp_is_cfa) {
577       m_state.fp_is_cfa = true;
578       m_state.cfa_reg_info = *reg_info;
579       const uint32_t cfa_reg_num =
580           reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
581       assert(cfa_reg_num != LLDB_INVALID_REGNUM);
582       m_state.row.GetCFAValue().SetIsRegisterPlusOffset(
583           cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64());
584       m_curr_row_modified = true;
585     }
586     break;
587 
588   case EmulateInstruction::eContextRestoreStackPointer:
589     if (m_state.fp_is_cfa) {
590       m_state.fp_is_cfa = false;
591       m_state.cfa_reg_info = *reg_info;
592       const uint32_t cfa_reg_num =
593           reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
594       assert(cfa_reg_num != LLDB_INVALID_REGNUM);
595       m_state.row.GetCFAValue().SetIsRegisterPlusOffset(
596           cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64());
597       m_curr_row_modified = true;
598     }
599     break;
600 
601   case EmulateInstruction::eContextAdjustStackPointer:
602     // If we have created a frame using the frame pointer, don't follow
603     // subsequent adjustments to the stack pointer.
604     if (!m_state.fp_is_cfa) {
605       m_state.row.GetCFAValue().SetIsRegisterPlusOffset(
606           m_state.row.GetCFAValue().GetRegisterNumber(),
607           m_initial_sp - reg_value.GetAsUInt64());
608       m_curr_row_modified = true;
609     }
610     break;
611   }
612   return true;
613 }
614