diff options
author | emaste <emaste@FreeBSD.org> | 2014-11-26 16:48:12 +0000 |
---|---|---|
committer | emaste <emaste@FreeBSD.org> | 2014-11-26 16:48:12 +0000 |
commit | 0147dda7de9580d13778ecb4c9e92b83b7a63911 (patch) | |
tree | b16dc95f693ed59342b6141cd3fd9f59a6cd7e7e /contrib/llvm/tools/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp | |
parent | bfd4c39c61ae9b29542625bb12b6f7f4b1f8c727 (diff) | |
parent | 01ee1789d6aa7294e5966a97f8d29387f6f81699 (diff) | |
download | FreeBSD-src-0147dda7de9580d13778ecb4c9e92b83b7a63911.zip FreeBSD-src-0147dda7de9580d13778ecb4c9e92b83b7a63911.tar.gz |
Update LLDB snapshot to upstream r216948 (git 50f7fe44)
This is approximately "LLDB 3.5" although with a little bit of skew,
and will go along with the Clang 3.5 import.
Sponsored by: DARPA, AFRL
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.cpp | 475 |
1 files changed, 405 insertions, 70 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 1768798..32a21d2 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 @@ -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()); |