aboutsummaryrefslogtreecommitdiff
path: root/source/Plugins/UnwindAssembly
diff options
context:
space:
mode:
Diffstat (limited to 'source/Plugins/UnwindAssembly')
-rw-r--r--source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp1181
-rw-r--r--source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h292
-rw-r--r--source/Plugins/UnwindAssembly/x86/CMakeLists.txt1
-rw-r--r--source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp1667
-rw-r--r--source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h101
-rw-r--r--source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp1203
-rw-r--r--source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h181
7 files changed, 2343 insertions, 2283 deletions
diff --git a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp
index 72adf7576270..0852f1922034 100644
--- a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp
+++ b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp
@@ -1,4 +1,4 @@
-//===-- UnwindAssemblyInstEmulation.cpp --------------------------*- C++ -*-===//
+//===-- UnwindAssemblyInstEmulation.cpp --------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -21,658 +21,645 @@
#include "lldb/Core/StreamString.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
-#include "lldb/Target/Thread.h"
#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
using namespace lldb;
using namespace lldb_private;
-
-
//-----------------------------------------------------------------------------------------------
-// UnwindAssemblyInstEmulation method definitions
+// UnwindAssemblyInstEmulation method definitions
//-----------------------------------------------------------------------------------------------
-bool
-UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& range,
- Thread& thread,
- UnwindPlan& unwind_plan)
-{
- if (range.GetByteSize() > 0 &&
- range.GetBaseAddress().IsValid() &&
- m_inst_emulator_ap.get())
- {
-
- // The instruction emulation subclass setup the unwind plan for the
- // first instruction.
- m_inst_emulator_ap->CreateFunctionEntryUnwind (unwind_plan);
-
- // CreateFunctionEntryUnwind should have created the first row. If it
- // doesn't, then we are done.
- if (unwind_plan.GetRowCount() == 0)
- return false;
-
- ExecutionContext exe_ctx;
- thread.CalculateExecutionContext(exe_ctx);
- const bool prefer_file_cache = true;
- DisassemblerSP disasm_sp (Disassembler::DisassembleRange (m_arch,
- NULL,
- NULL,
- exe_ctx,
- range,
- prefer_file_cache));
-
- Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
-
- if (disasm_sp)
- {
-
- m_range_ptr = ⦥
- m_thread_ptr = &thread;
- m_unwind_plan_ptr = &unwind_plan;
-
- const uint32_t addr_byte_size = m_arch.GetAddressByteSize();
- const bool show_address = true;
- const bool show_bytes = true;
- m_inst_emulator_ap->GetRegisterInfo (unwind_plan.GetRegisterKind(),
- unwind_plan.GetInitialCFARegister(),
- m_cfa_reg_info);
-
- m_fp_is_cfa = false;
- m_register_values.clear();
- m_pushed_regs.clear();
-
- // 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_sp = (1ull << ((addr_byte_size * 8) - 1));
- RegisterValue cfa_reg_value;
- cfa_reg_value.SetUInt (m_initial_sp, m_cfa_reg_info.byte_size);
- SetRegisterValue (m_cfa_reg_info, cfa_reg_value);
-
- const InstructionList &inst_list = disasm_sp->GetInstructionList ();
- const size_t num_instructions = inst_list.GetSize();
-
- if (num_instructions > 0)
- {
- Instruction *inst = inst_list.GetInstructionAtIndex (0).get();
- const lldb::addr_t base_addr = inst->GetAddress().GetFileAddress();
-
- // Map for storing the unwind plan row and the value of the registers at a given offset.
- // When we see a forward branch we add a new entry to this map with the actual unwind plan
- // row and register context for the target address of the branch as the current data have
- // to be valid for the target address of the branch too if we are in the same function.
- std::map<lldb::addr_t, std::pair<UnwindPlan::RowSP, RegisterValueMap>> saved_unwind_states;
-
- // Make a copy of the current instruction Row and save it in m_curr_row
- // so we can add updates as we process the instructions.
- UnwindPlan::RowSP last_row = unwind_plan.GetLastRow();
+bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
+ AddressRange &range, Thread &thread, UnwindPlan &unwind_plan) {
+ std::vector<uint8_t> function_text(range.GetByteSize());
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ Error error;
+ const bool prefer_file_cache = true;
+ if (process_sp->GetTarget().ReadMemory(
+ range.GetBaseAddress(), prefer_file_cache, function_text.data(),
+ range.GetByteSize(), error) != range.GetByteSize()) {
+ return false;
+ }
+ }
+ return GetNonCallSiteUnwindPlanFromAssembly(
+ range, function_text.data(), function_text.size(), unwind_plan);
+}
+
+bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
+ AddressRange &range, uint8_t *opcode_data, size_t opcode_size,
+ UnwindPlan &unwind_plan) {
+ if (opcode_data == nullptr || opcode_size == 0)
+ return false;
+
+ if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() &&
+ m_inst_emulator_ap.get()) {
+
+ // The instruction emulation subclass setup the unwind plan for the
+ // first instruction.
+ m_inst_emulator_ap->CreateFunctionEntryUnwind(unwind_plan);
+
+ // CreateFunctionEntryUnwind should have created the first row. If it
+ // doesn't, then we are done.
+ if (unwind_plan.GetRowCount() == 0)
+ return false;
+
+ const bool prefer_file_cache = true;
+ DisassemblerSP disasm_sp(Disassembler::DisassembleBytes(
+ m_arch, NULL, NULL, range.GetBaseAddress(), opcode_data, opcode_size,
+ 99999, prefer_file_cache));
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (disasm_sp) {
+
+ m_range_ptr = &range;
+ m_unwind_plan_ptr = &unwind_plan;
+
+ const uint32_t addr_byte_size = m_arch.GetAddressByteSize();
+ const bool show_address = true;
+ const bool show_bytes = true;
+ m_inst_emulator_ap->GetRegisterInfo(unwind_plan.GetRegisterKind(),
+ unwind_plan.GetInitialCFARegister(),
+ m_cfa_reg_info);
+
+ m_fp_is_cfa = false;
+ m_register_values.clear();
+ m_pushed_regs.clear();
+
+ // 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_sp = (1ull << ((addr_byte_size * 8) - 1));
+ RegisterValue cfa_reg_value;
+ cfa_reg_value.SetUInt(m_initial_sp, m_cfa_reg_info.byte_size);
+ SetRegisterValue(m_cfa_reg_info, cfa_reg_value);
+
+ const InstructionList &inst_list = disasm_sp->GetInstructionList();
+ const size_t num_instructions = inst_list.GetSize();
+
+ if (num_instructions > 0) {
+ Instruction *inst = inst_list.GetInstructionAtIndex(0).get();
+ const lldb::addr_t base_addr = inst->GetAddress().GetFileAddress();
+
+ // Map for storing the unwind plan row and the value of the registers at
+ // a given offset.
+ // When we see a forward branch we add a new entry to this map with the
+ // actual unwind plan
+ // row and register context for the target address of the branch as the
+ // current data have
+ // to be valid for the target address of the branch too if we are in the
+ // same function.
+ std::map<lldb::addr_t, std::pair<UnwindPlan::RowSP, RegisterValueMap>>
+ saved_unwind_states;
+
+ // Make a copy of the current instruction Row and save it in m_curr_row
+ // so we can add updates as we process the instructions.
+ UnwindPlan::RowSP last_row = unwind_plan.GetLastRow();
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ if (last_row.get())
+ *newrow = *last_row.get();
+ m_curr_row.reset(newrow);
+
+ // Add the initial state to the save list with offset 0.
+ saved_unwind_states.insert({0, {last_row, m_register_values}});
+
+ // cache the pc register number (in whatever register numbering this
+ // UnwindPlan uses) for
+ // quick reference during instruction parsing.
+ RegisterInfo pc_reg_info;
+ m_inst_emulator_ap->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc_reg_info);
+
+ // cache the return address register number (in whatever register
+ // numbering this UnwindPlan uses) for
+ // quick reference during instruction parsing.
+ RegisterInfo ra_reg_info;
+ m_inst_emulator_ap->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, ra_reg_info);
+
+ // The architecture dependent condition code of the last processed
+ // instruction.
+ EmulateInstruction::InstructionCondition last_condition =
+ EmulateInstruction::UnconditionalCondition;
+ lldb::addr_t condition_block_start_offset = 0;
+
+ for (size_t idx = 0; idx < num_instructions; ++idx) {
+ m_curr_row_modified = false;
+ m_forward_branch_offset = 0;
+
+ inst = inst_list.GetInstructionAtIndex(idx).get();
+ if (inst) {
+ lldb::addr_t current_offset =
+ inst->GetAddress().GetFileAddress() - base_addr;
+ auto it = saved_unwind_states.upper_bound(current_offset);
+ assert(it != saved_unwind_states.begin() &&
+ "Unwind row for the function entry missing");
+ --it; // Move it to the row corresponding to the current offset
+
+ // If the offset of m_curr_row don't match with the offset we see in
+ // saved_unwind_states
+ // then we have to update m_curr_row and m_register_values based on
+ // the saved values. It
+ // is happenning after we processed an epilogue and a return to
+ // caller instruction.
+ if (it->second.first->GetOffset() != m_curr_row->GetOffset()) {
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *it->second.first;
+ m_curr_row.reset(newrow);
+ m_register_values = it->second.second;
+ }
+
+ m_inst_emulator_ap->SetInstruction(inst->GetOpcode(),
+ inst->GetAddress(), nullptr);
+
+ if (last_condition !=
+ m_inst_emulator_ap->GetInstructionCondition()) {
+ if (m_inst_emulator_ap->GetInstructionCondition() !=
+ EmulateInstruction::UnconditionalCondition &&
+ saved_unwind_states.count(current_offset) == 0) {
+ // If we don't have a saved row for the current offset then save
+ // our
+ // current state because we will have to restore it after the
+ // conditional block.
+ auto new_row =
+ std::make_shared<UnwindPlan::Row>(*m_curr_row.get());
+ saved_unwind_states.insert(
+ {current_offset, {new_row, m_register_values}});
+ }
+
+ // If the last instruction was conditional with a different
+ // condition
+ // then the then current condition then restore the condition.
+ if (last_condition !=
+ EmulateInstruction::UnconditionalCondition) {
+ const auto &saved_state =
+ saved_unwind_states.at(condition_block_start_offset);
+ m_curr_row =
+ std::make_shared<UnwindPlan::Row>(*saved_state.first);
+ m_curr_row->SetOffset(current_offset);
+ m_register_values = saved_state.second;
+ bool replace_existing =
+ true; // The last instruction might already
+ // created a row for this offset and
+ // we want to overwrite it.
+ unwind_plan.InsertRow(
+ std::make_shared<UnwindPlan::Row>(*m_curr_row),
+ replace_existing);
+ }
+
+ // We are starting a new conditional block at the catual offset
+ condition_block_start_offset = current_offset;
+ }
+
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+ lldb_private::FormatEntity::Entry format;
+ FormatEntity::Parse("${frame.pc}: ", format);
+ inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize(), show_address,
+ show_bytes, NULL, NULL, NULL, &format, 0);
+ log->PutString(strm.GetString());
+ }
+
+ last_condition = m_inst_emulator_ap->GetInstructionCondition();
+
+ m_inst_emulator_ap->EvaluateInstruction(
+ eEmulateInstructionOptionIgnoreConditions);
+
+ // If the current instruction is a branch forward then save the
+ // current CFI information
+ // for the offset where we are branching.
+ if (m_forward_branch_offset != 0 &&
+ range.ContainsFileAddress(inst->GetAddress().GetFileAddress() +
+ m_forward_branch_offset)) {
+ auto newrow =
+ std::make_shared<UnwindPlan::Row>(*m_curr_row.get());
+ newrow->SetOffset(current_offset + m_forward_branch_offset);
+ saved_unwind_states.insert(
+ {current_offset + m_forward_branch_offset,
+ {newrow, m_register_values}});
+ unwind_plan.InsertRow(newrow);
+ }
+
+ // Were there any changes to the CFI while evaluating this
+ // instruction?
+ if (m_curr_row_modified) {
+ // Save the modified row if we don't already have a CFI row in the
+ // currennt address
+ if (saved_unwind_states.count(
+ current_offset + inst->GetOpcode().GetByteSize()) == 0) {
+ m_curr_row->SetOffset(current_offset +
+ inst->GetOpcode().GetByteSize());
+ unwind_plan.InsertRow(m_curr_row);
+ saved_unwind_states.insert(
+ {current_offset + inst->GetOpcode().GetByteSize(),
+ {m_curr_row, m_register_values}});
+
+ // Allocate a new Row for m_curr_row, copy the current state
+ // into it
UnwindPlan::Row *newrow = new UnwindPlan::Row;
- if (last_row.get())
- *newrow = *last_row.get();
+ *newrow = *m_curr_row.get();
m_curr_row.reset(newrow);
-
- // Add the initial state to the save list with offset 0.
- saved_unwind_states.insert({0, {last_row, m_register_values}});
-
- // cache the pc register number (in whatever register numbering this UnwindPlan uses) for
- // quick reference during instruction parsing.
- RegisterInfo pc_reg_info;
- m_inst_emulator_ap->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc_reg_info);
-
- // cache the return address register number (in whatever register numbering this UnwindPlan uses) for
- // quick reference during instruction parsing.
- RegisterInfo ra_reg_info;
- m_inst_emulator_ap->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, ra_reg_info);
-
- // The architecture dependent condition code of the last processed instruction.
- EmulateInstruction::InstructionCondition last_condition = EmulateInstruction::UnconditionalCondition;
- lldb::addr_t condition_block_start_offset = 0;
-
- for (size_t idx=0; idx<num_instructions; ++idx)
- {
- m_curr_row_modified = false;
- m_forward_branch_offset = 0;
-
- inst = inst_list.GetInstructionAtIndex (idx).get();
- if (inst)
- {
- lldb::addr_t current_offset = inst->GetAddress().GetFileAddress() - base_addr;
- auto it = saved_unwind_states.upper_bound(current_offset);
- assert(it != saved_unwind_states.begin() && "Unwind row for the function entry missing");
- --it; // Move it to the row corresponding to the current offset
-
- // If the offset of m_curr_row don't match with the offset we see in saved_unwind_states
- // then we have to update m_curr_row and m_register_values based on the saved values. It
- // is happenning after we processed an epilogue and a return to caller instruction.
- if (it->second.first->GetOffset() != m_curr_row->GetOffset())
- {
- UnwindPlan::Row *newrow = new UnwindPlan::Row;
- *newrow = *it->second.first;
- m_curr_row.reset(newrow);
- m_register_values = it->second.second;
- }
-
- m_inst_emulator_ap->SetInstruction (inst->GetOpcode(),
- inst->GetAddress(),
- exe_ctx.GetTargetPtr());
-
- if (last_condition != m_inst_emulator_ap->GetInstructionCondition())
- {
- if (m_inst_emulator_ap->GetInstructionCondition() != EmulateInstruction::UnconditionalCondition &&
- saved_unwind_states.count(current_offset) == 0)
- {
- // If we don't have a saved row for the current offset then save our
- // current state because we will have to restore it after the
- // conditional block.
- auto new_row = std::make_shared<UnwindPlan::Row>(*m_curr_row.get());
- saved_unwind_states.insert({current_offset, {new_row, m_register_values}});
- }
-
- // If the last instruction was conditional with a different condition
- // then the then current condition then restore the condition.
- if (last_condition != EmulateInstruction::UnconditionalCondition)
- {
- const auto& saved_state = saved_unwind_states.at(condition_block_start_offset);
- m_curr_row = std::make_shared<UnwindPlan::Row>(*saved_state.first);
- m_curr_row->SetOffset(current_offset);
- m_register_values = saved_state.second;
- bool replace_existing = true; // The last instruction might already
- // created a row for this offset and
- // we want to overwrite it.
- unwind_plan.InsertRow(std::make_shared<UnwindPlan::Row>(*m_curr_row), replace_existing);
- }
-
- // We are starting a new conditional block at the catual offset
- condition_block_start_offset = current_offset;
- }
-
- if (log && log->GetVerbose ())
- {
- StreamString strm;
- lldb_private::FormatEntity::Entry format;
- FormatEntity::Parse("${frame.pc}: ", format);
- inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize (), show_address, show_bytes, NULL, NULL, NULL, &format, 0);
- log->PutCString (strm.GetData());
- }
-
- last_condition = m_inst_emulator_ap->GetInstructionCondition();
-
- m_inst_emulator_ap->EvaluateInstruction (eEmulateInstructionOptionIgnoreConditions);
-
- // If the current instruction is a branch forward then save the current CFI information
- // for the offset where we are branching.
- if (m_forward_branch_offset != 0 && range.ContainsFileAddress(inst->GetAddress().GetFileAddress() + m_forward_branch_offset))
- {
- auto newrow = std::make_shared<UnwindPlan::Row>(*m_curr_row.get());
- newrow->SetOffset(current_offset + m_forward_branch_offset);
- saved_unwind_states.insert({current_offset + m_forward_branch_offset, {newrow, m_register_values}});
- unwind_plan.InsertRow(newrow);
- }
-
- // Were there any changes to the CFI while evaluating this instruction?
- if (m_curr_row_modified)
- {
- // Save the modified row if we don't already have a CFI row in the currennt address
- if (saved_unwind_states.count(current_offset + inst->GetOpcode().GetByteSize()) == 0)
- {
- m_curr_row->SetOffset (current_offset + inst->GetOpcode().GetByteSize());
- unwind_plan.InsertRow (m_curr_row);
- saved_unwind_states.insert({current_offset + inst->GetOpcode().GetByteSize(), {m_curr_row, m_register_values}});
-
- // Allocate a new Row for m_curr_row, copy the current state into it
- UnwindPlan::Row *newrow = new UnwindPlan::Row;
- *newrow = *m_curr_row.get();
- m_curr_row.reset(newrow);
- }
- }
- }
- }
+ }
}
+ }
}
-
- if (log && log->GetVerbose ())
- {
- StreamString strm;
- lldb::addr_t base_addr = range.GetBaseAddress().GetLoadAddress(thread.CalculateTarget().get());
- strm.Printf ("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):", base_addr, base_addr + range.GetByteSize());
- unwind_plan.Dump(strm, &thread, base_addr);
- log->PutCString (strm.GetData());
- }
- return unwind_plan.GetRowCount() > 0;
+ }
}
- return false;
+
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+ lldb::addr_t base_addr = range.GetBaseAddress().GetFileAddress();
+ strm.Printf("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):",
+ base_addr, base_addr + range.GetByteSize());
+ unwind_plan.Dump(strm, nullptr, base_addr);
+ log->PutString(strm.GetString());
+ }
+ return unwind_plan.GetRowCount() > 0;
+ }
+ return false;
}
-bool
-UnwindAssemblyInstEmulation::AugmentUnwindPlanFromCallSite (AddressRange& func,
- Thread& thread,
- UnwindPlan& unwind_plan)
-{
- return false;
+bool UnwindAssemblyInstEmulation::AugmentUnwindPlanFromCallSite(
+ AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {
+ return false;
}
-bool
-UnwindAssemblyInstEmulation::GetFastUnwindPlan (AddressRange& func,
- Thread& thread,
- UnwindPlan &unwind_plan)
-{
- return false;
+bool UnwindAssemblyInstEmulation::GetFastUnwindPlan(AddressRange &func,
+ Thread &thread,
+ UnwindPlan &unwind_plan) {
+ return false;
}
-bool
-UnwindAssemblyInstEmulation::FirstNonPrologueInsn (AddressRange& func,
- const ExecutionContext &exe_ctx,
- Address& first_non_prologue_insn)
-{
- return false;
+bool UnwindAssemblyInstEmulation::FirstNonPrologueInsn(
+ AddressRange &func, const ExecutionContext &exe_ctx,
+ Address &first_non_prologue_insn) {
+ return false;
}
UnwindAssembly *
-UnwindAssemblyInstEmulation::CreateInstance (const ArchSpec &arch)
-{
- std::unique_ptr<EmulateInstruction> inst_emulator_ap (EmulateInstruction::FindPlugin (arch, eInstructionTypePrologueEpilogue, NULL));
- // Make sure that all prologue instructions are handled
- if (inst_emulator_ap.get())
- return new UnwindAssemblyInstEmulation (arch, inst_emulator_ap.release());
- return NULL;
+UnwindAssemblyInstEmulation::CreateInstance(const ArchSpec &arch) {
+ std::unique_ptr<EmulateInstruction> inst_emulator_ap(
+ EmulateInstruction::FindPlugin(arch, eInstructionTypePrologueEpilogue,
+ NULL));
+ // Make sure that all prologue instructions are handled
+ if (inst_emulator_ap.get())
+ return new UnwindAssemblyInstEmulation(arch, inst_emulator_ap.release());
+ return NULL;
}
-
//------------------------------------------------------------------
// PluginInterface protocol in UnwindAssemblyParser_x86
//------------------------------------------------------------------
-ConstString
-UnwindAssemblyInstEmulation::GetPluginName()
-{
- return GetPluginNameStatic();
+ConstString UnwindAssemblyInstEmulation::GetPluginName() {
+ return GetPluginNameStatic();
}
-uint32_t
-UnwindAssemblyInstEmulation::GetPluginVersion()
-{
- return 1;
-}
+uint32_t UnwindAssemblyInstEmulation::GetPluginVersion() { return 1; }
-void
-UnwindAssemblyInstEmulation::Initialize()
-{
- PluginManager::RegisterPlugin (GetPluginNameStatic(),
- GetPluginDescriptionStatic(),
- CreateInstance);
+void UnwindAssemblyInstEmulation::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
}
-void
-UnwindAssemblyInstEmulation::Terminate()
-{
- PluginManager::UnregisterPlugin (CreateInstance);
+void UnwindAssemblyInstEmulation::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
}
-
-ConstString
-UnwindAssemblyInstEmulation::GetPluginNameStatic()
-{
- static ConstString g_name("inst-emulation");
- return g_name;
+ConstString UnwindAssemblyInstEmulation::GetPluginNameStatic() {
+ static ConstString g_name("inst-emulation");
+ return g_name;
}
-const char *
-UnwindAssemblyInstEmulation::GetPluginDescriptionStatic()
-{
- return "Instruction emulation based unwind information.";
+const char *UnwindAssemblyInstEmulation::GetPluginDescriptionStatic() {
+ return "Instruction emulation based unwind information.";
}
-
-uint64_t
-UnwindAssemblyInstEmulation::MakeRegisterKindValuePair (const RegisterInfo &reg_info)
-{
- lldb::RegisterKind reg_kind;
- uint32_t reg_num;
- if (EmulateInstruction::GetBestRegisterKindAndNumber (&reg_info, reg_kind, reg_num))
- return (uint64_t)reg_kind << 24 | reg_num;
- return 0ull;
+uint64_t UnwindAssemblyInstEmulation::MakeRegisterKindValuePair(
+ const RegisterInfo &reg_info) {
+ lldb::RegisterKind reg_kind;
+ uint32_t reg_num;
+ if (EmulateInstruction::GetBestRegisterKindAndNumber(&reg_info, reg_kind,
+ reg_num))
+ return (uint64_t)reg_kind << 24 | reg_num;
+ return 0ull;
}
-void
-UnwindAssemblyInstEmulation::SetRegisterValue (const RegisterInfo &reg_info, const RegisterValue &reg_value)
-{
- m_register_values[MakeRegisterKindValuePair (reg_info)] = reg_value;
+void UnwindAssemblyInstEmulation::SetRegisterValue(
+ const RegisterInfo &reg_info, const RegisterValue &reg_value) {
+ m_register_values[MakeRegisterKindValuePair(reg_info)] = reg_value;
}
-bool
-UnwindAssemblyInstEmulation::GetRegisterValue (const RegisterInfo &reg_info, RegisterValue &reg_value)
-{
- const uint64_t reg_id = MakeRegisterKindValuePair (reg_info);
- RegisterValueMap::const_iterator pos = m_register_values.find(reg_id);
- if (pos != m_register_values.end())
- {
- reg_value = pos->second;
- return true; // We had a real value that comes from an opcode that wrote
- // to it...
- }
- // We are making up a value that is recognizable...
- reg_value.SetUInt(reg_id, reg_info.byte_size);
- return false;
+bool UnwindAssemblyInstEmulation::GetRegisterValue(const RegisterInfo &reg_info,
+ RegisterValue &reg_value) {
+ const uint64_t reg_id = MakeRegisterKindValuePair(reg_info);
+ RegisterValueMap::const_iterator pos = m_register_values.find(reg_id);
+ if (pos != m_register_values.end()) {
+ reg_value = pos->second;
+ return true; // We had a real value that comes from an opcode that wrote
+ // to it...
+ }
+ // We are making up a value that is recognizable...
+ reg_value.SetUInt(reg_id, reg_info.byte_size);
+ return false;
}
-
-size_t
-UnwindAssemblyInstEmulation::ReadMemory (EmulateInstruction *instruction,
- void *baton,
- const EmulateInstruction::Context &context,
- lldb::addr_t addr,
- void *dst,
- size_t dst_len)
-{
- Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
-
- if (log && log->GetVerbose ())
- {
- StreamString strm;
- strm.Printf ("UnwindAssemblyInstEmulation::ReadMemory (addr = 0x%16.16" PRIx64 ", dst = %p, dst_len = %" PRIu64 ", context = ",
- addr,
- dst,
- (uint64_t)dst_len);
- context.Dump(strm, instruction);
- log->PutCString (strm.GetData ());
- }
- memset (dst, 0, dst_len);
- return dst_len;
+size_t UnwindAssemblyInstEmulation::ReadMemory(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst,
+ size_t dst_len) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+ strm.Printf(
+ "UnwindAssemblyInstEmulation::ReadMemory (addr = 0x%16.16" PRIx64
+ ", dst = %p, dst_len = %" PRIu64 ", context = ",
+ addr, dst, (uint64_t)dst_len);
+ context.Dump(strm, instruction);
+ log->PutString(strm.GetString());
+ }
+ memset(dst, 0, dst_len);
+ return dst_len;
}
-size_t
-UnwindAssemblyInstEmulation::WriteMemory (EmulateInstruction *instruction,
- void *baton,
- const EmulateInstruction::Context &context,
- lldb::addr_t addr,
- const void *dst,
- size_t dst_len)
-{
- if (baton && dst && dst_len)
- return ((UnwindAssemblyInstEmulation *)baton)->WriteMemory (instruction, context, addr, dst, dst_len);
- return 0;
+size_t UnwindAssemblyInstEmulation::WriteMemory(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, lldb::addr_t addr,
+ const void *dst, size_t dst_len) {
+ if (baton && dst && dst_len)
+ return ((UnwindAssemblyInstEmulation *)baton)
+ ->WriteMemory(instruction, context, addr, dst, dst_len);
+ return 0;
}
-size_t
-UnwindAssemblyInstEmulation::WriteMemory (EmulateInstruction *instruction,
- const EmulateInstruction::Context &context,
- lldb::addr_t addr,
- const void *dst,
- size_t dst_len)
-{
- DataExtractor data (dst,
- dst_len,
- instruction->GetArchitecture ().GetByteOrder(),
- instruction->GetArchitecture ().GetAddressByteSize());
-
- Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
-
- if (log && log->GetVerbose ())
- {
- StreamString strm;
-
- strm.PutCString ("UnwindAssemblyInstEmulation::WriteMemory (");
- data.Dump(&strm, 0, eFormatBytes, 1, dst_len, UINT32_MAX, addr, 0, 0);
- strm.PutCString (", context = ");
- context.Dump(strm, instruction);
- log->PutCString (strm.GetData());
- }
-
- const bool cant_replace = false;
-
- switch (context.type)
- {
- default:
- case EmulateInstruction::eContextInvalid:
- case EmulateInstruction::eContextReadOpcode:
- case EmulateInstruction::eContextImmediate:
- case EmulateInstruction::eContextAdjustBaseRegister:
- case EmulateInstruction::eContextRegisterPlusOffset:
- case EmulateInstruction::eContextAdjustPC:
- case EmulateInstruction::eContextRegisterStore:
- case EmulateInstruction::eContextRegisterLoad:
- case EmulateInstruction::eContextRelativeBranchImmediate:
- case EmulateInstruction::eContextAbsoluteBranchRegister:
- case EmulateInstruction::eContextSupervisorCall:
- case EmulateInstruction::eContextTableBranchReadMemory:
- case EmulateInstruction::eContextWriteRegisterRandomBits:
- case EmulateInstruction::eContextWriteMemoryRandomBits:
- case EmulateInstruction::eContextArithmetic:
- case EmulateInstruction::eContextAdvancePC:
- case EmulateInstruction::eContextReturnFromException:
- case EmulateInstruction::eContextPopRegisterOffStack:
- case EmulateInstruction::eContextAdjustStackPointer:
- break;
-
- case EmulateInstruction::eContextPushRegisterOnStack:
- {
- uint32_t reg_num = LLDB_INVALID_REGNUM;
- uint32_t generic_regnum = LLDB_INVALID_REGNUM;
- if (context.info_type == EmulateInstruction::eInfoTypeRegisterToRegisterPlusOffset)
- {
- const uint32_t unwind_reg_kind = m_unwind_plan_ptr->GetRegisterKind();
- reg_num = context.info.RegisterToRegisterPlusOffset.data_reg.kinds[unwind_reg_kind];
- generic_regnum = context.info.RegisterToRegisterPlusOffset.data_reg.kinds[eRegisterKindGeneric];
- }
- else
- assert (!"unhandled case, add code to handle this!");
-
- if (reg_num != LLDB_INVALID_REGNUM && generic_regnum != LLDB_REGNUM_GENERIC_SP)
- {
- if (m_pushed_regs.find (reg_num) == m_pushed_regs.end())
- {
- m_pushed_regs[reg_num] = addr;
- const int32_t offset = addr - m_initial_sp;
- m_curr_row->SetRegisterLocationToAtCFAPlusOffset (reg_num, offset, cant_replace);
- m_curr_row_modified = true;
- }
- }
- }
- break;
-
+size_t UnwindAssemblyInstEmulation::WriteMemory(
+ EmulateInstruction *instruction, const EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst, size_t dst_len) {
+ DataExtractor data(dst, dst_len,
+ instruction->GetArchitecture().GetByteOrder(),
+ instruction->GetArchitecture().GetAddressByteSize());
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+
+ strm.PutCString("UnwindAssemblyInstEmulation::WriteMemory (");
+ data.Dump(&strm, 0, eFormatBytes, 1, dst_len, UINT32_MAX, addr, 0, 0);
+ strm.PutCString(", context = ");
+ context.Dump(strm, instruction);
+ log->PutString(strm.GetString());
+ }
+
+ const bool cant_replace = false;
+
+ switch (context.type) {
+ default:
+ case EmulateInstruction::eContextInvalid:
+ case EmulateInstruction::eContextReadOpcode:
+ case EmulateInstruction::eContextImmediate:
+ case EmulateInstruction::eContextAdjustBaseRegister:
+ case EmulateInstruction::eContextRegisterPlusOffset:
+ case EmulateInstruction::eContextAdjustPC:
+ case EmulateInstruction::eContextRegisterStore:
+ case EmulateInstruction::eContextRegisterLoad:
+ case EmulateInstruction::eContextRelativeBranchImmediate:
+ case EmulateInstruction::eContextAbsoluteBranchRegister:
+ case EmulateInstruction::eContextSupervisorCall:
+ case EmulateInstruction::eContextTableBranchReadMemory:
+ case EmulateInstruction::eContextWriteRegisterRandomBits:
+ case EmulateInstruction::eContextWriteMemoryRandomBits:
+ case EmulateInstruction::eContextArithmetic:
+ case EmulateInstruction::eContextAdvancePC:
+ case EmulateInstruction::eContextReturnFromException:
+ case EmulateInstruction::eContextPopRegisterOffStack:
+ case EmulateInstruction::eContextAdjustStackPointer:
+ break;
+
+ case EmulateInstruction::eContextPushRegisterOnStack: {
+ uint32_t reg_num = LLDB_INVALID_REGNUM;
+ uint32_t generic_regnum = LLDB_INVALID_REGNUM;
+ if (context.info_type ==
+ EmulateInstruction::eInfoTypeRegisterToRegisterPlusOffset) {
+ const uint32_t unwind_reg_kind = m_unwind_plan_ptr->GetRegisterKind();
+ reg_num = context.info.RegisterToRegisterPlusOffset.data_reg
+ .kinds[unwind_reg_kind];
+ generic_regnum = context.info.RegisterToRegisterPlusOffset.data_reg
+ .kinds[eRegisterKindGeneric];
+ } else
+ assert(!"unhandled case, add code to handle this!");
+
+ if (reg_num != LLDB_INVALID_REGNUM &&
+ generic_regnum != LLDB_REGNUM_GENERIC_SP) {
+ if (m_pushed_regs.find(reg_num) == m_pushed_regs.end()) {
+ m_pushed_regs[reg_num] = addr;
+ const int32_t offset = addr - m_initial_sp;
+ m_curr_row->SetRegisterLocationToAtCFAPlusOffset(reg_num, offset,
+ cant_replace);
+ m_curr_row_modified = true;
+ }
}
+ } break;
+ }
- return dst_len;
+ return dst_len;
}
-bool
-UnwindAssemblyInstEmulation::ReadRegister (EmulateInstruction *instruction,
- void *baton,
- const RegisterInfo *reg_info,
- RegisterValue &reg_value)
-{
-
- if (baton && reg_info)
- return ((UnwindAssemblyInstEmulation *)baton)->ReadRegister (instruction, reg_info, reg_value);
- return false;
+bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction,
+ void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+
+ if (baton && reg_info)
+ return ((UnwindAssemblyInstEmulation *)baton)
+ ->ReadRegister(instruction, reg_info, reg_value);
+ return false;
}
-bool
-UnwindAssemblyInstEmulation::ReadRegister (EmulateInstruction *instruction,
- const RegisterInfo *reg_info,
- RegisterValue &reg_value)
-{
- bool synthetic = GetRegisterValue (*reg_info, reg_value);
-
- Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
-
- if (log && log->GetVerbose ())
- {
-
- StreamString strm;
- strm.Printf ("UnwindAssemblyInstEmulation::ReadRegister (name = \"%s\") => synthetic_value = %i, value = ", reg_info->name, synthetic);
- reg_value.Dump(&strm, reg_info, false, false, eFormatDefault);
- log->PutCString(strm.GetData());
- }
- return true;
+bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ bool synthetic = GetRegisterValue(*reg_info, reg_value);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (log && log->GetVerbose()) {
+
+ StreamString strm;
+ strm.Printf("UnwindAssemblyInstEmulation::ReadRegister (name = \"%s\") => "
+ "synthetic_value = %i, value = ",
+ reg_info->name, synthetic);
+ reg_value.Dump(&strm, reg_info, false, false, eFormatDefault);
+ log->PutString(strm.GetString());
+ }
+ return true;
}
-bool
-UnwindAssemblyInstEmulation::WriteRegister (EmulateInstruction *instruction,
- void *baton,
- const EmulateInstruction::Context &context,
- const RegisterInfo *reg_info,
- const RegisterValue &reg_value)
-{
- if (baton && reg_info)
- return ((UnwindAssemblyInstEmulation *)baton)->WriteRegister (instruction, context, reg_info, reg_value);
- return false;
+bool UnwindAssemblyInstEmulation::WriteRegister(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ if (baton && reg_info)
+ return ((UnwindAssemblyInstEmulation *)baton)
+ ->WriteRegister(instruction, context, reg_info, reg_value);
+ return false;
}
-bool
-UnwindAssemblyInstEmulation::WriteRegister (EmulateInstruction *instruction,
- const EmulateInstruction::Context &context,
- const RegisterInfo *reg_info,
- const RegisterValue &reg_value)
-{
- Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
-
- if (log && log->GetVerbose ())
- {
-
- StreamString strm;
- strm.Printf ("UnwindAssemblyInstEmulation::WriteRegister (name = \"%s\", value = ", reg_info->name);
- reg_value.Dump(&strm, reg_info, false, false, eFormatDefault);
- strm.PutCString (", context = ");
- context.Dump(strm, instruction);
- log->PutCString(strm.GetData());
+bool UnwindAssemblyInstEmulation::WriteRegister(
+ EmulateInstruction *instruction, const EmulateInstruction::Context &context,
+ const RegisterInfo *reg_info, const RegisterValue &reg_value) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (log && log->GetVerbose()) {
+
+ StreamString strm;
+ strm.Printf(
+ "UnwindAssemblyInstEmulation::WriteRegister (name = \"%s\", value = ",
+ reg_info->name);
+ reg_value.Dump(&strm, reg_info, false, false, eFormatDefault);
+ strm.PutCString(", context = ");
+ context.Dump(strm, instruction);
+ log->PutString(strm.GetString());
+ }
+
+ SetRegisterValue(*reg_info, reg_value);
+
+ switch (context.type) {
+ case EmulateInstruction::eContextInvalid:
+ case EmulateInstruction::eContextReadOpcode:
+ case EmulateInstruction::eContextImmediate:
+ case EmulateInstruction::eContextAdjustBaseRegister:
+ case EmulateInstruction::eContextRegisterPlusOffset:
+ case EmulateInstruction::eContextAdjustPC:
+ case EmulateInstruction::eContextRegisterStore:
+ case EmulateInstruction::eContextSupervisorCall:
+ case EmulateInstruction::eContextTableBranchReadMemory:
+ case EmulateInstruction::eContextWriteRegisterRandomBits:
+ case EmulateInstruction::eContextWriteMemoryRandomBits:
+ case EmulateInstruction::eContextAdvancePC:
+ case EmulateInstruction::eContextReturnFromException:
+ case EmulateInstruction::eContextPushRegisterOnStack:
+ case EmulateInstruction::eContextRegisterLoad:
+ // {
+ // const uint32_t reg_num =
+ // reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
+ // if (reg_num != LLDB_INVALID_REGNUM)
+ // {
+ // const bool can_replace_only_if_unspecified = true;
+ //
+ // m_curr_row.SetRegisterLocationToUndefined (reg_num,
+ // can_replace_only_if_unspecified,
+ // can_replace_only_if_unspecified);
+ // m_curr_row_modified = true;
+ // }
+ // }
+ break;
+
+ case EmulateInstruction::eContextArithmetic: {
+ // If we adjusted the current frame pointer by a constant then adjust the
+ // CFA offset
+ // with the same amount.
+ lldb::RegisterKind kind = m_unwind_plan_ptr->GetRegisterKind();
+ if (m_fp_is_cfa && reg_info->kinds[kind] == m_cfa_reg_info.kinds[kind] &&
+ context.info_type == EmulateInstruction::eInfoTypeRegisterPlusOffset &&
+ context.info.RegisterPlusOffset.reg.kinds[kind] ==
+ m_cfa_reg_info.kinds[kind]) {
+ const int64_t offset = context.info.RegisterPlusOffset.signed_offset;
+ m_curr_row->GetCFAValue().IncOffset(-1 * offset);
+ m_curr_row_modified = true;
}
-
- SetRegisterValue (*reg_info, reg_value);
-
- switch (context.type)
- {
- case EmulateInstruction::eContextInvalid:
- case EmulateInstruction::eContextReadOpcode:
- case EmulateInstruction::eContextImmediate:
- case EmulateInstruction::eContextAdjustBaseRegister:
- case EmulateInstruction::eContextRegisterPlusOffset:
- case EmulateInstruction::eContextAdjustPC:
- case EmulateInstruction::eContextRegisterStore:
- case EmulateInstruction::eContextSupervisorCall:
- case EmulateInstruction::eContextTableBranchReadMemory:
- case EmulateInstruction::eContextWriteRegisterRandomBits:
- case EmulateInstruction::eContextWriteMemoryRandomBits:
- case EmulateInstruction::eContextAdvancePC:
- case EmulateInstruction::eContextReturnFromException:
- case EmulateInstruction::eContextPushRegisterOnStack:
- case EmulateInstruction::eContextRegisterLoad:
-// {
-// const uint32_t reg_num = reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
-// if (reg_num != LLDB_INVALID_REGNUM)
-// {
-// const bool can_replace_only_if_unspecified = true;
-//
-// m_curr_row.SetRegisterLocationToUndefined (reg_num,
-// can_replace_only_if_unspecified,
-// can_replace_only_if_unspecified);
-// m_curr_row_modified = true;
-// }
-// }
- break;
-
- case EmulateInstruction::eContextArithmetic:
- {
- // If we adjusted the current frame pointer by a constant then adjust the CFA offset
- // with the same amount.
- lldb::RegisterKind kind = m_unwind_plan_ptr->GetRegisterKind();
- if (m_fp_is_cfa && reg_info->kinds[kind] == m_cfa_reg_info.kinds[kind] &&
- context.info_type == EmulateInstruction::eInfoTypeRegisterPlusOffset &&
- context.info.RegisterPlusOffset.reg.kinds[kind] == m_cfa_reg_info.kinds[kind])
- {
- const int64_t offset = context.info.RegisterPlusOffset.signed_offset;
- m_curr_row->GetCFAValue().IncOffset(-1 * offset);
- m_curr_row_modified = true;
- }
- }
- break;
-
- case EmulateInstruction::eContextAbsoluteBranchRegister:
- case EmulateInstruction::eContextRelativeBranchImmediate:
- {
- if (context.info_type == EmulateInstruction::eInfoTypeISAAndImmediate &&
- context.info.ISAAndImmediate.unsigned_data32 > 0)
- {
- m_forward_branch_offset = context.info.ISAAndImmediateSigned.signed_data32;
- }
- else if (context.info_type == EmulateInstruction::eInfoTypeISAAndImmediateSigned &&
- context.info.ISAAndImmediateSigned.signed_data32 > 0)
- {
- m_forward_branch_offset = context.info.ISAAndImmediate.unsigned_data32;
- }
- else if (context.info_type == EmulateInstruction::eInfoTypeImmediate &&
- context.info.unsigned_immediate > 0)
- {
- m_forward_branch_offset = context.info.unsigned_immediate;
- }
- else if (context.info_type == EmulateInstruction::eInfoTypeImmediateSigned &&
- context.info.signed_immediate > 0)
- {
- m_forward_branch_offset = context.info.signed_immediate;
- }
- }
- break;
-
- case EmulateInstruction::eContextPopRegisterOffStack:
- {
- const uint32_t reg_num = reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
- const uint32_t generic_regnum = reg_info->kinds[eRegisterKindGeneric];
- if (reg_num != LLDB_INVALID_REGNUM && generic_regnum != LLDB_REGNUM_GENERIC_SP)
- {
- switch (context.info_type)
- {
- case EmulateInstruction::eInfoTypeAddress:
- if (m_pushed_regs.find(reg_num) != m_pushed_regs.end() &&
- context.info.address == m_pushed_regs[reg_num])
- {
- m_curr_row->SetRegisterLocationToSame(reg_num,
- false /*must_replace*/);
- m_curr_row_modified = true;
- }
- break;
- case EmulateInstruction::eInfoTypeISA:
- assert((generic_regnum == LLDB_REGNUM_GENERIC_PC ||
- generic_regnum == LLDB_REGNUM_GENERIC_FLAGS) &&
- "eInfoTypeISA used for poping a register other the the PC/FLAGS");
- if (generic_regnum != LLDB_REGNUM_GENERIC_FLAGS)
- {
- m_curr_row->SetRegisterLocationToSame(reg_num,
- false /*must_replace*/);
- m_curr_row_modified = true;
- }
- break;
- default:
- assert(false && "unhandled case, add code to handle this!");
- break;
- }
- }
- }
- break;
-
- case EmulateInstruction::eContextSetFramePointer:
- if (!m_fp_is_cfa)
- {
- m_fp_is_cfa = true;
- m_cfa_reg_info = *reg_info;
- const uint32_t cfa_reg_num = reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
- assert (cfa_reg_num != LLDB_INVALID_REGNUM);
- m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(cfa_reg_num, m_initial_sp -
- reg_value.GetAsUInt64());
- m_curr_row_modified = true;
- }
- break;
-
- case EmulateInstruction::eContextAdjustStackPointer:
- // If we have created a frame using the frame pointer, don't follow
- // subsequent adjustments to the stack pointer.
- if (!m_fp_is_cfa)
- {
- m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
- m_curr_row->GetCFAValue().GetRegisterNumber(),
- m_initial_sp - reg_value.GetAsUInt64());
- m_curr_row_modified = true;
- }
- break;
+ } break;
+
+ case EmulateInstruction::eContextAbsoluteBranchRegister:
+ case EmulateInstruction::eContextRelativeBranchImmediate: {
+ if (context.info_type == EmulateInstruction::eInfoTypeISAAndImmediate &&
+ context.info.ISAAndImmediate.unsigned_data32 > 0) {
+ m_forward_branch_offset =
+ context.info.ISAAndImmediateSigned.signed_data32;
+ } else if (context.info_type ==
+ EmulateInstruction::eInfoTypeISAAndImmediateSigned &&
+ context.info.ISAAndImmediateSigned.signed_data32 > 0) {
+ m_forward_branch_offset = context.info.ISAAndImmediate.unsigned_data32;
+ } else if (context.info_type == EmulateInstruction::eInfoTypeImmediate &&
+ context.info.unsigned_immediate > 0) {
+ m_forward_branch_offset = context.info.unsigned_immediate;
+ } else if (context.info_type ==
+ EmulateInstruction::eInfoTypeImmediateSigned &&
+ context.info.signed_immediate > 0) {
+ m_forward_branch_offset = context.info.signed_immediate;
}
- return true;
+ } break;
+
+ case EmulateInstruction::eContextPopRegisterOffStack: {
+ const uint32_t reg_num =
+ reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
+ const uint32_t generic_regnum = reg_info->kinds[eRegisterKindGeneric];
+ if (reg_num != LLDB_INVALID_REGNUM &&
+ generic_regnum != LLDB_REGNUM_GENERIC_SP) {
+ switch (context.info_type) {
+ case EmulateInstruction::eInfoTypeAddress:
+ if (m_pushed_regs.find(reg_num) != m_pushed_regs.end() &&
+ context.info.address == m_pushed_regs[reg_num]) {
+ m_curr_row->SetRegisterLocationToSame(reg_num,
+ false /*must_replace*/);
+ m_curr_row_modified = true;
+ }
+ break;
+ case EmulateInstruction::eInfoTypeISA:
+ assert(
+ (generic_regnum == LLDB_REGNUM_GENERIC_PC ||
+ generic_regnum == LLDB_REGNUM_GENERIC_FLAGS) &&
+ "eInfoTypeISA used for poping a register other the the PC/FLAGS");
+ if (generic_regnum != LLDB_REGNUM_GENERIC_FLAGS) {
+ m_curr_row->SetRegisterLocationToSame(reg_num,
+ false /*must_replace*/);
+ m_curr_row_modified = true;
+ }
+ break;
+ default:
+ assert(false && "unhandled case, add code to handle this!");
+ break;
+ }
+ }
+ } break;
+
+ case EmulateInstruction::eContextSetFramePointer:
+ if (!m_fp_is_cfa) {
+ m_fp_is_cfa = true;
+ m_cfa_reg_info = *reg_info;
+ const uint32_t cfa_reg_num =
+ reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
+ assert(cfa_reg_num != LLDB_INVALID_REGNUM);
+ m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
+ cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64());
+ m_curr_row_modified = true;
+ }
+ break;
+
+ case EmulateInstruction::eContextRestoreStackPointer:
+ if (m_fp_is_cfa) {
+ m_fp_is_cfa = false;
+ m_cfa_reg_info = *reg_info;
+ const uint32_t cfa_reg_num =
+ reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
+ assert(cfa_reg_num != LLDB_INVALID_REGNUM);
+ m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
+ cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64());
+ m_curr_row_modified = true;
+ }
+ break;
+
+ case EmulateInstruction::eContextAdjustStackPointer:
+ // If we have created a frame using the frame pointer, don't follow
+ // subsequent adjustments to the stack pointer.
+ if (!m_fp_is_cfa) {
+ m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
+ m_curr_row->GetCFAValue().GetRegisterNumber(),
+ m_initial_sp - reg_value.GetAsUInt64());
+ m_curr_row_modified = true;
+ }
+ break;
+ }
+ return true;
}
-
-
diff --git a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h
index 61d3ece3f6c3..e587c93b427c 100644
--- a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h
+++ b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h
@@ -14,172 +14,148 @@
// C++ Includes
// Other libraries and framework includes
// Project includes
-#include "lldb/lldb-private.h"
#include "lldb/Core/EmulateInstruction.h"
#include "lldb/Core/RegisterValue.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Target/UnwindAssembly.h"
+#include "lldb/lldb-private.h"
-class UnwindAssemblyInstEmulation : public lldb_private::UnwindAssembly
-{
+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
- 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 lldb_private::ConstString
- GetPluginNameStatic();
-
- static const char *
- GetPluginDescriptionStatic();
-
- lldb_private::ConstString
- GetPluginName() override;
-
- uint32_t
- GetPluginVersion() override;
-
+ ~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 lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
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_ap (inst_emulator),
- m_range_ptr (NULL),
- m_thread_ptr (NULL),
- m_unwind_plan_ptr (NULL),
- m_curr_row (),
- m_cfa_reg_info (),
- m_fp_is_cfa (false),
- m_register_values (),
- m_pushed_regs(),
- m_curr_row_modified (false),
- m_forward_branch_offset (0)
- {
- if (m_inst_emulator_ap.get())
- {
- m_inst_emulator_ap->SetBaton (this);
- m_inst_emulator_ap->SetCallbacks (ReadMemory, WriteMemory, ReadRegister, WriteRegister);
- }
+ // 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_ap(inst_emulator),
+ m_range_ptr(NULL), m_unwind_plan_ptr(NULL), m_curr_row(),
+ m_cfa_reg_info(), m_fp_is_cfa(false), m_register_values(),
+ m_pushed_regs(), m_curr_row_modified(false),
+ m_forward_branch_offset(0) {
+ if (m_inst_emulator_ap.get()) {
+ m_inst_emulator_ap->SetBaton(this);
+ m_inst_emulator_ap->SetCallbacks(ReadMemory, WriteMemory, ReadRegister,
+ WriteRegister);
}
-
- 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 &reg_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 &reg_value);
-
-// size_t
-// ReadMemory (lldb_private::EmulateInstruction *instruction,
-// const lldb_private::EmulateInstruction::Context &context,
-// lldb::addr_t addr,
-// void *dst,
-// size_t length);
-
- 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 &reg_value);
-
- bool
- WriteRegister (lldb_private::EmulateInstruction *instruction,
- const lldb_private::EmulateInstruction::Context &context,
- const lldb_private::RegisterInfo *reg_info,
- const lldb_private::RegisterValue &reg_value);
-
- static uint64_t
- MakeRegisterKindValuePair (const lldb_private::RegisterInfo &reg_info);
-
- void
- SetRegisterValue (const lldb_private::RegisterInfo &reg_info,
- const lldb_private::RegisterValue &reg_value);
-
- bool
- GetRegisterValue (const lldb_private::RegisterInfo &reg_info,
- lldb_private::RegisterValue &reg_value);
-
- std::unique_ptr<lldb_private::EmulateInstruction> m_inst_emulator_ap;
- lldb_private::AddressRange* m_range_ptr;
- lldb_private::Thread* m_thread_ptr;
- lldb_private::UnwindPlan* m_unwind_plan_ptr;
- lldb_private::UnwindPlan::RowSP m_curr_row;
- typedef std::map<uint64_t, uint64_t> PushedRegisterToAddrMap;
- uint64_t m_initial_sp;
- lldb_private::RegisterInfo m_cfa_reg_info;
- bool m_fp_is_cfa;
- typedef std::map<uint64_t, lldb_private::RegisterValue> RegisterValueMap;
- RegisterValueMap m_register_values;
- 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.
- uint32_t m_forward_branch_offset;
+ }
+
+ 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 &reg_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 &reg_value);
+
+ // size_t
+ // ReadMemory (lldb_private::EmulateInstruction *instruction,
+ // const lldb_private::EmulateInstruction::Context &context,
+ // lldb::addr_t addr,
+ // void *dst,
+ // size_t length);
+
+ 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 &reg_value);
+
+ bool WriteRegister(lldb_private::EmulateInstruction *instruction,
+ const lldb_private::EmulateInstruction::Context &context,
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value);
+
+ static uint64_t
+ MakeRegisterKindValuePair(const lldb_private::RegisterInfo &reg_info);
+
+ void SetRegisterValue(const lldb_private::RegisterInfo &reg_info,
+ const lldb_private::RegisterValue &reg_value);
+
+ bool GetRegisterValue(const lldb_private::RegisterInfo &reg_info,
+ lldb_private::RegisterValue &reg_value);
+
+ std::unique_ptr<lldb_private::EmulateInstruction> m_inst_emulator_ap;
+ lldb_private::AddressRange *m_range_ptr;
+ lldb_private::UnwindPlan *m_unwind_plan_ptr;
+ lldb_private::UnwindPlan::RowSP m_curr_row;
+ typedef std::map<uint64_t, uint64_t> PushedRegisterToAddrMap;
+ uint64_t m_initial_sp;
+ lldb_private::RegisterInfo m_cfa_reg_info;
+ bool m_fp_is_cfa;
+ typedef std::map<uint64_t, lldb_private::RegisterValue> RegisterValueMap;
+ RegisterValueMap m_register_values;
+ 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.
+ uint32_t m_forward_branch_offset;
};
#endif // liblldb_UnwindAssemblyInstEmulation_h_
diff --git a/source/Plugins/UnwindAssembly/x86/CMakeLists.txt b/source/Plugins/UnwindAssembly/x86/CMakeLists.txt
index 6ae63891bcc3..024b0dab2e2b 100644
--- a/source/Plugins/UnwindAssembly/x86/CMakeLists.txt
+++ b/source/Plugins/UnwindAssembly/x86/CMakeLists.txt
@@ -1,3 +1,4 @@
add_lldb_library(lldbPluginUnwindAssemblyX86
UnwindAssembly-x86.cpp
+ x86AssemblyInspectionEngine.cpp
)
diff --git a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
index 76f0b48d69e9..e298b856d395 100644
--- a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
+++ b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
@@ -8,1553 +8,272 @@
//===----------------------------------------------------------------------===//
#include "UnwindAssembly-x86.h"
+#include "x86AssemblyInspectionEngine.h"
#include "llvm-c/Disassembler.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/TargetSelect.h"
#include "lldb/Core/Address.h"
-#include "lldb/Core/Error.h"
#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/Error.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
-#include "lldb/Target/Thread.h"
#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
#include "lldb/Target/UnwindAssembly.h"
#include "lldb/Utility/RegisterNumber.h"
using namespace lldb;
using namespace lldb_private;
-enum CPU
-{
- k_i386,
- k_x86_64
-};
-
-enum i386_register_numbers
-{
- k_machine_eax = 0,
- k_machine_ecx = 1,
- k_machine_edx = 2,
- k_machine_ebx = 3,
- k_machine_esp = 4,
- k_machine_ebp = 5,
- k_machine_esi = 6,
- k_machine_edi = 7,
- k_machine_eip = 8
-};
-
-enum x86_64_register_numbers
-{
- k_machine_rax = 0,
- k_machine_rcx = 1,
- k_machine_rdx = 2,
- k_machine_rbx = 3,
- k_machine_rsp = 4,
- k_machine_rbp = 5,
- k_machine_rsi = 6,
- k_machine_rdi = 7,
- k_machine_r8 = 8,
- k_machine_r9 = 9,
- k_machine_r10 = 10,
- k_machine_r11 = 11,
- k_machine_r12 = 12,
- k_machine_r13 = 13,
- k_machine_r14 = 14,
- k_machine_r15 = 15,
- k_machine_rip = 16
-};
-
-struct regmap_ent
-{
- const char *name;
- int machine_regno;
- int lldb_regno;
-};
-
-static struct regmap_ent i386_register_map[] =
-{
- {"eax", k_machine_eax, -1},
- {"ecx", k_machine_ecx, -1},
- {"edx", k_machine_edx, -1},
- {"ebx", k_machine_ebx, -1},
- {"esp", k_machine_esp, -1},
- {"ebp", k_machine_ebp, -1},
- {"esi", k_machine_esi, -1},
- {"edi", k_machine_edi, -1},
- {"eip", k_machine_eip, -1}
-};
-
-const int size_of_i386_register_map = llvm::array_lengthof (i386_register_map);
-
-static int i386_register_map_initialized = 0;
-
-static struct regmap_ent x86_64_register_map[] =
-{
- {"rax", k_machine_rax, -1},
- {"rcx", k_machine_rcx, -1},
- {"rdx", k_machine_rdx, -1},
- {"rbx", k_machine_rbx, -1},
- {"rsp", k_machine_rsp, -1},
- {"rbp", k_machine_rbp, -1},
- {"rsi", k_machine_rsi, -1},
- {"rdi", k_machine_rdi, -1},
- {"r8", k_machine_r8, -1},
- {"r9", k_machine_r9, -1},
- {"r10", k_machine_r10, -1},
- {"r11", k_machine_r11, -1},
- {"r12", k_machine_r12, -1},
- {"r13", k_machine_r13, -1},
- {"r14", k_machine_r14, -1},
- {"r15", k_machine_r15, -1},
- {"rip", k_machine_rip, -1}
-};
-
-const int size_of_x86_64_register_map = llvm::array_lengthof (x86_64_register_map);
-
-static int x86_64_register_map_initialized = 0;
-
//-----------------------------------------------------------------------------------------------
-// AssemblyParse_x86 local-file class definition & implementation functions
+// UnwindAssemblyParser_x86 method definitions
//-----------------------------------------------------------------------------------------------
-class AssemblyParse_x86
-{
-public:
-
- AssemblyParse_x86 (const ExecutionContext &exe_ctx, int cpu, ArchSpec &arch, AddressRange func);
-
- ~AssemblyParse_x86 ();
-
- bool get_non_call_site_unwind_plan (UnwindPlan &unwind_plan);
-
- bool augment_unwind_plan_from_call_site (AddressRange& func, UnwindPlan &unwind_plan);
-
- bool get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_plan);
+UnwindAssembly_x86::UnwindAssembly_x86(const ArchSpec &arch)
+ : lldb_private::UnwindAssembly(arch),
+ m_assembly_inspection_engine(new x86AssemblyInspectionEngine(arch)) {}
- bool find_first_non_prologue_insn (Address &address);
-
-private:
- enum { kMaxInstructionByteSize = 32 };
-
- bool nonvolatile_reg_p (int machine_regno);
- bool push_rbp_pattern_p ();
- bool push_0_pattern_p ();
- bool mov_rsp_rbp_pattern_p ();
- bool sub_rsp_pattern_p (int& amount);
- bool add_rsp_pattern_p (int& amount);
- bool lea_rsp_pattern_p (int& amount);
- bool push_reg_p (int& regno);
- bool pop_reg_p (int& regno);
- bool push_imm_pattern_p ();
- bool mov_reg_to_local_stack_frame_p (int& regno, int& fp_offset);
- bool ret_pattern_p ();
- bool pop_rbp_pattern_p ();
- bool leave_pattern_p ();
- bool call_next_insn_pattern_p();
- uint32_t extract_4 (uint8_t *b);
- bool machine_regno_to_lldb_regno (int machine_regno, uint32_t& lldb_regno);
- bool instruction_length (Address addr, int &length);
-
- const ExecutionContext m_exe_ctx;
-
- AddressRange m_func_bounds;
-
- Address m_cur_insn;
- uint8_t m_cur_insn_bytes[kMaxInstructionByteSize];
-
- uint32_t m_machine_ip_regnum;
- uint32_t m_machine_sp_regnum;
- uint32_t m_machine_fp_regnum;
-
- uint32_t m_lldb_ip_regnum;
- uint32_t m_lldb_sp_regnum;
- uint32_t m_lldb_fp_regnum;
-
- int m_wordsize;
- int m_cpu;
- ArchSpec m_arch;
- ::LLVMDisasmContextRef m_disasm_context;
-
- DISALLOW_COPY_AND_ASSIGN (AssemblyParse_x86);
-};
-
-AssemblyParse_x86::AssemblyParse_x86 (const ExecutionContext &exe_ctx, int cpu, ArchSpec &arch, AddressRange func) :
- m_exe_ctx (exe_ctx),
- m_func_bounds(func),
- m_cur_insn (),
- m_machine_ip_regnum (LLDB_INVALID_REGNUM),
- m_machine_sp_regnum (LLDB_INVALID_REGNUM),
- m_machine_fp_regnum (LLDB_INVALID_REGNUM),
- m_lldb_ip_regnum (LLDB_INVALID_REGNUM),
- m_lldb_sp_regnum (LLDB_INVALID_REGNUM),
- m_lldb_fp_regnum (LLDB_INVALID_REGNUM),
- m_wordsize (-1),
- m_cpu(cpu),
- m_arch(arch)
-{
- int *initialized_flag = NULL;
- if (cpu == k_i386)
- {
- m_machine_ip_regnum = k_machine_eip;
- m_machine_sp_regnum = k_machine_esp;
- m_machine_fp_regnum = k_machine_ebp;
- m_wordsize = 4;
- initialized_flag = &i386_register_map_initialized;
- }
- else
- {
- m_machine_ip_regnum = k_machine_rip;
- m_machine_sp_regnum = k_machine_rsp;
- m_machine_fp_regnum = k_machine_rbp;
- m_wordsize = 8;
- initialized_flag = &x86_64_register_map_initialized;
- }
-
- // we only look at prologue - it will be complete earlier than 512 bytes into func
- if (m_func_bounds.GetByteSize() == 0)
- m_func_bounds.SetByteSize(512);
-
- Thread *thread = m_exe_ctx.GetThreadPtr();
- if (thread && *initialized_flag == 0)
- {
- RegisterContext *reg_ctx = thread->GetRegisterContext().get();
- if (reg_ctx)
- {
- struct regmap_ent *ent;
- int count, i;
- if (cpu == k_i386)
- {
- ent = i386_register_map;
- count = size_of_i386_register_map;
- }
- else
- {
- ent = x86_64_register_map;
- count = size_of_x86_64_register_map;
- }
- for (i = 0; i < count; i++, ent++)
- {
- const RegisterInfo *ri = reg_ctx->GetRegisterInfoByName (ent->name);
- if (ri)
- ent->lldb_regno = ri->kinds[eRegisterKindLLDB];
- }
- *initialized_flag = 1;
- }
- }
-
- // on initial construction we may not have a Thread so these have to remain
- // uninitialized until we can get a RegisterContext to set up the register map table
- if (*initialized_flag == 1)
- {
- uint32_t lldb_regno;
- if (machine_regno_to_lldb_regno (m_machine_sp_regnum, lldb_regno))
- m_lldb_sp_regnum = lldb_regno;
- if (machine_regno_to_lldb_regno (m_machine_fp_regnum, lldb_regno))
- m_lldb_fp_regnum = lldb_regno;
- if (machine_regno_to_lldb_regno (m_machine_ip_regnum, lldb_regno))
- m_lldb_ip_regnum = lldb_regno;
- }
-
- m_disasm_context = ::LLVMCreateDisasm(m_arch.GetTriple().getTriple().c_str(),
- (void*)this,
- /*TagType=*/1,
- NULL,
- NULL);
+UnwindAssembly_x86::~UnwindAssembly_x86() {
+ delete m_assembly_inspection_engine;
}
-AssemblyParse_x86::~AssemblyParse_x86 ()
-{
- ::LLVMDisasmDispose(m_disasm_context);
-}
-
-// This function expects an x86 native register number (i.e. the bits stripped out of the
-// actual instruction), not an lldb register number.
-
-bool
-AssemblyParse_x86::nonvolatile_reg_p (int machine_regno)
-{
- if (m_cpu == k_i386)
- {
- switch (machine_regno)
- {
- case k_machine_ebx:
- case k_machine_ebp: // not actually a nonvolatile but often treated as such by convention
- case k_machine_esi:
- case k_machine_edi:
- case k_machine_esp:
- return true;
- default:
- return false;
- }
- }
- if (m_cpu == k_x86_64)
- {
- switch (machine_regno)
- {
- case k_machine_rbx:
- case k_machine_rsp:
- case k_machine_rbp: // not actually a nonvolatile but often treated as such by convention
- case k_machine_r12:
- case k_machine_r13:
- case k_machine_r14:
- case k_machine_r15:
- return true;
- default:
- return false;
- }
- }
- return false;
-}
-
-
-// Macro to detect if this is a REX mode prefix byte.
-#define REX_W_PREFIX_P(opcode) (((opcode) & (~0x5)) == 0x48)
-
-// The high bit which should be added to the source register number (the "R" bit)
-#define REX_W_SRCREG(opcode) (((opcode) & 0x4) >> 2)
-
-// The high bit which should be added to the destination register number (the "B" bit)
-#define REX_W_DSTREG(opcode) ((opcode) & 0x1)
-
-// pushq %rbp [0x55]
-bool
-AssemblyParse_x86::push_rbp_pattern_p ()
-{
- uint8_t *p = m_cur_insn_bytes;
- if (*p == 0x55)
- return true;
- return false;
-}
-
-// pushq $0 ; the first instruction in start() [0x6a 0x00]
-bool
-AssemblyParse_x86::push_0_pattern_p ()
-{
- uint8_t *p = m_cur_insn_bytes;
- if (*p == 0x6a && *(p + 1) == 0x0)
- return true;
- return false;
-}
-
-// pushq $0
-// pushl $0
-bool
-AssemblyParse_x86::push_imm_pattern_p ()
-{
- uint8_t *p = m_cur_insn_bytes;
- if (*p == 0x68 || *p == 0x6a)
- return true;
+bool UnwindAssembly_x86::GetNonCallSiteUnwindPlanFromAssembly(
+ AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {
+ if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0)
return false;
-}
-
-// movq %rsp, %rbp [0x48 0x8b 0xec] or [0x48 0x89 0xe5]
-// movl %esp, %ebp [0x8b 0xec] or [0x89 0xe5]
-bool
-AssemblyParse_x86::mov_rsp_rbp_pattern_p ()
-{
- uint8_t *p = m_cur_insn_bytes;
- if (m_wordsize == 8 && *p == 0x48)
- p++;
- if (*(p) == 0x8b && *(p + 1) == 0xec)
- return true;
- if (*(p) == 0x89 && *(p + 1) == 0xe5)
- return true;
+ if (m_assembly_inspection_engine == nullptr)
return false;
-}
-
-// subq $0x20, %rsp
-bool
-AssemblyParse_x86::sub_rsp_pattern_p (int& amount)
-{
- uint8_t *p = m_cur_insn_bytes;
- if (m_wordsize == 8 && *p == 0x48)
- p++;
- // 8-bit immediate operand
- if (*p == 0x83 && *(p + 1) == 0xec)
- {
- amount = (int8_t) *(p + 2);
- return true;
- }
- // 32-bit immediate operand
- if (*p == 0x81 && *(p + 1) == 0xec)
- {
- amount = (int32_t) extract_4 (p + 2);
- return true;
- }
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp.get() == nullptr)
return false;
-}
-
-// addq $0x20, %rsp
-bool
-AssemblyParse_x86::add_rsp_pattern_p (int& amount)
-{
- uint8_t *p = m_cur_insn_bytes;
- if (m_wordsize == 8 && *p == 0x48)
- p++;
- // 8-bit immediate operand
- if (*p == 0x83 && *(p + 1) == 0xc4)
- {
- amount = (int8_t) *(p + 2);
- return true;
- }
- // 32-bit immediate operand
- if (*p == 0x81 && *(p + 1) == 0xc4)
- {
- amount = (int32_t) extract_4 (p + 2);
- return true;
- }
- return false;
-}
-
-// lea esp, [esp - 0x28]
-// lea esp, [esp + 0x28]
-bool
-AssemblyParse_x86::lea_rsp_pattern_p (int& amount)
-{
- uint8_t *p = m_cur_insn_bytes;
- if (m_wordsize == 8 && *p == 0x48)
- p++;
-
- // Check opcode
- if (*p != 0x8d)
- return false;
-
- // 8 bit displacement
- if (*(p + 1) == 0x64 && (*(p + 2) & 0x3f) == 0x24)
- {
- amount = (int8_t) *(p + 3);
- return true;
- }
-
- // 32 bit displacement
- if (*(p + 1) == 0xa4 && (*(p + 2) & 0x3f) == 0x24)
- {
- amount = (int32_t) extract_4 (p + 3);
- return true;
- }
-
+ const bool prefer_file_cache = true;
+ std::vector<uint8_t> function_text(func.GetByteSize());
+ Error error;
+ if (process_sp->GetTarget().ReadMemory(
+ func.GetBaseAddress(), prefer_file_cache, function_text.data(),
+ func.GetByteSize(), error) == func.GetByteSize()) {
+ RegisterContextSP reg_ctx(thread.GetRegisterContext());
+ m_assembly_inspection_engine->Initialize(reg_ctx);
+ return m_assembly_inspection_engine->GetNonCallSiteUnwindPlanFromAssembly(
+ function_text.data(), func.GetByteSize(), func, unwind_plan);
+ }
+ return false;
+}
+
+bool UnwindAssembly_x86::AugmentUnwindPlanFromCallSite(
+ AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {
+ bool do_augment_unwindplan = true;
+
+ UnwindPlan::RowSP first_row = unwind_plan.GetRowForFunctionOffset(0);
+ UnwindPlan::RowSP last_row = unwind_plan.GetRowForFunctionOffset(-1);
+
+ int wordsize = 8;
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp.get() == nullptr)
return false;
-}
-// pushq %rbx
-// pushl %ebx
-bool
-AssemblyParse_x86::push_reg_p (int& regno)
-{
- uint8_t *p = m_cur_insn_bytes;
- int regno_prefix_bit = 0;
- // If we have a rex prefix byte, check to see if a B bit is set
- if (m_wordsize == 8 && *p == 0x41)
- {
- regno_prefix_bit = 1 << 3;
- p++;
- }
- if (*p >= 0x50 && *p <= 0x57)
- {
- regno = (*p - 0x50) | regno_prefix_bit;
- return true;
- }
+ wordsize = process_sp->GetTarget().GetArchitecture().GetAddressByteSize();
+
+ RegisterNumber sp_regnum(thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP);
+ RegisterNumber pc_regnum(thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+
+ // Does this UnwindPlan describe the prologue? I want to see that the CFA is
+ // set
+ // in terms of the stack pointer plus an offset, and I want to see that rip is
+ // retrieved at the CFA-wordsize.
+ // If there is no description of the prologue, don't try to augment this
+ // eh_frame
+ // unwinder code, fall back to assembly parsing instead.
+
+ if (first_row->GetCFAValue().GetValueType() !=
+ UnwindPlan::Row::CFAValue::isRegisterPlusOffset ||
+ RegisterNumber(thread, unwind_plan.GetRegisterKind(),
+ first_row->GetCFAValue().GetRegisterNumber()) !=
+ sp_regnum ||
+ first_row->GetCFAValue().GetOffset() != wordsize) {
return false;
-}
-
-// popq %rbx
-// popl %ebx
-bool
-AssemblyParse_x86::pop_reg_p (int& regno)
-{
- uint8_t *p = m_cur_insn_bytes;
- int regno_prefix_bit = 0;
- // If we have a rex prefix byte, check to see if a B bit is set
- if (m_wordsize == 8 && *p == 0x41)
- {
- regno_prefix_bit = 1 << 3;
- p++;
- }
- if (*p >= 0x58 && *p <= 0x5f)
- {
- regno = (*p - 0x58) | regno_prefix_bit;
- return true;
- }
+ }
+ UnwindPlan::Row::RegisterLocation first_row_pc_loc;
+ if (first_row->GetRegisterInfo(
+ pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()),
+ first_row_pc_loc) == false ||
+ first_row_pc_loc.IsAtCFAPlusOffset() == false ||
+ first_row_pc_loc.GetOffset() != -wordsize) {
return false;
-}
-
-// popq %rbp [0x5d]
-// popl %ebp [0x5d]
-bool
-AssemblyParse_x86::pop_rbp_pattern_p ()
-{
- uint8_t *p = m_cur_insn_bytes;
- return (*p == 0x5d);
-}
-
-// leave [0xc9]
-bool
-AssemblyParse_x86::leave_pattern_p ()
-{
- uint8_t *p = m_cur_insn_bytes;
- return (*p == 0xc9);
-}
-
-// call $0 [0xe8 0x0 0x0 0x0 0x0]
-bool
-AssemblyParse_x86::call_next_insn_pattern_p ()
-{
- uint8_t *p = m_cur_insn_bytes;
- return (*p == 0xe8) && (*(p+1) == 0x0) && (*(p+2) == 0x0)
- && (*(p+3) == 0x0) && (*(p+4) == 0x0);
-}
-
-// Look for an instruction sequence storing a nonvolatile register
-// on to the stack frame.
-
-// movq %rax, -0x10(%rbp) [0x48 0x89 0x45 0xf0]
-// movl %eax, -0xc(%ebp) [0x89 0x45 0xf4]
-
-// The offset value returned in rbp_offset will be positive --
-// but it must be subtraced from the frame base register to get
-// the actual location. The positive value returned for the offset
-// is a convention used elsewhere for CFA offsets et al.
-
-bool
-AssemblyParse_x86::mov_reg_to_local_stack_frame_p (int& regno, int& rbp_offset)
-{
- uint8_t *p = m_cur_insn_bytes;
- int src_reg_prefix_bit = 0;
- int target_reg_prefix_bit = 0;
-
- if (m_wordsize == 8 && REX_W_PREFIX_P (*p))
- {
- src_reg_prefix_bit = REX_W_SRCREG (*p) << 3;
- target_reg_prefix_bit = REX_W_DSTREG (*p) << 3;
- if (target_reg_prefix_bit == 1)
- {
- // rbp/ebp don't need a prefix bit - we know this isn't the
- // reg we care about.
- return false;
+ }
+
+ // It looks like the prologue is described.
+ // Is the epilogue described? If it is, no need to do any augmentation.
+
+ if (first_row != last_row &&
+ first_row->GetOffset() != last_row->GetOffset()) {
+ // The first & last row have the same CFA register
+ // and the same CFA offset value
+ // and the CFA register is esp/rsp (the stack pointer).
+
+ // We're checking that both of them have an unwind rule like "CFA=esp+4" or
+ // CFA+rsp+8".
+
+ if (first_row->GetCFAValue().GetValueType() ==
+ last_row->GetCFAValue().GetValueType() &&
+ first_row->GetCFAValue().GetRegisterNumber() ==
+ last_row->GetCFAValue().GetRegisterNumber() &&
+ first_row->GetCFAValue().GetOffset() ==
+ last_row->GetCFAValue().GetOffset()) {
+ // Get the register locations for eip/rip from the first & last rows.
+ // Are they both CFA plus an offset? Is it the same offset?
+
+ UnwindPlan::Row::RegisterLocation last_row_pc_loc;
+ if (last_row->GetRegisterInfo(
+ pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()),
+ last_row_pc_loc)) {
+ if (last_row_pc_loc.IsAtCFAPlusOffset() &&
+ first_row_pc_loc.GetOffset() == last_row_pc_loc.GetOffset()) {
+
+ // One last sanity check: Is the unwind rule for getting the caller
+ // pc value
+ // "deref the CFA-4" or "deref the CFA-8"?
+
+ // If so, we have an UnwindPlan that already describes the epilogue
+ // and we don't need
+ // to modify it at all.
+
+ if (first_row_pc_loc.GetOffset() == -wordsize) {
+ do_augment_unwindplan = false;
+ }
}
- p++;
+ }
}
+ }
- if (*p == 0x89)
- {
- /* Mask off the 3-5 bits which indicate the destination register
- if this is a ModR/M byte. */
- int opcode_destreg_masked_out = *(p + 1) & (~0x38);
-
- /* Is this a ModR/M byte with Mod bits 01 and R/M bits 101
- and three bits between them, e.g. 01nnn101
- We're looking for a destination of ebp-disp8 or ebp-disp32. */
- int immsize;
- if (opcode_destreg_masked_out == 0x45)
- immsize = 2;
- else if (opcode_destreg_masked_out == 0x85)
- immsize = 4;
- else
- return false;
-
- int offset = 0;
- if (immsize == 2)
- offset = (int8_t) *(p + 2);
- if (immsize == 4)
- offset = (uint32_t) extract_4 (p + 2);
- if (offset > 0)
- return false;
-
- regno = ((*(p + 1) >> 3) & 0x7) | src_reg_prefix_bit;
- rbp_offset = offset > 0 ? offset : -offset;
- return true;
- }
- return false;
-}
-
-// ret [0xc9] or [0xc2 imm8] or [0xca imm8]
-bool
-AssemblyParse_x86::ret_pattern_p ()
-{
- uint8_t *p = m_cur_insn_bytes;
- if (*p == 0xc9 || *p == 0xc2 || *p == 0xca || *p == 0xc3)
- return true;
- return false;
-}
-
-uint32_t
-AssemblyParse_x86::extract_4 (uint8_t *b)
-{
- uint32_t v = 0;
- for (int i = 3; i >= 0; i--)
- v = (v << 8) | b[i];
- return v;
-}
-
-bool
-AssemblyParse_x86::machine_regno_to_lldb_regno (int machine_regno, uint32_t &lldb_regno)
-{
- struct regmap_ent *ent;
- int count, i;
- if (m_cpu == k_i386)
- {
- ent = i386_register_map;
- count = size_of_i386_register_map;
- }
- else
- {
- ent = x86_64_register_map;
- count = size_of_x86_64_register_map;
- }
- for (i = 0; i < count; i++, ent++)
- {
- if (ent->machine_regno == machine_regno)
- if (ent->lldb_regno != -1)
- {
- lldb_regno = ent->lldb_regno;
- return true;
- }
- }
- return false;
-}
-
-bool
-AssemblyParse_x86::instruction_length (Address addr, int &length)
-{
- const uint32_t max_op_byte_size = m_arch.GetMaximumOpcodeByteSize();
- llvm::SmallVector <uint8_t, 32> opcode_data;
- opcode_data.resize (max_op_byte_size);
-
- if (!addr.IsValid())
- return false;
-
+ if (do_augment_unwindplan) {
+ if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0)
+ return false;
+ if (m_assembly_inspection_engine == nullptr)
+ return false;
const bool prefer_file_cache = true;
+ std::vector<uint8_t> function_text(func.GetByteSize());
Error error;
- Target *target = m_exe_ctx.GetTargetPtr();
- if (target->ReadMemory (addr, prefer_file_cache, opcode_data.data(),
- max_op_byte_size, error) == static_cast<size_t>(-1))
- {
- return false;
+ if (process_sp->GetTarget().ReadMemory(
+ func.GetBaseAddress(), prefer_file_cache, function_text.data(),
+ func.GetByteSize(), error) == func.GetByteSize()) {
+ RegisterContextSP reg_ctx(thread.GetRegisterContext());
+ m_assembly_inspection_engine->Initialize(reg_ctx);
+ return m_assembly_inspection_engine->AugmentUnwindPlanFromCallSite(
+ function_text.data(), func.GetByteSize(), func, unwind_plan, reg_ctx);
}
+ }
- char out_string[512];
- const addr_t pc = addr.GetFileAddress();
- const size_t inst_size = ::LLVMDisasmInstruction (m_disasm_context,
- opcode_data.data(),
- max_op_byte_size,
- pc, // PC value
- out_string,
- sizeof(out_string));
-
- length = inst_size;
- return true;
+ return false;
}
+bool UnwindAssembly_x86::GetFastUnwindPlan(AddressRange &func, Thread &thread,
+ UnwindPlan &unwind_plan) {
+ // if prologue is
+ // 55 pushl %ebp
+ // 89 e5 movl %esp, %ebp
+ // or
+ // 55 pushq %rbp
+ // 48 89 e5 movq %rsp, %rbp
-bool
-AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan)
-{
- UnwindPlan::RowSP row(new UnwindPlan::Row);
- m_cur_insn = m_func_bounds.GetBaseAddress ();
- addr_t current_func_text_offset = 0;
- int current_sp_bytes_offset_from_cfa = 0;
- UnwindPlan::Row::RegisterLocation initial_regloc;
- Error error;
-
- if (!m_cur_insn.IsValid())
- {
- return false;
- }
-
- unwind_plan.SetPlanValidAddressRange (m_func_bounds);
- unwind_plan.SetRegisterKind (eRegisterKindLLDB);
-
- // At the start of the function, find the CFA by adding wordsize to the SP register
- row->SetOffset (current_func_text_offset);
- row->GetCFAValue().SetIsRegisterPlusOffset(m_lldb_sp_regnum, m_wordsize);
-
- // caller's stack pointer value before the call insn is the CFA address
- initial_regloc.SetIsCFAPlusOffset (0);
- row->SetRegisterInfo (m_lldb_sp_regnum, initial_regloc);
-
- // saved instruction pointer can be found at CFA - wordsize.
- current_sp_bytes_offset_from_cfa = m_wordsize;
- initial_regloc.SetAtCFAPlusOffset (-current_sp_bytes_offset_from_cfa);
- row->SetRegisterInfo (m_lldb_ip_regnum, initial_regloc);
+ // We should pull in the ABI architecture default unwind plan and return that
- unwind_plan.AppendRow (row);
-
- // Allocate a new Row, populate it with the existing Row contents.
- UnwindPlan::Row *newrow = new UnwindPlan::Row;
- *newrow = *row.get();
- row.reset(newrow);
-
- // Track which registers have been saved so far in the prologue.
- // If we see another push of that register, it's not part of the prologue.
- // The register numbers used here are the machine register #'s
- // (i386_register_numbers, x86_64_register_numbers).
- std::vector<bool> saved_registers(32, false);
+ llvm::SmallVector<uint8_t, 4> opcode_data;
+ ProcessSP process_sp = thread.GetProcess();
+ if (process_sp) {
+ Target &target(process_sp->GetTarget());
const bool prefer_file_cache = true;
-
- // Once the prologue has completed we'll save a copy of the unwind instructions
- // If there is an epilogue in the middle of the function, after that epilogue we'll reinstate
- // the unwind setup -- we assume that some code path jumps over the mid-function epilogue
-
- UnwindPlan::RowSP prologue_completed_row; // copy of prologue row of CFI
- int prologue_completed_sp_bytes_offset_from_cfa; // The sp value before the epilogue started executed
- std::vector<bool> prologue_completed_saved_registers;
-
- Target *target = m_exe_ctx.GetTargetPtr();
- while (m_func_bounds.ContainsFileAddress (m_cur_insn))
- {
- int stack_offset, insn_len;
- int machine_regno; // register numbers masked directly out of instructions
- uint32_t lldb_regno; // register numbers in lldb's eRegisterKindLLDB numbering scheme
-
- bool in_epilogue = false; // we're in the middle of an epilogue sequence
- bool row_updated = false; // The UnwindPlan::Row 'row' has been updated
-
- if (!instruction_length (m_cur_insn, insn_len) || insn_len == 0 || insn_len > kMaxInstructionByteSize)
- {
- // An unrecognized/junk instruction
- break;
- }
-
- if (target->ReadMemory (m_cur_insn, prefer_file_cache, m_cur_insn_bytes,
- insn_len, error) == static_cast<size_t>(-1))
- {
- // Error reading the instruction out of the file, stop scanning
- break;
- }
-
- if (push_rbp_pattern_p ())
- {
- current_sp_bytes_offset_from_cfa += m_wordsize;
- row->GetCFAValue().SetOffset (current_sp_bytes_offset_from_cfa);
- UnwindPlan::Row::RegisterLocation regloc;
- regloc.SetAtCFAPlusOffset (-row->GetCFAValue().GetOffset());
- row->SetRegisterInfo (m_lldb_fp_regnum, regloc);
- saved_registers[m_machine_fp_regnum] = true;
- row_updated = true;
- }
-
- else if (mov_rsp_rbp_pattern_p ())
- {
- row->GetCFAValue().SetIsRegisterPlusOffset(m_lldb_fp_regnum, row->GetCFAValue().GetOffset());
- row_updated = true;
- }
-
- // This is the start() function (or a pthread equivalent), it starts with a pushl $0x0 which puts the
- // saved pc value of 0 on the stack. In this case we want to pretend we didn't see a stack movement at all --
- // normally the saved pc value is already on the stack by the time the function starts executing.
- else if (push_0_pattern_p ())
- {
- }
-
- else if (push_reg_p (machine_regno))
- {
- current_sp_bytes_offset_from_cfa += m_wordsize;
- // the PUSH instruction has moved the stack pointer - if the CFA is set in terms of the stack pointer,
- // we need to add a new row of instructions.
- if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum)
- {
- row->GetCFAValue().SetOffset (current_sp_bytes_offset_from_cfa);
- row_updated = true;
- }
- // record where non-volatile (callee-saved, spilled) registers are saved on the stack
- if (nonvolatile_reg_p (machine_regno)
- && machine_regno_to_lldb_regno (machine_regno, lldb_regno)
- && saved_registers[machine_regno] == false)
- {
- UnwindPlan::Row::RegisterLocation regloc;
- regloc.SetAtCFAPlusOffset (-current_sp_bytes_offset_from_cfa);
- row->SetRegisterInfo (lldb_regno, regloc);
- saved_registers[machine_regno] = true;
- row_updated = true;
- }
- }
-
- else if (pop_reg_p (machine_regno))
- {
- current_sp_bytes_offset_from_cfa -= m_wordsize;
-
- if (nonvolatile_reg_p (machine_regno)
- && machine_regno_to_lldb_regno (machine_regno, lldb_regno)
- && saved_registers[machine_regno] == true)
- {
- saved_registers[machine_regno] = false;
- row->RemoveRegisterInfo (lldb_regno);
-
- if (machine_regno == (int)m_machine_fp_regnum)
- {
- row->GetCFAValue().SetIsRegisterPlusOffset (m_lldb_sp_regnum, row->GetCFAValue().GetOffset());
- }
-
- in_epilogue = true;
- row_updated = true;
- }
-
- // the POP instruction has moved the stack pointer - if the CFA is set in terms of the stack pointer,
- // we need to add a new row of instructions.
- if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum)
- {
- row->GetCFAValue().SetIsRegisterPlusOffset (m_lldb_sp_regnum, current_sp_bytes_offset_from_cfa);
- row_updated = true;
- }
- }
-
- // The LEAVE instruction moves the value from rbp into rsp and pops
- // a value off the stack into rbp (restoring the caller's rbp value).
- // It is the opposite of ENTER, or 'push rbp, mov rsp rbp'.
- else if (leave_pattern_p ())
- {
- // We're going to copy the value in rbp into rsp, so re-set the sp offset
- // based on the CFAValue. Also, adjust it to recognize that we're popping
- // the saved rbp value off the stack.
- current_sp_bytes_offset_from_cfa = row->GetCFAValue().GetOffset();
- current_sp_bytes_offset_from_cfa -= m_wordsize;
- row->GetCFAValue().SetOffset (current_sp_bytes_offset_from_cfa);
-
- // rbp is restored to the caller's value
- saved_registers[m_machine_fp_regnum] = false;
- row->RemoveRegisterInfo (m_lldb_fp_regnum);
-
- // cfa is now in terms of rsp again.
- row->GetCFAValue().SetIsRegisterPlusOffset (m_lldb_sp_regnum, row->GetCFAValue().GetOffset());
- row->GetCFAValue().SetOffset (current_sp_bytes_offset_from_cfa);
-
- in_epilogue = true;
- row_updated = true;
- }
-
- else if (mov_reg_to_local_stack_frame_p (machine_regno, stack_offset)
- && nonvolatile_reg_p (machine_regno)
- && machine_regno_to_lldb_regno (machine_regno, lldb_regno)
- && saved_registers[machine_regno] == false)
- {
- saved_registers[machine_regno] = true;
-
- UnwindPlan::Row::RegisterLocation regloc;
-
- // stack_offset for 'movq %r15, -80(%rbp)' will be 80.
- // In the Row, we want to express this as the offset from the CFA. If the frame base
- // is rbp (like the above instruction), the CFA offset for rbp is probably 16. So we
- // want to say that the value is stored at the CFA address - 96.
- regloc.SetAtCFAPlusOffset (-(stack_offset + row->GetCFAValue().GetOffset()));
-
- row->SetRegisterInfo (lldb_regno, regloc);
-
- row_updated = true;
- }
-
- else if (sub_rsp_pattern_p (stack_offset))
- {
- current_sp_bytes_offset_from_cfa += stack_offset;
- if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum)
- {
- row->GetCFAValue().SetOffset (current_sp_bytes_offset_from_cfa);
- row_updated = true;
- }
- }
-
- else if (add_rsp_pattern_p (stack_offset))
- {
- current_sp_bytes_offset_from_cfa -= stack_offset;
- if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum)
- {
- row->GetCFAValue().SetOffset (current_sp_bytes_offset_from_cfa);
- row_updated = true;
- }
- in_epilogue = true;
- }
-
- else if (lea_rsp_pattern_p (stack_offset))
- {
- current_sp_bytes_offset_from_cfa -= stack_offset;
- if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum)
- {
- row->GetCFAValue().SetOffset (current_sp_bytes_offset_from_cfa);
- row_updated = true;
- }
- if (stack_offset > 0)
- in_epilogue = true;
- }
-
- else if (ret_pattern_p () && prologue_completed_row.get())
- {
- // Reinstate the saved prologue setup for any instructions
- // that come after the ret instruction
-
- UnwindPlan::Row *newrow = new UnwindPlan::Row;
- *newrow = *prologue_completed_row.get();
- row.reset (newrow);
- current_sp_bytes_offset_from_cfa = prologue_completed_sp_bytes_offset_from_cfa;
-
- saved_registers.clear();
- saved_registers.resize(prologue_completed_saved_registers.size(), false);
- for (size_t i = 0; i < prologue_completed_saved_registers.size(); ++i)
- {
- saved_registers[i] = prologue_completed_saved_registers[i];
- }
-
- in_epilogue = true;
- row_updated = true;
- }
-
- // call next instruction
- // call 0
- // => pop %ebx
- // This is used in i386 programs to get the PIC base address for finding global data
- else if (call_next_insn_pattern_p ())
- {
- current_sp_bytes_offset_from_cfa += m_wordsize;
- if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum)
- {
- row->GetCFAValue().SetOffset (current_sp_bytes_offset_from_cfa);
- row_updated = true;
- }
- }
-
- if (row_updated)
- {
- if (current_func_text_offset + insn_len < m_func_bounds.GetByteSize())
- {
- row->SetOffset (current_func_text_offset + insn_len);
- unwind_plan.AppendRow (row);
- // Allocate a new Row, populate it with the existing Row contents.
- newrow = new UnwindPlan::Row;
- *newrow = *row.get();
- row.reset(newrow);
- }
- }
-
- if (in_epilogue == false && row_updated)
- {
- // If we're not in an epilogue sequence, save the updated Row
- UnwindPlan::Row *newrow = new UnwindPlan::Row;
- *newrow = *row.get();
- prologue_completed_row.reset (newrow);
-
- prologue_completed_saved_registers.clear();
- prologue_completed_saved_registers.resize(saved_registers.size(), false);
- for (size_t i = 0; i < saved_registers.size(); ++i)
- {
- prologue_completed_saved_registers[i] = saved_registers[i];
- }
- }
-
- // We may change the sp value without adding a new Row necessarily -- keep
- // track of it either way.
- if (in_epilogue == false)
- {
- prologue_completed_sp_bytes_offset_from_cfa = current_sp_bytes_offset_from_cfa;
- }
-
- m_cur_insn.SetOffset (m_cur_insn.GetOffset() + insn_len);
- current_func_text_offset += insn_len;
- }
-
- unwind_plan.SetSourceName ("assembly insn profiling");
- unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
- unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes);
-
- return true;
-}
-
-bool
-AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, UnwindPlan &unwind_plan)
-{
- // Is func address valid?
- Address addr_start = func.GetBaseAddress();
- if (!addr_start.IsValid())
- return false;
-
- // Is original unwind_plan valid?
- // unwind_plan should have at least one row which is ABI-default (CFA register is sp),
- // and another row in mid-function.
- if (unwind_plan.GetRowCount() < 2)
- return false;
- UnwindPlan::RowSP first_row = unwind_plan.GetRowAtIndex (0);
- if (first_row->GetOffset() != 0)
- return false;
- uint32_t cfa_reg = m_exe_ctx.GetThreadPtr()->GetRegisterContext()
- ->ConvertRegisterKindToRegisterNumber (unwind_plan.GetRegisterKind(),
- first_row->GetCFAValue().GetRegisterNumber());
- if (cfa_reg != m_lldb_sp_regnum || first_row->GetCFAValue().GetOffset() != m_wordsize)
- return false;
-
- UnwindPlan::RowSP original_last_row = unwind_plan.GetRowForFunctionOffset (-1);
-
- Target *target = m_exe_ctx.GetTargetPtr();
- m_cur_insn = func.GetBaseAddress();
- uint64_t offset = 0;
- int row_id = 1;
- bool unwind_plan_updated = false;
- UnwindPlan::RowSP row(new UnwindPlan::Row(*first_row));
-
- // After a mid-function epilogue we will need to re-insert the original unwind rules
- // so unwinds work for the remainder of the function. These aren't common with clang/gcc
- // on x86 but it is possible.
- bool reinstate_unwind_state = false;
-
- while (func.ContainsFileAddress (m_cur_insn))
- {
- int insn_len;
- if (!instruction_length (m_cur_insn, insn_len)
- || insn_len == 0 || insn_len > kMaxInstructionByteSize)
- {
- // An unrecognized/junk instruction.
- break;
- }
- const bool prefer_file_cache = true;
- Error error;
- if (target->ReadMemory (m_cur_insn, prefer_file_cache, m_cur_insn_bytes,
- insn_len, error) == static_cast<size_t>(-1))
- {
- // Error reading the instruction out of the file, stop scanning.
- break;
- }
-
- // Advance offsets.
- offset += insn_len;
- m_cur_insn.SetOffset(m_cur_insn.GetOffset() + insn_len);
-
- if (reinstate_unwind_state)
- {
- // that was the last instruction of this function
- if (func.ContainsFileAddress (m_cur_insn) == false)
- continue;
-
- UnwindPlan::RowSP new_row(new UnwindPlan::Row());
- *new_row = *original_last_row;
- new_row->SetOffset (offset);
- unwind_plan.AppendRow (new_row);
- row.reset (new UnwindPlan::Row());
- *row = *new_row;
- reinstate_unwind_state = false;
- unwind_plan_updated = true;
- continue;
- }
-
- // If we already have one row for this instruction, we can continue.
- while (row_id < unwind_plan.GetRowCount()
- && unwind_plan.GetRowAtIndex (row_id)->GetOffset() <= offset)
- {
- row_id++;
- }
- UnwindPlan::RowSP original_row = unwind_plan.GetRowAtIndex (row_id - 1);
- if (original_row->GetOffset() == offset)
- {
- *row = *original_row;
- continue;
- }
-
- if (row_id == 0)
- {
- // If we are here, compiler didn't generate CFI for prologue.
- // This won't happen to GCC or clang.
- // In this case, bail out directly.
- return false;
- }
-
- // Inspect the instruction to check if we need a new row for it.
- cfa_reg = m_exe_ctx.GetThreadPtr()->GetRegisterContext()
- ->ConvertRegisterKindToRegisterNumber (unwind_plan.GetRegisterKind(),
- row->GetCFAValue().GetRegisterNumber());
- if (cfa_reg == m_lldb_sp_regnum)
- {
- // CFA register is sp.
-
- // call next instruction
- // call 0
- // => pop %ebx
- if (call_next_insn_pattern_p ())
- {
- row->SetOffset (offset);
- row->GetCFAValue().IncOffset (m_wordsize);
-
- UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
- unwind_plan.InsertRow (new_row);
- unwind_plan_updated = true;
- continue;
- }
-
- // push/pop register
- int regno;
- if (push_reg_p (regno))
- {
- row->SetOffset (offset);
- row->GetCFAValue().IncOffset (m_wordsize);
-
- UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
- unwind_plan.InsertRow (new_row);
- unwind_plan_updated = true;
- continue;
- }
- if (pop_reg_p (regno))
- {
- // Technically, this might be a nonvolatile register recover in epilogue.
- // We should reset RegisterInfo for the register.
- // But in practice, previous rule for the register is still valid...
- // So we ignore this case.
-
- row->SetOffset (offset);
- row->GetCFAValue().IncOffset (-m_wordsize);
-
- UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
- unwind_plan.InsertRow (new_row);
- unwind_plan_updated = true;
- continue;
- }
-
- // push imm
- if (push_imm_pattern_p ())
- {
- row->SetOffset (offset);
- row->GetCFAValue().IncOffset (m_wordsize);
- UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
- unwind_plan.InsertRow (new_row);
- unwind_plan_updated = true;
- continue;
- }
-
- // add/sub %rsp/%esp
- int amount;
- if (add_rsp_pattern_p (amount))
- {
- row->SetOffset (offset);
- row->GetCFAValue().IncOffset (-amount);
-
- UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
- unwind_plan.InsertRow (new_row);
- unwind_plan_updated = true;
- continue;
- }
- if (sub_rsp_pattern_p (amount))
- {
- row->SetOffset (offset);
- row->GetCFAValue().IncOffset (amount);
-
- UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
- unwind_plan.InsertRow (new_row);
- unwind_plan_updated = true;
- continue;
- }
-
- // lea %rsp, [%rsp + $offset]
- if (lea_rsp_pattern_p (amount))
- {
- row->SetOffset (offset);
- row->GetCFAValue().IncOffset (-amount);
-
- UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
- unwind_plan.InsertRow (new_row);
- unwind_plan_updated = true;
- continue;
- }
-
- if (ret_pattern_p ())
- {
- reinstate_unwind_state = true;
- continue;
- }
- }
- else if (cfa_reg == m_lldb_fp_regnum)
- {
- // CFA register is fp.
-
- // The only case we care about is epilogue:
- // [0x5d] pop %rbp/%ebp
- // => [0xc3] ret
- if (pop_rbp_pattern_p () || leave_pattern_p ())
- {
- if (target->ReadMemory (m_cur_insn, prefer_file_cache, m_cur_insn_bytes,
- 1, error) != static_cast<size_t>(-1)
- && ret_pattern_p ())
- {
- row->SetOffset (offset);
- row->GetCFAValue().SetIsRegisterPlusOffset (first_row->GetCFAValue().GetRegisterNumber(),
- m_wordsize);
-
- UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
- unwind_plan.InsertRow (new_row);
- unwind_plan_updated = true;
- reinstate_unwind_state = true;
- continue;
- }
- }
- }
- else
- {
- // CFA register is not sp or fp.
-
- // This must be hand-written assembly.
- // Just trust eh_frame and assume we have finished.
- break;
- }
- }
-
- unwind_plan.SetPlanValidAddressRange (func);
- if (unwind_plan_updated)
- {
- std::string unwind_plan_source (unwind_plan.GetSourceName().AsCString());
- unwind_plan_source += " plus augmentation from assembly parsing";
- unwind_plan.SetSourceName (unwind_plan_source.c_str());
- unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
- unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes);
- }
- return true;
-}
-
-/* The "fast unwind plan" is valid for functions that follow the usual convention of
- using the frame pointer register (ebp, rbp), i.e. the function prologue looks like
- push %rbp [0x55]
- mov %rsp,%rbp [0x48 0x89 0xe5] (this is a 2-byte insn seq on i386)
-*/
-
-bool
-AssemblyParse_x86::get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_plan)
-{
- UnwindPlan::RowSP row(new UnwindPlan::Row);
- UnwindPlan::Row::RegisterLocation pc_reginfo;
- UnwindPlan::Row::RegisterLocation sp_reginfo;
- UnwindPlan::Row::RegisterLocation fp_reginfo;
- unwind_plan.SetRegisterKind (eRegisterKindLLDB);
-
- if (!func.GetBaseAddress().IsValid())
- return false;
-
- Target *target = m_exe_ctx.GetTargetPtr();
-
- uint8_t bytebuf[4];
Error error;
- const bool prefer_file_cache = true;
- if (target->ReadMemory (func.GetBaseAddress(), prefer_file_cache, bytebuf,
- sizeof (bytebuf), error) == static_cast<size_t>(-1))
- return false;
-
- uint8_t i386_prologue[] = {0x55, 0x89, 0xe5};
- uint8_t x86_64_prologue[] = {0x55, 0x48, 0x89, 0xe5};
- int prologue_size;
-
- if (memcmp (bytebuf, i386_prologue, sizeof (i386_prologue)) == 0)
- {
- prologue_size = sizeof (i386_prologue);
- }
- else if (memcmp (bytebuf, x86_64_prologue, sizeof (x86_64_prologue)) == 0)
- {
- prologue_size = sizeof (x86_64_prologue);
- }
- else
- {
- return false;
- }
-
- pc_reginfo.SetAtCFAPlusOffset (-m_wordsize);
- row->SetRegisterInfo (m_lldb_ip_regnum, pc_reginfo);
-
- sp_reginfo.SetIsCFAPlusOffset (0);
- row->SetRegisterInfo (m_lldb_sp_regnum, sp_reginfo);
-
- // Zero instructions into the function
- row->GetCFAValue().SetIsRegisterPlusOffset (m_lldb_sp_regnum, m_wordsize);
- row->SetOffset (0);
- unwind_plan.AppendRow (row);
- UnwindPlan::Row *newrow = new UnwindPlan::Row;
- *newrow = *row.get();
- row.reset(newrow);
-
- // push %rbp has executed - stack moved, rbp now saved
- row->GetCFAValue().IncOffset (m_wordsize);
- fp_reginfo.SetAtCFAPlusOffset (2 * -m_wordsize);
- row->SetRegisterInfo (m_lldb_fp_regnum, fp_reginfo);
- row->SetOffset (1);
- unwind_plan.AppendRow (row);
-
- newrow = new UnwindPlan::Row;
- *newrow = *row.get();
- row.reset(newrow);
-
- // mov %rsp, %rbp has executed
- row->GetCFAValue().SetIsRegisterPlusOffset (m_lldb_fp_regnum, 2 * m_wordsize);
- row->SetOffset (prologue_size); /// 3 or 4 bytes depending on arch
- unwind_plan.AppendRow (row);
-
- newrow = new UnwindPlan::Row;
- *newrow = *row.get();
- row.reset(newrow);
-
- unwind_plan.SetPlanValidAddressRange (func);
- unwind_plan.SetSourceName ("fast unwind assembly profiling");
- unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
- unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
- return true;
-}
+ if (target.ReadMemory(func.GetBaseAddress(), prefer_file_cache,
+ opcode_data.data(), 4, error) == 4) {
+ uint8_t i386_push_mov[] = {0x55, 0x89, 0xe5};
+ uint8_t x86_64_push_mov[] = {0x55, 0x48, 0x89, 0xe5};
-bool
-AssemblyParse_x86::find_first_non_prologue_insn (Address &address)
-{
- m_cur_insn = m_func_bounds.GetBaseAddress ();
- if (!m_cur_insn.IsValid())
- {
- return false;
- }
-
- const bool prefer_file_cache = true;
- Target *target = m_exe_ctx.GetTargetPtr();
- while (m_func_bounds.ContainsFileAddress (m_cur_insn))
- {
- Error error;
- int insn_len, offset, regno;
- if (!instruction_length (m_cur_insn, insn_len) || insn_len > kMaxInstructionByteSize || insn_len == 0)
- {
- // An error parsing the instruction, i.e. probably data/garbage - stop scanning
- break;
- }
- if (target->ReadMemory (m_cur_insn, prefer_file_cache, m_cur_insn_bytes,
- insn_len, error) == static_cast<size_t>(-1))
- {
- // Error reading the instruction out of the file, stop scanning
- break;
- }
-
- if (push_rbp_pattern_p () || mov_rsp_rbp_pattern_p () || sub_rsp_pattern_p (offset)
- || push_reg_p (regno) || mov_reg_to_local_stack_frame_p (regno, offset)
- || (lea_rsp_pattern_p (offset) && offset < 0))
- {
- m_cur_insn.SetOffset (m_cur_insn.GetOffset() + insn_len);
- continue;
+ if (memcmp(opcode_data.data(), i386_push_mov, sizeof(i386_push_mov)) ==
+ 0 ||
+ memcmp(opcode_data.data(), x86_64_push_mov,
+ sizeof(x86_64_push_mov)) == 0) {
+ ABISP abi_sp = process_sp->GetABI();
+ if (abi_sp) {
+ return abi_sp->CreateDefaultUnwindPlan(unwind_plan);
}
-
- // Unknown non-prologue instruction - stop scanning
- break;
+ }
}
-
- address = m_cur_insn;
- return true;
-}
-
-
-
-
-
-
-//-----------------------------------------------------------------------------------------------
-// UnwindAssemblyParser_x86 method definitions
-//-----------------------------------------------------------------------------------------------
-
-UnwindAssembly_x86::UnwindAssembly_x86 (const ArchSpec &arch, int cpu) :
- lldb_private::UnwindAssembly(arch),
- m_cpu(cpu),
- m_arch(arch)
-{
-}
-
-
-UnwindAssembly_x86::~UnwindAssembly_x86 ()
-{
-}
-
-bool
-UnwindAssembly_x86::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& func, Thread& thread, UnwindPlan& unwind_plan)
-{
- ExecutionContext exe_ctx (thread.shared_from_this());
- AssemblyParse_x86 asm_parse(exe_ctx, m_cpu, m_arch, func);
- return asm_parse.get_non_call_site_unwind_plan (unwind_plan);
+ }
+ return false;
}
-bool
-UnwindAssembly_x86::AugmentUnwindPlanFromCallSite (AddressRange& func, Thread& thread, UnwindPlan& unwind_plan)
-{
- bool do_augment_unwindplan = true;
-
- UnwindPlan::RowSP first_row = unwind_plan.GetRowForFunctionOffset (0);
- UnwindPlan::RowSP last_row = unwind_plan.GetRowForFunctionOffset (-1);
-
- int wordsize = 8;
- ProcessSP process_sp (thread.GetProcess());
- if (process_sp)
- {
- wordsize = process_sp->GetTarget().GetArchitecture().GetAddressByteSize();
- }
-
- RegisterNumber sp_regnum (thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
- RegisterNumber pc_regnum (thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
-
- // Does this UnwindPlan describe the prologue? I want to see that the CFA is set
- // in terms of the stack pointer plus an offset, and I want to see that rip is
- // retrieved at the CFA-wordsize.
- // If there is no description of the prologue, don't try to augment this eh_frame
- // unwinder code, fall back to assembly parsing instead.
-
- if (first_row->GetCFAValue().GetValueType() != UnwindPlan::Row::CFAValue::isRegisterPlusOffset
- || RegisterNumber (thread, unwind_plan.GetRegisterKind(),
- first_row->GetCFAValue().GetRegisterNumber()) != sp_regnum
- || first_row->GetCFAValue().GetOffset() != wordsize)
- {
- return false;
- }
- UnwindPlan::Row::RegisterLocation first_row_pc_loc;
- if (first_row->GetRegisterInfo (pc_regnum.GetAsKind (unwind_plan.GetRegisterKind()), first_row_pc_loc) == false
- || first_row_pc_loc.IsAtCFAPlusOffset() == false
- || first_row_pc_loc.GetOffset() != -wordsize)
- {
- return false;
- }
-
-
- // It looks like the prologue is described.
- // Is the epilogue described? If it is, no need to do any augmentation.
-
- if (first_row != last_row && first_row->GetOffset() != last_row->GetOffset())
- {
- // The first & last row have the same CFA register
- // and the same CFA offset value
- // and the CFA register is esp/rsp (the stack pointer).
-
- // We're checking that both of them have an unwind rule like "CFA=esp+4" or CFA+rsp+8".
-
- if (first_row->GetCFAValue().GetValueType() == last_row->GetCFAValue().GetValueType()
- && first_row->GetCFAValue().GetRegisterNumber() == last_row->GetCFAValue().GetRegisterNumber()
- && first_row->GetCFAValue().GetOffset() == last_row->GetCFAValue().GetOffset())
- {
- // Get the register locations for eip/rip from the first & last rows.
- // Are they both CFA plus an offset? Is it the same offset?
-
- UnwindPlan::Row::RegisterLocation last_row_pc_loc;
- if (last_row->GetRegisterInfo (pc_regnum.GetAsKind (unwind_plan.GetRegisterKind()), last_row_pc_loc))
- {
- if (last_row_pc_loc.IsAtCFAPlusOffset()
- && first_row_pc_loc.GetOffset() == last_row_pc_loc.GetOffset())
- {
-
- // One last sanity check: Is the unwind rule for getting the caller pc value
- // "deref the CFA-4" or "deref the CFA-8"?
+bool UnwindAssembly_x86::FirstNonPrologueInsn(
+ AddressRange &func, const ExecutionContext &exe_ctx,
+ Address &first_non_prologue_insn) {
- // If so, we have an UnwindPlan that already describes the epilogue and we don't need
- // to modify it at all.
-
- if (first_row_pc_loc.GetOffset() == -wordsize)
- {
- do_augment_unwindplan = false;
- }
- }
- }
- }
- }
-
- if (do_augment_unwindplan)
- {
- ExecutionContext exe_ctx (thread.shared_from_this());
- AssemblyParse_x86 asm_parse(exe_ctx, m_cpu, m_arch, func);
- return asm_parse.augment_unwind_plan_from_call_site (func, unwind_plan);
- }
-
+ if (!func.GetBaseAddress().IsValid())
return false;
-}
-
-bool
-UnwindAssembly_x86::GetFastUnwindPlan (AddressRange& func, Thread& thread, UnwindPlan &unwind_plan)
-{
- // if prologue is
- // 55 pushl %ebp
- // 89 e5 movl %esp, %ebp
- // or
- // 55 pushq %rbp
- // 48 89 e5 movq %rsp, %rbp
-
- // We should pull in the ABI architecture default unwind plan and return that
- llvm::SmallVector <uint8_t, 4> opcode_data;
-
- ProcessSP process_sp = thread.GetProcess();
- if (process_sp)
- {
- Target &target (process_sp->GetTarget());
- const bool prefer_file_cache = true;
- Error error;
- if (target.ReadMemory (func.GetBaseAddress (), prefer_file_cache, opcode_data.data(),
- 4, error) == 4)
- {
- uint8_t i386_push_mov[] = {0x55, 0x89, 0xe5};
- uint8_t x86_64_push_mov[] = {0x55, 0x48, 0x89, 0xe5};
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target == nullptr)
+ return false;
- if (memcmp (opcode_data.data(), i386_push_mov, sizeof (i386_push_mov)) == 0
- || memcmp (opcode_data.data(), x86_64_push_mov, sizeof (x86_64_push_mov)) == 0)
- {
- ABISP abi_sp = process_sp->GetABI();
- if (abi_sp)
- {
- return abi_sp->CreateDefaultUnwindPlan (unwind_plan);
- }
- }
- }
- }
+ if (m_assembly_inspection_engine == nullptr)
return false;
-}
-bool
-UnwindAssembly_x86::FirstNonPrologueInsn (AddressRange& func, const ExecutionContext &exe_ctx, Address& first_non_prologue_insn)
-{
- AssemblyParse_x86 asm_parse(exe_ctx, m_cpu, m_arch, func);
- return asm_parse.find_first_non_prologue_insn (first_non_prologue_insn);
+ const bool prefer_file_cache = true;
+ std::vector<uint8_t> function_text(func.GetByteSize());
+ Error error;
+ if (target->ReadMemory(func.GetBaseAddress(), prefer_file_cache,
+ function_text.data(), func.GetByteSize(),
+ error) == func.GetByteSize()) {
+ size_t offset;
+ if (m_assembly_inspection_engine->FindFirstNonPrologueInstruction(
+ function_text.data(), func.GetByteSize(), offset)) {
+ first_non_prologue_insn = func.GetBaseAddress();
+ first_non_prologue_insn.Slide(offset);
+ }
+ return true;
+ }
+ return false;
}
-UnwindAssembly *
-UnwindAssembly_x86::CreateInstance (const ArchSpec &arch)
-{
- const llvm::Triple::ArchType cpu = arch.GetMachine ();
- if (cpu == llvm::Triple::x86)
- return new UnwindAssembly_x86 (arch, k_i386);
- else if (cpu == llvm::Triple::x86_64)
- return new UnwindAssembly_x86 (arch, k_x86_64);
- return NULL;
+UnwindAssembly *UnwindAssembly_x86::CreateInstance(const ArchSpec &arch) {
+ const llvm::Triple::ArchType cpu = arch.GetMachine();
+ if (cpu == llvm::Triple::x86 || cpu == llvm::Triple::x86_64)
+ return new UnwindAssembly_x86(arch);
+ return NULL;
}
-
//------------------------------------------------------------------
// PluginInterface protocol in UnwindAssemblyParser_x86
//------------------------------------------------------------------
-ConstString
-UnwindAssembly_x86::GetPluginName()
-{
- return GetPluginNameStatic();
+ConstString UnwindAssembly_x86::GetPluginName() {
+ return GetPluginNameStatic();
}
+uint32_t UnwindAssembly_x86::GetPluginVersion() { return 1; }
-uint32_t
-UnwindAssembly_x86::GetPluginVersion()
-{
- return 1;
-}
-
-void
-UnwindAssembly_x86::Initialize()
-{
- PluginManager::RegisterPlugin (GetPluginNameStatic(),
- GetPluginDescriptionStatic(),
- CreateInstance);
+void UnwindAssembly_x86::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
}
-void
-UnwindAssembly_x86::Terminate()
-{
- PluginManager::UnregisterPlugin (CreateInstance);
+void UnwindAssembly_x86::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
}
-
-lldb_private::ConstString
-UnwindAssembly_x86::GetPluginNameStatic()
-{
- static ConstString g_name("x86");
- return g_name;
+lldb_private::ConstString UnwindAssembly_x86::GetPluginNameStatic() {
+ static ConstString g_name("x86");
+ return g_name;
}
-const char *
-UnwindAssembly_x86::GetPluginDescriptionStatic()
-{
- return "i386 and x86_64 assembly language profiler plugin.";
+const char *UnwindAssembly_x86::GetPluginDescriptionStatic() {
+ return "i386 and x86_64 assembly language profiler plugin.";
}
diff --git a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h
index 4d43a6e02b73..2beaa4a6510a 100644
--- a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h
+++ b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h
@@ -13,67 +13,60 @@
// C Includes
// C++ Includes
// Other libraries and framework includes
-#include "llvm-c/Disassembler.h"
+#include "x86AssemblyInspectionEngine.h"
// Project includes
-#include "lldb/lldb-private.h"
#include "lldb/Target/UnwindAssembly.h"
+#include "lldb/lldb-private.h"
-class UnwindAssembly_x86 : public lldb_private::UnwindAssembly
-{
+class UnwindAssembly_x86 : public lldb_private::UnwindAssembly {
public:
- ~UnwindAssembly_x86() override;
-
- bool
- GetNonCallSiteUnwindPlanFromAssembly(lldb_private::AddressRange& func,
- lldb_private::Thread& thread,
- lldb_private::UnwindPlan& unwind_plan) override;
-
- 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 lldb_private::ConstString
- GetPluginNameStatic();
-
- static const char *
- GetPluginDescriptionStatic();
-
- lldb_private::ConstString
- GetPluginName() override;
-
- uint32_t
- GetPluginVersion() override;
-
+ ~UnwindAssembly_x86() override;
+
+ bool GetNonCallSiteUnwindPlanFromAssembly(
+ lldb_private::AddressRange &func, lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ 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 lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
private:
- UnwindAssembly_x86 (const lldb_private::ArchSpec &arch, int cpu);
+ UnwindAssembly_x86(const lldb_private::ArchSpec &arch);
+
+ lldb_private::ArchSpec m_arch;
- int m_cpu;
- lldb_private::ArchSpec m_arch;
+ lldb_private::x86AssemblyInspectionEngine *m_assembly_inspection_engine;
};
#endif // liblldb_UnwindAssembly_x86_h_
diff --git a/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp b/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp
new file mode 100644
index 000000000000..e731a5a02ab0
--- /dev/null
+++ b/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp
@@ -0,0 +1,1203 @@
+//===-- x86AssemblyInspectionEngine.cpp -------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "x86AssemblyInspectionEngine.h"
+
+#include "llvm-c/Disassembler.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/UnwindAssembly.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+x86AssemblyInspectionEngine::x86AssemblyInspectionEngine(const ArchSpec &arch)
+ : m_cur_insn(nullptr), m_machine_ip_regnum(LLDB_INVALID_REGNUM),
+ m_machine_sp_regnum(LLDB_INVALID_REGNUM),
+ m_machine_fp_regnum(LLDB_INVALID_REGNUM),
+ m_lldb_ip_regnum(LLDB_INVALID_REGNUM),
+ m_lldb_sp_regnum(LLDB_INVALID_REGNUM),
+ m_lldb_fp_regnum(LLDB_INVALID_REGNUM),
+
+ m_reg_map(), m_arch(arch), m_cpu(k_cpu_unspecified), m_wordsize(-1),
+ m_register_map_initialized(false), m_disasm_context() {
+ m_disasm_context =
+ ::LLVMCreateDisasm(arch.GetTriple().getTriple().c_str(), nullptr,
+ /*TagType=*/1, nullptr, nullptr);
+}
+
+x86AssemblyInspectionEngine::~x86AssemblyInspectionEngine() {
+ ::LLVMDisasmDispose(m_disasm_context);
+}
+
+void x86AssemblyInspectionEngine::Initialize(RegisterContextSP &reg_ctx) {
+ m_cpu = k_cpu_unspecified;
+ m_wordsize = -1;
+ m_register_map_initialized = false;
+
+ const llvm::Triple::ArchType cpu = m_arch.GetMachine();
+ if (cpu == llvm::Triple::x86)
+ m_cpu = k_i386;
+ else if (cpu == llvm::Triple::x86_64)
+ m_cpu = k_x86_64;
+
+ if (m_cpu == k_cpu_unspecified)
+ return;
+
+ if (reg_ctx.get() == nullptr)
+ return;
+
+ if (m_cpu == k_i386) {
+ m_machine_ip_regnum = k_machine_eip;
+ m_machine_sp_regnum = k_machine_esp;
+ m_machine_fp_regnum = k_machine_ebp;
+ m_wordsize = 4;
+
+ struct lldb_reg_info reginfo;
+ reginfo.name = "eax";
+ m_reg_map[k_machine_eax] = reginfo;
+ reginfo.name = "edx";
+ m_reg_map[k_machine_edx] = reginfo;
+ reginfo.name = "esp";
+ m_reg_map[k_machine_esp] = reginfo;
+ reginfo.name = "esi";
+ m_reg_map[k_machine_esi] = reginfo;
+ reginfo.name = "eip";
+ m_reg_map[k_machine_eip] = reginfo;
+ reginfo.name = "ecx";
+ m_reg_map[k_machine_ecx] = reginfo;
+ reginfo.name = "ebx";
+ m_reg_map[k_machine_ebx] = reginfo;
+ reginfo.name = "ebp";
+ m_reg_map[k_machine_ebp] = reginfo;
+ reginfo.name = "edi";
+ m_reg_map[k_machine_edi] = reginfo;
+ } else {
+ m_machine_ip_regnum = k_machine_rip;
+ m_machine_sp_regnum = k_machine_rsp;
+ m_machine_fp_regnum = k_machine_rbp;
+ m_wordsize = 8;
+
+ struct lldb_reg_info reginfo;
+ reginfo.name = "rax";
+ m_reg_map[k_machine_rax] = reginfo;
+ reginfo.name = "rdx";
+ m_reg_map[k_machine_rdx] = reginfo;
+ reginfo.name = "rsp";
+ m_reg_map[k_machine_rsp] = reginfo;
+ reginfo.name = "rsi";
+ m_reg_map[k_machine_rsi] = reginfo;
+ reginfo.name = "r8";
+ m_reg_map[k_machine_r8] = reginfo;
+ reginfo.name = "r10";
+ m_reg_map[k_machine_r10] = reginfo;
+ reginfo.name = "r12";
+ m_reg_map[k_machine_r12] = reginfo;
+ reginfo.name = "r14";
+ m_reg_map[k_machine_r14] = reginfo;
+ reginfo.name = "rip";
+ m_reg_map[k_machine_rip] = reginfo;
+ reginfo.name = "rcx";
+ m_reg_map[k_machine_rcx] = reginfo;
+ reginfo.name = "rbx";
+ m_reg_map[k_machine_rbx] = reginfo;
+ reginfo.name = "rbp";
+ m_reg_map[k_machine_rbp] = reginfo;
+ reginfo.name = "rdi";
+ m_reg_map[k_machine_rdi] = reginfo;
+ reginfo.name = "r9";
+ m_reg_map[k_machine_r9] = reginfo;
+ reginfo.name = "r11";
+ m_reg_map[k_machine_r11] = reginfo;
+ reginfo.name = "r13";
+ m_reg_map[k_machine_r13] = reginfo;
+ reginfo.name = "r15";
+ m_reg_map[k_machine_r15] = reginfo;
+ }
+
+ for (MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.begin();
+ it != m_reg_map.end(); ++it) {
+ const RegisterInfo *ri = reg_ctx->GetRegisterInfoByName(it->second.name);
+ if (ri)
+ it->second.lldb_regnum = ri->kinds[eRegisterKindLLDB];
+ }
+
+ uint32_t lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_sp_regnum, lldb_regno))
+ m_lldb_sp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_fp_regnum, lldb_regno))
+ m_lldb_fp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_ip_regnum, lldb_regno))
+ m_lldb_ip_regnum = lldb_regno;
+
+ m_register_map_initialized = true;
+}
+
+void x86AssemblyInspectionEngine::Initialize(
+ std::vector<lldb_reg_info> &reg_info) {
+ m_cpu = k_cpu_unspecified;
+ m_wordsize = -1;
+ m_register_map_initialized = false;
+
+ const llvm::Triple::ArchType cpu = m_arch.GetMachine();
+ if (cpu == llvm::Triple::x86)
+ m_cpu = k_i386;
+ else if (cpu == llvm::Triple::x86_64)
+ m_cpu = k_x86_64;
+
+ if (m_cpu == k_cpu_unspecified)
+ return;
+
+ if (m_cpu == k_i386) {
+ m_machine_ip_regnum = k_machine_eip;
+ m_machine_sp_regnum = k_machine_esp;
+ m_machine_fp_regnum = k_machine_ebp;
+ m_wordsize = 4;
+
+ struct lldb_reg_info reginfo;
+ reginfo.name = "eax";
+ m_reg_map[k_machine_eax] = reginfo;
+ reginfo.name = "edx";
+ m_reg_map[k_machine_edx] = reginfo;
+ reginfo.name = "esp";
+ m_reg_map[k_machine_esp] = reginfo;
+ reginfo.name = "esi";
+ m_reg_map[k_machine_esi] = reginfo;
+ reginfo.name = "eip";
+ m_reg_map[k_machine_eip] = reginfo;
+ reginfo.name = "ecx";
+ m_reg_map[k_machine_ecx] = reginfo;
+ reginfo.name = "ebx";
+ m_reg_map[k_machine_ebx] = reginfo;
+ reginfo.name = "ebp";
+ m_reg_map[k_machine_ebp] = reginfo;
+ reginfo.name = "edi";
+ m_reg_map[k_machine_edi] = reginfo;
+ } else {
+ m_machine_ip_regnum = k_machine_rip;
+ m_machine_sp_regnum = k_machine_rsp;
+ m_machine_fp_regnum = k_machine_rbp;
+ m_wordsize = 8;
+
+ struct lldb_reg_info reginfo;
+ reginfo.name = "rax";
+ m_reg_map[k_machine_rax] = reginfo;
+ reginfo.name = "rdx";
+ m_reg_map[k_machine_rdx] = reginfo;
+ reginfo.name = "rsp";
+ m_reg_map[k_machine_rsp] = reginfo;
+ reginfo.name = "rsi";
+ m_reg_map[k_machine_rsi] = reginfo;
+ reginfo.name = "r8";
+ m_reg_map[k_machine_r8] = reginfo;
+ reginfo.name = "r10";
+ m_reg_map[k_machine_r10] = reginfo;
+ reginfo.name = "r12";
+ m_reg_map[k_machine_r12] = reginfo;
+ reginfo.name = "r14";
+ m_reg_map[k_machine_r14] = reginfo;
+ reginfo.name = "rip";
+ m_reg_map[k_machine_rip] = reginfo;
+ reginfo.name = "rcx";
+ m_reg_map[k_machine_rcx] = reginfo;
+ reginfo.name = "rbx";
+ m_reg_map[k_machine_rbx] = reginfo;
+ reginfo.name = "rbp";
+ m_reg_map[k_machine_rbp] = reginfo;
+ reginfo.name = "rdi";
+ m_reg_map[k_machine_rdi] = reginfo;
+ reginfo.name = "r9";
+ m_reg_map[k_machine_r9] = reginfo;
+ reginfo.name = "r11";
+ m_reg_map[k_machine_r11] = reginfo;
+ reginfo.name = "r13";
+ m_reg_map[k_machine_r13] = reginfo;
+ reginfo.name = "r15";
+ m_reg_map[k_machine_r15] = reginfo;
+ }
+
+ for (MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.begin();
+ it != m_reg_map.end(); ++it) {
+ for (size_t i = 0; i < reg_info.size(); ++i) {
+ if (::strcmp(reg_info[i].name, it->second.name) == 0) {
+ it->second.lldb_regnum = reg_info[i].lldb_regnum;
+ break;
+ }
+ }
+ }
+
+ uint32_t lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_sp_regnum, lldb_regno))
+ m_lldb_sp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_fp_regnum, lldb_regno))
+ m_lldb_fp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_ip_regnum, lldb_regno))
+ m_lldb_ip_regnum = lldb_regno;
+
+ m_register_map_initialized = true;
+}
+
+// This function expects an x86 native register number (i.e. the bits stripped
+// out of the
+// actual instruction), not an lldb register number.
+//
+// FIXME: This is ABI dependent, it shouldn't be hardcoded here.
+
+bool x86AssemblyInspectionEngine::nonvolatile_reg_p(int machine_regno) {
+ if (m_cpu == k_i386) {
+ switch (machine_regno) {
+ case k_machine_ebx:
+ case k_machine_ebp: // not actually a nonvolatile but often treated as such
+ // by convention
+ case k_machine_esi:
+ case k_machine_edi:
+ case k_machine_esp:
+ return true;
+ default:
+ return false;
+ }
+ }
+ if (m_cpu == k_x86_64) {
+ switch (machine_regno) {
+ case k_machine_rbx:
+ case k_machine_rsp:
+ case k_machine_rbp: // not actually a nonvolatile but often treated as such
+ // by convention
+ case k_machine_r12:
+ case k_machine_r13:
+ case k_machine_r14:
+ case k_machine_r15:
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+// Macro to detect if this is a REX mode prefix byte.
+#define REX_W_PREFIX_P(opcode) (((opcode) & (~0x5)) == 0x48)
+
+// The high bit which should be added to the source register number (the "R"
+// bit)
+#define REX_W_SRCREG(opcode) (((opcode)&0x4) >> 2)
+
+// The high bit which should be added to the destination register number (the
+// "B" bit)
+#define REX_W_DSTREG(opcode) ((opcode)&0x1)
+
+// pushq %rbp [0x55]
+bool x86AssemblyInspectionEngine::push_rbp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (*p == 0x55)
+ return true;
+ return false;
+}
+
+// pushq $0 ; the first instruction in start() [0x6a 0x00]
+bool x86AssemblyInspectionEngine::push_0_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (*p == 0x6a && *(p + 1) == 0x0)
+ return true;
+ return false;
+}
+
+// pushq $0
+// pushl $0
+bool x86AssemblyInspectionEngine::push_imm_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (*p == 0x68 || *p == 0x6a)
+ return true;
+ return false;
+}
+
+// pushl imm8(%esp)
+//
+// e.g. 0xff 0x74 0x24 0x20 - 'pushl 0x20(%esp)'
+// (same byte pattern for 'pushq 0x20(%rsp)' in an x86_64 program)
+//
+// 0xff (with opcode bits '6' in next byte, PUSH r/m32)
+// 0x74 (ModR/M byte with three bits used to specify the opcode)
+// mod == b01, opcode == b110, R/M == b100
+// "+disp8"
+// 0x24 (SIB byte - scaled index = 0, r32 == esp)
+// 0x20 imm8 value
+
+bool x86AssemblyInspectionEngine::push_extended_pattern_p() {
+ if (*m_cur_insn == 0xff) {
+ // Get the 3 opcode bits from the ModR/M byte
+ uint8_t opcode = (*(m_cur_insn + 1) >> 3) & 7;
+ if (opcode == 6) {
+ // I'm only looking for 0xff /6 here - I
+ // don't really care what value is being pushed,
+ // just that we're pushing a 32/64 bit value on
+ // to the stack is enough.
+ return true;
+ }
+ }
+ return false;
+}
+
+// instructions only valid in 32-bit mode:
+// 0x0e - push cs
+// 0x16 - push ss
+// 0x1e - push ds
+// 0x06 - push es
+bool x86AssemblyInspectionEngine::push_misc_reg_p() {
+ uint8_t p = *m_cur_insn;
+ if (m_wordsize == 4) {
+ if (p == 0x0e || p == 0x16 || p == 0x1e || p == 0x06)
+ return true;
+ }
+ return false;
+}
+
+// pushq %rbx
+// pushl %ebx
+bool x86AssemblyInspectionEngine::push_reg_p(int &regno) {
+ uint8_t *p = m_cur_insn;
+ int regno_prefix_bit = 0;
+ // If we have a rex prefix byte, check to see if a B bit is set
+ if (m_wordsize == 8 && *p == 0x41) {
+ regno_prefix_bit = 1 << 3;
+ p++;
+ }
+ if (*p >= 0x50 && *p <= 0x57) {
+ regno = (*p - 0x50) | regno_prefix_bit;
+ return true;
+ }
+ return false;
+}
+
+// movq %rsp, %rbp [0x48 0x8b 0xec] or [0x48 0x89 0xe5]
+// movl %esp, %ebp [0x8b 0xec] or [0x89 0xe5]
+bool x86AssemblyInspectionEngine::mov_rsp_rbp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ if (*(p) == 0x8b && *(p + 1) == 0xec)
+ return true;
+ if (*(p) == 0x89 && *(p + 1) == 0xe5)
+ return true;
+ return false;
+}
+
+// subq $0x20, %rsp
+bool x86AssemblyInspectionEngine::sub_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ // 8-bit immediate operand
+ if (*p == 0x83 && *(p + 1) == 0xec) {
+ amount = (int8_t) * (p + 2);
+ return true;
+ }
+ // 32-bit immediate operand
+ if (*p == 0x81 && *(p + 1) == 0xec) {
+ amount = (int32_t)extract_4(p + 2);
+ return true;
+ }
+ return false;
+}
+
+// addq $0x20, %rsp
+bool x86AssemblyInspectionEngine::add_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ // 8-bit immediate operand
+ if (*p == 0x83 && *(p + 1) == 0xc4) {
+ amount = (int8_t) * (p + 2);
+ return true;
+ }
+ // 32-bit immediate operand
+ if (*p == 0x81 && *(p + 1) == 0xc4) {
+ amount = (int32_t)extract_4(p + 2);
+ return true;
+ }
+ return false;
+}
+
+// lea esp, [esp - 0x28]
+// lea esp, [esp + 0x28]
+bool x86AssemblyInspectionEngine::lea_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+
+ // Check opcode
+ if (*p != 0x8d)
+ return false;
+
+ // 8 bit displacement
+ if (*(p + 1) == 0x64 && (*(p + 2) & 0x3f) == 0x24) {
+ amount = (int8_t) * (p + 3);
+ return true;
+ }
+
+ // 32 bit displacement
+ if (*(p + 1) == 0xa4 && (*(p + 2) & 0x3f) == 0x24) {
+ amount = (int32_t)extract_4(p + 3);
+ return true;
+ }
+
+ return false;
+}
+
+// popq %rbx
+// popl %ebx
+bool x86AssemblyInspectionEngine::pop_reg_p(int &regno) {
+ uint8_t *p = m_cur_insn;
+ int regno_prefix_bit = 0;
+ // If we have a rex prefix byte, check to see if a B bit is set
+ if (m_wordsize == 8 && *p == 0x41) {
+ regno_prefix_bit = 1 << 3;
+ p++;
+ }
+ if (*p >= 0x58 && *p <= 0x5f) {
+ regno = (*p - 0x58) | regno_prefix_bit;
+ return true;
+ }
+ return false;
+}
+
+// popq %rbp [0x5d]
+// popl %ebp [0x5d]
+bool x86AssemblyInspectionEngine::pop_rbp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return (*p == 0x5d);
+}
+
+// instructions valid only in 32-bit mode:
+// 0x1f - pop ds
+// 0x07 - pop es
+// 0x17 - pop ss
+bool x86AssemblyInspectionEngine::pop_misc_reg_p() {
+ uint8_t p = *m_cur_insn;
+ if (m_wordsize == 4) {
+ if (p == 0x1f || p == 0x07 || p == 0x17)
+ return true;
+ }
+ return false;
+}
+
+// leave [0xc9]
+bool x86AssemblyInspectionEngine::leave_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return (*p == 0xc9);
+}
+
+// call $0 [0xe8 0x0 0x0 0x0 0x0]
+bool x86AssemblyInspectionEngine::call_next_insn_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return (*p == 0xe8) && (*(p + 1) == 0x0) && (*(p + 2) == 0x0) &&
+ (*(p + 3) == 0x0) && (*(p + 4) == 0x0);
+}
+
+// Look for an instruction sequence storing a nonvolatile register
+// on to the stack frame.
+
+// movq %rax, -0x10(%rbp) [0x48 0x89 0x45 0xf0]
+// movl %eax, -0xc(%ebp) [0x89 0x45 0xf4]
+
+// The offset value returned in rbp_offset will be positive --
+// but it must be subtraced from the frame base register to get
+// the actual location. The positive value returned for the offset
+// is a convention used elsewhere for CFA offsets et al.
+
+bool x86AssemblyInspectionEngine::mov_reg_to_local_stack_frame_p(
+ int &regno, int &rbp_offset) {
+ uint8_t *p = m_cur_insn;
+ int src_reg_prefix_bit = 0;
+ int target_reg_prefix_bit = 0;
+
+ if (m_wordsize == 8 && REX_W_PREFIX_P(*p)) {
+ src_reg_prefix_bit = REX_W_SRCREG(*p) << 3;
+ target_reg_prefix_bit = REX_W_DSTREG(*p) << 3;
+ if (target_reg_prefix_bit == 1) {
+ // rbp/ebp don't need a prefix bit - we know this isn't the
+ // reg we care about.
+ return false;
+ }
+ p++;
+ }
+
+ if (*p == 0x89) {
+ /* Mask off the 3-5 bits which indicate the destination register
+ if this is a ModR/M byte. */
+ int opcode_destreg_masked_out = *(p + 1) & (~0x38);
+
+ /* Is this a ModR/M byte with Mod bits 01 and R/M bits 101
+ and three bits between them, e.g. 01nnn101
+ We're looking for a destination of ebp-disp8 or ebp-disp32. */
+ int immsize;
+ if (opcode_destreg_masked_out == 0x45)
+ immsize = 2;
+ else if (opcode_destreg_masked_out == 0x85)
+ immsize = 4;
+ else
+ return false;
+
+ int offset = 0;
+ if (immsize == 2)
+ offset = (int8_t) * (p + 2);
+ if (immsize == 4)
+ offset = (uint32_t)extract_4(p + 2);
+ if (offset > 0)
+ return false;
+
+ regno = ((*(p + 1) >> 3) & 0x7) | src_reg_prefix_bit;
+ rbp_offset = offset > 0 ? offset : -offset;
+ return true;
+ }
+ return false;
+}
+
+// ret [0xc9] or [0xc2 imm8] or [0xca imm8]
+bool x86AssemblyInspectionEngine::ret_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (*p == 0xc9 || *p == 0xc2 || *p == 0xca || *p == 0xc3)
+ return true;
+ return false;
+}
+
+uint32_t x86AssemblyInspectionEngine::extract_4(uint8_t *b) {
+ uint32_t v = 0;
+ for (int i = 3; i >= 0; i--)
+ v = (v << 8) | b[i];
+ return v;
+}
+
+bool x86AssemblyInspectionEngine::instruction_length(uint8_t *insn_p,
+ int &length) {
+
+ const uint32_t max_op_byte_size = m_arch.GetMaximumOpcodeByteSize();
+ llvm::SmallVector<uint8_t, 32> opcode_data;
+ opcode_data.resize(max_op_byte_size);
+
+ char out_string[512];
+ const size_t inst_size =
+ ::LLVMDisasmInstruction(m_disasm_context, insn_p, max_op_byte_size, 0,
+ out_string, sizeof(out_string));
+
+ length = inst_size;
+ return true;
+}
+
+bool x86AssemblyInspectionEngine::machine_regno_to_lldb_regno(
+ int machine_regno, uint32_t &lldb_regno) {
+ MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.find(machine_regno);
+ if (it != m_reg_map.end()) {
+ lldb_regno = it->second.lldb_regnum;
+ return true;
+ }
+ return false;
+ return false;
+}
+
+bool x86AssemblyInspectionEngine::GetNonCallSiteUnwindPlanFromAssembly(
+ uint8_t *data, size_t size, AddressRange &func_range,
+ UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+
+ if (data == nullptr || size == 0)
+ return false;
+
+ if (m_register_map_initialized == false)
+ return false;
+
+ addr_t current_func_text_offset = 0;
+ int current_sp_bytes_offset_from_cfa = 0;
+ UnwindPlan::Row::RegisterLocation initial_regloc;
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ unwind_plan.SetPlanValidAddressRange(func_range);
+ unwind_plan.SetRegisterKind(eRegisterKindLLDB);
+
+ // At the start of the function, find the CFA by adding wordsize to the SP
+ // register
+ row->SetOffset(current_func_text_offset);
+ row->GetCFAValue().SetIsRegisterPlusOffset(m_lldb_sp_regnum, m_wordsize);
+
+ // caller's stack pointer value before the call insn is the CFA address
+ initial_regloc.SetIsCFAPlusOffset(0);
+ row->SetRegisterInfo(m_lldb_sp_regnum, initial_regloc);
+
+ // saved instruction pointer can be found at CFA - wordsize.
+ current_sp_bytes_offset_from_cfa = m_wordsize;
+ initial_regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_cfa);
+ row->SetRegisterInfo(m_lldb_ip_regnum, initial_regloc);
+
+ unwind_plan.AppendRow(row);
+
+ // Allocate a new Row, populate it with the existing Row contents.
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ row.reset(newrow);
+
+ // Track which registers have been saved so far in the prologue.
+ // If we see another push of that register, it's not part of the prologue.
+ // The register numbers used here are the machine register #'s
+ // (i386_register_numbers, x86_64_register_numbers).
+ std::vector<bool> saved_registers(32, false);
+
+ // Once the prologue has completed we'll save a copy of the unwind
+ // instructions
+ // If there is an epilogue in the middle of the function, after that epilogue
+ // we'll reinstate
+ // the unwind setup -- we assume that some code path jumps over the
+ // mid-function epilogue
+
+ UnwindPlan::RowSP prologue_completed_row; // copy of prologue row of CFI
+ int prologue_completed_sp_bytes_offset_from_cfa; // The sp value before the
+ // epilogue started executed
+ std::vector<bool> prologue_completed_saved_registers;
+
+ while (current_func_text_offset < size) {
+ int stack_offset, insn_len;
+ int machine_regno; // register numbers masked directly out of instructions
+ uint32_t lldb_regno; // register numbers in lldb's eRegisterKindLLDB
+ // numbering scheme
+
+ bool in_epilogue = false; // we're in the middle of an epilogue sequence
+ bool row_updated = false; // The UnwindPlan::Row 'row' has been updated
+
+ m_cur_insn = data + current_func_text_offset;
+ if (!instruction_length(m_cur_insn, insn_len) || insn_len == 0 ||
+ insn_len > kMaxInstructionByteSize) {
+ // An unrecognized/junk instruction
+ break;
+ }
+
+ if (push_rbp_pattern_p()) {
+ current_sp_bytes_offset_from_cfa += m_wordsize;
+ row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
+ UnwindPlan::Row::RegisterLocation regloc;
+ regloc.SetAtCFAPlusOffset(-row->GetCFAValue().GetOffset());
+ row->SetRegisterInfo(m_lldb_fp_regnum, regloc);
+ saved_registers[m_machine_fp_regnum] = true;
+ row_updated = true;
+ }
+
+ else if (mov_rsp_rbp_pattern_p()) {
+ row->GetCFAValue().SetIsRegisterPlusOffset(
+ m_lldb_fp_regnum, row->GetCFAValue().GetOffset());
+ row_updated = true;
+ }
+
+ // This is the start() function (or a pthread equivalent), it starts with a
+ // pushl $0x0 which puts the
+ // saved pc value of 0 on the stack. In this case we want to pretend we
+ // didn't see a stack movement at all --
+ // normally the saved pc value is already on the stack by the time the
+ // function starts executing.
+ else if (push_0_pattern_p()) {
+ }
+
+ else if (push_reg_p(machine_regno)) {
+ current_sp_bytes_offset_from_cfa += m_wordsize;
+ // the PUSH instruction has moved the stack pointer - if the CFA is set in
+ // terms of the stack pointer,
+ // we need to add a new row of instructions.
+ if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
+ row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
+ row_updated = true;
+ }
+ // record where non-volatile (callee-saved, spilled) registers are saved
+ // on the stack
+ if (nonvolatile_reg_p(machine_regno) &&
+ machine_regno_to_lldb_regno(machine_regno, lldb_regno) &&
+ saved_registers[machine_regno] == false) {
+ UnwindPlan::Row::RegisterLocation regloc;
+ regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_cfa);
+ row->SetRegisterInfo(lldb_regno, regloc);
+ saved_registers[machine_regno] = true;
+ row_updated = true;
+ }
+ }
+
+ else if (pop_reg_p(machine_regno)) {
+ current_sp_bytes_offset_from_cfa -= m_wordsize;
+
+ if (nonvolatile_reg_p(machine_regno) &&
+ machine_regno_to_lldb_regno(machine_regno, lldb_regno) &&
+ saved_registers[machine_regno] == true) {
+ saved_registers[machine_regno] = false;
+ row->RemoveRegisterInfo(lldb_regno);
+
+ if (machine_regno == (int)m_machine_fp_regnum) {
+ row->GetCFAValue().SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, row->GetCFAValue().GetOffset());
+ }
+
+ in_epilogue = true;
+ row_updated = true;
+ }
+
+ // the POP instruction has moved the stack pointer - if the CFA is set in
+ // terms of the stack pointer,
+ // we need to add a new row of instructions.
+ if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
+ row->GetCFAValue().SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, current_sp_bytes_offset_from_cfa);
+ row_updated = true;
+ }
+ }
+
+ else if (pop_misc_reg_p()) {
+ current_sp_bytes_offset_from_cfa -= m_wordsize;
+ if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
+ row->GetCFAValue().SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, current_sp_bytes_offset_from_cfa);
+ row_updated = true;
+ }
+ }
+
+ // The LEAVE instruction moves the value from rbp into rsp and pops
+ // a value off the stack into rbp (restoring the caller's rbp value).
+ // It is the opposite of ENTER, or 'push rbp, mov rsp rbp'.
+ else if (leave_pattern_p()) {
+ // We're going to copy the value in rbp into rsp, so re-set the sp offset
+ // based on the CFAValue. Also, adjust it to recognize that we're popping
+ // the saved rbp value off the stack.
+ current_sp_bytes_offset_from_cfa = row->GetCFAValue().GetOffset();
+ current_sp_bytes_offset_from_cfa -= m_wordsize;
+ row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
+
+ // rbp is restored to the caller's value
+ saved_registers[m_machine_fp_regnum] = false;
+ row->RemoveRegisterInfo(m_lldb_fp_regnum);
+
+ // cfa is now in terms of rsp again.
+ row->GetCFAValue().SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, row->GetCFAValue().GetOffset());
+ row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
+
+ in_epilogue = true;
+ row_updated = true;
+ }
+
+ else if (mov_reg_to_local_stack_frame_p(machine_regno, stack_offset) &&
+ nonvolatile_reg_p(machine_regno) &&
+ machine_regno_to_lldb_regno(machine_regno, lldb_regno) &&
+ saved_registers[machine_regno] == false) {
+ saved_registers[machine_regno] = true;
+
+ UnwindPlan::Row::RegisterLocation regloc;
+
+ // stack_offset for 'movq %r15, -80(%rbp)' will be 80.
+ // In the Row, we want to express this as the offset from the CFA. If the
+ // frame base
+ // is rbp (like the above instruction), the CFA offset for rbp is probably
+ // 16. So we
+ // want to say that the value is stored at the CFA address - 96.
+ regloc.SetAtCFAPlusOffset(
+ -(stack_offset + row->GetCFAValue().GetOffset()));
+
+ row->SetRegisterInfo(lldb_regno, regloc);
+
+ row_updated = true;
+ }
+
+ else if (sub_rsp_pattern_p(stack_offset)) {
+ current_sp_bytes_offset_from_cfa += stack_offset;
+ if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
+ row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
+ row_updated = true;
+ }
+ }
+
+ else if (add_rsp_pattern_p(stack_offset)) {
+ current_sp_bytes_offset_from_cfa -= stack_offset;
+ if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
+ row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
+ row_updated = true;
+ }
+ in_epilogue = true;
+ }
+
+ else if (push_extended_pattern_p() || push_imm_pattern_p() ||
+ push_misc_reg_p()) {
+ current_sp_bytes_offset_from_cfa += m_wordsize;
+ if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
+ row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
+ row_updated = true;
+ }
+ }
+
+ else if (lea_rsp_pattern_p(stack_offset)) {
+ current_sp_bytes_offset_from_cfa -= stack_offset;
+ if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
+ row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
+ row_updated = true;
+ }
+ if (stack_offset > 0)
+ in_epilogue = true;
+ }
+
+ else if (ret_pattern_p() && prologue_completed_row.get()) {
+ // Reinstate the saved prologue setup for any instructions
+ // that come after the ret instruction
+
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *prologue_completed_row.get();
+ row.reset(newrow);
+ current_sp_bytes_offset_from_cfa =
+ prologue_completed_sp_bytes_offset_from_cfa;
+
+ saved_registers.clear();
+ saved_registers.resize(prologue_completed_saved_registers.size(), false);
+ for (size_t i = 0; i < prologue_completed_saved_registers.size(); ++i) {
+ saved_registers[i] = prologue_completed_saved_registers[i];
+ }
+
+ in_epilogue = true;
+ row_updated = true;
+ }
+
+ // call next instruction
+ // call 0
+ // => pop %ebx
+ // This is used in i386 programs to get the PIC base address for finding
+ // global data
+ else if (call_next_insn_pattern_p()) {
+ current_sp_bytes_offset_from_cfa += m_wordsize;
+ if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
+ row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
+ row_updated = true;
+ }
+ }
+
+ if (row_updated) {
+ if (current_func_text_offset + insn_len < size) {
+ row->SetOffset(current_func_text_offset + insn_len);
+ unwind_plan.AppendRow(row);
+ // Allocate a new Row, populate it with the existing Row contents.
+ newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ row.reset(newrow);
+ }
+ }
+
+ if (in_epilogue == false && row_updated) {
+ // If we're not in an epilogue sequence, save the updated Row
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ prologue_completed_row.reset(newrow);
+
+ prologue_completed_saved_registers.clear();
+ prologue_completed_saved_registers.resize(saved_registers.size(), false);
+ for (size_t i = 0; i < saved_registers.size(); ++i) {
+ prologue_completed_saved_registers[i] = saved_registers[i];
+ }
+ }
+
+ // We may change the sp value without adding a new Row necessarily -- keep
+ // track of it either way.
+ if (in_epilogue == false) {
+ prologue_completed_sp_bytes_offset_from_cfa =
+ current_sp_bytes_offset_from_cfa;
+ }
+
+ m_cur_insn = m_cur_insn + insn_len;
+ current_func_text_offset += insn_len;
+ }
+
+ unwind_plan.SetSourceName("assembly insn profiling");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+
+ return true;
+}
+
+bool x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite(
+ uint8_t *data, size_t size, AddressRange &func_range,
+ UnwindPlan &unwind_plan, RegisterContextSP &reg_ctx) {
+ Address addr_start = func_range.GetBaseAddress();
+ if (!addr_start.IsValid())
+ return false;
+
+ // We either need a live RegisterContext, or we need the UnwindPlan to already
+ // be in the lldb register numbering scheme.
+ if (reg_ctx.get() == nullptr &&
+ unwind_plan.GetRegisterKind() != eRegisterKindLLDB)
+ return false;
+
+ // Is original unwind_plan valid?
+ // unwind_plan should have at least one row which is ABI-default (CFA register
+ // is sp),
+ // and another row in mid-function.
+ if (unwind_plan.GetRowCount() < 2)
+ return false;
+
+ UnwindPlan::RowSP first_row = unwind_plan.GetRowAtIndex(0);
+ if (first_row->GetOffset() != 0)
+ return false;
+ uint32_t cfa_reg = first_row->GetCFAValue().GetRegisterNumber();
+ if (unwind_plan.GetRegisterKind() != eRegisterKindLLDB) {
+ cfa_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ unwind_plan.GetRegisterKind(),
+ first_row->GetCFAValue().GetRegisterNumber());
+ }
+ if (cfa_reg != m_lldb_sp_regnum ||
+ first_row->GetCFAValue().GetOffset() != m_wordsize)
+ return false;
+
+ UnwindPlan::RowSP original_last_row = unwind_plan.GetRowForFunctionOffset(-1);
+
+ size_t offset = 0;
+ int row_id = 1;
+ bool unwind_plan_updated = false;
+ UnwindPlan::RowSP row(new UnwindPlan::Row(*first_row));
+ m_cur_insn = data + offset;
+
+ // After a mid-function epilogue we will need to re-insert the original unwind
+ // rules
+ // so unwinds work for the remainder of the function. These aren't common
+ // with clang/gcc
+ // on x86 but it is possible.
+ bool reinstate_unwind_state = false;
+
+ while (offset < size) {
+ m_cur_insn = data + offset;
+ int insn_len;
+ if (!instruction_length(m_cur_insn, insn_len) || insn_len == 0 ||
+ insn_len > kMaxInstructionByteSize) {
+ // An unrecognized/junk instruction.
+ break;
+ }
+
+ // Advance offsets.
+ offset += insn_len;
+ m_cur_insn = data + offset;
+
+ if (reinstate_unwind_state) {
+ // that was the last instruction of this function
+ if (offset >= size)
+ continue;
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row());
+ *new_row = *original_last_row;
+ new_row->SetOffset(offset);
+ unwind_plan.AppendRow(new_row);
+ row.reset(new UnwindPlan::Row());
+ *row = *new_row;
+ reinstate_unwind_state = false;
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // If we already have one row for this instruction, we can continue.
+ while (row_id < unwind_plan.GetRowCount() &&
+ unwind_plan.GetRowAtIndex(row_id)->GetOffset() <= offset) {
+ row_id++;
+ }
+ UnwindPlan::RowSP original_row = unwind_plan.GetRowAtIndex(row_id - 1);
+ if (original_row->GetOffset() == offset) {
+ *row = *original_row;
+ continue;
+ }
+
+ if (row_id == 0) {
+ // If we are here, compiler didn't generate CFI for prologue.
+ // This won't happen to GCC or clang.
+ // In this case, bail out directly.
+ return false;
+ }
+
+ // Inspect the instruction to check if we need a new row for it.
+ cfa_reg = row->GetCFAValue().GetRegisterNumber();
+ if (unwind_plan.GetRegisterKind() != eRegisterKindLLDB) {
+ cfa_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ unwind_plan.GetRegisterKind(),
+ row->GetCFAValue().GetRegisterNumber());
+ }
+ if (cfa_reg == m_lldb_sp_regnum) {
+ // CFA register is sp.
+
+ // call next instruction
+ // call 0
+ // => pop %ebx
+ if (call_next_insn_pattern_p()) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // push/pop register
+ int regno;
+ if (push_reg_p(regno)) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+ if (pop_reg_p(regno)) {
+ // Technically, this might be a nonvolatile register recover in
+ // epilogue.
+ // We should reset RegisterInfo for the register.
+ // But in practice, previous rule for the register is still valid...
+ // So we ignore this case.
+
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(-m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ if (pop_misc_reg_p()) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(-m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // push imm
+ if (push_imm_pattern_p()) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(m_wordsize);
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // push extended
+ if (push_extended_pattern_p() || push_misc_reg_p()) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(m_wordsize);
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // add/sub %rsp/%esp
+ int amount;
+ if (add_rsp_pattern_p(amount)) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(-amount);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+ if (sub_rsp_pattern_p(amount)) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(amount);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // lea %rsp, [%rsp + $offset]
+ if (lea_rsp_pattern_p(amount)) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(-amount);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ if (ret_pattern_p()) {
+ reinstate_unwind_state = true;
+ continue;
+ }
+ } else if (cfa_reg == m_lldb_fp_regnum) {
+ // CFA register is fp.
+
+ // The only case we care about is epilogue:
+ // [0x5d] pop %rbp/%ebp
+ // => [0xc3] ret
+ if (pop_rbp_pattern_p() || leave_pattern_p()) {
+ offset += 1;
+ row->SetOffset(offset);
+ row->GetCFAValue().SetIsRegisterPlusOffset(
+ first_row->GetCFAValue().GetRegisterNumber(), m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ reinstate_unwind_state = true;
+ continue;
+ }
+ } else {
+ // CFA register is not sp or fp.
+
+ // This must be hand-written assembly.
+ // Just trust eh_frame and assume we have finished.
+ break;
+ }
+ }
+
+ unwind_plan.SetPlanValidAddressRange(func_range);
+ if (unwind_plan_updated) {
+ std::string unwind_plan_source(unwind_plan.GetSourceName().AsCString());
+ unwind_plan_source += " plus augmentation from assembly parsing";
+ unwind_plan.SetSourceName(unwind_plan_source.c_str());
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ }
+ return true;
+}
+
+bool x86AssemblyInspectionEngine::FindFirstNonPrologueInstruction(
+ uint8_t *data, size_t size, size_t &offset) {
+ offset = 0;
+
+ if (m_register_map_initialized == false)
+ return false;
+
+ while (offset < size) {
+ int regno;
+ int insn_len;
+ int scratch;
+
+ m_cur_insn = data + offset;
+ if (!instruction_length(m_cur_insn, insn_len) ||
+ insn_len > kMaxInstructionByteSize || insn_len == 0) {
+ // An error parsing the instruction, i.e. probably data/garbage - stop
+ // scanning
+ break;
+ }
+
+ if (push_rbp_pattern_p() || mov_rsp_rbp_pattern_p() ||
+ sub_rsp_pattern_p(scratch) || push_reg_p(regno) ||
+ mov_reg_to_local_stack_frame_p(regno, scratch) ||
+ (lea_rsp_pattern_p(scratch) && offset == 0)) {
+ offset += insn_len;
+ continue;
+ }
+ //
+ // Unknown non-prologue instruction - stop scanning
+ break;
+ }
+
+ return true;
+}
diff --git a/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h b/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h
new file mode 100644
index 000000000000..0294f5a0c282
--- /dev/null
+++ b/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h
@@ -0,0 +1,181 @@
+//===-- x86AssemblyInspectionEngine.h ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_x86AssemblyInspectionEngine_h_
+#define liblldb_x86AssemblyInspectionEngine_h_
+
+#include "llvm-c/Disassembler.h"
+
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private.h"
+
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/ConstString.h"
+
+#include <map>
+#include <vector>
+
+namespace lldb_private {
+
+// x86AssemblyInspectionEngine - a class which will take a buffer of bytes
+// of i386/x86_64 instructions and create an UnwindPlan based on those
+// assembly instructions.
+class x86AssemblyInspectionEngine {
+
+public:
+ /// default ctor
+ x86AssemblyInspectionEngine(const lldb_private::ArchSpec &arch);
+
+ /// default dtor
+ ~x86AssemblyInspectionEngine();
+
+ /// One of the two initialize methods that can be called on this object;
+ /// they must be called before any of the assembly inspection methods
+ /// are called. This one should be used if the caller has access to a
+ /// valid RegisterContext.
+ void Initialize(lldb::RegisterContextSP &reg_ctx);
+
+ /// One of the two initialize methods that can be called on this object;
+ /// they must be called before any of the assembly inspection methods
+ /// are called. This one takes a vector of register name and lldb
+ /// register numbers.
+ struct lldb_reg_info {
+ const char *name;
+ uint32_t lldb_regnum;
+ lldb_reg_info() : name(nullptr), lldb_regnum(LLDB_INVALID_REGNUM) {}
+ };
+ void Initialize(std::vector<lldb_reg_info> &reg_info);
+
+ /// Create an UnwindPlan for a "non-call site" stack frame situation.
+ /// This is usually when this function/method is currently executing, and may
+ /// be at
+ /// a location where exception-handling style unwind information (eh_frame,
+ /// compact unwind info, arm unwind info)
+ /// are not valid.
+ /// \p data is a pointer to the instructions for the function
+ /// \p size is the size of the instruction buffer above
+ /// \p func_range is the start Address and size of the function, to be
+ /// included in the UnwindPlan
+ /// \p unwind_plan is the unwind plan that this method creates
+ /// \returns true if it was able to create an UnwindPlan; false if not.
+ bool
+ GetNonCallSiteUnwindPlanFromAssembly(uint8_t *data, size_t size,
+ lldb_private::AddressRange &func_range,
+ lldb_private::UnwindPlan &unwind_plan);
+
+ /// Take an existing UnwindPlan, probably from eh_frame which may be missing
+ /// description
+ /// of the epilogue instructions, and add the epilogue description to it based
+ /// on the
+ /// instructions in the function.
+ ///
+ /// The \p unwind_plan 's register numbers must be converted into the lldb
+ /// register numbering
+ /// scheme OR a RegisterContext must be provided in \p reg_ctx. If the \p
+ /// unwind_plan
+ /// register numbers are already in lldb register numbering, \p reg_ctx may be
+ /// null.
+ /// \returns true if the \p unwind_plan was updated, false if it was not.
+ bool AugmentUnwindPlanFromCallSite(uint8_t *data, size_t size,
+ lldb_private::AddressRange &func_range,
+ lldb_private::UnwindPlan &unwind_plan,
+ lldb::RegisterContextSP &reg_ctx);
+
+ bool FindFirstNonPrologueInstruction(uint8_t *data, size_t size,
+ size_t &offset);
+
+private:
+ bool nonvolatile_reg_p(int machine_regno);
+ bool push_rbp_pattern_p();
+ bool push_0_pattern_p();
+ bool push_imm_pattern_p();
+ bool push_extended_pattern_p();
+ bool push_misc_reg_p();
+ bool mov_rsp_rbp_pattern_p();
+ bool sub_rsp_pattern_p(int &amount);
+ bool add_rsp_pattern_p(int &amount);
+ bool lea_rsp_pattern_p(int &amount);
+ bool push_reg_p(int &regno);
+ bool pop_reg_p(int &regno);
+ bool pop_rbp_pattern_p();
+ bool pop_misc_reg_p();
+ bool leave_pattern_p();
+ bool call_next_insn_pattern_p();
+ bool mov_reg_to_local_stack_frame_p(int &regno, int &rbp_offset);
+ bool ret_pattern_p();
+ uint32_t extract_4(uint8_t *b);
+
+ bool instruction_length(uint8_t *insn, int &length);
+
+ bool machine_regno_to_lldb_regno(int machine_regno, uint32_t &lldb_regno);
+
+ enum CPU { k_i386, k_x86_64, k_cpu_unspecified };
+
+ enum i386_register_numbers {
+ k_machine_eax = 0,
+ k_machine_ecx = 1,
+ k_machine_edx = 2,
+ k_machine_ebx = 3,
+ k_machine_esp = 4,
+ k_machine_ebp = 5,
+ k_machine_esi = 6,
+ k_machine_edi = 7,
+ k_machine_eip = 8
+ };
+
+ enum x86_64_register_numbers {
+ k_machine_rax = 0,
+ k_machine_rcx = 1,
+ k_machine_rdx = 2,
+ k_machine_rbx = 3,
+ k_machine_rsp = 4,
+ k_machine_rbp = 5,
+ k_machine_rsi = 6,
+ k_machine_rdi = 7,
+ k_machine_r8 = 8,
+ k_machine_r9 = 9,
+ k_machine_r10 = 10,
+ k_machine_r11 = 11,
+ k_machine_r12 = 12,
+ k_machine_r13 = 13,
+ k_machine_r14 = 14,
+ k_machine_r15 = 15,
+ k_machine_rip = 16
+ };
+
+ enum { kMaxInstructionByteSize = 32 };
+
+ uint8_t *m_cur_insn;
+
+ uint32_t m_machine_ip_regnum;
+ uint32_t m_machine_sp_regnum;
+ uint32_t m_machine_fp_regnum;
+ uint32_t m_lldb_ip_regnum;
+ uint32_t m_lldb_sp_regnum;
+ uint32_t m_lldb_fp_regnum;
+
+ typedef std::map<uint32_t, lldb_reg_info> MachineRegnumToNameAndLLDBRegnum;
+
+ MachineRegnumToNameAndLLDBRegnum m_reg_map;
+
+ lldb_private::ArchSpec m_arch;
+ CPU m_cpu;
+ int m_wordsize;
+
+ bool m_register_map_initialized;
+
+ ::LLVMDisasmContextRef m_disasm_context;
+
+ DISALLOW_COPY_AND_ASSIGN(x86AssemblyInspectionEngine);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_x86AssemblyInspectionEngine_h_