diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c index b5b0d2d79ded..8b55bc33dedd 100644 --- a/gdb/riscv-tdep.c +++ b/gdb/riscv-tdep.c @@ -1409,6 +1409,8 @@ class riscv_insn LUI, SD, SW, + LD, + LW, /* These are needed for software breakpoint support. */ JAL, JALR, @@ -1519,6 +1521,15 @@ class riscv_insn m_imm.s = EXTRACT_CITYPE_IMM (ival); } + /* Helper for DECODE, decode 16-bit compressed CL-type instruction. */ + void decode_cl_type_insn (enum opcode opcode, ULONGEST ival) + { + m_opcode = opcode; + m_rd = decode_register_index_short (ival, OP_SH_CRS2S); + m_rs1 = decode_register_index_short (ival, OP_SH_CRS1S); + m_imm.s = EXTRACT_CLTYPE_IMM (ival); + } + /* Helper for DECODE, decode 32-bit S-type instruction. */ void decode_s_type_insn (enum opcode opcode, ULONGEST ival) { @@ -1715,6 +1726,10 @@ riscv_insn::decode (struct gdbarch *gdbarch, CORE_ADDR pc) decode_r_type_insn (SC, ival); else if (is_ecall_insn (ival)) decode_i_type_insn (ECALL, ival); + else if (is_ld_insn (ival)) + decode_i_type_insn (LD, ival); + else if (is_lw_insn (ival)) + decode_i_type_insn (LW, ival); else /* None of the other fields are valid in this case. */ m_opcode = OTHER; @@ -1783,6 +1798,10 @@ riscv_insn::decode (struct gdbarch *gdbarch, CORE_ADDR pc) decode_cb_type_insn (BEQ, ival); else if (is_c_bnez_insn (ival)) decode_cb_type_insn (BNE, ival); + else if (is_c_ld_insn (ival)) + decode_cl_type_insn (LD, ival); + else if (is_c_lw_insn (ival)) + decode_cl_type_insn (LW, ival); else /* None of the other fields of INSN are valid in this case. */ m_opcode = OTHER; @@ -1931,6 +1950,20 @@ riscv_scan_prologue (struct gdbarch *gdbarch, gdb_assert (insn.rs2 () < RISCV_NUM_INTEGER_REGS); regs[insn.rd ()] = pv_add (regs[insn.rs1 ()], regs[insn.rs2 ()]); } + else if (insn.opcode () == riscv_insn::LD + || insn.opcode () == riscv_insn::LW) + { + /* Handle: ld reg, offset(rs1) + or: c.ld reg, offset(rs1) + or: lw reg, offset(rs1) + or: c.lw reg, offset(rs1) */ + gdb_assert (insn.rd () < RISCV_NUM_INTEGER_REGS); + gdb_assert (insn.rs1 () < RISCV_NUM_INTEGER_REGS); + regs[insn.rd ()] + = stack.fetch (pv_add_constant (regs[insn.rs1 ()], + insn.imm_signed ()), + (insn.opcode () == riscv_insn::LW ? 4 : 8)); + } else { end_prologue_addr = cur_pc; diff --git a/gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-ld-lw-foo.s b/gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-ld-lw-foo.s new file mode 100644 index 000000000000..ebc27ff1e8c0 --- /dev/null +++ b/gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-ld-lw-foo.s @@ -0,0 +1,74 @@ +/* Copyright 2021 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* This testcase contains a function where the 'ld', 'c.ld', 'lw' or 'c.lw' + instruction is used in the prologue before the RA register have been saved + on the stack. + + This mimics a pattern observed in the __pthread_clockjoin_ex function + in libpthread.so.0 (from glibc-2.33-0ubuntu5) where a canary value is + loaded and placed on the stack in order to detect stack smashing. + + The skeleton for this file was generated using the following command: + + gcc -x c -S -c -o - - <. + +# This tests GDB's ability to use the RISC-V prologue scanner in order to +# unwind through a function that uses the 'ld' instruction in its prologue. + +if {![istarget "riscv64-*-*"]} { + verbose "Skipping ${gdb_test_file_name}." + return +} + +standard_testfile riscv64-unwind-prologue-with-ld.c \ + riscv64-unwind-prologue-with-ld-lw-foo.s +if {[prepare_for_testing "failed to prepare" $testfile \ + "$srcfile $srcfile2" nodebug]} { + return -1 +} + +if ![runto_main] then { + fail "can't run to main" + return 0 +} + +gdb_breakpoint "bar" +gdb_continue_to_breakpoint "bar" +gdb_test "bt" \ + [multi_line \ + "#0\[ \t\]*$hex in bar \\\(\\\)" \ + "#1\[ \t\]*$hex in foo \\\(\\\)" \ + "#2\[ \t\]*$hex in main \\\(\\\)"] \ + "Backtrace to the main frame" +gdb_test "finish" "foo \\\(\\\)" "finish bar" +gdb_test "finish" "main \\\(\\\)" "finish foo" diff --git a/gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-ld.c b/gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-ld.c new file mode 100644 index 000000000000..9ff950df2731 --- /dev/null +++ b/gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-ld.c @@ -0,0 +1,30 @@ +/* Copyright 2021 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* See riscv64-unwind-prologue-with-ld-foo.s for implementation. */ +extern int foo (void); + +int +bar () +{ + return 0; +} + +int +main () +{ + return foo (); +} +