diff options
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 | 473 |
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 |