diff --git a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp --- a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp +++ b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp @@ -614,6 +614,26 @@ m_curr_row->SetRegisterLocationToSame(reg_num, false /*must_replace*/); m_curr_row_modified = true; + + // FP has been restored to its original value, we are back + // to using SP to calculate the CFA. + if (m_fp_is_cfa) { + m_fp_is_cfa = false; + RegisterInfo sp_reg_info; + lldb::RegisterKind sp_reg_kind = eRegisterKindGeneric; + uint32_t sp_reg_num = LLDB_REGNUM_GENERIC_SP; + m_inst_emulator_up->GetRegisterInfo(sp_reg_kind, sp_reg_num, + sp_reg_info); + RegisterValue sp_reg_val; + if (GetRegisterValue(sp_reg_info, sp_reg_val)) { + m_cfa_reg_info = sp_reg_info; + const uint32_t cfa_reg_num = + sp_reg_info.kinds[m_unwind_plan_ptr->GetRegisterKind()]; + assert(cfa_reg_num != LLDB_INVALID_REGNUM); + m_curr_row->GetCFAValue().SetIsRegisterPlusOffset( + cfa_reg_num, m_initial_sp - sp_reg_val.GetAsUInt64()); + } + } } break; case EmulateInstruction::eInfoTypeISA: diff --git a/lldb/test/API/symbol_ondemand/shared_library/TestSharedLib.py b/lldb/test/API/symbol_ondemand/shared_library/TestSharedLib.py deleted file mode 100644 --- a/lldb/test/API/symbol_ondemand/shared_library/TestSharedLib.py +++ /dev/null @@ -1,157 +0,0 @@ -"""Test that types defined in shared libraries work correctly.""" - - -import lldb -import unittest2 -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -import lldbsuite.test.lldbutil as lldbutil - - -class SharedLibTestCase(TestBase): - - mydir = TestBase.compute_mydir(__file__) - - def setUp(self): - # Call super's setUp(). - TestBase.setUp(self) - # Find the line number to break inside main(). - self.source = "shared.c" - self.line = line_number(self.source, "// Set breakpoint 0 here.") - self.shlib_names = ["foo"] - - def common_setup(self): - # Run in synchronous mode - self.dbg.SetAsync(False) - self.runCmd("settings set symbols.load-on-demand true") - - # Create a target by the debugger. - self.target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) - self.assertTrue(self.target, VALID_TARGET) - - # Register our shared libraries for remote targets so they get - # automatically uploaded - self.environment = self.registerSharedLibrariesWithTarget( - self.target, self.shlib_names - ) - - ctx = self.platformContext - self.shared_lib_name = ctx.shlib_prefix + "foo." + ctx.shlib_extension - - def test_source_line_breakpoint(self): - self.build() - self.common_setup() - - lldbutil.run_break_set_by_file_and_line( - self, "foo.c", 4, num_expected_locations=1, loc_exact=True - ) - - # Now launch the process, and do not stop at entry point. - process = self.target.LaunchSimple( - None, self.environment, self.get_process_working_directory() - ) - self.assertTrue(process, PROCESS_IS_VALID) - - # The stop reason of the thread should be breakpoint. - self.expect( - "thread list", - STOPPED_DUE_TO_BREAKPOINT, - substrs=["stopped", "stop reason = breakpoint"], - ) - # The breakpoint should have a hit count of 1. - lldbutil.check_breakpoint(self, bpno=1, expected_hit_count=1) - - thread = process.GetSelectedThread() - stack_frames = lldbutil.get_stack_frames(thread) - self.assertGreater(len(stack_frames), 2) - - leaf_frame = stack_frames[0] - self.assertEqual("foo.c", leaf_frame.GetLineEntry().GetFileSpec().GetFilename()) - self.assertEqual(4, leaf_frame.GetLineEntry().GetLine()) - - parent_frame = stack_frames[1] - self.assertEqual( - "shared.c", parent_frame.GetLineEntry().GetFileSpec().GetFilename() - ) - self.assertEqual(7, parent_frame.GetLineEntry().GetLine()) - - def test_symbolic_breakpoint(self): - self.build() - self.common_setup() - - lldbutil.run_break_set_by_symbol( - self, "foo", sym_exact=True, num_expected_locations=1 - ) - - # Now launch the process, and do not stop at entry point. - process = self.target.LaunchSimple( - None, self.environment, self.get_process_working_directory() - ) - self.assertTrue(process, PROCESS_IS_VALID) - - # The stop reason of the thread should be breakpoint. - self.expect( - "thread list", - STOPPED_DUE_TO_BREAKPOINT, - substrs=["stopped", "stop reason = breakpoint"], - ) - # The breakpoint should have a hit count of 1. - lldbutil.check_breakpoint(self, bpno=1, expected_hit_count=1) - - thread = process.GetSelectedThread() - stack_frames = lldbutil.get_stack_frames(thread) - self.assertGreater(len(stack_frames), 2) - - leaf_frame = stack_frames[0] - self.assertEqual("foo.c", leaf_frame.GetLineEntry().GetFileSpec().GetFilename()) - self.assertEqual(4, leaf_frame.GetLineEntry().GetLine()) - - parent_frame = stack_frames[1] - self.assertEqual( - "shared.c", parent_frame.GetLineEntry().GetFileSpec().GetFilename() - ) - self.assertEqual(7, parent_frame.GetLineEntry().GetLine()) - - def test_global_variable_hydration(self): - self.build() - self.common_setup() - - lldbutil.run_break_set_by_file_and_line( - self, self.source, self.line, num_expected_locations=1, loc_exact=True - ) - - # Now launch the process, and do not stop at entry point. - process = self.target.LaunchSimple( - None, self.environment, self.get_process_working_directory() - ) - self.assertTrue(process, PROCESS_IS_VALID) - - # The stop reason of the thread should be breakpoint. - self.expect( - "thread list", - STOPPED_DUE_TO_BREAKPOINT, - substrs=["stopped", "stop reason = breakpoint"], - ) - - # The breakpoint should have a hit count of 1. - lldbutil.check_breakpoint(self, bpno=1, expected_hit_count=1) - - self.expect( - "target variable --shlib a.out", - "Breakpoint in a.out should have hydrated the debug info", - substrs=["global_shared = 897"], - ) - - self.expect( - "target variable --shlib " + self.shared_lib_name, - "shared library should not have debug info by default", - matching=False, - substrs=["global_foo"], - ) - - self.expect( - "target variable global_foo --shlib " + self.shared_lib_name, - "Match global_foo in symbol table should hydrate debug info", - matching=True, - substrs=["global_foo = 321"], - ) diff --git a/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp b/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp --- a/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp +++ b/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp @@ -778,3 +778,65 @@ EXPECT_EQ(32, row_sp->GetCFAValue().GetOffset()); } +TEST_F(TestArm64InstEmulation, TestCFAResetToSP) { + ArchSpec arch("arm64-apple-ios15"); + std::unique_ptr engine( + static_cast( + UnwindAssemblyInstEmulation::CreateInstance(arch))); + ASSERT_NE(nullptr, engine); + + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + UnwindPlan::Row::RegisterLocation regloc; + + // The called_from_nodebug() from TestStepNoDebug.py + // Most of the previous unit tests have $sp being set as + // $fp plus an offset, and the unwinder recognizes that + // as a CFA change. This codegen overwrites $fp and we + // need to know that CFA is now in terms of $sp. + uint8_t data[] = { + // prologue + 0xff, 0x83, 0x00, 0xd1, // 0: 0xd10083ff sub sp, sp, #0x20 + 0xfd, 0x7b, 0x01, 0xa9, // 4: 0xa9017bfd stp x29, x30, [sp, #0x10] + 0xfd, 0x43, 0x00, 0x91, // 8: 0x910043fd add x29, sp, #0x10 + + // epilogue + 0xfd, 0x7b, 0x41, 0xa9, // 12: 0xa9417bfd ldp x29, x30, [sp, #0x10] + 0xff, 0x83, 0x00, 0x91, // 16: 0x910083ff add sp, sp, #0x20 + 0xc0, 0x03, 0x5f, 0xd6, // 20: 0xd65f03c0 ret + }; + + // UnwindPlan we expect: + // row[0]: 0: CFA=sp +0 => + // row[1]: 4: CFA=sp+32 => + // row[2]: 8: CFA=sp+32 => fp=[CFA-16] lr=[CFA-8] + // row[3]: 12: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] + // row[4]: 16: CFA=sp+32 => x0= fp= lr= + // row[5]: 20: CFA=sp +0 => x0= fp= lr= + + // The specific issue we're testing for is after the + // ldp x29, x30, [sp, #0x10] + // when $fp and $lr have been restored to the original values, + // the CFA is now set in terms of the stack pointer. If it is + // left as being in terms of the frame pointer, $fp now has the + // caller function's $fp value and our StackID will be wrong etc. + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + sample_range, data, sizeof(data), unwind_plan)); + + // Confirm CFA before epilogue instructions is in terms of $fp + row_sp = unwind_plan.GetRowForFunctionOffset(12); + EXPECT_EQ(12ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + + // Confirm that after restoring $fp to caller's value, CFA is now in + // terms of $sp + row_sp = unwind_plan.GetRowForFunctionOffset(16); + EXPECT_EQ(16ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); +}