| //===-- UnwindAssemblyInstEmulation.h ---------------------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLDB_SOURCE_PLUGINS_UNWINDASSEMBLY_INSTEMULATION_UNWINDASSEMBLYINSTEMULATION_H | |
| #define LLDB_SOURCE_PLUGINS_UNWINDASSEMBLY_INSTEMULATION_UNWINDASSEMBLYINSTEMULATION_H | |
| #include "lldb/Core/EmulateInstruction.h" | |
| #include "lldb/Symbol/UnwindPlan.h" | |
| #include "lldb/Target/UnwindAssembly.h" | |
| #include "lldb/Utility/RegisterValue.h" | |
| #include "lldb/lldb-private.h" | |
| class UnwindAssemblyInstEmulation : public lldb_private::UnwindAssembly { | |
| public: | |
| ~UnwindAssemblyInstEmulation() override = default; | |
| bool GetNonCallSiteUnwindPlanFromAssembly( | |
| lldb_private::AddressRange &func, lldb_private::Thread &thread, | |
| lldb_private::UnwindPlan &unwind_plan) override; | |
| bool | |
| GetNonCallSiteUnwindPlanFromAssembly(lldb_private::AddressRange &func, | |
| uint8_t *opcode_data, size_t opcode_size, | |
| lldb_private::UnwindPlan &unwind_plan); | |
| bool | |
| AugmentUnwindPlanFromCallSite(lldb_private::AddressRange &func, | |
| lldb_private::Thread &thread, | |
| lldb_private::UnwindPlan &unwind_plan) override; | |
| bool GetFastUnwindPlan(lldb_private::AddressRange &func, | |
| lldb_private::Thread &thread, | |
| lldb_private::UnwindPlan &unwind_plan) override; | |
| // thread may be NULL in which case we only use the Target (e.g. if this is | |
| // called pre-process-launch). | |
| bool | |
| FirstNonPrologueInsn(lldb_private::AddressRange &func, | |
| const lldb_private::ExecutionContext &exe_ctx, | |
| lldb_private::Address &first_non_prologue_insn) override; | |
| static lldb_private::UnwindAssembly * | |
| CreateInstance(const lldb_private::ArchSpec &arch); | |
| // PluginInterface protocol | |
| static void Initialize(); | |
| static void Terminate(); | |
| static llvm::StringRef GetPluginNameStatic() { return "inst-emulation"; } | |
| static llvm::StringRef GetPluginDescriptionStatic(); | |
| llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } | |
| private: | |
| // Call CreateInstance to get an instance of this class | |
| UnwindAssemblyInstEmulation(const lldb_private::ArchSpec &arch, | |
| lldb_private::EmulateInstruction *inst_emulator) | |
| : UnwindAssembly(arch), m_inst_emulator_up(inst_emulator), | |
| m_range_ptr(nullptr), m_unwind_plan_ptr(nullptr), | |
| m_curr_row_modified(false) { | |
| if (m_inst_emulator_up) { | |
| m_inst_emulator_up->SetBaton(this); | |
| m_inst_emulator_up->SetCallbacks(ReadMemory, WriteMemory, ReadRegister, | |
| WriteRegister); | |
| } | |
| // Initialize the CFA with a known value. In the 32 bit case it will be | |
| // 0x80000000, and in the 64 bit case 0x8000000000000000. We use the address | |
| // byte size to be safe for any future address sizes | |
| m_initial_cfa = (1ull << ((m_arch.GetAddressByteSize() * 8) - 1)); | |
| } | |
| static size_t | |
| ReadMemory(lldb_private::EmulateInstruction *instruction, void *baton, | |
| const lldb_private::EmulateInstruction::Context &context, | |
| lldb::addr_t addr, void *dst, size_t length); | |
| static size_t | |
| WriteMemory(lldb_private::EmulateInstruction *instruction, void *baton, | |
| const lldb_private::EmulateInstruction::Context &context, | |
| lldb::addr_t addr, const void *dst, size_t length); | |
| static bool ReadRegister(lldb_private::EmulateInstruction *instruction, | |
| void *baton, | |
| const lldb_private::RegisterInfo *reg_info, | |
| lldb_private::RegisterValue ®_value); | |
| static bool | |
| WriteRegister(lldb_private::EmulateInstruction *instruction, void *baton, | |
| const lldb_private::EmulateInstruction::Context &context, | |
| const lldb_private::RegisterInfo *reg_info, | |
| const lldb_private::RegisterValue ®_value); | |
| size_t WriteMemory(lldb_private::EmulateInstruction *instruction, | |
| const lldb_private::EmulateInstruction::Context &context, | |
| lldb::addr_t addr, const void *dst, size_t length); | |
| bool ReadRegister(lldb_private::EmulateInstruction *instruction, | |
| const lldb_private::RegisterInfo *reg_info, | |
| lldb_private::RegisterValue ®_value); | |
| bool WriteRegister(lldb_private::EmulateInstruction *instruction, | |
| const lldb_private::EmulateInstruction::Context &context, | |
| const lldb_private::RegisterInfo *reg_info, | |
| const lldb_private::RegisterValue ®_value); | |
| static uint64_t | |
| MakeRegisterKindValuePair(const lldb_private::RegisterInfo ®_info); | |
| void SetRegisterValue(const lldb_private::RegisterInfo ®_info, | |
| const lldb_private::RegisterValue ®_value); | |
| bool GetRegisterValue(const lldb_private::RegisterInfo ®_info, | |
| lldb_private::RegisterValue ®_value); | |
| typedef std::map<uint64_t, lldb_private::RegisterValue> RegisterValueMap; | |
| struct UnwindState { | |
| lldb_private::UnwindPlan::Row row = {}; | |
| lldb_private::RegisterInfo cfa_reg_info = {}; | |
| bool fp_is_cfa = false; | |
| RegisterValueMap register_values = {}; | |
| }; | |
| /// Follow a call into a compiler-outlined helper and emulate its body in the | |
| /// caller's state. Outlined functions don't respect ABI, which is why this | |
| /// unwinder must follow outlined function calls. A prime target for | |
| /// outlining is function prologues. This function supports emulating | |
| /// straightline code with no branches, which is the case for prologues. | |
| bool EmulateOutlinedFunction(lldb_private::Address func_addr); | |
| std::unique_ptr<lldb_private::EmulateInstruction> m_inst_emulator_up; | |
| lldb_private::AddressRange *m_range_ptr; | |
| lldb_private::UnwindPlan *m_unwind_plan_ptr; | |
| UnwindState m_state; | |
| uint64_t m_initial_cfa; | |
| typedef std::map<uint64_t, uint64_t> PushedRegisterToAddrMap; | |
| PushedRegisterToAddrMap m_pushed_regs; | |
| // While processing the instruction stream, we need to communicate some state | |
| // change | |
| // information up to the higher level loop that makes decisions about how to | |
| // push | |
| // the unwind instructions for the UnwindPlan we're constructing. | |
| // The instruction we're processing updated the UnwindPlan::Row contents | |
| bool m_curr_row_modified; | |
| // The instruction is branching forward with the given offset. 0 value means | |
| // no branching. | |
| int64_t m_branch_offset = 0; | |
| // The instruction is a non-tail function call. | |
| bool m_branch_is_call = false; | |
| }; | |
| #endif // LLDB_SOURCE_PLUGINS_UNWINDASSEMBLY_INSTEMULATION_UNWINDASSEMBLYINSTEMULATION_H |