aboutsummaryrefslogtreecommitdiff
path: root/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp')
-rw-r--r--source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp475
1 files changed, 405 insertions, 70 deletions
diff --git a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
index 1768798f71d4..32a21d2b8bb8 100644
--- a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
+++ b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
@@ -27,12 +27,14 @@
using namespace lldb;
using namespace lldb_private;
-enum CPU {
+enum CPU
+{
k_i386,
k_x86_64
};
-enum i386_register_numbers {
+enum i386_register_numbers
+{
k_machine_eax = 0,
k_machine_ecx = 1,
k_machine_edx = 2,
@@ -44,7 +46,8 @@ enum i386_register_numbers {
k_machine_eip = 8
};
-enum x86_64_register_numbers {
+enum x86_64_register_numbers
+{
k_machine_rax = 0,
k_machine_rcx = 1,
k_machine_rdx = 2,
@@ -64,13 +67,15 @@ enum x86_64_register_numbers {
k_machine_rip = 16
};
-struct regmap_ent {
+struct regmap_ent
+{
const char *name;
int machine_regno;
int lldb_regno;
};
-static struct regmap_ent i386_register_map[] = {
+static struct regmap_ent i386_register_map[] =
+{
{"eax", k_machine_eax, -1},
{"ecx", k_machine_ecx, -1},
{"edx", k_machine_edx, -1},
@@ -82,11 +87,12 @@ static struct regmap_ent i386_register_map[] = {
{"eip", k_machine_eip, -1}
};
-const int size_of_i386_register_map = sizeof (i386_register_map) / sizeof (struct regmap_ent);
+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[] = {
+static struct regmap_ent x86_64_register_map[] =
+{
{"rax", k_machine_rax, -1},
{"rcx", k_machine_rcx, -1},
{"rdx", k_machine_rdx, -1},
@@ -106,7 +112,7 @@ static struct regmap_ent x86_64_register_map[] = {
{"rip", k_machine_rip, -1}
};
-const int size_of_x86_64_register_map = sizeof (x86_64_register_map) / sizeof (struct regmap_ent);
+const int size_of_x86_64_register_map = llvm::array_lengthof (x86_64_register_map);
static int x86_64_register_map_initialized = 0;
@@ -114,7 +120,8 @@ static int x86_64_register_map_initialized = 0;
// AssemblyParse_x86 local-file class definition & implementation functions
//-----------------------------------------------------------------------------------------------
-class AssemblyParse_x86 {
+class AssemblyParse_x86
+{
public:
AssemblyParse_x86 (const ExecutionContext &exe_ctx, int cpu, ArchSpec &arch, AddressRange func);
@@ -123,6 +130,8 @@ public:
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);
bool find_first_non_prologue_insn (Address &address);
@@ -135,9 +144,14 @@ private:
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 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 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);
@@ -149,13 +163,13 @@ private:
Address m_cur_insn;
uint8_t m_cur_insn_bytes[kMaxInstructionByteSize];
- int m_machine_ip_regnum;
- int m_machine_sp_regnum;
- int m_machine_fp_regnum;
+ uint32_t m_machine_ip_regnum;
+ uint32_t m_machine_sp_regnum;
+ uint32_t m_machine_fp_regnum;
- int m_lldb_ip_regnum;
- int m_lldb_sp_regnum;
- int m_lldb_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;
@@ -166,16 +180,16 @@ private:
};
AssemblyParse_x86::AssemblyParse_x86 (const ExecutionContext &exe_ctx, int cpu, ArchSpec &arch, AddressRange func) :
- m_exe_ctx (exe_ctx),
- m_func_bounds(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_ip_regnum (LLDB_INVALID_REGNUM),
m_lldb_sp_regnum (LLDB_INVALID_REGNUM),
m_lldb_fp_regnum (LLDB_INVALID_REGNUM),
- m_wordsize (-1),
+ m_wordsize (-1),
m_cpu(cpu),
m_arch(arch)
{
@@ -242,8 +256,8 @@ AssemblyParse_x86::AssemblyParse_x86 (const ExecutionContext &exe_ctx, int cpu,
m_lldb_ip_regnum = lldb_regno;
}
- m_disasm_context = ::LLVMCreateDisasm(m_arch.GetTriple().getTriple().c_str(),
- (void*)this,
+ m_disasm_context = ::LLVMCreateDisasm(m_arch.GetTriple().getTriple().c_str(),
+ (void*)this,
/*TagType=*/1,
NULL,
NULL);
@@ -254,7 +268,7 @@ AssemblyParse_x86::~AssemblyParse_x86 ()
::LLVMDisasmDispose(m_disasm_context);
}
-// This function expects an x86 native register number (i.e. the bits stripped out of the
+// This function expects an x86 native register number (i.e. the bits stripped out of the
// actual instruction), not an lldb register number.
bool
@@ -262,7 +276,8 @@ AssemblyParse_x86::nonvolatile_reg_p (int machine_regno)
{
if (m_cpu == k_i386)
{
- switch (machine_regno) {
+ 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:
@@ -275,7 +290,8 @@ AssemblyParse_x86::nonvolatile_reg_p (int machine_regno)
}
if (m_cpu == k_x86_64)
{
- switch (machine_regno) {
+ 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
@@ -292,7 +308,7 @@ AssemblyParse_x86::nonvolatile_reg_p (int machine_regno)
}
-// Macro to detect if this is a REX mode prefix byte.
+// 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)
@@ -302,7 +318,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)
return true;
@@ -318,9 +335,20 @@ bool AssemblyParse_x86::push_0_pattern_p ()
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;
+ 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 () {
+bool AssemblyParse_x86::mov_rsp_rbp_pattern_p ()
+{
uint8_t *p = m_cur_insn_bytes;
if (m_wordsize == 8 && *p == 0x48)
p++;
@@ -331,57 +359,127 @@ bool AssemblyParse_x86::mov_rsp_rbp_pattern_p () {
return false;
}
-// subq $0x20, %rsp
-bool AssemblyParse_x86::sub_rsp_pattern_p (int& amount) {
+// 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) {
+ if (*p == 0x83 && *(p + 1) == 0xec)
+ {
amount = (int8_t) *(p + 2);
return true;
}
// 32-bit immediate operand
- if (*p == 0x81 && *(p + 1) == 0xec) {
+ if (*p == 0x81 && *(p + 1) == 0xec)
+ {
+ amount = (int32_t) extract_4 (p + 2);
+ return true;
+ }
+ 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;
}
- // Not handled: [0x83 0xc4] for imm8 with neg values
- // [0x81 0xc4] for imm32 with neg values
return false;
}
// pushq %rbx
-// pushl $ebx
-bool AssemblyParse_x86::push_reg_p (int& regno) {
+// 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) {
+ if (m_wordsize == 8 && *p == 0x41)
+ {
regno_prefix_bit = 1 << 3;
p++;
}
- if (*p >= 0x50 && *p <= 0x57) {
+ if (*p >= 0x50 && *p <= 0x57)
+ {
regno = (*p - 0x50) | regno_prefix_bit;
return true;
}
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;
+ }
+ 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);
+}
+
+// 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]
-bool AssemblyParse_x86::mov_reg_to_local_stack_frame_p (int& regno, int& rbp_offset) {
+
+// 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)) {
+ 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) {
+ 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;
@@ -389,12 +487,13 @@ bool AssemblyParse_x86::mov_reg_to_local_stack_frame_p (int& regno, int& rbp_off
p++;
}
- if (*p == 0x89) {
+ 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
+ /* 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;
@@ -421,8 +520,8 @@ bool AssemblyParse_x86::mov_reg_to_local_stack_frame_p (int& regno, int& rbp_off
}
// ret [0xc9] or [0xc2 imm8] or [0xca imm8]
-bool
-AssemblyParse_x86::ret_pattern_p ()
+bool
+AssemblyParse_x86::ret_pattern_p ()
{
uint8_t *p = m_cur_insn_bytes;
if (*p == 0xc9 || *p == 0xc2 || *p == 0xca || *p == 0xc3)
@@ -439,7 +538,7 @@ AssemblyParse_x86::extract_4 (uint8_t *b)
return v;
}
-bool
+bool
AssemblyParse_x86::machine_regno_to_lldb_regno (int machine_regno, uint32_t &lldb_regno)
{
struct regmap_ent *ent;
@@ -479,11 +578,12 @@ AssemblyParse_x86::instruction_length (Address addr, int &length)
const bool prefer_file_cache = true;
Error error;
Target *target = m_exe_ctx.GetTargetPtr();
- if (target->ReadMemory (addr, prefer_file_cache, opcode_data.data(), max_op_byte_size, error) == -1)
+ if (target->ReadMemory (addr, prefer_file_cache, opcode_data.data(),
+ max_op_byte_size, error) == static_cast<size_t>(-1))
{
return false;
}
-
+
char out_string[512];
const addr_t pc = addr.GetFileAddress();
const size_t inst_size = ::LLVMDisasmInstruction (m_disasm_context,
@@ -498,7 +598,7 @@ AssemblyParse_x86::instruction_length (Address addr, int &length)
}
-bool
+bool
AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan)
{
UnwindPlan::RowSP row(new UnwindPlan::Row);
@@ -552,7 +652,8 @@ AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan)
// An unrecognized/junk instruction
break;
}
- if (target->ReadMemory (m_cur_insn, prefer_file_cache, m_cur_insn_bytes, insn_len, error) == -1)
+ 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;
@@ -631,7 +732,13 @@ AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan)
{
row->SetOffset (current_func_text_offset + insn_len);
UnwindPlan::Row::RegisterLocation regloc;
- regloc.SetAtCFAPlusOffset (-row->GetCFAOffset());
+
+ // 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()));
+
row->SetRegisterInfo (lldb_regno, regloc);
unwind_plan.AppendRow (row);
// Allocate a new Row, populate it with the existing Row contents.
@@ -679,45 +786,65 @@ loopnext:
m_cur_insn.SetOffset (m_cur_insn.GetOffset() + insn_len);
current_func_text_offset += insn_len;
}
-
+
// 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
- // [ 0x5d ] mov %rbp, %rsp
+ // [ 0x5d ] mov %rbp, %rsp (aka pop %rbp)
+ // [ 0xc3 ] ret
+
+ // or
+
+ // [ 0x5d ] mov %rbp, %rsp (aka pop %rbp)
+ // [ 0xe9 xx xx xx xx ] jmp objc_retainAutoreleaseReturnValue (this is sometimes the final insn in the function)
+
+ // or
+
+ // [ 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)
// 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)
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());
-
+
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) != -1)
+ if (target->ReadMemory (last_seven_bytes, prefer_file_cache, bytebuf, 7,
+ error) != static_cast<size_t>(-1))
{
- if (bytebuf[5] == 0x5d && bytebuf[6] == 0xc3) // mov, ret
+ if (bytebuf[5] == 0x5d && bytebuf[6] == 0xc3) // mov & ret
{
ret_insn_offset = m_func_bounds.GetByteSize() - 1;
}
- else if (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3 && bytebuf[2] == 0xe8) // mov, ret, call
+ 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
{
ret_insn_offset = m_func_bounds.GetByteSize() - 6;
}
}
- } else if (m_func_bounds.GetByteSize() > 2)
+ }
+ 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) != -1)
+ if (target->ReadMemory (last_two_bytes, prefer_file_cache, bytebuf, 2,
+ error) != static_cast<size_t>(-1))
{
- if (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3) // mov, ret
+ if (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3) // mov & ret
{
ret_insn_offset = m_func_bounds.GetByteSize() - 1;
}
@@ -734,7 +861,7 @@ loopnext:
epi_row->SetOffset (ret_insn_offset);
epi_row->SetCFARegister (m_lldb_sp_regnum);
epi_row->SetCFAOffset (m_wordsize);
-
+
// 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);
@@ -745,7 +872,7 @@ loopnext:
unwind_plan.AppendRow (epi_row);
}
-
+
unwind_plan.SetSourceName ("assembly insn profiling");
unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes);
@@ -753,13 +880,211 @@ loopnext:
return true;
}
-/* The "fast unwind plan" is valid for functions that follow the usual convention of
+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->GetCFARegister());
+ if (cfa_reg != m_lldb_sp_regnum || first_row->GetCFAOffset() != m_wordsize)
+ return false;
+
+ 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));
+ 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 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->GetCFARegister());
+ 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->SetCFAOffset (m_wordsize + row->GetCFAOffset());
+
+ 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->SetCFAOffset (m_wordsize + row->GetCFAOffset());
+
+ 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->SetCFAOffset (-m_wordsize + row->GetCFAOffset());
+
+ 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->SetCFAOffset (m_wordsize + row->GetCFAOffset());
+ 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->SetCFAOffset (-amount + row->GetCFAOffset());
+
+ 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->SetCFAOffset (amount + row->GetCFAOffset());
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow (new_row);
+ unwind_plan_updated = 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 ())
+ {
+ 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->SetCFARegister (first_row->GetCFARegister());
+ row->SetCFAOffset (m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow (new_row);
+ unwind_plan_updated = 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);
+ }
+ 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
+bool
AssemblyParse_x86::get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_plan)
{
UnwindPlan::RowSP row(new UnwindPlan::Row);
@@ -776,7 +1101,8 @@ AssemblyParse_x86::get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_
uint8_t bytebuf[4];
Error error;
const bool prefer_file_cache = true;
- if (target->ReadMemory (func.GetBaseAddress(), prefer_file_cache, bytebuf, sizeof (bytebuf), error) == -1)
+ 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};
@@ -821,7 +1147,7 @@ AssemblyParse_x86::get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_
newrow = new UnwindPlan::Row;
*newrow = *row.get();
row.reset(newrow);
-
+
// mov %rsp, %rbp has executed
row->SetCFARegister (m_lldb_fp_regnum);
row->SetCFAOffset (2 * m_wordsize);
@@ -839,7 +1165,7 @@ AssemblyParse_x86::get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_
return true;
}
-bool
+bool
AssemblyParse_x86::find_first_non_prologue_insn (Address &address)
{
m_cur_insn = m_func_bounds.GetBaseAddress ();
@@ -859,7 +1185,8 @@ AssemblyParse_x86::find_first_non_prologue_insn (Address &address)
// 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) == -1)
+ 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;
@@ -886,11 +1213,11 @@ AssemblyParse_x86::find_first_non_prologue_insn (Address &address)
//-----------------------------------------------------------------------------------------------
-// UnwindAssemblyParser_x86 method definitions
+// UnwindAssemblyParser_x86 method definitions
//-----------------------------------------------------------------------------------------------
-UnwindAssembly_x86::UnwindAssembly_x86 (const ArchSpec &arch, int cpu) :
- lldb_private::UnwindAssembly(arch),
+UnwindAssembly_x86::UnwindAssembly_x86 (const ArchSpec &arch, int cpu) :
+ lldb_private::UnwindAssembly(arch),
m_cpu(cpu),
m_arch(arch)
{
@@ -910,6 +1237,14 @@ 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
UnwindAssembly_x86::GetFastUnwindPlan (AddressRange& func, Thread& thread, UnwindPlan &unwind_plan)
{
ExecutionContext exe_ctx (thread.shared_from_this());