|
13 | 13 | #include "lldb/Core/Disassembler.h"
|
14 | 14 | #include "lldb/Target/ExecutionContext.h"
|
15 | 15 | #include "lldb/Utility/ArchSpec.h"
|
| 16 | +#include "lldb/Utility/RegisterValue.h" |
16 | 17 |
|
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" |
18 | 21 |
|
19 | 22 | using namespace lldb;
|
20 | 23 | using namespace lldb_private;
|
21 | 24 |
|
22 | 25 | struct Arch64EmulatorTester : public EmulateInstructionARM64 {
|
| 26 | + RegisterInfoPOSIX_arm64::GPR gpr; |
| 27 | + uint8_t memory[64] = {0}; |
| 28 | + uint64_t memory_offset = 0; |
| 29 | + |
23 | 30 | Arch64EmulatorTester()
|
24 |
| - : EmulateInstructionARM64(ArchSpec("arm64-apple-ios")) {} |
| 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.SetUInt64(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.GetAsUInt64(); |
| 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 | + }; |
25 | 128 |
|
26 | 129 | static uint64_t AddWithCarry(uint32_t N, uint64_t x, uint64_t y, bool carry_in,
|
27 | 130 | EmulateInstructionARM64::ProcState &proc_state) {
|
@@ -60,3 +163,18 @@ TEST_F(TestAArch64Emulator, TestOverflow) {
|
60 | 163 | ASSERT_EQ(pstate.V, 1ULL);
|
61 | 164 | ASSERT_EQ(pstate.C, 0ULL);
|
62 | 165 | }
|
| 166 | + |
| 167 | +TEST_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); // 0x44332211 |
| 174 | + ASSERT_TRUE(emu.ReadInstruction()); |
| 175 | + ASSERT_TRUE( |
| 176 | + emu.EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC | |
| 177 | + eEmulateInstructionOptionIgnoreConditions)); |
| 178 | + ASSERT_EQ(emu.gpr.pc, 0x123456789abcde04); |
| 179 | + ASSERT_EQ(emu.gpr.x[8], 0x44332211); |
| 180 | +} |
0 commit comments