| 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 | |
| 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 | addr: range.GetBaseAddress(), dst: function_text.data(), dst_len: range.GetByteSize(), |
| 45 | error, force_live_memory) != range.GetByteSize()) { |
| 46 | return false; |
| 47 | } |
| 48 | } |
| 49 | return GetNonCallSiteUnwindPlanFromAssembly( |
| 50 | func&: range, opcode_data: function_text.data(), opcode_size: function_text.size(), unwind_plan); |
| 51 | } |
| 52 | |
| 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 | arch: m_arch, plugin_name: nullptr, flavor: nullptr, cpu: nullptr, features: nullptr, start: range.GetBaseAddress(), |
| 75 | bytes: opcode_data, length: opcode_size, max_num_instructions: 99999, data_from_file: prefer_file_cache)); |
| 76 | |
| 77 | if (!disasm_sp) |
| 78 | return false; |
| 79 | |
| 80 | Log *log = GetLog(mask: LLDBLog::Unwind); |
| 81 | |
| 82 | m_range_ptr = ⦥ |
| 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 | reg_kind: unwind_plan.GetRegisterKind(), reg_num: 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(uint: m_initial_sp, byte_size: m_state.cfa_reg_info.byte_size); |
| 103 | SetRegisterValue(reg_info: m_state.cfa_reg_info, reg_value: 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(idx: 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(args: 0, args&: 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(x: 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(insn_opcode: inst->GetOpcode(), inst_addr: inst->GetAddress(), |
| 155 | target: 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(k: current_offset, obj&: 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(format: "${frame.pc}: " , entry&: format); |
| 176 | inst->Dump(s: &strm, max_opcode_byte_size: inst_list.GetMaxOpcocdeByteSize(), show_address, |
| 177 | show_bytes, show_control_flow_kind, exe_ctx: nullptr, sym_ctx: nullptr, |
| 178 | prev_sym_ctx: nullptr, disassembly_addr_format: &format, max_address_text_size: 0); |
| 179 | log->PutString(str: strm.GetString()); |
| 180 | } |
| 181 | |
| 182 | last_condition = m_inst_emulator_up->GetInstructionCondition(); |
| 183 | |
| 184 | m_inst_emulator_up->EvaluateInstruction( |
| 185 | evaluate_options: 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(file_addr: inst->GetAddress().GetFileAddress() + |
| 191 | m_forward_branch_offset)) { |
| 192 | if (auto [it, inserted] = saved_unwind_states.emplace( |
| 193 | args: current_offset + m_forward_branch_offset, args&: 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(x: current_offset + |
| 203 | inst->GetOpcode().GetByteSize()) == 0) { |
| 204 | m_state.row.SetOffset(current_offset + |
| 205 | inst->GetOpcode().GetByteSize()); |
| 206 | saved_unwind_states.emplace( |
| 207 | args: current_offset + inst->GetOpcode().GetByteSize(), args&: m_state); |
| 208 | } |
| 209 | } |
| 210 | } |
| 211 | for (auto &[_, state] : saved_unwind_states) { |
| 212 | unwind_plan.InsertRow(row: 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(format: "Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):" , |
| 221 | base_addr, base_addr + range.GetByteSize()); |
| 222 | unwind_plan.Dump(s&: strm, thread: nullptr, base_addr); |
| 223 | log->PutString(str: strm.GetString()); |
| 224 | } |
| 225 | return unwind_plan.GetRowCount() > 0; |
| 226 | } |
| 227 | |
| 228 | bool UnwindAssemblyInstEmulation::AugmentUnwindPlanFromCallSite( |
| 229 | AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) { |
| 230 | return false; |
| 231 | } |
| 232 | |
| 233 | bool UnwindAssemblyInstEmulation::GetFastUnwindPlan(AddressRange &func, |
| 234 | Thread &thread, |
| 235 | UnwindPlan &unwind_plan) { |
| 236 | return false; |
| 237 | } |
| 238 | |
| 239 | bool UnwindAssemblyInstEmulation::FirstNonPrologueInsn( |
| 240 | AddressRange &func, const ExecutionContext &exe_ctx, |
| 241 | Address &first_non_prologue_insn) { |
| 242 | return false; |
| 243 | } |
| 244 | |
| 245 | UnwindAssembly * |
| 246 | UnwindAssemblyInstEmulation::CreateInstance(const ArchSpec &arch) { |
| 247 | std::unique_ptr<EmulateInstruction> inst_emulator_up( |
| 248 | EmulateInstruction::FindPlugin(arch, supported_inst_type: eInstructionTypePrologueEpilogue, |
| 249 | plugin_name: 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 | |
| 256 | void UnwindAssemblyInstEmulation::Initialize() { |
| 257 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
| 258 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance); |
| 259 | } |
| 260 | |
| 261 | void UnwindAssemblyInstEmulation::Terminate() { |
| 262 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
| 263 | } |
| 264 | |
| 265 | llvm::StringRef UnwindAssemblyInstEmulation::GetPluginDescriptionStatic() { |
| 266 | return "Instruction emulation based unwind information." ; |
| 267 | } |
| 268 | |
| 269 | uint64_t UnwindAssemblyInstEmulation::MakeRegisterKindValuePair( |
| 270 | const RegisterInfo ®_info) { |
| 271 | lldb::RegisterKind reg_kind; |
| 272 | uint32_t reg_num; |
| 273 | if (EmulateInstruction::GetBestRegisterKindAndNumber(reg_info: ®_info, reg_kind, |
| 274 | reg_num)) |
| 275 | return (uint64_t)reg_kind << 24 | reg_num; |
| 276 | return 0ull; |
| 277 | } |
| 278 | |
| 279 | void UnwindAssemblyInstEmulation::SetRegisterValue( |
| 280 | const RegisterInfo ®_info, const RegisterValue ®_value) { |
| 281 | m_state.register_values[MakeRegisterKindValuePair(reg_info)] = reg_value; |
| 282 | } |
| 283 | |
| 284 | bool UnwindAssemblyInstEmulation::GetRegisterValue(const RegisterInfo ®_info, |
| 285 | RegisterValue ®_value) { |
| 286 | const uint64_t reg_id = MakeRegisterKindValuePair(reg_info); |
| 287 | RegisterValueMap::const_iterator pos = m_state.register_values.find(x: 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(uint: reg_id, byte_size: reg_info.byte_size); |
| 295 | return false; |
| 296 | } |
| 297 | |
| 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(mask: LLDBLog::Unwind); |
| 303 | |
| 304 | if (log && log->GetVerbose()) { |
| 305 | StreamString strm; |
| 306 | strm.Printf( |
| 307 | format: "UnwindAssemblyInstEmulation::ReadMemory (addr = 0x%16.16" PRIx64 |
| 308 | ", dst = %p, dst_len = %" PRIu64 ", context = " , |
| 309 | addr, dst, (uint64_t)dst_len); |
| 310 | context.Dump(s&: strm, instruction); |
| 311 | log->PutString(str: strm.GetString()); |
| 312 | } |
| 313 | memset(s: dst, c: 0, n: dst_len); |
| 314 | return dst_len; |
| 315 | } |
| 316 | |
| 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, length: dst_len); |
| 324 | return 0; |
| 325 | } |
| 326 | |
| 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(mask: LLDBLog::Unwind); |
| 335 | |
| 336 | if (log && log->GetVerbose()) { |
| 337 | StreamString strm; |
| 338 | |
| 339 | strm.PutCString(cstr: "UnwindAssemblyInstEmulation::WriteMemory (" ); |
| 340 | DumpDataExtractor(DE: data, s: &strm, offset: 0, item_format: eFormatBytes, item_byte_size: 1, item_count: dst_len, UINT32_MAX, |
| 341 | base_addr: addr, item_bit_size: 0, item_bit_offset: 0); |
| 342 | strm.PutCString(cstr: ", context = " ); |
| 343 | context.Dump(s&: strm, instruction); |
| 344 | log->PutString(str: 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(k: reg_num, args&: 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 | |
| 397 | bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction, |
| 398 | void *baton, |
| 399 | const RegisterInfo *reg_info, |
| 400 | RegisterValue ®_value) { |
| 401 | |
| 402 | if (baton && reg_info) |
| 403 | return ((UnwindAssemblyInstEmulation *)baton) |
| 404 | ->ReadRegister(instruction, reg_info, reg_value); |
| 405 | return false; |
| 406 | } |
| 407 | bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction, |
| 408 | const RegisterInfo *reg_info, |
| 409 | RegisterValue ®_value) { |
| 410 | bool synthetic = GetRegisterValue(reg_info: *reg_info, reg_value); |
| 411 | |
| 412 | Log *log = GetLog(mask: LLDBLog::Unwind); |
| 413 | |
| 414 | if (log && log->GetVerbose()) { |
| 415 | |
| 416 | StreamString strm; |
| 417 | strm.Printf(format: "UnwindAssemblyInstEmulation::ReadRegister (name = \"%s\") => " |
| 418 | "synthetic_value = %i, value = " , |
| 419 | reg_info->name, synthetic); |
| 420 | DumpRegisterValue(reg_val: reg_value, s&: strm, reg_info: *reg_info, prefix_with_name: false, prefix_with_alt_name: false, format: eFormatDefault); |
| 421 | log->PutString(str: strm.GetString()); |
| 422 | } |
| 423 | return true; |
| 424 | } |
| 425 | |
| 426 | bool UnwindAssemblyInstEmulation::WriteRegister( |
| 427 | EmulateInstruction *instruction, void *baton, |
| 428 | const EmulateInstruction::Context &context, const RegisterInfo *reg_info, |
| 429 | const RegisterValue ®_value) { |
| 430 | if (baton && reg_info) |
| 431 | return ((UnwindAssemblyInstEmulation *)baton) |
| 432 | ->WriteRegister(instruction, context, reg_info, reg_value); |
| 433 | return false; |
| 434 | } |
| 435 | bool UnwindAssemblyInstEmulation::WriteRegister( |
| 436 | EmulateInstruction *instruction, const EmulateInstruction::Context &context, |
| 437 | const RegisterInfo *reg_info, const RegisterValue ®_value) { |
| 438 | Log *log = GetLog(mask: LLDBLog::Unwind); |
| 439 | |
| 440 | if (log && log->GetVerbose()) { |
| 441 | |
| 442 | StreamString strm; |
| 443 | strm.Printf( |
| 444 | format: "UnwindAssemblyInstEmulation::WriteRegister (name = \"%s\", value = " , |
| 445 | reg_info->name); |
| 446 | DumpRegisterValue(reg_val: reg_value, s&: strm, reg_info: *reg_info, prefix_with_name: false, prefix_with_alt_name: false, format: eFormatDefault); |
| 447 | strm.PutCString(cstr: ", context = " ); |
| 448 | context.Dump(s&: strm, instruction); |
| 449 | log->PutString(str: strm.GetString()); |
| 450 | } |
| 451 | |
| 452 | SetRegisterValue(reg_info: *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(delta: -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(x: reg_num); |
| 532 | it != m_pushed_regs.end() && context.info.address == it->second) { |
| 533 | m_state.row.SetRegisterLocationToSame(reg_num, |
| 534 | must_replace: 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(reg_kind: sp_reg_kind, reg_num: sp_reg_num); |
| 545 | RegisterValue sp_reg_val; |
| 546 | if (GetRegisterValue(reg_info: sp_reg_info, reg_value&: 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 | reg_num: cfa_reg_num, offset: 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 | must_replace: 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 | reg_num: cfa_reg_num, offset: 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 | reg_num: cfa_reg_num, offset: 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 | reg_num: m_state.row.GetCFAValue().GetRegisterNumber(), |
| 607 | offset: m_initial_sp - reg_value.GetAsUInt64()); |
| 608 | m_curr_row_modified = true; |
| 609 | } |
| 610 | break; |
| 611 | } |
| 612 | return true; |
| 613 | } |
| 614 | |