summaryrefslogtreecommitdiffstats
path: root/contrib/llvm/tools/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp')
-rw-r--r--contrib/llvm/tools/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp473
1 files changed, 313 insertions, 160 deletions
diff --git a/contrib/llvm/tools/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp b/contrib/llvm/tools/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
index 32a21d2..dbf37d8 100644
--- a/contrib/llvm/tools/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
+++ b/contrib/llvm/tools/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
@@ -23,6 +23,7 @@
#include "lldb/Target/Thread.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/UnwindAssembly.h"
+#include "lldb/Utility/RegisterNumber.h"
using namespace lldb;
using namespace lldb_private;
@@ -318,7 +319,8 @@ AssemblyParse_x86::nonvolatile_reg_p (int machine_regno)
#define REX_W_DSTREG(opcode) ((opcode) & 0x1)
// pushq %rbp [0x55]
-bool AssemblyParse_x86::push_rbp_pattern_p ()
+bool
+AssemblyParse_x86::push_rbp_pattern_p ()
{
uint8_t *p = m_cur_insn_bytes;
if (*p == 0x55)
@@ -327,7 +329,8 @@ bool AssemblyParse_x86::push_rbp_pattern_p ()
}
// pushq $0 ; the first instruction in start() [0x6a 0x00]
-bool AssemblyParse_x86::push_0_pattern_p ()
+bool
+AssemblyParse_x86::push_0_pattern_p ()
{
uint8_t *p = m_cur_insn_bytes;
if (*p == 0x6a && *(p + 1) == 0x0)
@@ -337,7 +340,8 @@ bool AssemblyParse_x86::push_0_pattern_p ()
// pushq $0
// pushl $0
-bool AssemblyParse_x86::push_imm_pattern_p ()
+bool
+AssemblyParse_x86::push_imm_pattern_p ()
{
uint8_t *p = m_cur_insn_bytes;
if (*p == 0x68 || *p == 0x6a)
@@ -347,7 +351,8 @@ bool AssemblyParse_x86::push_imm_pattern_p ()
// 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 ()
+bool
+AssemblyParse_x86::mov_rsp_rbp_pattern_p ()
{
uint8_t *p = m_cur_insn_bytes;
if (m_wordsize == 8 && *p == 0x48)
@@ -360,7 +365,8 @@ bool AssemblyParse_x86::mov_rsp_rbp_pattern_p ()
}
// subq $0x20, %rsp
-bool AssemblyParse_x86::sub_rsp_pattern_p (int& amount)
+bool
+AssemblyParse_x86::sub_rsp_pattern_p (int& amount)
{
uint8_t *p = m_cur_insn_bytes;
if (m_wordsize == 8 && *p == 0x48)
@@ -381,7 +387,8 @@ bool AssemblyParse_x86::sub_rsp_pattern_p (int& amount)
}
// addq $0x20, %rsp
-bool AssemblyParse_x86::add_rsp_pattern_p (int& amount)
+bool
+AssemblyParse_x86::add_rsp_pattern_p (int& amount)
{
uint8_t *p = m_cur_insn_bytes;
if (m_wordsize == 8 && *p == 0x48)
@@ -403,7 +410,8 @@ bool AssemblyParse_x86::add_rsp_pattern_p (int& amount)
// pushq %rbx
// pushl %ebx
-bool AssemblyParse_x86::push_reg_p (int& regno)
+bool
+AssemblyParse_x86::push_reg_p (int& regno)
{
uint8_t *p = m_cur_insn_bytes;
int regno_prefix_bit = 0;
@@ -423,7 +431,8 @@ bool AssemblyParse_x86::push_reg_p (int& regno)
// popq %rbx
// popl %ebx
-bool AssemblyParse_x86::pop_reg_p (int& regno)
+bool
+AssemblyParse_x86::pop_reg_p (int& regno)
{
uint8_t *p = m_cur_insn_bytes;
int regno_prefix_bit = 0;
@@ -443,14 +452,16 @@ bool AssemblyParse_x86::pop_reg_p (int& regno)
// popq %rbp [0x5d]
// popl %ebp [0x5d]
-bool AssemblyParse_x86::pop_rbp_pattern_p ()
+bool
+AssemblyParse_x86::pop_rbp_pattern_p ()
{
uint8_t *p = m_cur_insn_bytes;
return (*p == 0x5d);
}
// call $0 [0xe8 0x0 0x0 0x0 0x0]
-bool AssemblyParse_x86::call_next_insn_pattern_p ()
+bool
+AssemblyParse_x86::call_next_insn_pattern_p ()
{
uint8_t *p = m_cur_insn_bytes;
return (*p == 0xe8) && (*(p+1) == 0x0) && (*(p+2) == 0x0)
@@ -468,7 +479,8 @@ bool AssemblyParse_x86::call_next_insn_pattern_p ()
// 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)
+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;
@@ -602,7 +614,6 @@ bool
AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan)
{
UnwindPlan::RowSP row(new UnwindPlan::Row);
- int non_prologue_insn_count = 0;
m_cur_insn = m_func_bounds.GetBaseAddress ();
int current_func_text_offset = 0;
int current_sp_bytes_offset_from_cfa = 0;
@@ -638,20 +649,38 @@ AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan)
*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);
+
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) && non_prologue_insn_count < 10)
+ 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))
{
@@ -661,216 +690,193 @@ AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan)
if (push_rbp_pattern_p ())
{
- row->SetOffset (current_func_text_offset + insn_len);
current_sp_bytes_offset_from_cfa += m_wordsize;
row->SetCFAOffset (current_sp_bytes_offset_from_cfa);
UnwindPlan::Row::RegisterLocation regloc;
regloc.SetAtCFAPlusOffset (-row->GetCFAOffset());
row->SetRegisterInfo (m_lldb_fp_regnum, regloc);
- 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);
- goto loopnext;
+ saved_registers[m_machine_fp_regnum] = true;
+ row_updated = true;
}
- if (mov_rsp_rbp_pattern_p ())
+ else if (mov_rsp_rbp_pattern_p ())
{
- row->SetOffset (current_func_text_offset + insn_len);
row->SetCFARegister (m_lldb_fp_regnum);
- 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);
- goto loopnext;
+ 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.
- if (push_0_pattern_p ())
+ else if (push_0_pattern_p ())
{
- goto loopnext;
}
- if (push_reg_p (machine_regno))
+ else if (push_reg_p (machine_regno))
{
current_sp_bytes_offset_from_cfa += m_wordsize;
- bool need_to_push_row = false;
// 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->GetCFARegister() == m_lldb_sp_regnum)
{
- need_to_push_row = true;
row->SetCFAOffset (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))
+ if (nonvolatile_reg_p (machine_regno)
+ && machine_regno_to_lldb_regno (machine_regno, lldb_regno)
+ && saved_registers[machine_regno] == false)
{
- need_to_push_row = true;
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;
}
- if (need_to_push_row)
- {
- 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);
- }
- goto loopnext;
}
- if (mov_reg_to_local_stack_frame_p (machine_regno, stack_offset) && nonvolatile_reg_p (machine_regno))
+ else if (pop_reg_p (machine_regno))
{
- if (machine_regno_to_lldb_regno (machine_regno, lldb_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)
{
- row->SetOffset (current_func_text_offset + insn_len);
- UnwindPlan::Row::RegisterLocation regloc;
+ saved_registers[machine_regno] = false;
+ row->RemoveRegisterInfo (lldb_regno);
- // 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->GetCFAOffset()));
+ if (machine_regno == m_machine_fp_regnum)
+ {
+ row->SetCFARegister (m_lldb_sp_regnum);
+ }
- row->SetRegisterInfo (lldb_regno, regloc);
- 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);
- goto loopnext;
+ in_epilogue = true;
+ row_updated = true;
}
- }
- if (sub_rsp_pattern_p (stack_offset))
- {
- current_sp_bytes_offset_from_cfa += stack_offset;
+ // 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->GetCFARegister() == m_lldb_sp_regnum)
{
- row->SetOffset (current_func_text_offset + insn_len);
row->SetCFAOffset (current_sp_bytes_offset_from_cfa);
- 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);
+ row_updated = true;
}
- goto loopnext;
}
- if (ret_pattern_p ())
+ 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)
{
- // we know where the end of the function is; set the limit on the PlanValidAddressRange
- // in case our initial "high pc" value was overly large
- // int original_size = m_func_bounds.GetByteSize();
- // int calculated_size = m_cur_insn.GetOffset() - m_func_bounds.GetBaseAddress().GetOffset() + insn_len + 1;
- // m_func_bounds.SetByteSize (calculated_size);
- // unwind_plan.SetPlanValidAddressRange (m_func_bounds);
- break;
- }
+ saved_registers[machine_regno] = true;
- // FIXME recognize the i386 picbase setup instruction sequence,
- // 0x1f16: call 0x1f1b ; main + 11 at /private/tmp/a.c:3
- // 0x1f1b: popl %eax
- // and record the temporary stack movements if the CFA is not expressed in terms of ebp.
+ UnwindPlan::Row::RegisterLocation regloc;
- non_prologue_insn_count++;
-loopnext:
- m_cur_insn.SetOffset (m_cur_insn.GetOffset() + insn_len);
- current_func_text_offset += insn_len;
- }
+ // 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->GetCFAOffset()));
- // Now look at the byte at the end of the AddressRange for a limited attempt at describing the
- // epilogue. We're looking for the sequence
+ row->SetRegisterInfo (lldb_regno, regloc);
- // [ 0x5d ] mov %rbp, %rsp (aka pop %rbp)
- // [ 0xc3 ] ret
+ row_updated = true;
+ }
- // or
+ else if (sub_rsp_pattern_p (stack_offset))
+ {
+ current_sp_bytes_offset_from_cfa += stack_offset;
+ if (row->GetCFARegister() == m_lldb_sp_regnum)
+ {
+ row->SetCFAOffset (current_sp_bytes_offset_from_cfa);
+ row_updated = true;
+ }
+ }
- // [ 0x5d ] mov %rbp, %rsp (aka pop %rbp)
- // [ 0xe9 xx xx xx xx ] jmp objc_retainAutoreleaseReturnValue (this is sometimes the final insn in the function)
+ else if (add_rsp_pattern_p (stack_offset))
+ {
+ current_sp_bytes_offset_from_cfa -= stack_offset;
+ if (row->GetCFARegister() == m_lldb_sp_regnum)
+ {
+ row->SetCFAOffset (current_sp_bytes_offset_from_cfa);
+ row_updated = true;
+ }
+ in_epilogue = true;
+ }
- // or
+ else if (ret_pattern_p () && prologue_completed_row.get())
+ {
+ // Reinstate the saved prologue setup for any instructions
+ // that come after the ret instruction
- // [ 0x5d ] mov %rbp, %rsp (aka pop %rbp)
- // [ 0xc3 ] ret
- // [ 0xe8 xx xx xx xx ] call __stack_chk_fail (this is sometimes the final insn in the function)
+ 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;
- // We want to add a Row describing how to unwind when we're stopped on the 'ret' instruction where the
- // CFA is no longer defined in terms of rbp, but is now defined in terms of rsp like on function entry.
- // (or the 'jmp' instruction in the second case)
+ 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];
+ }
- uint64_t ret_insn_offset = LLDB_INVALID_ADDRESS;
- Address end_of_fun(m_func_bounds.GetBaseAddress());
- end_of_fun.SetOffset (end_of_fun.GetOffset() + m_func_bounds.GetByteSize());
+ in_epilogue = true;
+ row_updated = true;
+ }
- if (m_func_bounds.GetByteSize() > 7)
- {
- uint8_t bytebuf[7];
- Address last_seven_bytes(end_of_fun);
- last_seven_bytes.SetOffset (last_seven_bytes.GetOffset() - 7);
- if (target->ReadMemory (last_seven_bytes, prefer_file_cache, bytebuf, 7,
- error) != static_cast<size_t>(-1))
+ // 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 ())
{
- if (bytebuf[5] == 0x5d && bytebuf[6] == 0xc3) // mov & ret
- {
- ret_insn_offset = m_func_bounds.GetByteSize() - 1;
- }
- else if (bytebuf[1] == 0x5d && bytebuf[2] == 0xe9) // mov & jmp
- {
- // When the pc is sitting on the 'jmp' instruction, we have the same
- // unwind state as if it was sitting on a 'ret' instruction.
- ret_insn_offset = m_func_bounds.GetByteSize() - 5;
- }
- else if (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3 && bytebuf[2] == 0xe8) // mov & ret & call
+ current_sp_bytes_offset_from_cfa += m_wordsize;
+ if (row->GetCFARegister() == m_lldb_sp_regnum)
{
- ret_insn_offset = m_func_bounds.GetByteSize() - 6;
+ row->SetCFAOffset (current_sp_bytes_offset_from_cfa);
+ row_updated = true;
}
}
- }
- else if (m_func_bounds.GetByteSize() > 2)
- {
- uint8_t bytebuf[2];
- Address last_two_bytes(end_of_fun);
- last_two_bytes.SetOffset (last_two_bytes.GetOffset() - 2);
- if (target->ReadMemory (last_two_bytes, prefer_file_cache, bytebuf, 2,
- error) != static_cast<size_t>(-1))
+
+ if (row_updated)
{
- if (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3) // mov & ret
+ if (current_func_text_offset + insn_len < m_func_bounds.GetByteSize())
{
- ret_insn_offset = m_func_bounds.GetByteSize() - 1;
+ 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 (ret_insn_offset != LLDB_INVALID_ADDRESS)
- {
- // Create a fresh, empty Row and RegisterLocation - don't mention any other registers
- UnwindPlan::RowSP epi_row(new UnwindPlan::Row);
- UnwindPlan::Row::RegisterLocation epi_regloc;
-
- // When the ret instruction is about to be executed, here's our state
- epi_row->SetOffset (ret_insn_offset);
- epi_row->SetCFARegister (m_lldb_sp_regnum);
- epi_row->SetCFAOffset (m_wordsize);
+ 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);
- // caller's stack pointer value before the call insn is the CFA address
- epi_regloc.SetIsCFAPlusOffset (0);
- epi_row->SetRegisterInfo (m_lldb_sp_regnum, epi_regloc);
+ 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];
+ }
+ }
- // saved instruction pointer can be found at CFA - wordsize
- epi_regloc.SetAtCFAPlusOffset (-m_wordsize);
- epi_row->SetRegisterInfo (m_lldb_ip_regnum, epi_regloc);
+ // 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;
+ }
- unwind_plan.AppendRow (epi_row);
+ m_cur_insn.SetOffset (m_cur_insn.GetOffset() + insn_len);
+ current_func_text_offset += insn_len;
}
unwind_plan.SetSourceName ("assembly insn profiling");
@@ -902,12 +908,20 @@ AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, Unwin
if (cfa_reg != m_lldb_sp_regnum || first_row->GetCFAOffset() != 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;
@@ -930,10 +944,29 @@ AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, Unwin
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)
{
@@ -1032,6 +1065,11 @@ AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, Unwin
unwind_plan_updated = true;
continue;
}
+ if (ret_pattern_p ())
+ {
+ reinstate_unwind_state = true;
+ continue;
+ }
}
else if (cfa_reg == m_lldb_fp_regnum)
{
@@ -1053,6 +1091,7 @@ AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, Unwin
UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
unwind_plan.InsertRow (new_row);
unwind_plan_updated = true;
+ reinstate_unwind_state = true;
continue;
}
}
@@ -1074,6 +1113,7 @@ AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, Unwin
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;
}
@@ -1239,17 +1279,130 @@ UnwindAssembly_x86::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& func, Th
bool
UnwindAssembly_x86::AugmentUnwindPlanFromCallSite (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.augment_unwind_plan_from_call_site (func, 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->GetCFAType() != UnwindPlan::Row::CFAType::CFAIsRegisterPlusOffset
+ || RegisterNumber (thread, unwind_plan.GetRegisterKind(), first_row->GetCFARegister()) != sp_regnum
+ || first_row->GetCFAOffset() != 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->GetCFAType() == last_row->GetCFAType()
+ && first_row->GetCFARegister() == last_row->GetCFARegister()
+ && first_row->GetCFAOffset() == last_row->GetCFAOffset())
+ {
+ // 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;
+ }
+ }
+ }
+ }
+ }
+
+ 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);
+ }
+
+ return false;
}
bool
UnwindAssembly_x86::GetFastUnwindPlan (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_fast_unwind_plan (func, 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};
+
+ 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);
+ }
+ }
+ }
+ }
+ return false;
}
bool
OpenPOWER on IntegriCloud