gdb: Support the c.mv insn in the riscv prologue scanner.
authorLancelot SIX <lsix@lancelotsix.com>
Mon, 27 Sep 2021 21:56:58 +0000 (21:56 +0000)
committerLancelot SIX <lsix@lancelotsix.com>
Sun, 3 Oct 2021 14:00:58 +0000 (14:00 +0000)
While working on other problems, I encountered situations where GDB
fails to properly unwind the stack because some functions use the C.MV
instruction in the prologue.  The prologue scanner stops when it hits
this instruction assuming its job is done at this point.  Unfortunately
the prologue is not necessarily finished yet, preventing GDB to properly
unwind.

This commit adds support for handling such instruction in
riscv_scan_prologue.

Note that C.MV is part of the compressed instruction set.  The MV
counterpart from the base ISA is a pseudo instruction that expands to
'ADDI RD,RS1,0' which is already supported.

Tested on riscv64-linux-gnu.

All feedback are welcome.

gdb/riscv-tdep.c
gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-mv.c [new file with mode: 0644]
gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-mv.exp [new file with mode: 0644]
gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-mv.s [new file with mode: 0644]

index 8b55bc33dedd956928937b3af4f11471ba81ace6..b8844b61aae1eb8a0f6193260f6d975aa6743370 100644 (file)
@@ -1411,6 +1411,7 @@ public:
       SW,
       LD,
       LW,
+      MV,
       /* These are needed for software breakpoint support.  */
       JAL,
       JALR,
@@ -1789,9 +1790,11 @@ riscv_insn::decode (struct gdbarch *gdbarch, CORE_ADDR pc)
       else if (xlen != 4 && is_c_sdsp_insn (ival))
        decode_css_type_insn (SD, ival, EXTRACT_CSSTYPE_SDSP_IMM (ival));
       /* C_JR and C_MV have the same opcode.  If RS2 is 0, then this is a C_JR.
-        So must try to match C_JR first as it ahs more bits in mask.  */
+        So must try to match C_JR first as it has more bits in mask.  */
       else if (is_c_jr_insn (ival))
        decode_cr_type_insn (JALR, ival);
+      else if (is_c_mv_insn (ival))
+       decode_cr_type_insn (MV, ival);
       else if (is_c_j_insn (ival))
        decode_cj_type_insn (JAL, ival);
       else if (is_c_beqz_insn (ival))
@@ -1964,6 +1967,14 @@ riscv_scan_prologue (struct gdbarch *gdbarch,
                                            insn.imm_signed ()),
                           (insn.opcode () == riscv_insn::LW ? 4 : 8));
        }
+      else if (insn.opcode () == riscv_insn::MV)
+       {
+         /* Handle: c.mv RD, RS2  */
+         gdb_assert (insn.rd () < RISCV_NUM_INTEGER_REGS);
+         gdb_assert (insn.rs2 () < RISCV_NUM_INTEGER_REGS);
+         gdb_assert (insn.rs2 () > 0);
+         regs[insn.rd ()] = regs[insn.rs2 ()];
+       }
       else
        {
          end_prologue_addr = cur_pc;
diff --git a/gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-mv.c b/gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-mv.c
new file mode 100644 (file)
index 0000000..bb8a8ee
--- /dev/null
@@ -0,0 +1,29 @@
+/* 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 <http://www.gnu.org/licenses/>.  */
+
+/* See riscv64-unwind-prologue-with-mv.s for implementation.  */
+
+extern int bar ();
+
+/* See riscv64-unwind-prologue-with-mv.s for implementation.  */
+
+extern int foo ();
+
+int
+main ()
+{
+  return foo ();
+}
+
diff --git a/gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-mv.exp b/gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-mv.exp
new file mode 100644 (file)
index 0000000..47429ba
--- /dev/null
@@ -0,0 +1,44 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# This tests GDB's ability to use the RISC-V prologue scanner in order to
+# unwind through a function that uses the 'c.mv' instruction in its prologue.
+
+if {![istarget "riscv64-*-*"]} {
+    verbose "Skipping ${gdb_test_file_name}."
+    return
+}
+
+standard_testfile .c .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-mv.s b/gdb/testsuite/gdb.arch/riscv64-unwind-prologue-with-mv.s
new file mode 100644 (file)
index 0000000..a1ec538
--- /dev/null
@@ -0,0 +1,47 @@
+/* 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 <http://www.gnu.org/licenses/>.  */
+
+/* This testcase contains a function where the 'c.mv' instruction is used in
+   the prologue.
+
+   The following functions are roughly equivalent to the following C code (with
+   prologue crafted to contain the c.mv instruction):
+
+     int bar () { return 0; }
+     int foo () { return bar (); } */
+
+       .option pic
+       .text
+       .align  1
+       .globl  bar
+       .type   bar, @function
+bar:
+       li      a0,0
+       jr      ra
+       .size   bar, .-bar
+
+       .align  1
+       .globl  foo
+       .type   foo, @function
+foo:
+       addi    sp,sp,-32
+       c.mv    t3,ra
+       sd      t3,8(sp)
+       call    bar
+       ld      t3,8(sp)
+       mv      ra,t3
+       addi    sp,sp,32
+       jr      ra
+       .size   foo, .-foo