Skip to content

[lldb][RISCV] fix LR/SC atomic sequence handling in lldb-server #127505

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions lldb/include/lldb/Core/EmulateInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include "lldb/lldb-private-types.h"
#include "lldb/lldb-types.h"

#include "llvm/Support/Error.h"

#include <cstddef>
#include <cstdint>

Expand All @@ -32,6 +34,38 @@ class RegisterValue;
class Stream;
class Target;
class UnwindPlan;
class EmulateInstruction;

using BreakpointLocations = std::vector<lldb::addr_t>;

class SingleStepBreakpointLocationsPredictor {
public:
SingleStepBreakpointLocationsPredictor(
std::unique_ptr<EmulateInstruction> emulator_up)
: m_emulator_up{std::move(emulator_up)} {}

virtual BreakpointLocations GetBreakpointLocations(Status &status);

virtual llvm::Expected<unsigned>
GetBreakpointSize([[maybe_unused]] lldb::addr_t bp_addr) {
return 4;
}

virtual ~SingleStepBreakpointLocationsPredictor() = default;

protected:
// This function retrieves the address of the next instruction as it appears
// in the binary file. Essentially, it reads the value of the PC register,
// determines the size of the current instruction (where the PC is pointing),
// and returns the sum of these two values.
lldb::addr_t GetNextInstructionAddress(Status &error);

lldb::addr_t GetBreakpointLocationAddress(lldb::addr_t entry_pc,
Status &error);

std::unique_ptr<EmulateInstruction> m_emulator_up;
bool m_emulation_result = false;
};

/// \class EmulateInstruction EmulateInstruction.h
/// "lldb/Core/EmulateInstruction.h"
Expand Down Expand Up @@ -497,7 +531,19 @@ class EmulateInstruction : public PluginInterface {
static uint32_t GetInternalRegisterNumber(RegisterContext *reg_ctx,
const RegisterInfo &reg_info);

static std::unique_ptr<SingleStepBreakpointLocationsPredictor>
CreateBreakpointLocationPredictor(
std::unique_ptr<EmulateInstruction> emulator_up);

// Helper functions
std::optional<lldb::addr_t> ReadPC();
bool WritePC(lldb::addr_t addr);

protected:
using BreakpointLocationsPredictorCreator =
std::function<std::unique_ptr<SingleStepBreakpointLocationsPredictor>(
std::unique_ptr<EmulateInstruction>)>;

ArchSpec m_arch;
void *m_baton = nullptr;
ReadMemoryCallback m_read_mem_callback = &ReadMemoryDefault;
Expand All @@ -508,6 +554,21 @@ class EmulateInstruction : public PluginInterface {
Opcode m_opcode;

private:
virtual BreakpointLocationsPredictorCreator
GetSingleStepBreakpointLocationsPredictorCreator() {
if (!m_arch.IsMIPS() && !m_arch.GetTriple().isPPC64() &&
!m_arch.GetTriple().isLoongArch()) {
// Unsupported architecture
return [](std::unique_ptr<EmulateInstruction> emulator_up) {
return nullptr;
};
}
return [](std::unique_ptr<EmulateInstruction> emulator_up) {
return std::make_unique<SingleStepBreakpointLocationsPredictor>(
std::move(emulator_up));
};
}

// For EmulateInstruction only
EmulateInstruction(const EmulateInstruction &) = delete;
const EmulateInstruction &operator=(const EmulateInstruction &) = delete;
Expand Down
92 changes: 92 additions & 0 deletions lldb/source/Core/EmulateInstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,99 @@ EmulateInstruction::GetInternalRegisterNumber(RegisterContext *reg_ctx,
return LLDB_INVALID_REGNUM;
}

std::unique_ptr<SingleStepBreakpointLocationsPredictor>
EmulateInstruction::CreateBreakpointLocationPredictor(
std::unique_ptr<EmulateInstruction> emulator_up) {
auto creator =
emulator_up->GetSingleStepBreakpointLocationsPredictorCreator();
return creator(std::move(emulator_up));
}

std::optional<lldb::addr_t> EmulateInstruction::ReadPC() {
bool success = false;
auto addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
LLDB_INVALID_ADDRESS, &success);
return success ? std::optional<addr_t>(addr) : std::nullopt;
}

bool EmulateInstruction::WritePC(lldb::addr_t addr) {
EmulateInstruction::Context ctx;
ctx.type = eContextAdvancePC;
ctx.SetNoArgs();
return WriteRegisterUnsigned(ctx, eRegisterKindGeneric,
LLDB_REGNUM_GENERIC_PC, addr);
}

bool EmulateInstruction::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) {
unwind_plan.Clear();
return false;
}

