Skip to content

Commit 1c95d80

Browse files
barsolo2000Bar Soloveychik
andauthored
RISCV enable assembly unwinding (#158161)
**Added emulator unwinding support for RISCV files.** Emulated Instructions: ADD (addi sp, sp, imm) STORE (sd ra, offset(sp)) LOAD (ld ra, offset(sp)). We had to overwrite SetInstructions() since UnwindAssemblyInstEmulation calls EvaluateInstruction() directly after calling SetInstruction(), but it never calls ReadInstruction(). This means that the m_decoded member variable in the instruction emulator is never properly initialized. By overriding SetInstruction(), we decode the instruction bytes and set m_decoded directly. This ensures that subsequent emulation (including unwinding) operates on the correct instruction. We also had to change the the OpCode GetOpcodeBytes function since recent changes made it so GetOpcodeBytes will return None for type eType16_32Tuples (an alternative and longer way, would've been to type check during the overwritten SetInstruction() and call a DataExtractor with .GetU16(&offset) to set the inst_data. Added a test - TestSimpleRiscvFunction (took inspiration from: [link](https://github.com/llvm/llvm-project/blob/main/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp)) [----------] 1 test from TestRiscvInstEmulation [ RUN ] TestRiscvInstEmulation.TestSimpleRiscvFunction [ OK ] TestRiscvInstEmulation.TestSimpleRiscvFunction (1 ms) [----------] 1 test from TestRiscvInstEmulation (1 ms total) [----------] Global test environment tear-down [==========] 63 tests from 5 test suites ran. (11 ms total) [ PASSED ] 63 tests. --------- Co-authored-by: Bar Soloveychik <[email protected]>
1 parent 1cee4fa commit 1c95d80

File tree

5 files changed

+364
-13
lines changed

5 files changed

+364
-13
lines changed

lldb/include/lldb/Core/Opcode.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,9 @@ class Opcode {
223223
int Dump(Stream *s, uint32_t min_byte_width) const;
224224

225225
const void *GetOpcodeBytes() const {
226-
return ((m_type == Opcode::eTypeBytes) ? m_data.inst.bytes : nullptr);
226+
return ((m_type == Opcode::eTypeBytes || m_type == Opcode::eType16_32Tuples)
227+
? m_data.inst.bytes
228+
: nullptr);
227229
}
228230

229231
uint32_t GetByteSize() const {

lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp

Lines changed: 164 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionRISCV, InstructionRISCV)
3333

3434
namespace lldb_private {
3535

36+
// RISC-V General Purpose Register numbers
37+
static constexpr uint32_t RISCV_GPR_SP = 2; // x2 is the stack pointer
38+
static constexpr uint32_t RISCV_GPR_FP = 8; // x8 is the frame pointer
39+
3640
/// Returns all values wrapped in Optional, or std::nullopt if any of the values
3741
/// is std::nullopt.
3842
template <typename... Ts>
@@ -108,6 +112,16 @@ static uint32_t FPREncodingToLLDB(uint32_t reg_encode) {
108112
return LLDB_INVALID_REGNUM;
109113
}
110114

115+
// Helper function to get register info from GPR encoding
116+
static std::optional<RegisterInfo>
117+
GPREncodingToRegisterInfo(EmulateInstructionRISCV &emulator,
118+
uint32_t reg_encode) {
119+
uint32_t lldb_reg = GPREncodingToLLDB(reg_encode);
120+
if (lldb_reg == LLDB_INVALID_REGNUM)
121+
return std::nullopt;
122+
return emulator.GetRegisterInfo(eRegisterKindLLDB, lldb_reg);
123+
}
124+
111125
bool Rd::Write(EmulateInstructionRISCV &emulator, uint64_t value) {
112126
uint32_t lldb_reg = GPREncodingToLLDB(rd);
113127
EmulateInstruction::Context ctx;
@@ -230,10 +244,34 @@ Load(EmulateInstructionRISCV &emulator, I inst, uint64_t (*extend)(E)) {
230244
auto addr = LoadStoreAddr(emulator, inst);
231245
if (!addr)
232246
return false;
233-
return transformOptional(
234-
emulator.ReadMem<T>(*addr),
235-
[&](T t) { return inst.rd.Write(emulator, extend(E(t))); })
236-
.value_or(false);
247+
248+
// Set up context for the load operation, similar to ARM64.
249+
EmulateInstructionRISCV::Context context;
250+
251+
// Get register info for base register
252+
std::optional<RegisterInfo> reg_info_rs1 =
253+
GPREncodingToRegisterInfo(emulator, inst.rs1.rs);
254+
255+
if (!reg_info_rs1)
256+
return false;
257+
258+
// Set context type based on whether this is a stack-based load.
259+
if (inst.rs1.rs == RISCV_GPR_SP)
260+
context.type = EmulateInstruction::eContextPopRegisterOffStack;
261+
else
262+
context.type = EmulateInstruction::eContextRegisterLoad;
263+
264+
// Set the context address information
265+
context.SetAddress(*addr);
266+
267+
// Read from memory with context and write to register.
268+
bool success = false;
269+
uint64_t value =
270+
emulator.ReadMemoryUnsigned(context, *addr, sizeof(T), 0, &success);
271+
if (!success)
272+
return false;
273+
274+
return inst.rd.Write(emulator, extend(E(T(value))));
237275
}
238276

239277
template <typename I, typename T>
@@ -242,9 +280,35 @@ Store(EmulateInstructionRISCV &emulator, I inst) {
242280
auto addr = LoadStoreAddr(emulator, inst);
243281
if (!addr)
244282
return false;
245-
return transformOptional(
246-
inst.rs2.Read(emulator),
247-
[&](uint64_t rs2) { return emulator.WriteMem<T>(*addr, rs2); })
283+
284+
// Set up context for the store operation, similar to ARM64.
285+
EmulateInstructionRISCV::Context context;
286+
287+
// Get register info for source and base registers.
288+
std::optional<RegisterInfo> reg_info_rs1 =
289+
GPREncodingToRegisterInfo(emulator, inst.rs1.rs);
290+
std::optional<RegisterInfo> reg_info_rs2 =
291+
GPREncodingToRegisterInfo(emulator, inst.rs2.rs);
292+
293+
if (!reg_info_rs1 || !reg_info_rs2)
294+
return false;
295+
296+
// Set context type based on whether this is a stack-based store.
297+
if (inst.rs1.rs == RISCV_GPR_SP)
298+
context.type = EmulateInstruction::eContextPushRegisterOnStack;
299+
else
300+
context.type = EmulateInstruction::eContextRegisterStore;
301+
302+
// Set the context to show which register is being stored to which base
303+
// register + offset.
304+
context.SetRegisterToRegisterPlusOffset(*reg_info_rs2, *reg_info_rs1,
305+
SignExt(inst.imm));
306+
307+
return transformOptional(inst.rs2.Read(emulator),
308+
[&](uint64_t rs2) {
309+
return emulator.WriteMemoryUnsigned(
310+
context, *addr, rs2, sizeof(T));
311+
})
248312
.value_or(false);
249313
}
250314

@@ -737,11 +801,44 @@ class Executor {
737801
bool operator()(SH inst) { return Store<SH, uint16_t>(m_emu, inst); }
738802
bool operator()(SW inst) { return Store<SW, uint32_t>(m_emu, inst); }
739803
bool operator()(ADDI inst) {
740-
return transformOptional(inst.rs1.ReadI64(m_emu),
741-
[&](int64_t rs1) {
742-
return inst.rd.Write(
743-
m_emu, rs1 + int64_t(SignExt(inst.imm)));
744-
})
804+
return transformOptional(
805+
inst.rs1.ReadI64(m_emu),
806+
[&](int64_t rs1) {
807+
int64_t result = rs1 + int64_t(SignExt(inst.imm));
808+
// Check if this is a stack pointer adjustment.
809+
if (inst.rd.rd == RISCV_GPR_SP &&
810+
inst.rs1.rs == RISCV_GPR_SP) {
811+
EmulateInstruction::Context context;
812+
context.type =
813+
EmulateInstruction::eContextAdjustStackPointer;
814+
context.SetImmediateSigned(SignExt(inst.imm));
815+
uint32_t sp_lldb_reg = GPREncodingToLLDB(RISCV_GPR_SP);
816+
RegisterValue registerValue;
817+
registerValue.SetUInt64(result);
818+
return m_emu.WriteRegister(context, eRegisterKindLLDB,
819+
sp_lldb_reg, registerValue);
820+
}
821+
// Check if this is setting up the frame pointer.
822+
// addi fp, sp, imm -> fp = sp + imm (frame pointer setup).
823+
if (inst.rd.rd == RISCV_GPR_FP &&
824+
inst.rs1.rs == RISCV_GPR_SP) {
825+
EmulateInstruction::Context context;
826+
context.type = EmulateInstruction::eContextSetFramePointer;
827+
auto sp_reg_info = m_emu.GetRegisterInfo(
828+
eRegisterKindLLDB, GPREncodingToLLDB(RISCV_GPR_SP));
829+
if (sp_reg_info) {
830+
context.SetRegisterPlusOffset(*sp_reg_info,
831+
SignExt(inst.imm));
832+
}
833+
uint32_t fp_lldb_reg = GPREncodingToLLDB(RISCV_GPR_FP);
834+
RegisterValue registerValue;
835+
registerValue.SetUInt64(result);
836+
return m_emu.WriteRegister(context, eRegisterKindLLDB,
837+
fp_lldb_reg, registerValue);
838+
}
839+
// Regular ADDI instruction.
840+
return inst.rd.Write(m_emu, result);
841+
})
745842
.value_or(false);
746843
}
747844
bool operator()(SLTI inst) {
@@ -1745,6 +1842,61 @@ EmulateInstructionRISCV::GetRegisterInfo(RegisterKind reg_kind,
17451842
return array[reg_index];
17461843
}
17471844

1845+
bool EmulateInstructionRISCV::SetInstruction(const Opcode &opcode,
1846+
const Address &inst_addr,
1847+
Target *target) {
1848+
// Call the base class implementation.
1849+
if (!EmulateInstruction::SetInstruction(opcode, inst_addr, target))
1850+
return false;
1851+
1852+
// Extract instruction data from the opcode.
1853+
uint32_t inst_data = 0;
1854+
const void *opcode_data = m_opcode.GetOpcodeBytes();
1855+
if (!opcode_data)
1856+
return false;
1857+
1858+
if (m_opcode.GetByteSize() == 2) {
1859+
// 16-bit compressed instruction.
1860+
const uint16_t *data = static_cast<const uint16_t *>(opcode_data);
1861+
inst_data = *data;
1862+
} else if (m_opcode.GetByteSize() == 4) {
1863+
// 32-bit instruction.
1864+
const uint32_t *data = static_cast<const uint32_t *>(opcode_data);
1865+
inst_data = *data;
1866+
} else {
1867+
return false;
1868+
}
1869+
1870+
// Decode the instruction.
1871+
auto decoded_inst = Decode(inst_data);
1872+
if (!decoded_inst)
1873+
return false;
1874+
1875+
// Store the decoded result.
1876+
m_decoded = *decoded_inst;
1877+
return true;
1878+
}
1879+
1880+
bool EmulateInstructionRISCV::CreateFunctionEntryUnwind(
1881+
UnwindPlan &unwind_plan) {
1882+
unwind_plan.Clear();
1883+
unwind_plan.SetRegisterKind(eRegisterKindLLDB);
1884+
1885+
UnwindPlan::Row row;
1886+
1887+
row.GetCFAValue().SetIsRegisterPlusOffset(gpr_sp_riscv, 0);
1888+
row.SetRegisterLocationToSame(gpr_ra_riscv, /*must_replace=*/false);
1889+
row.SetRegisterLocationToSame(gpr_fp_riscv, /*must_replace=*/false);
1890+
1891+
unwind_plan.AppendRow(std::move(row));
1892+
unwind_plan.SetSourceName("EmulateInstructionRISCV");
1893+
unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
1894+
unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
1895+
unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo);
1896+
unwind_plan.SetReturnAddressRegister(gpr_ra_riscv);
1897+
return true;
1898+
}
1899+
17481900
bool EmulateInstructionRISCV::SetTargetTriple(const ArchSpec &arch) {
17491901
return SupportsThisArch(arch);
17501902
}

lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class EmulateInstructionRISCV : public EmulateInstruction {
6161
case eInstructionTypePCModifying:
6262
return true;
6363
case eInstructionTypePrologueEpilogue:
64+
return true;
6465
case eInstructionTypeAll:
6566
return false;
6667
}
@@ -85,6 +86,7 @@ class EmulateInstructionRISCV : public EmulateInstruction {
8586
return SupportsThisInstructionType(inst_type);
8687
}
8788

89+
bool CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) override;
8890
bool SetTargetTriple(const ArchSpec &arch) override;
8991
bool ReadInstruction() override;
9092
std::optional<uint32_t> GetLastInstrSize() override { return m_last_size; }
@@ -94,6 +96,8 @@ class EmulateInstructionRISCV : public EmulateInstruction {
9496
std::optional<RegisterInfo> GetRegisterInfo(lldb::RegisterKind reg_kind,
9597
uint32_t reg_num) override;
9698

99+
bool SetInstruction(const Opcode &opcode, const Address &inst_addr,
100+
Target *target) override;
97101
std::optional<DecodeResult> ReadInstructionAt(lldb::addr_t addr);
98102
std::optional<DecodeResult> Decode(uint32_t inst);
99103
bool Execute(DecodeResult inst, bool ignore_cond);

lldb/unittests/Instruction/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@ add_lldb_unittest(EmulatorTests
22
ARM64/TestAArch64Emulator.cpp
33
LoongArch/TestLoongArchEmulator.cpp
44
RISCV/TestRISCVEmulator.cpp
5+
RISCV/TestRiscvInstEmulation.cpp
56

67
LINK_COMPONENTS
78
Support
9+
${LLVM_TARGETS_TO_BUILD}
810
LINK_LIBS
911
lldbCore
1012
lldbSymbol
1113
lldbTarget
1214
lldbPluginInstructionARM64
1315
lldbPluginInstructionLoongArch
1416
lldbPluginInstructionRISCV
17+
lldbPluginDisassemblerLLVMC
18+
lldbPluginUnwindAssemblyInstEmulation
19+
lldbPluginProcessUtility
1520
)

0 commit comments

Comments
 (0)