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