Skip to content

Commit 3bc1fc6

Browse files
authored
[lldb][RISCV] fix LR/SC atomic sequence handling in lldb-server (#127505)
lldb-server had limited support for single-stepping through the lr/sc atomic sequence. This patch enhances that support for all possible atomic sequences.
1 parent 7d2293d commit 3bc1fc6

19 files changed

+643
-188
lines changed

lldb/include/lldb/Core/EmulateInstruction.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "lldb/lldb-private-types.h"
2323
#include "lldb/lldb-types.h"
2424

25+
#include "llvm/Support/Error.h"
26+
2527
#include <cstddef>
2628
#include <cstdint>
2729

@@ -32,6 +34,38 @@ class RegisterValue;
3234
class Stream;
3335
class Target;
3436
class UnwindPlan;
37+
class EmulateInstruction;
38+
39+
using BreakpointLocations = std::vector<lldb::addr_t>;
40+
41+
class SingleStepBreakpointLocationsPredictor {
42+
public:
43+
SingleStepBreakpointLocationsPredictor(
44+
std::unique_ptr<EmulateInstruction> emulator_up)
45+
: m_emulator_up{std::move(emulator_up)} {}
46+
47+
virtual BreakpointLocations GetBreakpointLocations(Status &status);
48+
49+
virtual llvm::Expected<unsigned>
50+
GetBreakpointSize([[maybe_unused]] lldb::addr_t bp_addr) {
51+
return 4;
52+
}
53+
54+
virtual ~SingleStepBreakpointLocationsPredictor() = default;
55+
56+
protected:
57+
// This function retrieves the address of the next instruction as it appears
58+
// in the binary file. Essentially, it reads the value of the PC register,
59+
// determines the size of the current instruction (where the PC is pointing),
60+
// and returns the sum of these two values.
61+
lldb::addr_t GetNextInstructionAddress(Status &error);
62+
63+
lldb::addr_t GetBreakpointLocationAddress(lldb::addr_t entry_pc,
64+
Status &error);
65+
66+
std::unique_ptr<EmulateInstruction> m_emulator_up;
67+
bool m_emulation_result = false;
68+
};
3569

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

534+
static std::unique_ptr<SingleStepBreakpointLocationsPredictor>
535+
CreateBreakpointLocationPredictor(
536+
std::unique_ptr<EmulateInstruction> emulator_up);
537+
538+
// Helper functions
539+
std::optional<lldb::addr_t> ReadPC();
540+
bool WritePC(lldb::addr_t addr);
541+
500542
protected:
543+
using BreakpointLocationsPredictorCreator =
544+
std::function<std::unique_ptr<SingleStepBreakpointLocationsPredictor>(
545+
std::unique_ptr<EmulateInstruction>)>;
546+
501547
ArchSpec m_arch;
502548
void *m_baton = nullptr;
503549
ReadMemoryCallback m_read_mem_callback = &ReadMemoryDefault;
@@ -508,6 +554,21 @@ class EmulateInstruction : public PluginInterface {
508554
Opcode m_opcode;
509555

510556
private:
557+
virtual BreakpointLocationsPredictorCreator
558+
GetSingleStepBreakpointLocationsPredictorCreator() {
559+
if (!m_arch.IsMIPS() && !m_arch.GetTriple().isPPC64() &&
560+
!m_arch.GetTriple().isLoongArch()) {
561+
// Unsupported architecture
562+
return [](std::unique_ptr<EmulateInstruction> emulator_up) {
563+
return nullptr;
564+
};
565+
}
566+
return [](std::unique_ptr<EmulateInstruction> emulator_up) {
567+
return std::make_unique<SingleStepBreakpointLocationsPredictor>(
568+
std::move(emulator_up));
569+
};
570+
}
571+
511572
// For EmulateInstruction only
512573
EmulateInstruction(const EmulateInstruction &) = delete;
513574
const EmulateInstruction &operator=(const EmulateInstruction &) = delete;

lldb/source/Core/EmulateInstruction.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,99 @@ EmulateInstruction::GetInternalRegisterNumber(RegisterContext *reg_ctx,
588588
return LLDB_INVALID_REGNUM;
589589
}
590590

591+
std::unique_ptr<SingleStepBreakpointLocationsPredictor>
592+
EmulateInstruction::CreateBreakpointLocationPredictor(
593+
std::unique_ptr<EmulateInstruction> emulator_up) {
594+
auto creator =
595+
emulator_up->GetSingleStepBreakpointLocationsPredictorCreator();
596+
return creator(std::move(emulator_up));
597+
}
598+
599+
std::optional<lldb::addr_t> EmulateInstruction::ReadPC() {
600+
bool success = false;
601+
auto addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
602+
LLDB_INVALID_ADDRESS, &success);
603+
return success ? std::optional<addr_t>(addr) : std::nullopt;
604+
}
605+
606+
bool EmulateInstruction::WritePC(lldb::addr_t addr) {
607+
EmulateInstruction::Context ctx;
608+
ctx.type = eContextAdvancePC;
609+
ctx.SetNoArgs();
610+
return WriteRegisterUnsigned(ctx, eRegisterKindGeneric,
611+
LLDB_REGNUM_GENERIC_PC, addr);
612+
}
613+
591614
bool EmulateInstruction::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) {
592615
unwind_plan.Clear();
593616
return false;
594617
}
618+
619+
BreakpointLocations
620+
SingleStepBreakpointLocationsPredictor::GetBreakpointLocations(Status &status) {
621+
if (!m_emulator_up->ReadInstruction()) {
622+
// try to get at least the size of next instruction to set breakpoint.
623+
lldb::addr_t next_pc = GetNextInstructionAddress(status);
624+
return BreakpointLocations{next_pc};
625+
}
626+
627+
auto entry_pc = m_emulator_up->ReadPC();
628+
if (!entry_pc) {
629+
status = Status("Can't read PC");
630+
return {};
631+
}
632+
633+
m_emulation_result = m_emulator_up->EvaluateInstruction(
634+
eEmulateInstructionOptionAutoAdvancePC);
635+
636+
lldb::addr_t next_pc = GetBreakpointLocationAddress(*entry_pc, status);
637+
return BreakpointLocations{next_pc};
638+
}
639+
640+
lldb::addr_t SingleStepBreakpointLocationsPredictor::GetNextInstructionAddress(
641+
Status &error) {
642+
auto instr_size = m_emulator_up->GetLastInstrSize();
643+
if (!instr_size) {
644+
error = Status("Read instruction failed!");
645+
return LLDB_INVALID_ADDRESS;
646+
}
647+
648+
auto pc = m_emulator_up->ReadPC();
649+
if (!pc) {
650+
error = Status("Can't read PC");
651+
return LLDB_INVALID_ADDRESS;
652+
}
653+
654+
lldb::addr_t next_pc = *pc + *instr_size;
655+
return next_pc;
656+
}
657+
658+
lldb::addr_t
659+
SingleStepBreakpointLocationsPredictor::GetBreakpointLocationAddress(
660+
lldb::addr_t entry_pc, Status &error) {
661+
auto addr = m_emulator_up->ReadPC();
662+
if (!addr) {
663+
error = Status("Can't read PC");
664+
return LLDB_INVALID_ADDRESS;
665+
}
666+
lldb::addr_t pc = *addr;
667+
668+
if (m_emulation_result) {
669+
assert(entry_pc != pc && "Emulation was successfull but PC wasn't updated");
670+
return pc;
671+
}
672+
673+
if (entry_pc == pc) {
674+
// Emulate instruction failed and it hasn't changed PC. Advance PC with
675+
// the size of the current opcode because the emulation of all
676+
// PC modifying instruction should be successful. The failure most
677+
// likely caused by an unsupported instruction which does not modify PC.
678+
return pc + m_emulator_up->GetOpcode().GetByteSize();
679+
}
680+
681+
// The instruction emulation failed after it modified the PC. It is an
682+
// unknown error where we can't continue because the next instruction is
683+
// modifying the PC but we don't know how.
684+
error = Status("Instruction emulation failed unexpectedly.");
685+
return LLDB_INVALID_ADDRESS;
686+
}

lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14471,3 +14471,16 @@ bool EmulateInstructionARM::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) {
1447114471
unwind_plan.SetReturnAddressRegister(dwarf_lr);
1447214472
return true;
1447314473
}
14474+
14475+
llvm::Expected<unsigned>
14476+
ARMSingleStepBreakpointLocationsPredictor::GetBreakpointSize(
14477+
lldb::addr_t bp_addr) {
14478+
auto flags = m_emulator_up->ReadRegisterUnsigned(
14479+
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_ADDRESS,
14480+
nullptr);
14481+
if (flags == LLDB_INVALID_ADDRESS)
14482+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
14483+
"Reading flags failed!");
14484+
14485+
return (flags & 0x20) ? /* Thumb mode */ 2 : /* Arm mode */ 4;
14486+
}

lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@
1616

1717
namespace lldb_private {
1818

19+
class ARMSingleStepBreakpointLocationsPredictor
20+
: public SingleStepBreakpointLocationsPredictor {
21+
public:
22+
ARMSingleStepBreakpointLocationsPredictor(
23+
std::unique_ptr<EmulateInstruction> emulator_up)
24+
: SingleStepBreakpointLocationsPredictor{std::move(emulator_up)} {}
25+
26+
llvm::Expected<unsigned> GetBreakpointSize(lldb::addr_t bp_addr) override;
27+
};
28+
1929
// ITSession - Keep track of the IT Block progression.
2030
class ITSession {
2131
public:
@@ -770,6 +780,14 @@ class EmulateInstructionARM : public EmulateInstruction {
770780
// B6.2.13 SUBS PC, LR and related instructions
771781
bool EmulateSUBSPcLrEtc(const uint32_t opcode, const ARMEncoding encoding);
772782

783+
BreakpointLocationsPredictorCreator
784+
GetSingleStepBreakpointLocationsPredictorCreator() override {
785+
return [](std::unique_ptr<EmulateInstruction> emulator_up) {
786+
return std::make_unique<ARMSingleStepBreakpointLocationsPredictor>(
787+
std::move(emulator_up));
788+
};
789+
}
790+
773791
uint32_t m_arm_isa;
774792
Mode m_opcode_mode;
775793
uint32_t m_opcode_cpsr;

0 commit comments

Comments
 (0)