[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.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
@@ -770,6 +780,14 @@ protected:
|
||||
// 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;
|
||||
|
||||
@@ -86,7 +86,6 @@ bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) {
|
||||
uint32_t inst_size = m_opcode.GetByteSize();
|
||||
uint32_t inst = m_opcode.GetOpcode32();
|
||||
bool increase_pc = options & eEmulateInstructionOptionAutoAdvancePC;
|
||||
bool success = false;
|
||||
|
||||
Opcode *opcode_data = GetOpcodeForInstruction(inst);
|
||||
if (!opcode_data)
|
||||
@@ -94,9 +93,10 @@ bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) {
|
||||
|
||||
lldb::addr_t old_pc = 0;
|
||||
if (increase_pc) {
|
||||
old_pc = ReadPC(&success);
|
||||
if (!success)
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
old_pc = *addr;
|
||||
}
|
||||
|
||||
// Call the Emulate... function.
|
||||
@@ -104,9 +104,10 @@ bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) {
|
||||
return false;
|
||||
|
||||
if (increase_pc) {
|
||||
lldb::addr_t new_pc = ReadPC(&success);
|
||||
if (!success)
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
lldb::addr_t new_pc = *addr;
|
||||
|
||||
if (new_pc == old_pc && !WritePC(old_pc + inst_size))
|
||||
return false;
|
||||
@@ -115,13 +116,14 @@ bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) {
|
||||
}
|
||||
|
||||
bool EmulateInstructionLoongArch::ReadInstruction() {
|
||||
bool success = false;
|
||||
m_addr = ReadPC(&success);
|
||||
if (!success) {
|
||||
auto addr = ReadPC();
|
||||
if (!addr) {
|
||||
m_addr = LLDB_INVALID_ADDRESS;
|
||||
return false;
|
||||
}
|
||||
m_addr = *addr;
|
||||
|
||||
bool success = false;
|
||||
Context ctx;
|
||||
ctx.type = eContextReadOpcode;
|
||||
ctx.SetNoArgs();
|
||||
@@ -131,19 +133,6 @@ bool EmulateInstructionLoongArch::ReadInstruction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
lldb::addr_t EmulateInstructionLoongArch::ReadPC(bool *success) {
|
||||
return ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
|
||||
LLDB_INVALID_ADDRESS, success);
|
||||
}
|
||||
|
||||
bool EmulateInstructionLoongArch::WritePC(lldb::addr_t pc) {
|
||||
EmulateInstruction::Context ctx;
|
||||
ctx.type = eContextAdvancePC;
|
||||
ctx.SetNoArgs();
|
||||
return WriteRegisterUnsigned(ctx, eRegisterKindGeneric,
|
||||
LLDB_REGNUM_GENERIC_PC, pc);
|
||||
}
|
||||
|
||||
std::optional<RegisterInfo>
|
||||
EmulateInstructionLoongArch::GetRegisterInfo(lldb::RegisterKind reg_kind,
|
||||
uint32_t reg_index) {
|
||||
@@ -273,9 +262,12 @@ bool EmulateInstructionLoongArch::EmulateNonJMP(uint32_t inst) { return false; }
|
||||
bool EmulateInstructionLoongArch::EmulateBEQZ64(uint32_t inst) {
|
||||
bool success = false;
|
||||
uint32_t rj = Bits32(inst, 9, 5);
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16);
|
||||
uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
|
||||
if (!success)
|
||||
@@ -293,9 +285,12 @@ bool EmulateInstructionLoongArch::EmulateBEQZ64(uint32_t inst) {
|
||||
bool EmulateInstructionLoongArch::EmulateBNEZ64(uint32_t inst) {
|
||||
bool success = false;
|
||||
uint32_t rj = Bits32(inst, 9, 5);
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16);
|
||||
uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
|
||||
if (!success)
|
||||
@@ -313,9 +308,12 @@ bool EmulateInstructionLoongArch::EmulateBNEZ64(uint32_t inst) {
|
||||
bool EmulateInstructionLoongArch::EmulateBCEQZ64(uint32_t inst) {
|
||||
bool success = false;
|
||||
uint32_t cj = Bits32(inst, 7, 5) + fpr_fcc0_loongarch;
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16);
|
||||
uint8_t cj_val =
|
||||
(uint8_t)ReadRegisterUnsigned(eRegisterKindLLDB, cj, 0, &success);
|
||||
@@ -335,9 +333,12 @@ bool EmulateInstructionLoongArch::EmulateBCEQZ64(uint32_t inst) {
|
||||
bool EmulateInstructionLoongArch::EmulateBCNEZ64(uint32_t inst) {
|
||||
bool success = false;
|
||||
uint32_t cj = Bits32(inst, 7, 5) + fpr_fcc0_loongarch;
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16);
|
||||
uint8_t cj_val =
|
||||
(uint8_t)ReadRegisterUnsigned(eRegisterKindLLDB, cj, 0, &success);
|
||||
@@ -358,9 +359,12 @@ bool EmulateInstructionLoongArch::EmulateJIRL64(uint32_t inst) {
|
||||
uint32_t rj = Bits32(inst, 9, 5);
|
||||
uint32_t rd = Bits32(inst, 4, 0);
|
||||
bool success = false;
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
EmulateInstruction::Context ctx;
|
||||
if (!WriteRegisterUnsigned(ctx, eRegisterKindLLDB, rd, pc + 4))
|
||||
return false;
|
||||
@@ -374,10 +378,11 @@ bool EmulateInstructionLoongArch::EmulateJIRL64(uint32_t inst) {
|
||||
// b offs26
|
||||
// PC = PC + SignExtend({offs26, 2' b0}, GRLEN)
|
||||
bool EmulateInstructionLoongArch::EmulateB64(uint32_t inst) {
|
||||
bool success = false;
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
uint32_t offs26 = Bits32(inst, 25, 10) + (Bits32(inst, 9, 0) << 16);
|
||||
uint64_t next_pc = pc + llvm::SignExtend64<28>(offs26 << 2);
|
||||
return WritePC(next_pc);
|
||||
@@ -387,10 +392,11 @@ bool EmulateInstructionLoongArch::EmulateB64(uint32_t inst) {
|
||||
// GR[1] = PC + 4
|
||||
// PC = PC + SignExtend({offs26, 2'b0}, GRLEN)
|
||||
bool EmulateInstructionLoongArch::EmulateBL64(uint32_t inst) {
|
||||
bool success = false;
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
EmulateInstruction::Context ctx;
|
||||
if (!WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_loongarch, pc + 4))
|
||||
return false;
|
||||
@@ -406,9 +412,12 @@ bool EmulateInstructionLoongArch::EmulateBEQ64(uint32_t inst) {
|
||||
bool success = false;
|
||||
uint32_t rj = Bits32(inst, 9, 5);
|
||||
uint32_t rd = Bits32(inst, 4, 0);
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
|
||||
if (!success)
|
||||
return false;
|
||||
@@ -429,9 +438,12 @@ bool EmulateInstructionLoongArch::EmulateBNE64(uint32_t inst) {
|
||||
bool success = false;
|
||||
uint32_t rj = Bits32(inst, 9, 5);
|
||||
uint32_t rd = Bits32(inst, 4, 0);
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
|
||||
if (!success)
|
||||
return false;
|
||||
@@ -452,9 +464,12 @@ bool EmulateInstructionLoongArch::EmulateBLT64(uint32_t inst) {
|
||||
bool success = false;
|
||||
uint32_t rj = Bits32(inst, 9, 5);
|
||||
uint32_t rd = Bits32(inst, 4, 0);
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
int64_t rj_val =
|
||||
(int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
|
||||
if (!success)
|
||||
@@ -477,9 +492,12 @@ bool EmulateInstructionLoongArch::EmulateBGE64(uint32_t inst) {
|
||||
bool success = false;
|
||||
uint32_t rj = Bits32(inst, 9, 5);
|
||||
uint32_t rd = Bits32(inst, 4, 0);
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
int64_t rj_val =
|
||||
(int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
|
||||
if (!success)
|
||||
@@ -502,9 +520,12 @@ bool EmulateInstructionLoongArch::EmulateBLTU64(uint32_t inst) {
|
||||
bool success = false;
|
||||
uint32_t rj = Bits32(inst, 9, 5);
|
||||
uint32_t rd = Bits32(inst, 4, 0);
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
|
||||
if (!success)
|
||||
return false;
|
||||
@@ -525,9 +546,12 @@ bool EmulateInstructionLoongArch::EmulateBGEU64(uint32_t inst) {
|
||||
bool success = false;
|
||||
uint32_t rj = Bits32(inst, 9, 5);
|
||||
uint32_t rd = Bits32(inst, 4, 0);
|
||||
uint64_t pc = ReadPC(&success);
|
||||
if (!success)
|
||||
|
||||
auto addr = ReadPC();
|
||||
if (!addr)
|
||||
return false;
|
||||
uint64_t pc = *addr;
|
||||
|
||||
uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
@@ -57,8 +57,6 @@ public:
|
||||
|
||||
std::optional<RegisterInfo> GetRegisterInfo(lldb::RegisterKind reg_kind,
|
||||
uint32_t reg_num) override;
|
||||
lldb::addr_t ReadPC(bool *success);
|
||||
bool WritePC(lldb::addr_t pc);
|
||||
bool IsLoongArch64() { return m_arch_subtype == llvm::Triple::loongarch64; }
|
||||
bool TestExecute(uint32_t inst);
|
||||
|
||||
|
||||
@@ -650,9 +650,10 @@ std::optional<DecodeResult> EmulateInstructionRISCV::Decode(uint32_t inst) {
|
||||
for (const InstrPattern &pat : PATTERNS) {
|
||||
if ((inst & pat.type_mask) == pat.eigen &&
|
||||
(inst_type & pat.inst_type) != 0) {
|
||||
LLDB_LOGF(
|
||||
log, "EmulateInstructionRISCV::%s: inst(%x at %" PRIx64 ") was decoded to %s",
|
||||
__FUNCTION__, inst, m_addr, pat.name);
|
||||
LLDB_LOGF(log,
|
||||
"EmulateInstructionRISCV::%s: inst(%x at %" PRIx64
|
||||
") was decoded to %s",
|
||||
__FUNCTION__, inst, m_addr, pat.name);
|
||||
auto decoded = is_16b ? pat.decode(try_rvc) : pat.decode(inst);
|
||||
return DecodeResult{decoded, inst, is_16b, pat};
|
||||
}
|
||||
@@ -1649,21 +1650,6 @@ bool EmulateInstructionRISCV::ReadInstruction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<addr_t> EmulateInstructionRISCV::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 EmulateInstructionRISCV::WritePC(addr_t pc) {
|
||||
EmulateInstruction::Context ctx;
|
||||
ctx.type = eContextAdvancePC;
|
||||
ctx.SetNoArgs();
|
||||
return WriteRegisterUnsigned(ctx, eRegisterKindGeneric,
|
||||
LLDB_REGNUM_GENERIC_PC, pc);
|
||||
}
|
||||
|
||||
RoundingMode EmulateInstructionRISCV::GetRoundingMode() {
|
||||
bool success = false;
|
||||
auto fcsr = ReadRegisterUnsigned(eRegisterKindLLDB, fpr_fcsr_riscv,
|
||||
@@ -1792,4 +1778,128 @@ bool EmulateInstructionRISCV::SupportsThisArch(const ArchSpec &arch) {
|
||||
return arch.GetTriple().isRISCV();
|
||||
}
|
||||
|
||||
BreakpointLocations
|
||||
RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointLocations(
|
||||
Status &status) {
|
||||
EmulateInstructionRISCV *riscv_emulator =
|
||||
static_cast<EmulateInstructionRISCV *>(m_emulator_up.get());
|
||||
|
||||
auto pc = riscv_emulator->ReadPC();
|
||||
if (!pc) {
|
||||
status = Status("Can't read PC");
|
||||
return {};
|
||||
}
|
||||
|
||||
auto inst = riscv_emulator->ReadInstructionAt(*pc);
|
||||
if (!inst) {
|
||||
// Can't read instruction. Try default handler.
|
||||
return SingleStepBreakpointLocationsPredictor::GetBreakpointLocations(
|
||||
status);
|
||||
}
|
||||
|
||||
if (FoundLoadReserve(inst->decoded))
|
||||
return HandleAtomicSequence(*pc, status);
|
||||
|
||||
if (FoundStoreConditional(inst->decoded)) {
|
||||
// Ill-formed atomic sequence (SC doesn't have corresponding LR
|
||||
// instruction). Consider SC instruction like a non-atomic store and set a
|
||||
// breakpoint at the next instruction.
|
||||
Log *log = GetLog(LLDBLog::Unwind);
|
||||
LLDB_LOGF(log,
|
||||
"RISCVSingleStepBreakpointLocationsPredictor::%s: can't find "
|
||||
"corresponding load reserve insturuction",
|
||||
__FUNCTION__);
|
||||
return {*pc + (inst->is_rvc ? 2u : 4u)};
|
||||
}
|
||||
|
||||
return SingleStepBreakpointLocationsPredictor::GetBreakpointLocations(status);
|
||||
}
|
||||
|
||||
llvm::Expected<unsigned>
|
||||
RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointSize(
|
||||
lldb::addr_t bp_addr) {
|
||||
EmulateInstructionRISCV *riscv_emulator =
|
||||
static_cast<EmulateInstructionRISCV *>(m_emulator_up.get());
|
||||
|
||||
if (auto inst = riscv_emulator->ReadInstructionAt(bp_addr); inst)
|
||||
return inst->is_rvc ? 2 : 4;
|
||||
|
||||
// Try last instruction size.
|
||||
if (auto last_instr_size = riscv_emulator->GetLastInstrSize();
|
||||
last_instr_size)
|
||||
return *last_instr_size;
|
||||
|
||||
// Just place non-compressed software trap.
|
||||
return 4;
|
||||
}
|
||||
|
||||
BreakpointLocations
|
||||
RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence(
|
||||
lldb::addr_t pc, Status &error) {
|
||||
EmulateInstructionRISCV *riscv_emulator =
|
||||
static_cast<EmulateInstructionRISCV *>(m_emulator_up.get());
|
||||
|
||||
// Handle instructions between LR and SC. According to unprivilleged
|
||||
// RISC-V ISA there can be at most 16 instructions in the sequence.
|
||||
|
||||
lldb::addr_t entry_pc = pc; // LR instruction address
|
||||
auto lr_inst = riscv_emulator->ReadInstructionAt(entry_pc);
|
||||
pc += lr_inst->is_rvc ? 2 : 4;
|
||||
|
||||
size_t atomic_length = 0;
|
||||
std::optional<DecodeResult> inst;
|
||||
std::vector<lldb::addr_t> bp_addrs;
|
||||
do {
|
||||
inst = riscv_emulator->ReadInstructionAt(pc);
|
||||
if (!inst) {
|
||||
error = Status("Can't read instruction");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (B *branch = std::get_if<B>(&inst->decoded))
|
||||
bp_addrs.push_back(pc + SignExt(branch->imm));
|
||||
|
||||
unsigned addent = inst->is_rvc ? 2 : 4;
|
||||
pc += addent;
|
||||
atomic_length += addent;
|
||||
} while ((atomic_length < s_max_atomic_sequence_length) &&
|
||||
!FoundStoreConditional(inst->decoded));
|
||||
|
||||
if (atomic_length >= s_max_atomic_sequence_length) {
|
||||
// Ill-formed atomic sequence (LR doesn't have corresponding SC
|
||||
// instruction). In this case consider LR like a non-atomic load instruction
|
||||
// and set a breakpoint at the next after LR instruction.
|
||||
Log *log = GetLog(LLDBLog::Unwind);
|
||||
LLDB_LOGF(log,
|
||||
"RISCVSingleStepBreakpointLocationsPredictor::%s: can't find "
|
||||
"corresponding store conditional insturuction",
|
||||
__FUNCTION__);
|
||||
return {entry_pc + (lr_inst->is_rvc ? 2u : 4u)};
|
||||
}
|
||||
|
||||
lldb::addr_t exit_pc = pc;
|
||||
|
||||
// Check if we have a branch to the start of the atomic sequence after SC
|
||||
// instruction. If we have such branch, consider it as a part of the atomic
|
||||
// sequence.
|
||||
inst = riscv_emulator->ReadInstructionAt(exit_pc);
|
||||
if (inst) {
|
||||
B *branch = std::get_if<B>(&inst->decoded);
|
||||
if (branch && (exit_pc + SignExt(branch->imm)) == entry_pc)
|
||||
exit_pc += inst->is_rvc ? 2 : 4;
|
||||
}
|
||||
|
||||
// Set breakpoints at the jump addresses of the forward branches that points
|
||||
// after the end of the atomic sequence.
|
||||
bp_addrs.erase(llvm::remove_if(bp_addrs,
|
||||
[exit_pc](lldb::addr_t bp_addr) {
|
||||
return exit_pc >= bp_addr;
|
||||
}),
|
||||
bp_addrs.end());
|
||||
|
||||
// Set breakpoint at the end of atomic sequence.
|
||||
bp_addrs.push_back(exit_pc);
|
||||
return bp_addrs;
|
||||
}
|
||||
|
||||
} // namespace lldb_private
|
||||
|
||||
@@ -20,6 +20,33 @@
|
||||
|
||||
namespace lldb_private {
|
||||
|
||||
class RISCVSingleStepBreakpointLocationsPredictor
|
||||
: public SingleStepBreakpointLocationsPredictor {
|
||||
public:
|
||||
RISCVSingleStepBreakpointLocationsPredictor(
|
||||
std::unique_ptr<EmulateInstruction> emulator)
|
||||
: SingleStepBreakpointLocationsPredictor{std::move(emulator)} {}
|
||||
|
||||
BreakpointLocations GetBreakpointLocations(Status &status) override;
|
||||
|
||||
llvm::Expected<unsigned> GetBreakpointSize(lldb::addr_t bp_addr) override;
|
||||
|
||||
private:
|
||||
static bool FoundLoadReserve(const RISCVInst &inst) {
|
||||
return std::holds_alternative<LR_W>(inst) ||
|
||||
std::holds_alternative<LR_D>(inst);
|
||||
}
|
||||
|
||||
static bool FoundStoreConditional(const RISCVInst &inst) {
|
||||
return std::holds_alternative<SC_W>(inst) ||
|
||||
std::holds_alternative<SC_D>(inst);
|
||||
}
|
||||
|
||||
BreakpointLocations HandleAtomicSequence(lldb::addr_t pc, Status &error);
|
||||
|
||||
static constexpr size_t s_max_atomic_sequence_length = 64;
|
||||
};
|
||||
|
||||
class EmulateInstructionRISCV : public EmulateInstruction {
|
||||
public:
|
||||
static llvm::StringRef GetPluginNameStatic() { return "riscv"; }
|
||||
@@ -67,9 +94,6 @@ public:
|
||||
std::optional<RegisterInfo> GetRegisterInfo(lldb::RegisterKind reg_kind,
|
||||
uint32_t reg_num) override;
|
||||
|
||||
std::optional<lldb::addr_t> ReadPC();
|
||||
bool WritePC(lldb::addr_t pc);
|
||||
|
||||
std::optional<DecodeResult> ReadInstructionAt(lldb::addr_t addr);
|
||||
std::optional<DecodeResult> Decode(uint32_t inst);
|
||||
bool Execute(DecodeResult inst, bool ignore_cond);
|
||||
@@ -98,6 +122,13 @@ public:
|
||||
bool SetAccruedExceptions(llvm::APFloatBase::opStatus);
|
||||
|
||||
private:
|
||||
BreakpointLocationsPredictorCreator
|
||||
GetSingleStepBreakpointLocationsPredictorCreator() override {
|
||||
return [](std::unique_ptr<EmulateInstruction> emulator_up) {
|
||||
return std::make_unique<RISCVSingleStepBreakpointLocationsPredictor>(
|
||||
std::move(emulator_up));
|
||||
};
|
||||
}
|
||||
/// Last decoded instruction from m_opcode
|
||||
DecodeResult m_decoded;
|
||||
/// Last decoded instruction size estimate.
|
||||
|
||||
@@ -324,12 +324,14 @@ void NativeProcessFreeBSD::MonitorSIGTRAP(lldb::pid_t pid) {
|
||||
auto thread_info =
|
||||
m_threads_stepping_with_breakpoint.find(thread->GetID());
|
||||
if (thread_info != m_threads_stepping_with_breakpoint.end() &&
|
||||
thread_info->second == regctx.GetPC()) {
|
||||
llvm::is_contained(thread_info->second, regctx.GetPC())) {
|
||||
thread->SetStoppedByTrace();
|
||||
Status brkpt_error = RemoveBreakpoint(thread_info->second);
|
||||
if (brkpt_error.Fail())
|
||||
LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}",
|
||||
thread_info->first, brkpt_error);
|
||||
for (auto &&bp_addr : thread_info->second) {
|
||||
Status brkpt_error = RemoveBreakpoint(bp_addr);
|
||||
if (brkpt_error.Fail())
|
||||
LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}",
|
||||
thread_info->first, brkpt_error);
|
||||
}
|
||||
m_threads_stepping_with_breakpoint.erase(thread_info);
|
||||
} else
|
||||
thread->SetStoppedByBreakpoint();
|
||||
|
||||
@@ -833,7 +833,7 @@ void NativeProcessLinux::MonitorBreakpoint(NativeThreadLinux &thread) {
|
||||
auto stepping_with_bp_it =
|
||||
m_threads_stepping_with_breakpoint.find(thread.GetID());
|
||||
if (stepping_with_bp_it != m_threads_stepping_with_breakpoint.end() &&
|
||||
stepping_with_bp_it->second == reg_ctx.GetPC())
|
||||
llvm::is_contained(stepping_with_bp_it->second, reg_ctx.GetPC()))
|
||||
thread.SetStoppedByTrace();
|
||||
|
||||
StopRunningThreads(thread.GetID());
|
||||
@@ -1960,10 +1960,12 @@ void NativeProcessLinux::SignalIfAllThreadsStopped() {
|
||||
// Clear any temporary breakpoints we used to implement software single
|
||||
// stepping.
|
||||
for (const auto &thread_info : m_threads_stepping_with_breakpoint) {
|
||||
Status error = RemoveBreakpoint(thread_info.second);
|
||||
if (error.Fail())
|
||||
LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}",
|
||||
thread_info.first, error);
|
||||
for (auto &&bp_addr : thread_info.second) {
|
||||
Status error = RemoveBreakpoint(bp_addr);
|
||||
if (error.Fail())
|
||||
LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}",
|
||||
thread_info.first, error);
|
||||
}
|
||||
}
|
||||
m_threads_stepping_with_breakpoint.clear();
|
||||
|
||||
|
||||
@@ -87,34 +87,10 @@ static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton,
|
||||
return length;
|
||||
}
|
||||
|
||||
static lldb::addr_t ReadFlags(NativeRegisterContext ®siter_context) {
|
||||
const RegisterInfo *flags_info = regsiter_context.GetRegisterInfo(
|
||||
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
|
||||
return regsiter_context.ReadRegisterAsUnsigned(flags_info,
|
||||
LLDB_INVALID_ADDRESS);
|
||||
}
|
||||
|
||||
static int GetSoftwareBreakpointSize(const ArchSpec &arch,
|
||||
lldb::addr_t next_flags) {
|
||||
if (arch.GetMachine() == llvm::Triple::arm) {
|
||||
if (next_flags & 0x20)
|
||||
// Thumb mode
|
||||
return 2;
|
||||
// Arm mode
|
||||
return 4;
|
||||
}
|
||||
if (arch.IsMIPS() || arch.GetTriple().isPPC64() ||
|
||||
arch.GetTriple().isRISCV() || arch.GetTriple().isLoongArch())
|
||||
return 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Status SetSoftwareBreakpointOnPC(const ArchSpec &arch, lldb::addr_t pc,
|
||||
lldb::addr_t next_flags,
|
||||
NativeProcessProtocol &process) {
|
||||
int size_hint = GetSoftwareBreakpointSize(arch, next_flags);
|
||||
static Status SetSoftwareBreakpoint(lldb::addr_t bp_addr, unsigned bp_size,
|
||||
NativeProcessProtocol &process) {
|
||||
Status error;
|
||||
error = process.SetBreakpoint(pc, size_hint, /*hardware=*/false);
|
||||
error = process.SetBreakpoint(bp_addr, bp_size, /*hardware=*/false);
|
||||
|
||||
// If setting the breakpoint fails because pc is out of the address
|
||||
// space, ignore it and let the debugee segfault.
|
||||
@@ -136,7 +112,6 @@ Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping(
|
||||
std::unique_ptr<EmulateInstruction> emulator_up(
|
||||
EmulateInstruction::FindPlugin(arch, eInstructionTypePCModifying,
|
||||
nullptr));
|
||||
|
||||
if (emulator_up == nullptr)
|
||||
return Status::FromErrorString("Instruction emulator not found!");
|
||||
|
||||
@@ -147,65 +122,24 @@ Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping(
|
||||
emulator_up->SetWriteMemCallback(&WriteMemoryCallback);
|
||||
emulator_up->SetWriteRegCallback(&WriteRegisterCallback);
|
||||
|
||||
if (!emulator_up->ReadInstruction()) {
|
||||
// try to get at least the size of next instruction to set breakpoint.
|
||||
auto instr_size = emulator_up->GetLastInstrSize();
|
||||
if (!instr_size)
|
||||
return Status::FromErrorString("Read instruction failed!");
|
||||
bool success = false;
|
||||
auto pc = emulator_up->ReadRegisterUnsigned(eRegisterKindGeneric,
|
||||
LLDB_REGNUM_GENERIC_PC,
|
||||
LLDB_INVALID_ADDRESS, &success);
|
||||
if (!success)
|
||||
return Status::FromErrorString("Reading pc failed!");
|
||||
lldb::addr_t next_pc = pc + *instr_size;
|
||||
auto result =
|
||||
SetSoftwareBreakpointOnPC(arch, next_pc, /* next_flags */ 0x0, process);
|
||||
m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
|
||||
return result;
|
||||
auto bp_locaions_predictor =
|
||||
EmulateInstruction::CreateBreakpointLocationPredictor(
|
||||
std::move(emulator_up));
|
||||
|
||||
auto bp_locations = bp_locaions_predictor->GetBreakpointLocations(error);
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
for (auto &&bp_addr : bp_locations) {
|
||||
auto bp_size = bp_locaions_predictor->GetBreakpointSize(bp_addr);
|
||||
if (auto err = bp_size.takeError())
|
||||
return Status(toString(std::move(err)));
|
||||
|
||||
error = SetSoftwareBreakpoint(bp_addr, *bp_size, process);
|
||||
if (error.Fail())
|
||||
return error;
|
||||
}
|
||||
|
||||
bool emulation_result =
|
||||
emulator_up->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
|
||||
|
||||
const RegisterInfo *reg_info_pc = register_context.GetRegisterInfo(
|
||||
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
|
||||
const RegisterInfo *reg_info_flags = register_context.GetRegisterInfo(
|
||||
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
|
||||
|
||||
auto pc_it =
|
||||
baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]);
|
||||
auto flags_it = reg_info_flags == nullptr
|
||||
? baton.m_register_values.end()
|
||||
: baton.m_register_values.find(
|
||||
reg_info_flags->kinds[eRegisterKindDWARF]);
|
||||
|
||||
lldb::addr_t next_pc;
|
||||
lldb::addr_t next_flags;
|
||||
if (emulation_result) {
|
||||
assert(pc_it != baton.m_register_values.end() &&
|
||||
"Emulation was successfull but PC wasn't updated");
|
||||
next_pc = pc_it->second.GetAsUInt64();
|
||||
|
||||
if (flags_it != baton.m_register_values.end())
|
||||
next_flags = flags_it->second.GetAsUInt64();
|
||||
else
|
||||
next_flags = ReadFlags(register_context);
|
||||
} else if (pc_it == baton.m_register_values.end()) {
|
||||
// Emulate instruction failed and it haven'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 a not supported instruction which don't modify PC.
|
||||
next_pc = register_context.GetPC() + emulator_up->GetOpcode().GetByteSize();
|
||||
next_flags = ReadFlags(register_context);
|
||||
} else {
|
||||
// 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.
|
||||
return Status::FromErrorString(
|
||||
"Instruction emulation failed unexpectedly.");
|
||||
}
|
||||
auto result = SetSoftwareBreakpointOnPC(arch, next_pc, next_flags, process);
|
||||
m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
|
||||
return result;
|
||||
m_threads_stepping_with_breakpoint.insert({thread.GetID(), bp_locations});
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ public:
|
||||
protected:
|
||||
// List of thread ids stepping with a breakpoint with the address of
|
||||
// the relevan breakpoint
|
||||
std::map<lldb::tid_t, lldb::addr_t> m_threads_stepping_with_breakpoint;
|
||||
std::map<lldb::tid_t, std::vector<lldb::addr_t>>
|
||||
m_threads_stepping_with_breakpoint;
|
||||
};
|
||||
|
||||
} // namespace lldb_private
|
||||
|
||||
Reference in New Issue
Block a user