diff options
author | Jason Molenda <jmolenda@apple.com> | 2015-01-13 06:04:04 +0000 |
---|---|---|
committer | Jason Molenda <jmolenda@apple.com> | 2015-01-13 06:04:04 +0000 |
commit | 81b38e4468013332ea955b09612d6e0a9da44d1b (patch) | |
tree | 0a288d9c848df78cec7c6cba96db4931348a32ce | |
parent | 9455d776e4547e25df168543afdb8c94263efab1 (diff) |
Change the x86 assembly instruction unwind parser to
step through the complete function looking for any epilogue
instructions. If we find an epilogue sequence, re-instate
the correct unwind instructions if there is more code past
that epilogue -- this will correctly handle an x86 function
with multiple epilogues in it.
NB there is still a bug with the "eh_frame augmented"
UnwindPlans and mid-function epilogues. Looking at that next.
<rdar://problem/18863406>
git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@225770 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/lldb/Symbol/FuncUnwinders.h | 17 | ||||
-rw-r--r-- | include/lldb/Symbol/UnwindPlan.h | 5 | ||||
-rw-r--r-- | source/Commands/CommandObjectTarget.cpp | 84 | ||||
-rw-r--r-- | source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp | 299 | ||||
-rw-r--r-- | source/Symbol/UnwindPlan.cpp | 12 |
5 files changed, 216 insertions, 201 deletions
diff --git a/include/lldb/Symbol/FuncUnwinders.h b/include/lldb/Symbol/FuncUnwinders.h index 203503061..1e579c42a 100644 --- a/include/lldb/Symbol/FuncUnwinders.h +++ b/include/lldb/Symbol/FuncUnwinders.h @@ -84,10 +84,11 @@ public: Address GetPersonalityRoutinePtrAddress (Target &target); -private: - lldb::UnwindAssemblySP - GetUnwindAssemblyProfiler (); + + // The following methods to retrieve specific unwind plans should rarely be used. + // Instead, clients should ask for the *behavior* they are looking for, using one + // of the above UnwindPlan retrieval methods. lldb::UnwindPlanSP GetAssemblyUnwindPlan (Target &target, Thread &thread, int current_offset); @@ -102,13 +103,15 @@ private: GetCompactUnwindUnwindPlan (Target &target, int current_offset); lldb::UnwindPlanSP - GetFastUnwindPlan (Target &target, int current_offset); + GetArchDefaultUnwindPlan (Thread &thread); lldb::UnwindPlanSP - GetArchDefaultUnwindPlan (Target &target, int current_offset); + GetArchDefaultAtFuncEntryUnwindPlan (Thread &thread); - lldb::UnwindPlanSP - GetArchDefaultAtFuncEntryUnwindPlan (Target &target, int current_offset); +private: + + lldb::UnwindAssemblySP + GetUnwindAssemblyProfiler (); UnwindTable& m_unwind_table; AddressRange m_range; diff --git a/include/lldb/Symbol/UnwindPlan.h b/include/lldb/Symbol/UnwindPlan.h index ab846e145..c482739cb 100644 --- a/include/lldb/Symbol/UnwindPlan.h +++ b/include/lldb/Symbol/UnwindPlan.h @@ -257,7 +257,10 @@ public: void SetRegisterInfo (uint32_t reg_num, const RegisterLocation register_location); - + + void + RemoveRegisterInfo (uint32_t reg_num); + lldb::addr_t GetOffset() const { diff --git a/source/Commands/CommandObjectTarget.cpp b/source/Commands/CommandObjectTarget.cpp index d48a29bc0..0d9ffda1e 100644 --- a/source/Commands/CommandObjectTarget.cpp +++ b/source/Commands/CommandObjectTarget.cpp @@ -3744,45 +3744,85 @@ protected: if (func_unwinders_sp.get() == NULL) continue; - Address first_non_prologue_insn (func_unwinders_sp->GetFirstNonPrologueInsn(*target)); - if (first_non_prologue_insn.IsValid()) - { - result.GetOutputStream().Printf("First non-prologue instruction is at address 0x%" PRIx64 " or offset %" PRId64 " into the function.\n", first_non_prologue_insn.GetLoadAddress(target), first_non_prologue_insn.GetLoadAddress(target) - start_addr); - result.GetOutputStream().Printf ("\n"); - } + result.GetOutputStream().Printf("UNWIND PLANS for %s`%s (start addr 0x%" PRIx64 ")\n\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); UnwindPlanSP non_callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtNonCallSite(*target, *thread.get(), -1); if (non_callsite_unwind_plan.get()) { - result.GetOutputStream().Printf("Asynchronous (not restricted to call-sites) UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); - non_callsite_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); - result.GetOutputStream().Printf ("\n"); + result.GetOutputStream().Printf("Asynchronous (not restricted to call-sites) UnwindPlan is '%s'\n", non_callsite_unwind_plan->GetSourceName().AsCString()); } - UnwindPlanSP callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(*target, -1); if (callsite_unwind_plan.get()) { - result.GetOutputStream().Printf("Synchronous (restricted to call-sites) UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); - callsite_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); - result.GetOutputStream().Printf ("\n"); + result.GetOutputStream().Printf("Synchronous (restricted to call-sites) UnwindPlan is '%s'\n", callsite_unwind_plan->GetSourceName().AsCString()); + } + UnwindPlanSP fast_unwind_plan = func_unwinders_sp->GetUnwindPlanFastUnwind(*thread.get()); + if (fast_unwind_plan.get()) + { + result.GetOutputStream().Printf("Fast UnwindPlan is '%s'\n", fast_unwind_plan->GetSourceName().AsCString()); } - UnwindPlanSP arch_default_unwind_plan = func_unwinders_sp->GetUnwindPlanArchitectureDefault(*thread.get()); - if (arch_default_unwind_plan.get()) + result.GetOutputStream().Printf("\n"); + + UnwindPlanSP assembly_sp = func_unwinders_sp->GetAssemblyUnwindPlan(*target, *thread.get(), 0); + if (assembly_sp) { - result.GetOutputStream().Printf("Architecture default UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); - arch_default_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); - result.GetOutputStream().Printf ("\n"); + result.GetOutputStream().Printf("Assembly language inspection UnwindPlan:\n"); + assembly_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf("\n"); } + - UnwindPlanSP fast_unwind_plan = func_unwinders_sp->GetUnwindPlanFastUnwind(*thread.get()); - if (fast_unwind_plan.get()) + UnwindPlanSP ehframe_sp = func_unwinders_sp->GetEHFrameUnwindPlan(*target, 0); + if (ehframe_sp) + { + result.GetOutputStream().Printf("eh_frame UnwindPlan:\n"); + ehframe_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf("\n"); + } + + UnwindPlanSP ehframe_augmented_sp = func_unwinders_sp->GetEHFrameAugmentedUnwindPlan(*target, *thread.get(), 0); + if (ehframe_augmented_sp) { - result.GetOutputStream().Printf("Fast UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); + result.GetOutputStream().Printf("eh_frame augmented UnwindPlan:\n"); + ehframe_augmented_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf("\n"); + } + + UnwindPlanSP compact_unwind_sp = func_unwinders_sp->GetCompactUnwindUnwindPlan(*target, 0); + if (compact_unwind_sp) + { + result.GetOutputStream().Printf("Compact unwind UnwindPlan:\n"); + compact_unwind_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf("\n"); + } + + if (fast_unwind_plan) + { + result.GetOutputStream().Printf("Fast UnwindPlan:\n"); fast_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); - result.GetOutputStream().Printf ("\n"); + result.GetOutputStream().Printf("\n"); } + ABISP abi_sp = process->GetABI(); + if (abi_sp) + { + UnwindPlan arch_default(lldb::eRegisterKindGeneric); + if (abi_sp->CreateDefaultUnwindPlan (arch_default)) + { + result.GetOutputStream().Printf("Arch default UnwindPlan:\n"); + arch_default.Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf("\n"); + } + + UnwindPlan arch_entry(lldb::eRegisterKindGeneric); + if (abi_sp->CreateFunctionEntryUnwindPlan (arch_entry)) + { + result.GetOutputStream().Printf("Arch default at entry point UnwindPlan:\n"); + arch_entry.Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf("\n"); + } + } result.GetOutputStream().Printf ("\n"); } diff --git a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp index af70858e5..0cbc338b7 100644 --- a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp +++ b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp @@ -319,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) @@ -328,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) @@ -338,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) @@ -348,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) @@ -361,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) @@ -382,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) @@ -404,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; @@ -424,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; @@ -444,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) @@ -469,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; @@ -603,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; @@ -647,18 +657,29 @@ AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan) 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 + + bool in_epilogue = false; // we're in the middle of an epilogue sequence + 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 + 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 row_updated = false; + 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)) { @@ -668,232 +689,170 @@ 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) && 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) && saved_registers[machine_regno] == false) - { - saved_registers[machine_regno] = true; + current_sp_bytes_offset_from_cfa -= m_wordsize; - row->SetOffset (current_func_text_offset + insn_len); - UnwindPlan::Row::RegisterLocation regloc; + if (nonvolatile_reg_p (machine_regno) + && machine_regno_to_lldb_regno (machine_regno, lldb_regno) + && saved_registers[machine_regno] == true) + { + 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; - } - - // 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. - - non_prologue_insn_count++; -loopnext: - m_cur_insn.SetOffset (m_cur_insn.GetOffset() + insn_len); - current_func_text_offset += insn_len; - } + saved_registers[machine_regno] = true; - // 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 + UnwindPlan::Row::RegisterLocation regloc; - // [ 0x5d ] mov %rbp, %rsp (aka pop %rbp) - // [ 0xc3 ] ret + // 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())); - // or + row->SetRegisterInfo (lldb_regno, regloc); - // [ 0x5d ] mov %rbp, %rsp (aka pop %rbp) - // [ 0xe9 xx xx xx xx ] jmp objc_retainAutoreleaseReturnValue (this is sometimes the final insn in the function) + 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) - // [ 0xc3 ] ret - // [ 0xe8 xx xx xx xx ] call __stack_chk_fail (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 - // [ 0x0f 0x1f 0x44 xx xx ] nopl (%rax,%rax) (aka nop) + 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) + in_epilogue = true; + row_updated = true; + } - 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()); + // 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. - 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)) + if (row_updated) { - 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 + if (current_func_text_offset + insn_len < m_func_bounds.GetByteSize()) { - // 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 (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3 - && bytebuf[2] == 0x0f && bytebuf[3] == 0x1f && bytebuf[4] == 0x44) // mov & ret & nop - { - ret_insn_offset = m_func_bounds.GetByteSize() - 6; + 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); } } - } - 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 (in_epilogue == false && row_updated) { - if (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3) // mov & ret - { - ret_insn_offset = m_func_bounds.GetByteSize() - 1; - } + // 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); } - } - - 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); - - // 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); - - // 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"); diff --git a/source/Symbol/UnwindPlan.cpp b/source/Symbol/UnwindPlan.cpp index d32955e73..87d0f4942 100644 --- a/source/Symbol/UnwindPlan.cpp +++ b/source/Symbol/UnwindPlan.cpp @@ -168,7 +168,7 @@ UnwindPlan::Row::Dump (Stream& s, const UnwindPlan* unwind_plan, Thread* thread, if (base_addr != LLDB_INVALID_ADDRESS) s.Printf ("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset()); else - s.Printf ("0x%8.8" PRIx64 ": CFA=", GetOffset()); + s.Printf ("%4" PRId64 ": CFA=", GetOffset()); if (reg_info) s.Printf ("%s", reg_info->name); @@ -211,6 +211,16 @@ UnwindPlan::Row::GetRegisterInfo (uint32_t reg_num, UnwindPlan::Row::RegisterLoc } void +UnwindPlan::Row::RemoveRegisterInfo (uint32_t reg_num) +{ + collection::const_iterator pos = m_register_locations.find(reg_num); + if (pos != m_register_locations.end()) + { + m_register_locations.erase(pos); + } +} + +void UnwindPlan::Row::SetRegisterInfo (uint32_t reg_num, const UnwindPlan::Row::RegisterLocation register_location) { m_register_locations[reg_num] = register_location; |