[Lldb-commits] [lldb] [lldb] Fix auto advance PC in `EmulateInstructionARM64` if PC >= 4G (PR #151460)
Igor Kudrin via lldb-commits
lldb-commits at lists.llvm.org
Wed Jul 30 23:35:29 PDT 2025
https://github.com/igorkudrin created https://github.com/llvm/llvm-project/pull/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.
>From e7ae06b1388e172a83fea0340f81d437b9da3c88 Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Wed, 30 Jul 2025 23:28:59 -0700
Subject: [PATCH] [lldb] Fix auto advance PC in 'EmulateInstructionARM64' if PC
>= 4G
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.
---
.../ARM64/EmulateInstructionARM64.cpp | 4 +-
.../Instruction/ARM64/TestAArch64Emulator.cpp | 126 +++++++++++++++++-
2 files changed, 127 insertions(+), 3 deletions(-)
diff --git a/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp b/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
index 29f03fee47b0d..a8901beda3970 100644
--- a/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
+++ b/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
@@ -404,7 +404,7 @@ bool EmulateInstructionARM64::EvaluateInstruction(uint32_t evaluate_options) {
if (!success && !m_ignore_conditions)
return false;
- uint32_t orig_pc_value = 0;
+ uint64_t orig_pc_value = 0;
if (auto_advance_pc) {
orig_pc_value =
ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_arm64, 0, &success);
@@ -418,7 +418,7 @@ bool EmulateInstructionARM64::EvaluateInstruction(uint32_t evaluate_options) {
return false;
if (auto_advance_pc) {
- uint32_t new_pc_value =
+ uint64_t new_pc_value =
ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_arm64, 0, &success);
if (!success)
return false;
diff --git a/lldb/unittests/Instruction/ARM64/TestAArch64Emulator.cpp b/lldb/unittests/Instruction/ARM64/TestAArch64Emulator.cpp
index 4506c200dee3b..c021d994bb062 100644
--- a/lldb/unittests/Instruction/ARM64/TestAArch64Emulator.cpp
+++ b/lldb/unittests/Instruction/ARM64/TestAArch64Emulator.cpp
@@ -13,15 +13,124 @@
#include "lldb/Core/Disassembler.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/RegisterValue.h"
#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
+#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
using namespace lldb;
using namespace lldb_private;
struct Arch64EmulatorTester : public EmulateInstructionARM64 {
+ RegisterInfoPOSIX_arm64::GPR gpr;
+ uint8_t memory[64] = {0};
+ uint64_t memory_offset = 0;
+
Arch64EmulatorTester()
- : EmulateInstructionARM64(ArchSpec("arm64-apple-ios")) {}
+ : EmulateInstructionARM64(ArchSpec("arm64-apple-ios")) {
+ memset(&gpr, 0, sizeof(gpr));
+ EmulateInstruction::SetCallbacks(ReadMemoryCallback, WriteMemoryCallback,
+ ReadRegisterCallback,
+ WriteRegisterCallback);
+ }
+
+ static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue ®_value) {
+ auto *tester = static_cast<Arch64EmulatorTester *>(instruction);
+ uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ if (reg >= gpr_x1_arm64 && reg <= gpr_x28_arm64) {
+ reg_value.SetUInt64(tester->gpr.x[reg - gpr_x0_arm64]);
+ return true;
+ }
+ if (reg >= gpr_w1_arm64 && reg <= gpr_w28_arm64) {
+ reg_value.SetUInt32(tester->gpr.x[reg - gpr_w0_arm64]);
+ return true;
+ }
+ switch (reg) {
+ case gpr_x0_arm64:
+ reg_value.SetUInt64(0);
+ return true;
+ case gpr_w0_arm64:
+ reg_value.SetUInt32(0);
+ return true;
+ case gpr_fp_arm64:
+ reg_value.SetUInt64(tester->gpr.fp);
+ return true;
+ case gpr_lr_arm64:
+ reg_value.SetUInt64(tester->gpr.lr);
+ return true;
+ case gpr_sp_arm64:
+ reg_value.SetUInt64(tester->gpr.sp);
+ return true;
+ case gpr_pc_arm64:
+ reg_value.SetUInt64(tester->gpr.pc);
+ return true;
+ case gpr_cpsr_arm64:
+ reg_value.SetUInt64(tester->gpr.cpsr);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static bool WriteRegisterCallback(EmulateInstruction *instruction,
+ void *baton, const Context &context,
+ const RegisterInfo *reg_info,
+ const RegisterValue ®_value) {
+ auto *tester = static_cast<Arch64EmulatorTester *>(instruction);
+ uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ if (reg >= gpr_x1_arm64 && reg <= gpr_x28_arm64) {
+ tester->gpr.x[reg - gpr_x0_arm64] = reg_value.GetAsUInt64();
+ return true;
+ }
+ if (reg >= gpr_w1_arm64 && reg <= gpr_w28_arm64) {
+ tester->gpr.x[reg - gpr_w0_arm64] = reg_value.GetAsUInt32();
+ return true;
+ }
+ switch (reg) {
+ case gpr_fp_arm64:
+ tester->gpr.fp = reg_value.GetAsUInt64();
+ return true;
+ case gpr_lr_arm64:
+ tester->gpr.lr = reg_value.GetAsUInt64();
+ return true;
+ case gpr_sp_arm64:
+ tester->gpr.sp = reg_value.GetAsUInt64();
+ return true;
+ case gpr_pc_arm64:
+ tester->gpr.pc = reg_value.GetAsUInt64();
+ return true;
+ case gpr_cpsr_arm64:
+ tester->gpr.cpsr = reg_value.GetAsUInt64();
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton,
+ const Context &context, addr_t addr,
+ void *dst, size_t length) {
+ auto *tester = static_cast<Arch64EmulatorTester *>(instruction);
+ assert(addr >= tester->memory_offset);
+ assert(addr - tester->memory_offset + length <= sizeof(tester->memory));
+ if (addr >= tester->memory_offset &&
+ addr - tester->memory_offset + length <= sizeof(tester->memory)) {
+ memcpy(dst, tester->memory + addr - tester->memory_offset, length);
+ return length;
+ }
+ return 0;
+ };
+
+ static size_t WriteMemoryCallback(EmulateInstruction *instruction,
+ void *baton, const Context &context,
+ addr_t addr, const void *dst,
+ size_t length) {
+ // TODO: implement when required
+ return 0;
+ };
static uint64_t AddWithCarry(uint32_t N, uint64_t x, uint64_t y, bool carry_in,
EmulateInstructionARM64::ProcState &proc_state) {
@@ -60,3 +169,18 @@ TEST_F(TestAArch64Emulator, TestOverflow) {
ASSERT_EQ(pstate.V, 1ULL);
ASSERT_EQ(pstate.C, 0ULL);
}
+
+TEST_F(TestAArch64Emulator, TestAutoAdvancePC) {
+ Arch64EmulatorTester emu;
+ emu.memory_offset = 0x1234567800;
+ emu.gpr.pc = 0x1234567800;
+ emu.gpr.x[8] = 0x1234567820;
+ memcpy(emu.memory, "\x08\x01\x40\xb9", 4); // ldr w8, [x8]
+ memcpy(emu.memory + 0x20, "\x11\x22\x33\x44", 4); // 0x44332211
+ ASSERT_TRUE(emu.ReadInstruction());
+ ASSERT_TRUE(
+ emu.EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC |
+ eEmulateInstructionOptionIgnoreConditions));
+ ASSERT_EQ(emu.gpr.pc, 0x1234567804);
+ ASSERT_EQ(emu.gpr.x[8], 0x44332211);
+}
More information about the lldb-commits
mailing list