181 lines · cpp
1//===-- TestAArch64Emulator.cpp ------------------------------------------===//2 3//4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5// See https://llvm.org/LICENSE.txt for license information.6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7//8//===----------------------------------------------------------------------===//9 10#include "gtest/gtest.h"11 12#include "lldb/Core/Address.h"13#include "lldb/Core/Disassembler.h"14#include "lldb/Target/ExecutionContext.h"15#include "lldb/Utility/ArchSpec.h"16#include "lldb/Utility/RegisterValue.h"17 18#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h"19#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"20#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"21 22using namespace lldb;23using namespace lldb_private;24 25struct Arch64EmulatorTester : public EmulateInstructionARM64 {26 RegisterInfoPOSIX_arm64::GPR gpr;27 uint8_t memory[64] = {0};28 uint64_t memory_offset = 0;29 30 Arch64EmulatorTester()31 : EmulateInstructionARM64(ArchSpec("arm64-apple-ios")) {32 memset(&gpr, 0, sizeof(gpr));33 EmulateInstruction::SetCallbacks(ReadMemoryCallback, WriteMemoryCallback,34 ReadRegisterCallback,35 WriteRegisterCallback);36 }37 38 static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,39 const RegisterInfo *reg_info,40 RegisterValue ®_value) {41 auto *tester = static_cast<Arch64EmulatorTester *>(instruction);42 uint32_t reg = reg_info->kinds[eRegisterKindLLDB];43 if (reg >= gpr_x0_arm64 && reg <= gpr_x28_arm64) {44 reg_value.SetUInt64(tester->gpr.x[reg - gpr_x0_arm64]);45 return true;46 }47 if (reg >= gpr_w0_arm64 && reg <= gpr_w28_arm64) {48 reg_value.SetUInt32(tester->gpr.x[reg - gpr_w0_arm64]);49 return true;50 }51 switch (reg) {52 case gpr_fp_arm64:53 reg_value.SetUInt64(tester->gpr.fp);54 return true;55 case gpr_lr_arm64:56 reg_value.SetUInt64(tester->gpr.lr);57 return true;58 case gpr_sp_arm64:59 reg_value.SetUInt64(tester->gpr.sp);60 return true;61 case gpr_pc_arm64:62 reg_value.SetUInt64(tester->gpr.pc);63 return true;64 case gpr_cpsr_arm64:65 reg_value.SetUInt32(tester->gpr.cpsr);66 return true;67 default:68 return false;69 }70 }71 72 static bool WriteRegisterCallback(EmulateInstruction *instruction,73 void *baton, const Context &context,74 const RegisterInfo *reg_info,75 const RegisterValue ®_value) {76 auto *tester = static_cast<Arch64EmulatorTester *>(instruction);77 uint32_t reg = reg_info->kinds[eRegisterKindLLDB];78 if (reg >= gpr_x0_arm64 && reg <= gpr_x28_arm64) {79 tester->gpr.x[reg - gpr_x0_arm64] = reg_value.GetAsUInt64();80 return true;81 }82 if (reg >= gpr_w0_arm64 && reg <= gpr_w28_arm64) {83 tester->gpr.x[reg - gpr_w0_arm64] = reg_value.GetAsUInt32();84 return true;85 }86 switch (reg) {87 case gpr_fp_arm64:88 tester->gpr.fp = reg_value.GetAsUInt64();89 return true;90 case gpr_lr_arm64:91 tester->gpr.lr = reg_value.GetAsUInt64();92 return true;93 case gpr_sp_arm64:94 tester->gpr.sp = reg_value.GetAsUInt64();95 return true;96 case gpr_pc_arm64:97 tester->gpr.pc = reg_value.GetAsUInt64();98 return true;99 case gpr_cpsr_arm64:100 tester->gpr.cpsr = reg_value.GetAsUInt32();101 return true;102 default:103 return false;104 }105 }106 107 static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton,108 const Context &context, addr_t addr,109 void *dst, size_t length) {110 auto *tester = static_cast<Arch64EmulatorTester *>(instruction);111 assert(addr >= tester->memory_offset);112 assert(addr - tester->memory_offset + length <= sizeof(tester->memory));113 if (addr >= tester->memory_offset &&114 addr - tester->memory_offset + length <= sizeof(tester->memory)) {115 memcpy(dst, tester->memory + (addr - tester->memory_offset), length);116 return length;117 }118 return 0;119 };120 121 static size_t WriteMemoryCallback(EmulateInstruction *instruction,122 void *baton, const Context &context,123 addr_t addr, const void *dst,124 size_t length) {125 llvm_unreachable("implement when required");126 return 0;127 };128 129 static uint64_t AddWithCarry(uint32_t N, uint64_t x, uint64_t y, bool carry_in,130 EmulateInstructionARM64::ProcState &proc_state) {131 return EmulateInstructionARM64::AddWithCarry(N, x, y, carry_in, proc_state);132 }133};134 135class TestAArch64Emulator : public testing::Test {136public:137 static void SetUpTestCase();138 static void TearDownTestCase();139 140protected:141};142 143void TestAArch64Emulator::SetUpTestCase() {144 EmulateInstructionARM64::Initialize();145}146 147void TestAArch64Emulator::TearDownTestCase() {148 EmulateInstructionARM64::Terminate();149}150 151TEST_F(TestAArch64Emulator, TestOverflow) {152 EmulateInstructionARM64::ProcState pstate;153 memset(&pstate, 0, sizeof(pstate));154 uint64_t ll_max = std::numeric_limits<int64_t>::max();155 Arch64EmulatorTester emu;156 ASSERT_EQ(emu.AddWithCarry(64, ll_max, 0, 0, pstate), ll_max);157 ASSERT_EQ(pstate.V, 0ULL);158 ASSERT_EQ(pstate.C, 0ULL);159 ASSERT_EQ(emu.AddWithCarry(64, ll_max, 1, 0, pstate), (uint64_t)(ll_max + 1));160 ASSERT_EQ(pstate.V, 1ULL);161 ASSERT_EQ(pstate.C, 0ULL);162 ASSERT_EQ(emu.AddWithCarry(64, ll_max, 0, 1, pstate), (uint64_t)(ll_max + 1));163 ASSERT_EQ(pstate.V, 1ULL);164 ASSERT_EQ(pstate.C, 0ULL);165}166 167TEST_F(TestAArch64Emulator, TestAutoAdvancePC) {168 Arch64EmulatorTester emu;169 emu.memory_offset = 0x123456789abcde00;170 emu.gpr.pc = 0x123456789abcde00;171 emu.gpr.x[8] = 0x123456789abcde20;172 memcpy(emu.memory, "\x08\x01\x40\xb9", 4); // ldr w8, [x8]173 memcpy(emu.memory + 0x20, "\x11\x22\x33\x44", 4); // 0x44332211174 ASSERT_TRUE(emu.ReadInstruction());175 ASSERT_TRUE(176 emu.EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC |177 eEmulateInstructionOptionIgnoreConditions));178 ASSERT_EQ(emu.gpr.pc, (uint64_t)0x123456789abcde04);179 ASSERT_EQ(emu.gpr.x[8], (uint64_t)0x44332211);180}181