BreakpointLocations
SingleStepBreakpointLocationsPredictor::GetBreakpointLocations(Status &status) {
if (!m_emulator_up->ReadInstruction()) {
// try to get at least the size of next instruction to set breakpoint.
lldb::addr_t next_pc = GetNextInstructionAddress(status);
return BreakpointLocations{next_pc};
}

auto entry_pc = m_emulator_up->ReadPC();
if (!entry_pc) {
status = Status("Can't read PC");
return {};
}

m_emulation_result = m_emulator_up->EvaluateInstruction(
eEmulateInstructionOptionAutoAdvancePC);

lldb::addr_t next_pc = GetBreakpointLocationAddress(*entry_pc, status);
return BreakpointLocations{next_pc};
}

lldb::addr_t SingleStepBreakpointLocationsPredictor::GetNextInstructionAddress(
Status &error) {
auto instr_size = m_emulator_up->GetLastInstrSize();
if (!instr_size) {
error = Status("Read instruction failed!");
return LLDB_INVALID_ADDRESS;
}

auto pc = m_emulator_up->ReadPC();
if (!pc) {
error = Status("Can't read PC");
return LLDB_INVALID_ADDRESS;
}

lldb::addr_t next_pc = *pc + *instr_size;
return next_pc;
}

lldb::addr_t
SingleStepBreakpointLocationsPredictor::GetBreakpointLocationAddress(
lldb::addr_t entry_pc, Status &error) {
auto addr = m_emulator_up->ReadPC();
if (!addr) {
error = Status("Can't read PC");
return LLDB_INVALID_ADDRESS;
}
lldb::addr_t pc = *addr;

if (m_emulation_result) {
assert(entry_pc != pc && "Emulation was successfull but PC wasn't updated");
return pc;
}

if (entry_pc == pc) {
// Emulate instruction failed and it hasn't changed PC. Advance PC with
// the size of the current opcode because the emulation of all
// PC modifying instruction should be successful. The failure most
// likely caused by an unsupported instruction which does not modify PC.
return pc + m_emulator_up->GetOpcode().GetByteSize();
}

// The instruction emulation failed after it modified the PC. It is an
// unknown error where we can't continue because the next instruction is
// modifying the PC but we don't know how.
error = Status("Instruction emulation failed unexpectedly.");
return LLDB_INVALID_ADDRESS;
}
13 changes: 13 additions & 0 deletions lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14471,3 +14471,16 @@ bool EmulateInstructionARM::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) {
unwind_plan.SetReturnAddressRegister(dwarf_lr);
return true;
}

llvm::Expected<unsigned>
ARMSingleStepBreakpointLocationsPredictor::GetBreakpointSize(
lldb::addr_t bp_addr) {
auto flags = m_emulator_up->ReadRegisterUnsigned(
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_ADDRESS,
nullptr);
if (flags == LLDB_INVALID_ADDRESS)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Reading flags failed!");

return (flags & 0x20) ? /* Thumb mode */ 2 : /* Arm mode */ 4;
}
18 changes: 18 additions & 0 deletions lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@

namespace lldb_private {

class ARMSingleStepBreakpointLocationsPredictor
: public SingleStepBreakpointLocationsPredictor {
public:
ARMSingleStepBreakpointLocationsPredictor(
std::unique_ptr<EmulateInstruction> emulator_up)
: SingleStepBreakpointLocationsPredictor{std::move(emulator_up)} {}

llvm::Expected<unsigned> GetBreakpointSize(lldb::addr_t bp_addr) override;
};

// ITSession - Keep track of the IT Block progression.
class ITSession {
public:
Expand Down Expand Up @@ -770,6 +780,14 @@ class EmulateInstructionARM : public EmulateInstruction {
// B6.2.13 SUBS PC, LR and related instructions
bool EmulateSUBSPcLrEtc(const uint32_t opcode, const ARMEncoding encoding);

BreakpointLocationsPredictorCreator
GetSingleStepBreakpointLocationsPredictorCreator() override {
return [](std::unique_ptr<EmulateInstruction> emulator_up) {
return std::make_unique<ARMSingleStepBreakpointLocationsPredictor>(
std::move(emulator_up));
};
}

uint32_t m_arm_isa;
Mode m_opcode_mode;
uint32_t m_opcode_cpsr;
Expand Down
Loading
Loading