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/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); +}