Add a first unit test for the arm64 instruction profiled unwind
plan generator. Fix a small bug in EmulateInstructionARM64::GetFramePointerRegister which was returning the stack pointer reg instead of fp, prevented the unwinder from recognizing the switch to using the fp in a function. (<rdar://problem/28663117>) Add a new eContextRestoreStackPointer context hint so that the arm64 emulator can flag when the frame pointer value is copied back in to the stack pointer and that should be used to compute the canonical frame address again in an epilogue sequence. (<rdar://problem/28704862>) Small changes to UnwindAssemblyInstEmulation to have a method we can call without a live process/thread/etc for unit tests. <rdar://problem/28663117> <rdar://problem/28704862> <rdar://problem/28509178> llvm-svn: 283847
This commit is contained in:
parent
8e7fe2c741
commit
6853cca1c9
|
@ -111,6 +111,10 @@ public:
|
|||
// Adjust the frame pointer for the current frame
|
||||
eContextSetFramePointer,
|
||||
|
||||
// Typically in an epilogue sequence. Copy the frame pointer back
|
||||
// into the stack pointer, use SP for CFA calculations again.
|
||||
eContextRestoreStackPointer,
|
||||
|
||||
// Add or subtract a value from a base address register (other than SP)
|
||||
eContextAdjustBaseRegister,
|
||||
|
||||
|
|
|
@ -916,6 +916,7 @@
|
|||
AF20F76A1AF18F9000751A6E /* ABISysV_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF20F7681AF18F9000751A6E /* ABISysV_arm64.cpp */; };
|
||||
AF20F7701AF1902900751A6E /* RegisterContextLinux_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF20F76E1AF1902900751A6E /* RegisterContextLinux_arm64.cpp */; };
|
||||
AF23B4DB19009C66003E2A58 /* FreeBSDSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF23B4D919009C66003E2A58 /* FreeBSDSignals.cpp */; };
|
||||
AF248A4D1DA71C77000B814D /* TestArm64InstEmulation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF248A4C1DA71C77000B814D /* TestArm64InstEmulation.cpp */; };
|
||||
AF254E31170CCC33007AE5C9 /* PlatformDarwinKernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF254E2F170CCC33007AE5C9 /* PlatformDarwinKernel.cpp */; };
|
||||
AF25AB26188F685C0030DEC3 /* AppleGetQueuesHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF25AB24188F685C0030DEC3 /* AppleGetQueuesHandler.cpp */; };
|
||||
AF26703A1852D01E00B6CC36 /* Queue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF2670381852D01E00B6CC36 /* Queue.cpp */; };
|
||||
|
@ -2922,6 +2923,7 @@
|
|||
AF20F76F1AF1902900751A6E /* RegisterContextLinux_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextLinux_arm64.h; path = Utility/RegisterContextLinux_arm64.h; sourceTree = "<group>"; };
|
||||
AF23B4D919009C66003E2A58 /* FreeBSDSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FreeBSDSignals.cpp; path = Utility/FreeBSDSignals.cpp; sourceTree = "<group>"; };
|
||||
AF23B4DA19009C66003E2A58 /* FreeBSDSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FreeBSDSignals.h; path = Utility/FreeBSDSignals.h; sourceTree = "<group>"; };
|
||||
AF248A4C1DA71C77000B814D /* TestArm64InstEmulation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TestArm64InstEmulation.cpp; path = UnwindAssembly/InstEmulation/TestArm64InstEmulation.cpp; sourceTree = "<group>"; };
|
||||
AF254E2F170CCC33007AE5C9 /* PlatformDarwinKernel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformDarwinKernel.cpp; sourceTree = "<group>"; };
|
||||
AF254E30170CCC33007AE5C9 /* PlatformDarwinKernel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformDarwinKernel.h; sourceTree = "<group>"; };
|
||||
AF25AB24188F685C0030DEC3 /* AppleGetQueuesHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleGetQueuesHandler.cpp; sourceTree = "<group>"; };
|
||||
|
@ -6252,6 +6254,14 @@
|
|||
name = "SysV-arm64";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AF248A4B1DA71C67000B814D /* InstEmulation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AF248A4C1DA71C77000B814D /* TestArm64InstEmulation.cpp */,
|
||||
);
|
||||
name = InstEmulation;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AF2BCA6518C7EFDE005B4526 /* JITLoader */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -6301,7 +6311,7 @@
|
|||
AFEC5FD31D94F9130076A480 /* UnwindAssembly */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AFEC5FD51D94F9380076A480 /* Testx86AssemblyInspectionEngine.cpp */,
|
||||
AF248A4B1DA71C67000B814D /* InstEmulation */,
|
||||
AFEC5FD41D94F9270076A480 /* x86 */,
|
||||
);
|
||||
name = UnwindAssembly;
|
||||
|
@ -6310,6 +6320,7 @@
|
|||
AFEC5FD41D94F9270076A480 /* x86 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AFEC5FD51D94F9380076A480 /* Testx86AssemblyInspectionEngine.cpp */,
|
||||
);
|
||||
name = x86;
|
||||
sourceTree = "<group>";
|
||||
|
@ -6911,6 +6922,7 @@
|
|||
23CB15351D66DA9300EDDDE1 /* UriParserTest.cpp in Sources */,
|
||||
23CB15361D66DA9300EDDDE1 /* FileSpecTest.cpp in Sources */,
|
||||
23E2E5251D90373D006F38BB /* ArchSpecTest.cpp in Sources */,
|
||||
AF248A4D1DA71C77000B814D /* TestArm64InstEmulation.cpp in Sources */,
|
||||
23CB15371D66DA9300EDDDE1 /* PythonTestSuite.cpp in Sources */,
|
||||
23E2E5291D9037D9006F38BB /* SymbolFilePDBTests.cpp in Sources */,
|
||||
23E2E5321D903832006F38BB /* BreakpointIDTest.cpp in Sources */,
|
||||
|
|
|
@ -497,7 +497,7 @@ uint32_t EmulateInstructionARM64::GetFramePointerRegisterNumber() const {
|
|||
if (m_arch.GetTriple().isAndroid())
|
||||
return LLDB_INVALID_REGNUM; // Don't use frame pointer on android
|
||||
|
||||
return arm64_dwarf::sp;
|
||||
return arm64_dwarf::fp;
|
||||
}
|
||||
|
||||
bool EmulateInstructionARM64::UsingAArch32() {
|
||||
|
@ -693,8 +693,13 @@ bool EmulateInstructionARM64::EmulateADDSUBImm(const uint32_t opcode) {
|
|||
if (arm64_dwarf::GetRegisterInfo(n, reg_info_Rn))
|
||||
context.SetRegisterPlusOffset(reg_info_Rn, imm);
|
||||
|
||||
if ((n == arm64_dwarf::sp || n == GetFramePointerRegisterNumber()) &&
|
||||
d == arm64_dwarf::sp && !setflags) {
|
||||
if (n == GetFramePointerRegisterNumber() && d == arm64_dwarf::sp &&
|
||||
!setflags) {
|
||||
// 'mov sp, fp' - common epilogue instruction, CFA is now in terms
|
||||
// of the stack pointer, instead of frame pointer.
|
||||
context.type = EmulateInstruction::eContextRestoreStackPointer;
|
||||
} else if ((n == arm64_dwarf::sp || n == GetFramePointerRegisterNumber()) &&
|
||||
d == arm64_dwarf::sp && !setflags) {
|
||||
context.type = EmulateInstruction::eContextAdjustStackPointer;
|
||||
} else if (d == GetFramePointerRegisterNumber() && n == arm64_dwarf::sp &&
|
||||
!setflags) {
|
||||
|
|
|
@ -34,6 +34,27 @@ using namespace lldb_private;
|
|||
|
||||
bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
|
||||
AddressRange &range, Thread &thread, UnwindPlan &unwind_plan) {
|
||||
std::vector<uint8_t> function_text(range.GetByteSize());
|
||||
ProcessSP process_sp(thread.GetProcess());
|
||||
if (process_sp) {
|
||||
Error error;
|
||||
const bool prefer_file_cache = true;
|
||||
if (process_sp->GetTarget().ReadMemory(
|
||||
range.GetBaseAddress(), prefer_file_cache, function_text.data(),
|
||||
range.GetByteSize(), error) != range.GetByteSize()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return GetNonCallSiteUnwindPlanFromAssembly(
|
||||
range, function_text.data(), function_text.size(), unwind_plan);
|
||||
}
|
||||
|
||||
bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
|
||||
AddressRange &range, uint8_t *opcode_data, size_t opcode_size,
|
||||
UnwindPlan &unwind_plan) {
|
||||
if (opcode_data == nullptr || opcode_size == 0)
|
||||
return false;
|
||||
|
||||
if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() &&
|
||||
m_inst_emulator_ap.get()) {
|
||||
|
||||
|
@ -46,18 +67,16 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
|
|||
if (unwind_plan.GetRowCount() == 0)
|
||||
return false;
|
||||
|
||||
ExecutionContext exe_ctx;
|
||||
thread.CalculateExecutionContext(exe_ctx);
|
||||
const bool prefer_file_cache = true;
|
||||
DisassemblerSP disasm_sp(Disassembler::DisassembleRange(
|
||||
m_arch, NULL, NULL, exe_ctx, range, prefer_file_cache));
|
||||
DisassemblerSP disasm_sp(Disassembler::DisassembleBytes(
|
||||
m_arch, NULL, NULL, range.GetBaseAddress(), opcode_data, opcode_size,
|
||||
99999, prefer_file_cache));
|
||||
|
||||
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
|
||||
|
||||
if (disasm_sp) {
|
||||
|
||||
m_range_ptr = ⦥
|
||||
m_thread_ptr = &thread;
|
||||
m_unwind_plan_ptr = &unwind_plan;
|
||||
|
||||
const uint32_t addr_byte_size = m_arch.GetAddressByteSize();
|
||||
|
@ -154,8 +173,8 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
|
|||
m_register_values = it->second.second;
|
||||
}
|
||||
|
||||
m_inst_emulator_ap->SetInstruction(
|
||||
inst->GetOpcode(), inst->GetAddress(), exe_ctx.GetTargetPtr());
|
||||
m_inst_emulator_ap->SetInstruction(inst->GetOpcode(),
|
||||
inst->GetAddress(), nullptr);
|
||||
|
||||
if (last_condition !=
|
||||
m_inst_emulator_ap->GetInstructionCondition()) {
|
||||
|
@ -253,11 +272,10 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
|
|||
|
||||
if (log && log->GetVerbose()) {
|
||||
StreamString strm;
|
||||
lldb::addr_t base_addr =
|
||||
range.GetBaseAddress().GetLoadAddress(thread.CalculateTarget().get());
|
||||
lldb::addr_t base_addr = range.GetBaseAddress().GetFileAddress();
|
||||
strm.Printf("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):",
|
||||
base_addr, base_addr + range.GetByteSize());
|
||||
unwind_plan.Dump(strm, &thread, base_addr);
|
||||
unwind_plan.Dump(strm, nullptr, base_addr);
|
||||
log->PutCString(strm.GetData());
|
||||
}
|
||||
return unwind_plan.GetRowCount() > 0;
|
||||
|
@ -620,6 +638,19 @@ bool UnwindAssemblyInstEmulation::WriteRegister(
|
|||
}
|
||||
break;
|
||||
|
||||
case EmulateInstruction::eContextRestoreStackPointer:
|
||||
if (m_fp_is_cfa) {
|
||||
m_fp_is_cfa = false;
|
||||
m_cfa_reg_info = *reg_info;
|
||||
const uint32_t cfa_reg_num =
|
||||
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 - reg_value.GetAsUInt64());
|
||||
m_curr_row_modified = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case EmulateInstruction::eContextAdjustStackPointer:
|
||||
// If we have created a frame using the frame pointer, don't follow
|
||||
// subsequent adjustments to the stack pointer.
|
||||
|
|
|
@ -28,6 +28,11 @@ public:
|
|||
lldb_private::AddressRange &func, lldb_private::Thread &thread,
|
||||
lldb_private::UnwindPlan &unwind_plan) override;
|
||||
|
||||
bool
|
||||
GetNonCallSiteUnwindPlanFromAssembly(lldb_private::AddressRange &func,
|
||||
uint8_t *opcode_data, size_t opcode_size,
|
||||
lldb_private::UnwindPlan &unwind_plan);
|
||||
|
||||
bool
|
||||
AugmentUnwindPlanFromCallSite(lldb_private::AddressRange &func,
|
||||
lldb_private::Thread &thread,
|
||||
|
@ -67,8 +72,8 @@ private:
|
|||
UnwindAssemblyInstEmulation(const lldb_private::ArchSpec &arch,
|
||||
lldb_private::EmulateInstruction *inst_emulator)
|
||||
: UnwindAssembly(arch), m_inst_emulator_ap(inst_emulator),
|
||||
m_range_ptr(NULL), m_thread_ptr(NULL), m_unwind_plan_ptr(NULL),
|
||||
m_curr_row(), m_cfa_reg_info(), m_fp_is_cfa(false), m_register_values(),
|
||||
m_range_ptr(NULL), m_unwind_plan_ptr(NULL), m_curr_row(),
|
||||
m_cfa_reg_info(), m_fp_is_cfa(false), m_register_values(),
|
||||
m_pushed_regs(), m_curr_row_modified(false),
|
||||
m_forward_branch_offset(0) {
|
||||
if (m_inst_emulator_ap.get()) {
|
||||
|
@ -130,7 +135,6 @@ private:
|
|||
|
||||
std::unique_ptr<lldb_private::EmulateInstruction> m_inst_emulator_ap;
|
||||
lldb_private::AddressRange *m_range_ptr;
|
||||
lldb_private::Thread *m_thread_ptr;
|
||||
lldb_private::UnwindPlan *m_unwind_plan_ptr;
|
||||
lldb_private::UnwindPlan::RowSP m_curr_row;
|
||||
typedef std::map<uint64_t, uint64_t> PushedRegisterToAddrMap;
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
add_subdirectory(x86)
|
||||
add_subdirectory(InstEmulation)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
add_lldb_unittest(InstEmulationTests TestArm64InstEmulation.cpp)
|
|
@ -0,0 +1,166 @@
|
|||
//===-- TestArm64InstEmulation.cpp ------------------------------------*- C++
|
||||
//-*-===//
|
||||
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h"
|
||||
#include "Utility/ARM64_DWARF_Registers.h"
|
||||
|
||||
#include "lldb/Core/Address.h"
|
||||
#include "lldb/Core/AddressRange.h"
|
||||
#include "lldb/Core/ArchSpec.h"
|
||||
#include "lldb/Symbol/UnwindPlan.h"
|
||||
#include "lldb/Target/UnwindAssembly.h"
|
||||
|
||||
#include "Plugins/Disassembler/llvm/DisassemblerLLVMC.h"
|
||||
#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.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:
|
||||
};
|
||||
|
||||
static void init() {
|
||||
llvm::InitializeAllTargets();
|
||||
llvm::InitializeAllAsmPrinters();
|
||||
llvm::InitializeAllTargetMCs();
|
||||
llvm::InitializeAllDisassemblers();
|
||||
DisassemblerLLVMC::Initialize();
|
||||
EmulateInstructionARM64::Initialize();
|
||||
}
|
||||
|
||||
static void terminate() {
|
||||
DisassemblerLLVMC::Terminate();
|
||||
EmulateInstructionARM64::Terminate();
|
||||
}
|
||||
|
||||
TEST_F(TestArm64InstEmulation, TestSimpleFunction) {
|
||||
|
||||
init();
|
||||
|
||||
ArchSpec arch("arm64-apple-ios10", nullptr);
|
||||
UnwindAssemblyInstEmulation *engine =
|
||||
static_cast<UnwindAssemblyInstEmulation *>(
|
||||
UnwindAssemblyInstEmulation::CreateInstance(arch));
|
||||
EXPECT_TRUE(engine != nullptr);
|
||||
if (engine == nullptr)
|
||||
return;
|
||||
|
||||
UnwindPlan::RowSP row_sp;
|
||||
AddressRange sample_range;
|
||||
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
||||
UnwindPlan::Row::RegisterLocation regloc;
|
||||
|
||||
// 'int main() { }' compiled for arm64-apple-macosx 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>
|
||||
|
||||
// But this is missing the setup of the frame pointer register (x29) and
|
||||
// restore of the same. This is a bug in the instruction profiler --
|
||||
// it won't work if we have a stack frame with a variable length array, or
|
||||
// an alloca style call where the stack frame size is not fixed. We need
|
||||
// to recognize the setup of the frame pointer register.
|
||||
|
||||
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(0, row_sp->GetOffset());
|
||||
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == arm64_dwarf::sp);
|
||||
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(4, row_sp->GetOffset());
|
||||
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == arm64_dwarf::sp);
|
||||
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
||||
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
||||
|
||||
EXPECT_TRUE(row_sp->GetRegisterInfo(arm64_dwarf::fp, regloc));
|
||||
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
||||
EXPECT_EQ(-16, regloc.GetOffset());
|
||||
|
||||
EXPECT_TRUE(row_sp->GetRegisterInfo(arm64_dwarf::lr, 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(8, row_sp->GetOffset());
|
||||
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == arm64_dwarf::fp);
|
||||
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
||||
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
||||
|
||||
EXPECT_TRUE(row_sp->GetRegisterInfo(arm64_dwarf::fp, regloc));
|
||||
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
||||
EXPECT_EQ(-16, regloc.GetOffset());
|
||||
|
||||
EXPECT_TRUE(row_sp->GetRegisterInfo(arm64_dwarf::lr, 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(16, row_sp->GetOffset());
|
||||
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == arm64_dwarf::sp);
|
||||
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
||||
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
||||
|
||||
EXPECT_TRUE(row_sp->GetRegisterInfo(arm64_dwarf::fp, regloc));
|
||||
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
||||
EXPECT_EQ(-16, regloc.GetOffset());
|
||||
|
||||
EXPECT_TRUE(row_sp->GetRegisterInfo(arm64_dwarf::lr, 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(20, row_sp->GetOffset());
|
||||
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == arm64_dwarf::sp);
|
||||
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
||||
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
|
||||
|
||||
terminate();
|
||||
}
|
Loading…
Reference in New Issue