Files
clang-p2996/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp
Jason Molenda df552edb08 Update the CFA to use $sp when $fp is restored on arm64
In UnwindAssemblyInstEmulation we correctly recognize when a LDP
restores the fp & lr in an epilogue, and mark them as having the
caller's contents now, but we don't update the CFA register rule
at that point to indicate that the CFA is now calculated in terms
of $sp.  This doesn't impact the backtrace because the register
contents are all <same> now, but it can confuse the stepper when
the StackID changes mid-epilogue.

Differential Revision: https://reviews.llvm.org/D124492
rdar://92064415
2022-05-04 14:54:17 -07:00

843 lines
36 KiB
C++

//===-- TestArm64InstEmulation.cpp ----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "gtest/gtest.h"
#include <vector>
#include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/AddressRange.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Target/UnwindAssembly.h"
#include "lldb/Utility/ArchSpec.h"
#include "Plugins/Disassembler/LLVMC/DisassemblerLLVMC.h"
#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h"
#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
#include "llvm/Support/TargetSelect.h"
using namespace lldb;
using namespace lldb_private;
class TestArm64InstEmulation : public testing::Test {
public:
static void SetUpTestCase();
static void TearDownTestCase();
// virtual void SetUp() override { }
// virtual void TearDown() override { }
protected:
};
void TestArm64InstEmulation::SetUpTestCase() {
llvm::InitializeAllTargets();
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllDisassemblers();
DisassemblerLLVMC::Initialize();
EmulateInstructionARM64::Initialize();
}
void TestArm64InstEmulation::TearDownTestCase() {
DisassemblerLLVMC::Terminate();
EmulateInstructionARM64::Terminate();
}
TEST_F(TestArm64InstEmulation, TestSimpleDarwinFunction) {
ArchSpec arch("arm64-apple-ios10");
std::unique_ptr<UnwindAssemblyInstEmulation> engine(
static_cast<UnwindAssemblyInstEmulation *>(
UnwindAssemblyInstEmulation::CreateInstance(arch)));
ASSERT_NE(nullptr, engine);
UnwindPlan::RowSP row_sp;
AddressRange sample_range;
UnwindPlan unwind_plan(eRegisterKindLLDB);
UnwindPlan::Row::RegisterLocation regloc;
// 'int main() { }' compiled for arm64-apple-ios with clang
uint8_t data[] = {
0xfd, 0x7b, 0xbf, 0xa9, // 0xa9bf7bfd : stp x29, x30, [sp, #-0x10]!
0xfd, 0x03, 0x00, 0x91, // 0x910003fd : mov x29, sp
0xff, 0x43, 0x00, 0xd1, // 0xd10043ff : sub sp, sp, #0x10
0xbf, 0x03, 0x00, 0x91, // 0x910003bf : mov sp, x29
0xfd, 0x7b, 0xc1, 0xa8, // 0xa8c17bfd : ldp x29, x30, [sp], #16
0xc0, 0x03, 0x5f, 0xd6, // 0xd65f03c0 : ret
};
// UnwindPlan we expect:
// row[0]: 0: CFA=sp +0 =>
// row[1]: 4: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
// row[2]: 8: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]
// row[2]: 16: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
// row[3]: 20: CFA=sp +0 => fp= <same> lr= <same>
sample_range = AddressRange(0x1000, sizeof(data));
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
sample_range, data, sizeof(data), unwind_plan));
// CFA=sp +0
row_sp = unwind_plan.GetRowForFunctionOffset(0);
EXPECT_EQ(0ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
// CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(4);
EXPECT_EQ(4ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-16, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-8, regloc.GetOffset());
// CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(8);
EXPECT_EQ(8ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-16, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-8, regloc.GetOffset());
// CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
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);
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-16, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-8, regloc.GetOffset());
// CFA=sp +0 => fp= <same> lr= <same>
row_sp = unwind_plan.GetRowForFunctionOffset(20);
EXPECT_EQ(20ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
}
TEST_F(TestArm64InstEmulation, TestMediumDarwinFunction) {
ArchSpec arch("arm64-apple-ios10");
std::unique_ptr<UnwindAssemblyInstEmulation> engine(
static_cast<UnwindAssemblyInstEmulation *>(
UnwindAssemblyInstEmulation::CreateInstance(arch)));
ASSERT_NE(nullptr, engine);
UnwindPlan::RowSP row_sp;
AddressRange sample_range;
UnwindPlan unwind_plan(eRegisterKindLLDB);
UnwindPlan::Row::RegisterLocation regloc;
// disassembly of -[NSPlaceholderString initWithBytes:length:encoding:]
// from Foundation for iOS.
uint8_t data[] = {
0xf6, 0x57, 0xbd, 0xa9, // 0: 0xa9bd57f6 stp x22, x21, [sp, #-48]!
0xf4, 0x4f, 0x01, 0xa9, // 4: 0xa9014ff4 stp x20, x19, [sp, #16]
0xfd, 0x7b, 0x02, 0xa9, // 8: 0xa9027bfd stp x29, x30, [sp, #32]
0xfd, 0x83, 0x00, 0x91, // 12: 0x910083fd add x29, sp, #32
0xff, 0x43, 0x00, 0xd1, // 16: 0xd10043ff sub sp, sp, #16
// [... function body ...]
0x1f, 0x20, 0x03, 0xd5, // 20: 0xd503201f nop
0xbf, 0x83, 0x00, 0xd1, // 24: 0xd10083bf sub sp, x29, #32
0xfd, 0x7b, 0x42, 0xa9, // 28: 0xa9427bfd ldp x29, x30, [sp, #32]
0xf4, 0x4f, 0x41, 0xa9, // 32: 0xa9414ff4 ldp x20, x19, [sp, #16]
0xf6, 0x57, 0xc3, 0xa8, // 36: 0xa8c357f6 ldp x22, x21, [sp], #48
0x01, 0x16, 0x09, 0x14, // 40: 0x14091601 b 0x18f640524 ; symbol stub
// for: CFStringCreateWithBytes
};
// UnwindPlan we expect:
// 0: CFA=sp +0 =>
// 4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48]
// 8: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
// 12: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
// fp=[CFA-16] lr=[CFA-8]
// 16: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
// fp=[CFA-16] lr=[CFA-8]
// [... function body ...]
// 28: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
// fp=[CFA-16] lr=[CFA-8]
// 32: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] fp=
// <same> lr= <same>
// 36: CFA=sp+48 => x19= <same> x20= <same> x21=[CFA-40] x22=[CFA-48] fp=
// <same> lr= <same>
// 40: CFA=sp +0 => x19= <same> x20= <same> x21= <same> x22= <same> fp= <same>
// lr= <same>
sample_range = AddressRange(0x1000, sizeof(data));
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
sample_range, data, sizeof(data), unwind_plan));
// 0: CFA=sp +0 =>
row_sp = unwind_plan.GetRowForFunctionOffset(0);
EXPECT_EQ(0ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
// 4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48]
row_sp = unwind_plan.GetRowForFunctionOffset(4);
EXPECT_EQ(4ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-40, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-48, regloc.GetOffset());
// 8: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
row_sp = unwind_plan.GetRowForFunctionOffset(8);
EXPECT_EQ(8ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-24, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-32, regloc.GetOffset());
// 12: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
// fp=[CFA-16] lr=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(12);
EXPECT_EQ(12ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-16, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-8, regloc.GetOffset());
// 16: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
// fp=[CFA-16] lr=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(16);
EXPECT_EQ(16ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
// 28: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
// fp=[CFA-16] lr=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(28);
EXPECT_EQ(28ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset());
// 32: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] fp=
// <same> lr= <same>
row_sp = unwind_plan.GetRowForFunctionOffset(32);
EXPECT_EQ(32ull, row_sp->GetOffset());
// I'd prefer if these restored registers were cleared entirely instead of set
// to IsSame...
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc));
EXPECT_TRUE(regloc.IsSame());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc));
EXPECT_TRUE(regloc.IsSame());
// 36: CFA=sp+48 => x19= <same> x20= <same> x21=[CFA-40] x22=[CFA-48] fp=
// <same> lr= <same>
row_sp = unwind_plan.GetRowForFunctionOffset(36);
EXPECT_EQ(36ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc));
EXPECT_TRUE(regloc.IsSame());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc));
EXPECT_TRUE(regloc.IsSame());
// 40: CFA=sp +0 => x19= <same> x20= <same> x21= <same> x22= <same> fp= <same>
// lr= <same>
row_sp = unwind_plan.GetRowForFunctionOffset(40);
EXPECT_EQ(40ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc));
EXPECT_TRUE(regloc.IsSame());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc));
EXPECT_TRUE(regloc.IsSame());
}
TEST_F(TestArm64InstEmulation, TestFramelessThreeEpilogueFunction) {
ArchSpec arch("arm64-apple-ios10");
std::unique_ptr<UnwindAssemblyInstEmulation> engine(
static_cast<UnwindAssemblyInstEmulation *>(
UnwindAssemblyInstEmulation::CreateInstance(arch)));
ASSERT_NE(nullptr, engine);
UnwindPlan::RowSP row_sp;
AddressRange sample_range;
UnwindPlan unwind_plan(eRegisterKindLLDB);
UnwindPlan::Row::RegisterLocation regloc;
// disassembly of JSC::ARM64LogicalImmediate::findBitRange<16u>
// from JavaScriptcore for iOS.
uint8_t data[] = {
0x08, 0x3c, 0x0f, 0x53, // 0: 0x530f3c08 ubfx w8, w0, #15, #1
0x68, 0x00, 0x00, 0x39, // 4: 0x39000068 strb w8, [x3]
0x08, 0x3c, 0x40, 0xd2, // 8: 0xd2403c08 eor x8, x0, #0xffff
0x1f, 0x00, 0x71, 0xf2, // 12: 0xf271001f tst x0, #0x8000
// [...]
0x3f, 0x01, 0x0c, 0xeb, // 16: 0xeb0c013f cmp x9, x12
0x81, 0x00, 0x00, 0x54, // 20: 0x54000081 b.ne +34
0x5f, 0x00, 0x00, 0xb9, // 24: 0xb900005f str wzr, [x2]
0xe0, 0x03, 0x00, 0x32, // 28: 0x320003e0 orr w0, wzr, #0x1
0xc0, 0x03, 0x5f, 0xd6, // 32: 0xd65f03c0 ret
0x89, 0x01, 0x09, 0xca, // 36: 0xca090189 eor x9, x12, x9
// [...]
0x08, 0x05, 0x00, 0x11, // 40: 0x11000508 add w8, w8, #0x1
0x48, 0x00, 0x00, 0xb9, // 44: 0xb9000048 str w8, [x2]
0xe0, 0x03, 0x00, 0x32, // 48: 0x320003e0 orr w0, wzr, #0x1
0xc0, 0x03, 0x5f, 0xd6, // 52: 0xd65f03c0 ret
0x00, 0x00, 0x80, 0x52, // 56: 0x52800000 mov w0, #0x0
0xc0, 0x03, 0x5f, 0xd6, // 60: 0xd65f03c0 ret
};
// UnwindPlan we expect:
// 0: CFA=sp +0 =>
// (possibly with additional rows at offsets 36 and 56 saying the same thing)
sample_range = AddressRange(0x1000, sizeof(data));
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
sample_range, data, sizeof(data), unwind_plan));
// 0: CFA=sp +0 =>
row_sp = unwind_plan.GetRowForFunctionOffset(0);
EXPECT_EQ(0ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
row_sp = unwind_plan.GetRowForFunctionOffset(32);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc));
EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc));
EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc));
EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc));
EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x23_arm64, regloc));
EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x24_arm64, regloc));
EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x25_arm64, regloc));
EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x26_arm64, regloc));
EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x27_arm64, regloc));
EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x28_arm64, regloc));
EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc));
EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc));
row_sp = unwind_plan.GetRowForFunctionOffset(36);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
row_sp = unwind_plan.GetRowForFunctionOffset(52);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
row_sp = unwind_plan.GetRowForFunctionOffset(56);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
row_sp = unwind_plan.GetRowForFunctionOffset(60);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
}
TEST_F(TestArm64InstEmulation, TestRegisterSavedTwice) {
ArchSpec arch("arm64-apple-ios10");
std::unique_ptr<UnwindAssemblyInstEmulation> engine(
static_cast<UnwindAssemblyInstEmulation *>(
UnwindAssemblyInstEmulation::CreateInstance(arch)));
ASSERT_NE(nullptr, engine);
UnwindPlan::RowSP row_sp;
AddressRange sample_range;
UnwindPlan unwind_plan(eRegisterKindLLDB);
UnwindPlan::Row::RegisterLocation regloc;
// disassembly of mach_msg_sever_once from libsystem_kernel.dylib for iOS.
uint8_t data[] = {
0xfc, 0x6f, 0xba, 0xa9, // 0: 0xa9ba6ffc stp x28, x27, [sp, #-0x60]!
0xfa, 0x67, 0x01, 0xa9, // 4: 0xa90167fa stp x26, x25, [sp, #0x10]
0xf8, 0x5f, 0x02, 0xa9, // 8: 0xa9025ff8 stp x24, x23, [sp, #0x20]
0xf6, 0x57, 0x03, 0xa9, // 12: 0xa90357f6 stp x22, x21, [sp, #0x30]
0xf4, 0x4f, 0x04, 0xa9, // 16: 0xa9044ff4 stp x20, x19, [sp, #0x40]
0xfd, 0x7b, 0x05, 0xa9, // 20: 0xa9057bfd stp x29, x30, [sp, #0x50]
0xfd, 0x43, 0x01, 0x91, // 24: 0x910143fd add x29, sp, #0x50
0xff, 0xc3, 0x00, 0xd1, // 28: 0xd100c3ff sub sp, sp, #0x30
// mid-function, store x20 & x24 on the stack at a different location.
// this should not show up in the unwind plan; caller's values are not
// being saved to stack.
0xf8, 0x53, 0x01, 0xa9, // 32: 0xa90153f8 stp x24, x20, [sp, #0x10]
// mid-function, copy x20 and x19 off of the stack -- but not from
// their original locations. unwind plan should ignore this.
0xf4, 0x4f, 0x41, 0xa9, // 36: 0xa9414ff4 ldp x20, x19, [sp, #0x10]
// epilogue
0xbf, 0x43, 0x01, 0xd1, // 40: 0xd10143bf sub sp, x29, #0x50
0xfd, 0x7b, 0x45, 0xa9, // 44: 0xa9457bfd ldp x29, x30, [sp, #0x50]
0xf4, 0x4f, 0x44, 0xa9, // 48: 0xa9444ff4 ldp x20, x19, [sp, #0x40]
0xf6, 0x57, 0x43, 0xa9, // 52: 0xa94357f6 ldp x22, x21, [sp, #0x30]
0xf8, 0x5f, 0x42, 0xa9, // 56: 0xa9425ff8 ldp x24, x23, [sp, #0x20]
0xfa, 0x67, 0x41, 0xa9, // 60: 0xa94167fa ldp x26, x25, [sp, #0x10]
0xfc, 0x6f, 0xc6, 0xa8, // 64: 0xa8c66ffc ldp x28, x27, [sp], #0x60
0xc0, 0x03, 0x5f, 0xd6, // 68: 0xd65f03c0 ret
};
// UnwindPlan we expect:
// 0: CFA=sp +0 =>
// 4: CFA=sp+96 => x27=[CFA-88] x28=[CFA-96]
// 8: CFA=sp+96 => x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96]
// 12: CFA=sp+96 => x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80]
// x27=[CFA-88] x28=[CFA-96]
// 16: CFA=sp+96 => x21=[CFA-40] x22=[CFA-48] x23=[CFA-56] x24=[CFA-64]
// x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96]
// 20: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
// x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]
// x28=[CFA-96]
// 24: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
// x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]
// x28=[CFA-96] fp=[CFA-16] lr=[CFA-8]
// 28: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
// x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]
// x28=[CFA-96] fp=[CFA-16] lr=[CFA-8]
// 44: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
// x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]
// x28=[CFA-96] fp=[CFA-16] lr=[CFA-8]
// 48: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
// x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]
// x28=[CFA-96]
// 52: CFA=sp+96 => x21=[CFA-40] x22=[CFA-48] x23=[CFA-56] x24=[CFA-64]
// x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96]
// 56: CFA=sp+96 => x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80]
// x27=[CFA-88] x28=[CFA-96]
// 60: CFA=sp+96 => x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96]
// 64: CFA=sp+96 => x27=[CFA-88] x28=[CFA-96]
// 68: CFA=sp +0 =>
sample_range = AddressRange(0x1000, sizeof(data));
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
sample_range, data, sizeof(data), unwind_plan));
row_sp = unwind_plan.GetRowForFunctionOffset(36);
EXPECT_EQ(28ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-32, regloc.GetOffset());
row_sp = unwind_plan.GetRowForFunctionOffset(40);
EXPECT_EQ(28ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-32, regloc.GetOffset());
}
TEST_F(TestArm64InstEmulation, TestRegisterDoubleSpills) {
ArchSpec arch("arm64-apple-ios10");
std::unique_ptr<UnwindAssemblyInstEmulation> engine(
static_cast<UnwindAssemblyInstEmulation *>(
UnwindAssemblyInstEmulation::CreateInstance(arch)));
ASSERT_NE(nullptr, engine);
UnwindPlan::RowSP row_sp;
AddressRange sample_range;
UnwindPlan unwind_plan(eRegisterKindLLDB);
UnwindPlan::Row::RegisterLocation regloc;
// this file built with clang for iOS arch arm64 optimization -Os
// #include <stdio.h>
// double foo(double in) {
// double arr[32];
// for (int i = 0; i < 32; i++)
// arr[i] = in + i;
// for (int i = 2; i < 30; i++)
// arr[i] = ((((arr[i - 1] * arr[i - 2] * 0.2) + (0.7 * arr[i])) /
// ((((arr[i] * 0.73) + 0.65) * (arr[i - 1] + 0.2)) - ((arr[i + 1] + (arr[i]
// * 0.32) + 0.52) / 0.3) + (0.531 * arr[i - 2]))) + ((arr[i - 1] + 5) /
// ((arr[i + 2] + 0.4) / arr[i])) + (arr[5] * (0.17 + arr[7] * arr[i])) +
// ((i > 5 ? (arr[i - 3]) : arr[i - 1]) * 0.263) + (((arr[i - 2] + arr[i -
// 1]) * 0.3252) + 3.56) - (arr[i + 1] * 0.852311)) * ((arr[i] * 85234.1345)
// + (77342.451324 / (arr[i - 2] + arr[i - 1] - 73425341.33455))) + (arr[i]
// * 875712013.55) - (arr[i - 1] * 0.5555) - ((arr[i] * (arr[i + 1] +
// 17342834.44) / 8688200123.555)) + (arr[i - 2] + 8888.888);
// return arr[16];
//}
// int main(int argc, char **argv) { printf("%g\n", foo(argc)); }
// so function foo() uses enough registers that it spills the callee-saved
// floating point registers.
uint8_t data[] = {
// prologue
0xef, 0x3b, 0xba, 0x6d, // 0: 0x6dba3bef stp d15, d14, [sp, #-0x60]!
0xed, 0x33, 0x01, 0x6d, // 4: 0x6d0133ed stp d13, d12, [sp, #0x10]
0xeb, 0x2b, 0x02, 0x6d, // 8: 0x6d022beb stp d11, d10, [sp, #0x20]
0xe9, 0x23, 0x03, 0x6d, // 12: 0x6d0323e9 stp d9, d8, [sp, #0x30]
0xfc, 0x6f, 0x04, 0xa9, // 16: 0xa9046ffc stp x28, x27, [sp, #0x40]
0xfd, 0x7b, 0x05, 0xa9, // 20: 0xa9057bfd stp x29, x30, [sp, #0x50]
0xfd, 0x43, 0x01, 0x91, // 24: 0x910143fd add x29, sp, #0x50
0xff, 0x43, 0x04, 0xd1, // 28: 0xd10443ff sub sp, sp, #0x110
// epilogue
0xbf, 0x43, 0x01, 0xd1, // 32: 0xd10143bf sub sp, x29, #0x50
0xfd, 0x7b, 0x45, 0xa9, // 36: 0xa9457bfd ldp x29, x30, [sp, #0x50]
0xfc, 0x6f, 0x44, 0xa9, // 40: 0xa9446ffc ldp x28, x27, [sp, #0x40]
0xe9, 0x23, 0x43, 0x6d, // 44: 0x6d4323e9 ldp d9, d8, [sp, #0x30]
0xeb, 0x2b, 0x42, 0x6d, // 48: 0x6d422beb ldp d11, d10, [sp, #0x20]
0xed, 0x33, 0x41, 0x6d, // 52: 0x6d4133ed ldp d13, d12, [sp, #0x10]
0xef, 0x3b, 0xc6, 0x6c, // 56: 0x6cc63bef ldp d15, d14, [sp], #0x60
0xc0, 0x03, 0x5f, 0xd6, // 60: 0xd65f03c0 ret
};
// UnwindPlan we expect:
// 0: CFA=sp +0 =>
// 4: CFA=sp+96 => d14=[CFA-88] d15=[CFA-96]
// 8: CFA=sp+96 => d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
// 12: CFA=sp+96 => d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80]
// d14=[CFA-88] d15=[CFA-96]
// 16: CFA=sp+96 => d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64]
// d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
// 20: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] d8=[CFA-40] d9=[CFA-48]
// d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] d14=[CFA-88]
// d15=[CFA-96]
// 24: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8]
// d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72]
// d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
// 28: CFA=fp+16 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8]
// d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72]
// d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
// 36: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8]
// d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72]
// d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
// 40: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] d8=[CFA-40] d9=[CFA-48]
// d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] d14=[CFA-88]
// d15=[CFA-96]
// 44: CFA=sp+96 => d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64]
// d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
// 48: CFA=sp+96 => d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80]
// d14=[CFA-88] d15=[CFA-96]
// 52: CFA=sp+96 => d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
// 56: CFA=sp+96 => d14=[CFA-88] d15=[CFA-96]
// 60: CFA=sp +0 =>
sample_range = AddressRange(0x1000, sizeof(data));
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
sample_range, data, sizeof(data), unwind_plan));
// 28: CFA=fp+16 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8]
// d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72]
// d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
row_sp = unwind_plan.GetRowForFunctionOffset(28);
EXPECT_EQ(28ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d15_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-96, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d14_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-88, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d13_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-80, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d12_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-72, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d11_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-64, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d10_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-56, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d9_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-48, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d8_arm64, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-40, regloc.GetOffset());
// 60: CFA=sp +0 =>
row_sp = unwind_plan.GetRowForFunctionOffset(60);
EXPECT_EQ(60ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
if (row_sp->GetRegisterInfo(fpu_d8_arm64, regloc)) {
EXPECT_TRUE(regloc.IsSame());
}
if (row_sp->GetRegisterInfo(fpu_d9_arm64, regloc)) {
EXPECT_TRUE(regloc.IsSame());
}
if (row_sp->GetRegisterInfo(fpu_d10_arm64, regloc)) {
EXPECT_TRUE(regloc.IsSame());
}
if (row_sp->GetRegisterInfo(fpu_d11_arm64, regloc)) {
EXPECT_TRUE(regloc.IsSame());
}
if (row_sp->GetRegisterInfo(fpu_d12_arm64, regloc)) {
EXPECT_TRUE(regloc.IsSame());
}
if (row_sp->GetRegisterInfo(fpu_d13_arm64, regloc)) {
EXPECT_TRUE(regloc.IsSame());
}
if (row_sp->GetRegisterInfo(fpu_d14_arm64, regloc)) {
EXPECT_TRUE(regloc.IsSame());
}
if (row_sp->GetRegisterInfo(fpu_d15_arm64, regloc)) {
EXPECT_TRUE(regloc.IsSame());
}
if (row_sp->GetRegisterInfo(gpr_x27_arm64, regloc)) {
EXPECT_TRUE(regloc.IsSame());
}
if (row_sp->GetRegisterInfo(gpr_x28_arm64, regloc)) {
EXPECT_TRUE(regloc.IsSame());
}
}
TEST_F(TestArm64InstEmulation, TestCFARegisterTrackedAcrossJumps) {
ArchSpec arch("arm64-apple-ios10");
std::unique_ptr<UnwindAssemblyInstEmulation> engine(
static_cast<UnwindAssemblyInstEmulation *>(
UnwindAssemblyInstEmulation::CreateInstance(arch)));
ASSERT_NE(nullptr, engine);
UnwindPlan::RowSP row_sp;
AddressRange sample_range;
UnwindPlan unwind_plan(eRegisterKindLLDB);
UnwindPlan::Row::RegisterLocation regloc;
uint8_t data[] = {
// prologue
0xf4, 0x4f, 0xbe, 0xa9, // 0: 0xa9be4ff4 stp x20, x19, [sp, #-0x20]!
0xfd, 0x7b, 0x01, 0xa9, // 4: 0xa9017bfd stp x29, x30, [sp, #0x10]
0xfd, 0x43, 0x00, 0x91, // 8: 0x910043fd add x29, sp, #0x10
0xff, 0x43, 0x00, 0xd1, // 12: 0xd10043ff sub sp, sp, #0x10
// conditional branch over a mid-function epilogue
0xeb, 0x00, 0x00, 0x54, // 16: 0x540000eb b.lt <+44>
// mid-function epilogue
0x1f, 0x20, 0x03, 0xd5, // 20: 0xd503201f nop
0xe0, 0x03, 0x13, 0xaa, // 24: 0xaa1303e0 mov x0, x19
0xbf, 0x43, 0x00, 0xd1, // 28: 0xd10043bf sub sp, x29, #0x10
0xfd, 0x7b, 0x41, 0xa9, // 32: 0xa9417bfd ldp x29, x30, [sp, #0x10]
0xf4, 0x4f, 0xc2, 0xa8, // 36: 0xa8c24ff4 ldp x20, x19, [sp], #0x20
0xc0, 0x03, 0x5f, 0xd6, // 40: 0xd65f03c0 ret
// unwind state restored, we're using a frame pointer, let's change the
// stack pointer and see no change in how the CFA is computed
0x1f, 0x20, 0x03, 0xd5, // 44: 0xd503201f nop
0xff, 0x43, 0x00, 0xd1, // 48: 0xd10043ff sub sp, sp, #0x10
0x1f, 0x20, 0x03, 0xd5, // 52: 0xd503201f nop
// final epilogue
0xe0, 0x03, 0x13, 0xaa, // 56: 0xaa1303e0 mov x0, x19
0xbf, 0x43, 0x00, 0xd1, // 60: 0xd10043bf sub sp, x29, #0x10
0xfd, 0x7b, 0x41, 0xa9, // 64: 0xa9417bfd ldp x29, x30, [sp, #0x10]
0xf4, 0x4f, 0xc2, 0xa8, // 68: 0xa8c24ff4 ldp x20, x19, [sp], #0x20
0xc0, 0x03, 0x5f, 0xd6, // 72: 0xd65f03c0 ret
0x1f, 0x20, 0x03, 0xd5, // 52: 0xd503201f nop
};
// UnwindPlan we expect:
// row[0]: 0: CFA=sp +0 =>
// row[1]: 4: CFA=sp+32 => x19=[CFA-24] x20=[CFA-32]
// row[2]: 8: CFA=sp+32 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
// row[3]: 12: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
// row[4]: 32: CFA=sp+32 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
// row[5]: 36: CFA=sp+32 => x19=[CFA-24] x20=[CFA-32] fp= <same> lr= <same>
// row[6]: 40: CFA=sp +0 => x19= <same> x20= <same> fp= <same> lr= <same>
// row[7]: 44: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
// row[8]: 64: CFA=sp+32 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
// row[9]: 68: CFA=sp+32 => x19=[CFA-24] x20=[CFA-32] fp= <same> lr= <same>
// row[10]: 72: CFA=sp +0 => x19= <same> x20= <same> fp= <same> lr= <same>
// The specific bug we're looking for is this incorrect CFA definition,
// where the InstEmulation is using the $sp value mixed in with $fp,
// it looks like this:
//
// row[7]: 44: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
// row[8]: 52: CFA=fp+64 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
// row[9]: 68: CFA=fp+64 => x19=[CFA-24] x20=[CFA-32] fp= <same> lr= <same>
sample_range = AddressRange(0x1000, sizeof(data));
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
sample_range, data, sizeof(data), unwind_plan));
// Confirm CFA at mid-func epilogue 'ret' is $sp+0
row_sp = unwind_plan.GetRowForFunctionOffset(40);
EXPECT_EQ(40ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
// After the 'ret', confirm we're back to the correct CFA of $fp+16
row_sp = unwind_plan.GetRowForFunctionOffset(44);
EXPECT_EQ(44ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
// Confirm that we have no additional UnwindPlan rows before the
// real epilogue -- we still get the Row at offset 44.
row_sp = unwind_plan.GetRowForFunctionOffset(60);
EXPECT_EQ(44ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
// And in the epilogue, confirm that we start by switching back to
// defining the CFA in terms of $sp.
row_sp = unwind_plan.GetRowForFunctionOffset(64);
EXPECT_EQ(64ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(32, row_sp->GetCFAValue().GetOffset());
}
TEST_F(TestArm64InstEmulation, TestCFAResetToSP) {
ArchSpec arch("arm64-apple-ios15");
std::unique_ptr<UnwindAssemblyInstEmulation> engine(
static_cast<UnwindAssemblyInstEmulation *>(
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= <same> fp= <same> lr= <same>
// row[5]: 20: CFA=sp +0 => x0= <same> fp= <same> lr= <same>
// 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);
}