From: timurgol007 Date: Wed, 2 Jul 2025 15:59:34 +0000 (+0300) Subject: gdb/record: Support wfi, sfence.vma, sret and mret instructions in risc-v X-Git-Tag: gdb-17-branchpoint~147 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a784750d1691da97b12cc69841496f928872a948;p=thirdparty%2Fbinutils-gdb.git gdb/record: Support wfi, sfence.vma, sret and mret instructions in risc-v During testing of bare-metal applications on QEMU for RISC-V, it was discovered that the instructions wfi, sfence.vma, sret, and mret were not supported. This patch introduces support for these instructions. Additionally, it wraps fetch_instruction function in a try-catch block to gracefully handle errors that may occur when attempting to read invalid address. Reviewed-By: Guinevere Larsen Approved-By: Andrew Burgess --- diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c index f5b85230e71..680176ba2a4 100644 --- a/gdb/riscv-tdep.c +++ b/gdb/riscv-tdep.c @@ -4924,6 +4924,8 @@ public: /* Corner cases. */ ECALL, EBREAK, + SRET, + MRET, }; private: @@ -5008,6 +5010,14 @@ private: return (ival >> OP_SH_CSR) & OP_MASK_CSR; } + /* Set any record type. Always returns true. */ + bool + set_record_type (record_type type) noexcept + { + m_record_type = type; + return true; + } + /* Set ordinary record type. Always returns true. */ bool set_ordinary_record_type () noexcept @@ -5066,7 +5076,8 @@ private: return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival) || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival) || is_fence_insn (ival) || is_pause_insn (ival) - || is_fence_i_insn (ival)); + || is_fence_i_insn (ival) || is_wfi_insn (ival) + || is_sfence_vma_insn (ival)); } /* Returns true if instruction is classified. */ @@ -5270,14 +5281,26 @@ private: if (is_ecall_insn (ival)) { - m_record_type = record_type::ECALL; - return true; + return set_record_type (record_type::ECALL); } if (is_ebreak_insn (ival)) { - m_record_type = record_type::EBREAK; - return true; + return set_record_type (record_type::EBREAK); + } + + if (is_sret_insn (ival)) + { + return (!save_reg (RISCV_CSR_SSTATUS_REGNUM) + || !save_reg (RISCV_CSR_MEPC_REGNUM) + || set_record_type (record_type::SRET)); + } + + if (is_mret_insn (ival)) + { + return (!save_reg (RISCV_CSR_MSTATUS_REGNUM) + || !save_reg (RISCV_CSR_MEPC_REGNUM) + || set_record_type (record_type::MRET)); } if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival) @@ -5371,8 +5394,7 @@ private: if (is_c_ebreak_insn (ival)) { - m_record_type = record_type::EBREAK; - return true; + return set_record_type (record_type::EBREAK); } if (is_c_jalr_insn (ival)) @@ -5414,8 +5436,20 @@ public: gdb_assert (regcache != nullptr); int m_length = 0; + ULONGEST ival = 0; m_xlen = riscv_isa_xlen (gdbarch); - ULONGEST ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length); + + /* Since fetch_instruction can throw an exception, + it must be wrapped in a try-catch block. */ + try + { + ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length); + } + catch (const gdb_exception_error &ex) + { + warning ("%s", ex.what ()); + return false; + } if (!save_reg (RISCV_PC_REGNUM)) return false; @@ -5509,6 +5543,9 @@ riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache, switch (insn.get_record_type ()) { case riscv_recorded_insn::record_type::ORDINARY: + case riscv_recorded_insn::record_type::EBREAK: + case riscv_recorded_insn::record_type::SRET: + case riscv_recorded_insn::record_type::MRET: break; case riscv_recorded_insn::record_type::ECALL: @@ -5524,9 +5561,6 @@ riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache, return tdep->riscv_syscall_record (regcache, reg_val); } - case riscv_recorded_insn::record_type::EBREAK: - break; - default: return -1; }