Skip to content

Commit 49d5dd3

Browse files
committed
Reland "[lldb] Fix auto advance PC in EmulateInstructionARM64 if PC >= 4G (#151460)"
This reverts commit 600976f. The test was crashing trying to access any element of the GPR struct. (gdb) disas Dump of assembler code for function _ZN7testing8internal11CmpHelperEQIyyEENS_15AssertionResultEPKcS4_RKT_RKT0_: 0x00450afc <+0>: push {r4, r5, r6, r7, r8, r9, r10, r11, lr} 0x00450b00 <+4>: sub sp, sp, #60 ; 0x3c 0x00450b04 <+8>: ldr r5, [sp, #96] ; 0x60 => 0x00450b08 <+12>: ldm r3, {r4, r7} 0x00450b0c <+16>: ldm r5, {r6, r9} 0x00450b10 <+20>: eor r7, r7, r9 0x00450b14 <+24>: eor r6, r4, r6 0x00450b18 <+28>: orrs r7, r6, r7 (gdb) p/x r3 $3 = 0x3e300f6e "However, load and store multiple instructions (LDM and STM) and load and store double-word (LDRD or STRD) must be aligned to at least a word boundary." https://developer.arm.com/documentation/den0013/d/Porting/Alignment >>> 0x3e300f6e % 4 2 Program received signal SIGBUS, Bus error. 0x00450b08 in testing::AssertionResult testing::internal::CmpHelperEQ<unsigned long long, unsigned long long>(char const*, char const*, unsigned long long const&, unsigned long long const&) () The struct is packed with 1 byte alignment, but it needs to start at an aligned address for us to ldm from it. So I've done that with alignas. Also fixed some compiler warnings in the test itself.
1 parent b24ad98 commit 49d5dd3

File tree

3 files changed

+124
-4
lines changed

3 files changed

+124
-4
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/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ class RegisterInfoPOSIX_arm64
4545
};
4646

4747
// based on RegisterContextDarwin_arm64.h
48+
// Pack this so there are no extra bytes, but align its start address to at
49+
// least 4 bytes to prevent alignment errors on Arm 32-bit.
4850
LLVM_PACKED_START
49-
struct GPR {
51+
struct alignas(4) GPR {
5052
uint64_t x[29]; // x0-x28
5153
uint64_t fp; // x29
5254
uint64_t lr; // x30

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, (uint64_t)0x123456789abcde04);
179+
ASSERT_EQ(emu.gpr.x[8], (uint64_t)0x44332211);
180+
}

0 commit comments

Comments
 (0)