diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:51:52 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:51:52 +0000 |
commit | 5f29bb8a675e8f96452b632e7129113f7dec850e (patch) | |
tree | 3d3f2a0d3ad10872a4dcaba8ec8d1d20c87ab147 /source/Plugins/UnwindAssembly | |
parent | 88c643b6fec27eec436c8d138fee6346e92337d6 (diff) |
Notes
Diffstat (limited to 'source/Plugins/UnwindAssembly')
6 files changed, 309 insertions, 87 deletions
diff --git a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp index b70c2c44d3e6..4aa9fb634b61 100644 --- a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp +++ b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp @@ -1,9 +1,8 @@ //===-- UnwindAssemblyInstEmulation.cpp --------------------------*- C++-*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -29,9 +28,7 @@ using namespace lldb; using namespace lldb_private; -//----------------------------------------------------------------------------------------------- // UnwindAssemblyInstEmulation method definitions -//----------------------------------------------------------------------------------------------- bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( AddressRange &range, Thread &thread, UnwindPlan &unwind_plan) { @@ -57,11 +54,11 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( return false; if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() && - m_inst_emulator_ap.get()) { + m_inst_emulator_up.get()) { // The instruction emulation subclass setup the unwind plan for the first // instruction. - m_inst_emulator_ap->CreateFunctionEntryUnwind(unwind_plan); + m_inst_emulator_up->CreateFunctionEntryUnwind(unwind_plan); // CreateFunctionEntryUnwind should have created the first row. If it // doesn't, then we are done. @@ -70,8 +67,8 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( 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)); + m_arch, nullptr, nullptr, range.GetBaseAddress(), opcode_data, + opcode_size, 99999, prefer_file_cache)); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); @@ -83,7 +80,7 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( 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(), + m_inst_emulator_up->GetRegisterInfo(unwind_plan.GetRegisterKind(), unwind_plan.GetInitialCFARegister(), m_cfa_reg_info); @@ -129,14 +126,14 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( // 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( + m_inst_emulator_up->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( + m_inst_emulator_up->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, ra_reg_info); // The architecture dependent condition code of the last processed @@ -170,12 +167,12 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( m_register_values = it->second.second; } - m_inst_emulator_ap->SetInstruction(inst->GetOpcode(), + m_inst_emulator_up->SetInstruction(inst->GetOpcode(), inst->GetAddress(), nullptr); if (last_condition != - m_inst_emulator_ap->GetInstructionCondition()) { - if (m_inst_emulator_ap->GetInstructionCondition() != + m_inst_emulator_up->GetInstructionCondition()) { + if (m_inst_emulator_up->GetInstructionCondition() != EmulateInstruction::UnconditionalCondition && saved_unwind_states.count(current_offset) == 0) { // If we don't have a saved row for the current offset then @@ -216,13 +213,13 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( 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); + show_bytes, nullptr, nullptr, nullptr, &format, 0); log->PutString(strm.GetString()); } - last_condition = m_inst_emulator_ap->GetInstructionCondition(); + last_condition = m_inst_emulator_up->GetInstructionCondition(); - m_inst_emulator_ap->EvaluateInstruction( + m_inst_emulator_up->EvaluateInstruction( eEmulateInstructionOptionIgnoreConditions); // If the current instruction is a branch forward then save the @@ -297,18 +294,16 @@ bool UnwindAssemblyInstEmulation::FirstNonPrologueInsn( UnwindAssembly * UnwindAssemblyInstEmulation::CreateInstance(const ArchSpec &arch) { - std::unique_ptr<EmulateInstruction> inst_emulator_ap( + std::unique_ptr<EmulateInstruction> inst_emulator_up( EmulateInstruction::FindPlugin(arch, eInstructionTypePrologueEpilogue, - NULL)); + nullptr)); // Make sure that all prologue instructions are handled - if (inst_emulator_ap.get()) - return new UnwindAssemblyInstEmulation(arch, inst_emulator_ap.release()); - return NULL; + if (inst_emulator_up) + return new UnwindAssemblyInstEmulation(arch, inst_emulator_up.release()); + return nullptr; } -//------------------------------------------------------------------ // PluginInterface protocol in UnwindAssemblyParser_x86 -//------------------------------------------------------------------ ConstString UnwindAssemblyInstEmulation::GetPluginName() { return GetPluginNameStatic(); } diff --git a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h index 5539b5217e9e..9125bd5b1fe3 100644 --- a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h +++ b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h @@ -1,9 +1,8 @@ //===-- UnwindAssemblyInstEmulation.h ---------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -48,9 +47,7 @@ public: static lldb_private::UnwindAssembly * CreateInstance(const lldb_private::ArchSpec &arch); - //------------------------------------------------------------------ // PluginInterface protocol - //------------------------------------------------------------------ static void Initialize(); static void Terminate(); @@ -67,14 +64,14 @@ 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_unwind_plan_ptr(NULL), m_curr_row(), + : UnwindAssembly(arch), m_inst_emulator_up(inst_emulator), + m_range_ptr(nullptr), m_unwind_plan_ptr(nullptr), 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, + if (m_inst_emulator_up.get()) { + m_inst_emulator_up->SetBaton(this); + m_inst_emulator_up->SetCallbacks(ReadMemory, WriteMemory, ReadRegister, WriteRegister); } } @@ -129,7 +126,7 @@ private: bool GetRegisterValue(const lldb_private::RegisterInfo ®_info, lldb_private::RegisterValue ®_value); - std::unique_ptr<lldb_private::EmulateInstruction> m_inst_emulator_ap; + std::unique_ptr<lldb_private::EmulateInstruction> m_inst_emulator_up; lldb_private::AddressRange *m_range_ptr; lldb_private::UnwindPlan *m_unwind_plan_ptr; lldb_private::UnwindPlan::RowSP m_curr_row; diff --git a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp index 04f9cbbf03ae..ce168f021047 100644 --- a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp +++ b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp @@ -1,9 +1,8 @@ //===-- UnwindAssembly-x86.cpp ----------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -31,9 +30,7 @@ using namespace lldb; using namespace lldb_private; -//----------------------------------------------------------------------------------------------- // UnwindAssemblyParser_x86 method definitions -//----------------------------------------------------------------------------------------------- UnwindAssembly_x86::UnwindAssembly_x86(const ArchSpec &arch) : lldb_private::UnwindAssembly(arch), @@ -242,12 +239,10 @@ 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; + return nullptr; } -//------------------------------------------------------------------ // PluginInterface protocol in UnwindAssemblyParser_x86 -//------------------------------------------------------------------ ConstString UnwindAssembly_x86::GetPluginName() { return GetPluginNameStatic(); diff --git a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h index c4052a8489da..7c198bbc33af 100644 --- a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h +++ b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h @@ -1,9 +1,8 @@ //===-- UnwindAssembly-x86.h ------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -42,9 +41,7 @@ public: static lldb_private::UnwindAssembly * CreateInstance(const lldb_private::ArchSpec &arch); - //------------------------------------------------------------------ // PluginInterface protocol - //------------------------------------------------------------------ static void Initialize(); static void Terminate(); diff --git a/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp b/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp index f8e70204e509..43041ca1bb2f 100644 --- a/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp +++ b/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp @@ -1,14 +1,15 @@ //===-- x86AssemblyInspectionEngine.cpp -------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "x86AssemblyInspectionEngine.h" +#include <memory> + #include "llvm-c/Disassembler.h" #include "lldb/Core/Address.h" @@ -365,8 +366,8 @@ bool x86AssemblyInspectionEngine::push_reg_p(int ®no) { 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; + if (m_wordsize == 8 && (*p & 0xfe) == 0x40) { + regno_prefix_bit = (*p & 1) << 3; p++; } if (*p >= 0x50 && *p <= 0x57) { @@ -563,8 +564,8 @@ bool x86AssemblyInspectionEngine::pop_reg_p(int ®no) { 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; + if (m_wordsize == 8 && (*p & 0xfe) == 0x40) { + regno_prefix_bit = (*p & 1) << 3; p++; } if (*p >= 0x58 && *p <= 0x5f) { @@ -666,10 +667,207 @@ bool x86AssemblyInspectionEngine::mov_reg_to_local_stack_frame_p( return false; } -// ret [0xc9] or [0xc2 imm8] or [0xca imm8] +// Returns true if this is a jmp instruction where we can't +// know the destination address statically. +// +// ff e0 jmpq *%rax +// ff e1 jmpq *%rcx +// ff 60 28 jmpq *0x28(%rax) +// ff 60 60 jmpq *0x60(%rax) +bool x86AssemblyInspectionEngine::jmp_to_reg_p() { + if (*m_cur_insn != 0xff) + return false; + + // The second byte is a ModR/M /4 byte, strip off the registers + uint8_t second_byte_sans_reg = *(m_cur_insn + 1) & ~7; + + // Don't handle 0x24 disp32, because the target address is + // knowable statically - pc_rel_branch_or_jump_p() will + // return the target address. + + // [reg] + if (second_byte_sans_reg == 0x20) + return true; + + // [reg]+disp8 + if (second_byte_sans_reg == 0x60) + return true; + + // [reg]+disp32 + if (second_byte_sans_reg == 0xa0) + return true; + + // reg + if (second_byte_sans_reg == 0xe0) + return true; + + // disp32 + // jumps to an address stored in memory, the value can't be cached + // in an unwind plan. + if (second_byte_sans_reg == 0x24) + return true; + + // use SIB byte + // ff 24 fe jmpq *(%rsi,%rdi,8) + if (second_byte_sans_reg == 0x24) + return true; + + return false; +} + +// Detect branches to fixed pc-relative offsets. +// Returns the offset from the address of the next instruction +// that may be branch/jumped to. +// +// Cannot determine the offset of a JMP that jumps to the address in +// a register ("jmpq *%rax") or offset from a register value +// ("jmpq *0x28(%rax)"), this method will return false on those +// instructions. +// +// These instructions all end in either a relative 8/16/32 bit value +// depending on the instruction and the current execution mode of the +// inferior process. Once we know the size of the opcode instruction, +// we can use the total instruction length to determine the size of +// the relative offset without having to compute it correctly. + +bool x86AssemblyInspectionEngine::pc_rel_branch_or_jump_p ( + const int instruction_length, int &offset) +{ + int opcode_size = 0; + + uint8_t b1 = m_cur_insn[0]; + + switch (b1) { + case 0x77: // JA/JNBE rel8 + case 0x73: // JAE/JNB/JNC rel8 + case 0x72: // JB/JC/JNAE rel8 + case 0x76: // JBE/JNA rel8 + case 0xe3: // JCXZ/JECXZ/JRCXZ rel8 + case 0x74: // JE/JZ rel8 + case 0x7f: // JG/JNLE rel8 + case 0x7d: // JGE/JNL rel8 + case 0x7c: // JL/JNGE rel8 + case 0x7e: // JNG/JLE rel8 + case 0x71: // JNO rel8 + case 0x7b: // JNP/JPO rel8 + case 0x79: // JNS rel8 + case 0x75: // JNE/JNZ rel8 + case 0x70: // JO rel8 + case 0x7a: // JP/JPE rel8 + case 0x78: // JS rel8 + case 0xeb: // JMP rel8 + case 0xe9: // JMP rel16/rel32 + opcode_size = 1; + break; + default: + break; + } + if (b1 == 0x0f && opcode_size == 0) { + uint8_t b2 = m_cur_insn[1]; + switch (b2) { + case 0x87: // JA/JNBE rel16/rel32 + case 0x86: // JBE/JNA rel16/rel32 + case 0x84: // JE/JZ rel16/rel32 + case 0x8f: // JG/JNLE rel16/rel32 + case 0x8d: // JNL/JGE rel16/rel32 + case 0x8e: // JLE rel16/rel32 + case 0x82: // JB/JC/JNAE rel16/rel32 + case 0x83: // JAE/JNB/JNC rel16/rel32 + case 0x85: // JNE/JNZ rel16/rel32 + case 0x8c: // JL/JNGE rel16/rel32 + case 0x81: // JNO rel16/rel32 + case 0x8b: // JNP/JPO rel16/rel32 + case 0x89: // JNS rel16/rel32 + case 0x80: // JO rel16/rel32 + case 0x8a: // JP rel16/rel32 + case 0x88: // JS rel16/rel32 + opcode_size = 2; + break; + default: + break; + } + } + + if (opcode_size == 0) + return false; + + offset = 0; + if (instruction_length - opcode_size == 1) { + int8_t rel8 = (int8_t) *(m_cur_insn + opcode_size); + offset = rel8; + } else if (instruction_length - opcode_size == 2) { + int16_t rel16 = extract_2_signed (m_cur_insn + opcode_size); + offset = rel16; + } else if (instruction_length - opcode_size == 4) { + int32_t rel32 = extract_4_signed (m_cur_insn + opcode_size); + offset = rel32; + } else { + return false; + } + return true; +} + +// Returns true if this instruction is a intra-function branch or jump - +// a branch/jump within the bounds of this same function. +// Cannot predict where a jump through a register value ("jmpq *%rax") +// will go, so it will return false on that instruction. +bool x86AssemblyInspectionEngine::local_branch_p ( + const addr_t current_func_text_offset, + const AddressRange &func_range, + const int instruction_length, + addr_t &target_insn_offset) { + int offset; + if (pc_rel_branch_or_jump_p (instruction_length, offset) && offset != 0) { + addr_t next_pc_value = current_func_text_offset + instruction_length; + if (offset < 0 && addr_t(-offset) > current_func_text_offset) { + // Branch target is before the start of this function + return false; + } + if (offset + next_pc_value > func_range.GetByteSize()) { + // Branch targets outside this function's bounds + return false; + } + // This instruction branches to target_insn_offset (byte offset into the function) + target_insn_offset = next_pc_value + offset; + return true; + } + return false; +} + +// Returns true if this instruction is a inter-function branch or jump - a +// branch/jump to another function. +// Cannot predict where a jump through a register value ("jmpq *%rax") +// will go, so it will return false on that instruction. +bool x86AssemblyInspectionEngine::non_local_branch_p ( + const addr_t current_func_text_offset, + const AddressRange &func_range, + const int instruction_length) { + int offset; + addr_t target_insn_offset; + if (pc_rel_branch_or_jump_p (instruction_length, offset)) { + return !local_branch_p(current_func_text_offset,func_range,instruction_length,target_insn_offset); + } + return false; +} + +// ret [0xc3] or [0xcb] or [0xc2 imm16] or [0xca imm16] bool x86AssemblyInspectionEngine::ret_pattern_p() { uint8_t *p = m_cur_insn; - return *p == 0xc9 || *p == 0xc2 || *p == 0xca || *p == 0xc3; + return *p == 0xc3 || *p == 0xc2 || *p == 0xca || *p == 0xcb; +} + +uint16_t x86AssemblyInspectionEngine::extract_2(uint8_t *b) { + uint16_t v = 0; + for (int i = 1; i >= 0; i--) + v = (v << 8) | b[i]; + return v; +} + +int16_t x86AssemblyInspectionEngine::extract_2_signed(uint8_t *b) { + int16_t v = 0; + for (int i = 1; i >= 0; i--) + v = (v << 8) | b[i]; + return v; } uint32_t x86AssemblyInspectionEngine::extract_4(uint8_t *b) { @@ -679,6 +877,14 @@ uint32_t x86AssemblyInspectionEngine::extract_4(uint8_t *b) { return v; } +int32_t x86AssemblyInspectionEngine::extract_4_signed(uint8_t *b) { + int32_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, uint32_t buffer_remaining_bytes) { @@ -704,7 +910,6 @@ bool x86AssemblyInspectionEngine::machine_regno_to_lldb_regno( return true; } return false; - return false; } bool x86AssemblyInspectionEngine::GetNonCallSiteUnwindPlanFromAssembly( @@ -1028,25 +1233,47 @@ bool x86AssemblyInspectionEngine::GetNonCallSiteUnwindPlanFromAssembly( } } - 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_fa = - prologue_completed_sp_bytes_offset_from_cfa; - is_aligned = prologue_completed_is_aligned; - - 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]; + else if (prologue_completed_row.get() && + (ret_pattern_p() || + non_local_branch_p (current_func_text_offset, func_range, insn_len) || + jmp_to_reg_p())) { + // Check if the current instruction is the end of an epilogue sequence, + // and if so, re-instate the prologue-completed unwind state. + + // The current instruction is a branch/jump outside this function, + // a ret, or a jump through a register value which we cannot + // determine the effcts of. Verify that the stack frame state + // has been unwound to the same as it was at function entry to avoid + // mis-identifying a JMP instruction as an epilogue. + UnwindPlan::Row::RegisterLocation sp, pc; + if (row->GetRegisterInfo(m_lldb_sp_regnum, sp) && + row->GetRegisterInfo(m_lldb_ip_regnum, pc)) { + // Any ret instruction variant is definitely indicative of an + // epilogue; for other insn patterns verify that we're back to + // the original unwind state. + if (ret_pattern_p() || + (sp.IsCFAPlusOffset() && sp.GetOffset() == 0 && + pc.IsAtCFAPlusOffset() && pc.GetOffset() == -m_wordsize)) { + // Reinstate the saved prologue setup for any instructions that come + // after the epilogue + + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *prologue_completed_row.get(); + row.reset(newrow); + current_sp_bytes_offset_from_fa = + prologue_completed_sp_bytes_offset_from_cfa; + is_aligned = prologue_completed_is_aligned; + + 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; + } } - - in_epilogue = true; - row_updated = true; } // call next instruction @@ -1173,7 +1400,7 @@ bool x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite( *new_row = *original_last_row; new_row->SetOffset(offset); unwind_plan.AppendRow(new_row); - row.reset(new UnwindPlan::Row()); + row = std::make_shared<UnwindPlan::Row>(); *row = *new_row; reinstate_unwind_state = false; unwind_plan_updated = true; diff --git a/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h b/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h index e02b510ba1f9..680598abdeff 100644 --- a/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h +++ b/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h @@ -1,9 +1,8 @@ //===-- x86AssemblyInspectionEngine.h ---------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -115,7 +114,19 @@ private: bool call_next_insn_pattern_p(); bool mov_reg_to_local_stack_frame_p(int ®no, int &rbp_offset); bool ret_pattern_p(); + bool jmp_to_reg_p(); + bool pc_rel_branch_or_jump_p (const int instruction_length, int &offset); + bool non_local_branch_p (const lldb::addr_t current_func_text_offset, + const lldb_private::AddressRange &func_range, + const int instruction_length); + bool local_branch_p (const lldb::addr_t current_func_text_offset, + const lldb_private::AddressRange &func_range, + const int instruction_length, + lldb::addr_t &target_insn_offset); + uint16_t extract_2(uint8_t *b); + int16_t extract_2_signed(uint8_t *b); uint32_t extract_4(uint8_t *b); + int32_t extract_4_signed(uint8_t *b); bool instruction_length(uint8_t *insn, int &length, uint32_t buffer_remaining_bytes); |