Skip to content

Commit 64eba6e

Browse files
authored
[lldb] Fix auto advance PC in EmulateInstructionARM64 if PC >= 4G (#151460)
The `EmulateInstructionARM64::EvaluateInstruction()` method used `uint32_t` to store the PC value, causing addresses greater than 2^32 to be truncated. As for now, the issue does not affect the mainline because the method is never called with both `eEmulateInstructionOptionAutoAdvancePC` and `eEmulateInstructionOptionIgnoreConditions` options set. However, it can trigger on a downstream that uses software stepping.
1 parent 3bc1b15 commit 64eba6e

File tree

2 files changed

+121
-3
lines changed

2 files changed

+121
-3
lines changed

lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ bool EmulateInstructionARM64::EvaluateInstruction(uint32_t evaluate_options) {
404404
if (!success && !m_ignore_conditions)
405405
return false;
406406

407-
uint32_t orig_pc_value = 0;
407+
uint64_t orig_pc_value = 0;
408408
if (auto_advance_pc) {
409409
orig_pc_value =
410410
ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_arm64, 0, &success);
@@ -418,7 +418,7 @@ bool EmulateInstructionARM64::EvaluateInstruction(uint32_t evaluate_options) {
418418
return false;
419419

420420
if (auto_advance_pc) {
421-
uint32_t new_pc_value =
421+
uint64_t new_pc_value =
422422
ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_arm64, 0, &success);
423423
if (!success)
424424
return false;

lldb/unittests/Instruction/ARM64/TestAArch64Emulator.cpp

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,118 @@
1313
#include "lldb/Core/Disassembler.h"
1414
#include "lldb/Target/ExecutionContext.h"
1515
#include "lldb/Utility/ArchSpec.h"
16+
#include "lldb/Utility/RegisterValue.h"
1617

1718
#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h"
19+
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
20+
#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
1821

1922
using namespace lldb;
2023
using namespace lldb_private;
2124

2225
struct Arch64EmulatorTester : public EmulateInstructionARM64 {
26+
RegisterInfoPOSIX_arm64::GPR gpr;
27+
uint8_t memory[64] = {0};
28+
uint64_t memory_offset = 0;
29+
2330
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 &reg_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 &reg_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+
};
25128

26129
static uint64_t AddWithCarry(uint32_t N, uint64_t x, uint64_t y, bool carry_in,
27130
EmulateInstructionARM64::ProcState &proc_state) {
@@ -60,3 +163,18 @@ TEST_F(TestAArch64Emulator, TestOverflow) {
60163
ASSERT_EQ(pstate.V, 1ULL);
61164
ASSERT_EQ(pstate.C, 0ULL);
62165
}
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

Comments
 (0)