Index: packages/Python/lldbsuite/test/functionalities/unwind/nonabi/Makefile =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/unwind/nonabi/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +CFLAGS ?= -g -fomit-frame-pointer + +include $(LEVEL)/Makefile.rules Index: packages/Python/lldbsuite/test/functionalities/unwind/nonabi/TestNonABIFuncUnwind.py =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/unwind/nonabi/TestNonABIFuncUnwind.py @@ -0,0 +1,51 @@ +""" +Test that we can backtrace correctly from Non ABI functions on the stack +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class NonABIFuncUnwind(TestBase): + mydir = TestBase.compute_mydir(__file__) + + + @skipUnlessPlatform(['linux']) + @skipIf(archs=["aarch64", "arm", "i386", "i686"]) + def test (self): + """Test that we can backtrace correctly from Non ABI functions on the stack""" + self.build() + self.setTearDownCleanup() + + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + + self.assertTrue(target, VALID_TARGET) + + lldbutil.run_break_set_by_symbol (self, "func") + + process = target.LaunchSimple (["abc", "xyz"], None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.Launch() failed") + + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + stacktraces = lldbutil.print_stacktraces(process, string_buffer=True) + self.expect(stacktraces, exe=False, + substrs = ['(int)argc=3']) + + self.runCmd("thread step-inst") + + stacktraces = lldbutil.print_stacktraces(process, string_buffer=True) + self.expect(stacktraces, exe=False, + substrs = ['(int)argc=3']) \ No newline at end of file Index: packages/Python/lldbsuite/test/functionalities/unwind/nonabi/main.c =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/unwind/nonabi/main.c @@ -0,0 +1,20 @@ +void func() { + __asm__ ( + "pushq $0x10;" + ".cfi_def_cfa_offset 16;" + "jmp label;" + "movq $0x48, %rax;" +"label: subq $0x38, %rax;" + "movq $0x48, %rcx;" + "movq $0x48, %rdx;" + "movq $0x48, %rax;" + "popq %rax;" + ); + +} + + +int main(int argc, char const *argv[]) +{ + func(); +} \ No newline at end of file Index: source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h =================================================================== --- source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h +++ source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h @@ -72,6 +72,9 @@ uint32_t GetPluginVersion() override; + bool + AlwaysRelyOnEHUnwindInfo (lldb_private::SymbolContext &sym_ctx) override; + protected: /// Runtime linker rendezvous structure. DYLDRendezvous m_rendezvous; Index: source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp =================================================================== --- source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -105,6 +105,14 @@ } } +bool +DynamicLoaderPOSIXDYLD::AlwaysRelyOnEHUnwindInfo (SymbolContext &sym_ctx) +{ + if (sym_ctx.symbol->IsSynthetic()) + return true; + return DynamicLoader::AlwaysRelyOnEHUnwindInfo(sym_ctx); +} + void DynamicLoaderPOSIXDYLD::DidAttach() { Index: source/Plugins/Process/Utility/RegisterContextLLDB.cpp =================================================================== --- source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -210,12 +210,28 @@ m_frame_type = eNormalFrame; } + // We've set m_frame_type and m_sym_ctx before these calls. + + m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame (); + m_full_unwind_plan_sp = GetFullUnwindPlanForFrame (); + // If we were able to find a symbol/function, set addr_range to the bounds of that symbol/function. // else treat the current pc value as the start_pc and record no offset. if (addr_range.GetBaseAddress().IsValid()) { m_start_pc = addr_range.GetBaseAddress(); - if (m_current_pc.GetSection() == m_start_pc.GetSection()) + if (m_sym_ctx.symbol != nullptr && m_sym_ctx.symbol->IsSynthetic()) + { + // The current offset should be recalculated here. The m_current_offset is + // calculated from the base address of the symbol. The symbol can lie in the PLT + // (Procedure Linkage Table) i.e its a symbol stub for external call. In this case + // the base address for the unwindplan and the base address of the symbol maybe different, hence + // the m_current_offset will be wrong. + AddressRange unwind_address_range = m_full_unwind_plan_sp->GetAddressRange(); + if (unwind_address_range.ContainsFileAddress(m_current_pc)) + m_current_offset = m_current_pc.GetOffset() - unwind_address_range.GetBaseAddress().GetOffset(); + } + else if (m_current_pc.GetSection() == m_start_pc.GetSection()) { m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset(); } @@ -236,11 +252,6 @@ m_current_offset_backed_up_one = -1; } - // We've set m_frame_type and m_sym_ctx before these calls. - - m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame (); - m_full_unwind_plan_sp = GetFullUnwindPlanForFrame (); - UnwindPlan::RowSP active_row; lldb::RegisterKind row_register_kind = eRegisterKindGeneric; if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress (m_current_pc)) @@ -255,36 +266,14 @@ } } - if (!active_row.get()) - { - UnwindLogMsg ("could not find an unwindplan row for this frame's pc"); - m_frame_type = eNotAValidFrame; - return; - } - - if (!ReadCFAValueForRow (row_register_kind, active_row, m_cfa)) { // Try the fall back unwind plan since the // full unwind plan failed. - FuncUnwindersSP func_unwinders_sp; - UnwindPlanSP call_site_unwind_plan; bool cfa_status = false; + if (TryFallbackUnwindPlan()) + cfa_status = true; - if (m_sym_ctx_valid) - { - func_unwinders_sp = pc_module_sp->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx); - } - - if(func_unwinders_sp.get() != nullptr) - call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(), m_current_offset_backed_up_one); - - if (call_site_unwind_plan.get() != nullptr) - { - m_fallback_unwind_plan_sp = call_site_unwind_plan; - if(TryFallbackUnwindPlan()) - cfa_status = true; - } if (!cfa_status) { UnwindLogMsg ("could not read CFA value for first frame."); @@ -881,6 +870,8 @@ // call GetUnwindPlanAtCallSite() -- because CallSite may return an unwind plan sourced from // either eh_frame (that's what we intend) or compact unwind (this won't work) unwind_plan_sp = func_unwinders_sp->GetEHFrameUnwindPlan (process->GetTarget(), m_current_offset_backed_up_one); + m_fallback_unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (process->GetTarget(), m_thread, m_current_offset_backed_up_one); + if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc)) { UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan because the DynamicLoader suggested we prefer it", @@ -1608,8 +1599,8 @@ // If a compiler generated unwind plan failed, trying the arch default unwindplan // isn't going to do any better. - if (m_full_unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) - return false; +// if (m_full_unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) +// return false; // Get the caller's pc value and our own CFA value. @@ -1655,7 +1646,32 @@ m_full_unwind_plan_sp = m_fallback_unwind_plan_sp; - UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); + // The current offset should be recalculated here. The m_current_offset is + // calculated from the base address of the symbol. The symbol can lie in the PLT + // (Procedure Linkage Table) i.e its a symbol stub for external call. In this case + // the base address for the unwindplan and the base address of the symbol maybe different, hence + // the m_current_offset will be wrong. + + int fallback_offset = m_current_offset; + AddressRange fall_back_unwind_address_range = m_fallback_unwind_plan_sp->GetAddressRange(); + if (m_sym_ctx.symbol != nullptr && m_sym_ctx.symbol->IsSynthetic() && fall_back_unwind_address_range.ContainsFileAddress(m_current_pc)) + { + fallback_offset = m_current_pc.GetOffset() - fall_back_unwind_address_range.GetBaseAddress().GetOffset(); + } + else if (m_current_pc.GetSection() == m_start_pc.GetSection()) + { + fallback_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset(); + } + else if (m_current_pc.GetModule() == m_start_pc.GetModule()) + { + // This means that whatever symbol we kicked up isn't really correct + // --- we should not cross section boundaries ... We really should NULL out + // the function/symbol in this case unless there is a bad assumption + // here due to inlined functions? + fallback_offset = m_current_pc.GetFileAddress() - m_start_pc.GetFileAddress(); + } + + UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset (fallback_offset); if (active_row && active_row->GetCFAValue().GetValueType() != UnwindPlan::Row::CFAValue::unspecified) { @@ -1763,6 +1779,9 @@ const UnwindPlan::RowSP &row, addr_t &cfa_value) { + if (row.get() == nullptr) + return false; + RegisterValue reg_value; cfa_value = LLDB_INVALID_ADDRESS;