gdb/riscv: Improve non-dwarf stack unwinding
authorAndrew Burgess <andrew.burgess@embecosm.com>
Wed, 12 Sep 2018 16:35:08 +0000 (17:35 +0100)
committerAndrew Burgess <andrew.burgess@embecosm.com>
Wed, 26 Sep 2018 13:08:39 +0000 (14:08 +0100)
This commit improves the prologue scanning stack unwinder, to better
support AUIPC, LUI, and more variants of ADD and ADDI.

This allows unwinding over frames containing large local variables,
where the frame size does not fit into a single instruction immediate,
and is first loaded into a temporary register, before being added to
the stack pointer.

A new test is added that tests this behaviour.  As there's nothing
truely RiscV specific about this test I've added it into gdb.base, but
as this depends on target specific code to perform the unwind it is
possible that some targets might fail this new test.

gdb/ChangeLog:

* riscv-tdep.c (riscv_insn::decode): Decode c.lui.
(riscv_scan_prologue): Split handling of AUIPC, LUI, ADD, ADDI,
and NOP.

gdb/testsuite/ChangeLog:

* gdb.base/large-frame-1.c: New file.
* gdb.base/large-frame-2.c: New file.
* gdb.base/large-frame.exp: New file.
* gdb.base/large-frame.h: New file.

gdb/ChangeLog
gdb/riscv-tdep.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/large-frame-1.c [new file with mode: 0644]
gdb/testsuite/gdb.base/large-frame-2.c [new file with mode: 0644]
gdb/testsuite/gdb.base/large-frame.exp [new file with mode: 0644]
gdb/testsuite/gdb.base/large-frame.h [new file with mode: 0644]

index 70fc324985db60d1c52ee5ec0e079b444558d557..8cf9a6c9cadf79106fa0c66a1715c0a7010e9bd6 100644 (file)
@@ -1,3 +1,9 @@
+2018-09-26  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * riscv-tdep.c (riscv_insn::decode): Decode c.lui.
+       (riscv_scan_prologue): Split handling of AUIPC, LUI, ADD, ADDI,
+       and NOP.
+
 2018-09-26  Simon Marchi  <simon.marchi@ericsson.com>
 
        * elf32-nds32.c (elf32_nds32_allocate_dynrelocs): Remove.
index 254914c9c7e2b86f3392913aba4bfc510d042501..163c8ec231d1848f6f15bfe17393f63138268d84 100644 (file)
@@ -1227,7 +1227,11 @@ riscv_insn::decode (struct gdbarch *gdbarch, CORE_ADDR pc)
          m_imm.s = EXTRACT_RVC_ADDI4SPN_IMM (ival);
        }
       else if (is_c_lui_insn (ival))
-       m_opcode = OTHER;
+        {
+          m_opcode = LUI;
+          m_rd = decode_register_index (ival, OP_SH_CRS1S);
+          m_imm.s = EXTRACT_RVC_LUI_IMM (ival);
+        }
       /* C_SD and C_FSW have the same opcode.  C_SD is RV64 and RV128 only,
         and C_FSW is RV32 only.  */
       else if (xlen != 4 && is_c_sd_insn (ival))
@@ -1359,28 +1363,41 @@ riscv_scan_prologue (struct gdbarch *gdbarch,
           gdb_assert (insn.rs1 () < RISCV_NUM_INTEGER_REGS);
           regs[insn.rd ()] = pv_add_constant (regs[insn.rs1 ()], 0);
        }
-      else if ((insn.rd () == RISCV_GP_REGNUM
-               && (insn.opcode () == riscv_insn::AUIPC
-                   || insn.opcode () == riscv_insn::LUI
-                   || (insn.opcode () == riscv_insn::ADDI
-                       && insn.rs1 () == RISCV_GP_REGNUM)
-                   || (insn.opcode () == riscv_insn::ADD
-                       && (insn.rs1 () == RISCV_GP_REGNUM
-                           || insn.rs2 () == RISCV_GP_REGNUM))))
-              || (insn.opcode () == riscv_insn::ADDI
-                  && insn.rd () == RISCV_ZERO_REGNUM
-                  && insn.rs1 () == RISCV_ZERO_REGNUM
-                  && insn.imm_signed () == 0))
+      else if ((insn.opcode () == riscv_insn::ADDI
+                && insn.rd () == RISCV_ZERO_REGNUM
+                && insn.rs1 () == RISCV_ZERO_REGNUM
+                && insn.imm_signed () == 0))
        {
-         /* Handle: auipc gp, n
-            or:     addi gp, gp, n
-            or:     add gp, gp, reg
-            or:     add gp, reg, gp
-            or:     lui gp, n
-            or:     add x0, x0, 0   (NOP)  */
-         /* These instructions are part of the prologue, but we don't need
-            to do anything special to handle them.  */
+         /* Handle: add x0, x0, 0   (NOP)  */
        }
+      else if (insn.opcode () == riscv_insn::AUIPC)
+        {
+          gdb_assert (insn.rd () < RISCV_NUM_INTEGER_REGS);
+          regs[insn.rd ()] = pv_constant (cur_pc + insn.imm_signed ());
+        }
+      else if (insn.opcode () == riscv_insn::LUI)
+        {
+         /* Handle: lui REG, n
+             Where REG is not gp register.  */
+          gdb_assert (insn.rd () < RISCV_NUM_INTEGER_REGS);
+          regs[insn.rd ()] = pv_constant (insn.imm_signed ());
+        }
+      else if (insn.opcode () == riscv_insn::ADDI)
+        {
+          /* Handle: addi REG1, REG2, IMM  */
+          gdb_assert (insn.rd () < RISCV_NUM_INTEGER_REGS);
+          gdb_assert (insn.rs1 () < RISCV_NUM_INTEGER_REGS);
+          regs[insn.rd ()]
+            = pv_add_constant (regs[insn.rs1 ()], insn.imm_signed ());
+        }
+      else if (insn.opcode () == riscv_insn::ADD)
+        {
+          /* Handle: addi REG1, REG2, IMM  */
+          gdb_assert (insn.rd () < RISCV_NUM_INTEGER_REGS);
+          gdb_assert (insn.rs1 () < RISCV_NUM_INTEGER_REGS);
+          gdb_assert (insn.rs2 () < RISCV_NUM_INTEGER_REGS);
+          regs[insn.rd ()] = pv_add (regs[insn.rs1 ()], regs[insn.rs2 ()]);
+        }
       else
        {
          end_prologue_addr = cur_pc;
index d15fcff17bd09ad4d2845bdd9d4328e4d64327a3..2b4b0973ef22edeb4478d3023093a6c9b6ff0e08 100644 (file)
@@ -1,3 +1,10 @@
+2018-09-26  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * gdb.base/large-frame-1.c: New file.
+       * gdb.base/large-frame-2.c: New file.
+       * gdb.base/large-frame.exp: New file.
+       * gdb.base/large-frame.h: New file.
+
 2018-09-24  Jozef Lawrynowicz  <jozef.l@mittosystems.com>
 
        PR gdb/20948
diff --git a/gdb/testsuite/gdb.base/large-frame-1.c b/gdb/testsuite/gdb.base/large-frame-1.c
new file mode 100644 (file)
index 0000000..cd9c5cc
--- /dev/null
@@ -0,0 +1,32 @@
+/* This file is part of GDB, the GNU debugger.
+
+   Copyright 2018 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/>.  */
+
+#include "large-frame.h"
+
+__attribute__ ((noinline)) void
+blah (int *a)
+{
+  asm ("" :: "m" (a) : "memory");
+}
+
+int
+main ()
+{
+  func ();
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/large-frame-2.c b/gdb/testsuite/gdb.base/large-frame-2.c
new file mode 100644 (file)
index 0000000..2491c34
--- /dev/null
@@ -0,0 +1,25 @@
+/* This file is part of GDB, the GNU debugger.
+
+   Copyright 2018 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/>.  */
+
+#include "large-frame.h"
+
+__attribute__ ((noinline)) int
+func (void)
+{
+  int a[4096];
+  blah (a);
+}
diff --git a/gdb/testsuite/gdb.base/large-frame.exp b/gdb/testsuite/gdb.base/large-frame.exp
new file mode 100644 (file)
index 0000000..00090da
--- /dev/null
@@ -0,0 +1,57 @@
+# Copyright 2018 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 file is part of the gdb testsuite.
+
+# This test was added to test GDB's ability to backtrace over a large
+# stack frame for which there is no debug information.  This should
+# test the non-DWARF stack unwinder.
+#
+# The test was originally added for Risc-V where this case caused a
+# problem at one point, however, there's nothing Risc-V specific in
+# the test.
+
+proc run_test { opt_level } {
+    global srcfile srcfile2 binfile hex
+
+    standard_testfile large-frame-1.c large-frame-2.c
+
+    if {[prepare_for_testing_full "failed to prepare" \
+            [list ${binfile}-${opt_level} debug \
+                 $srcfile [list debug] \
+                 $srcfile2 [list nodebug optimize=-$opt_level]]]} {
+       return
+    }
+
+    if { ![runto_main] } then {
+       unsupported "runto main"
+       return
+    }
+
+    gdb_breakpoint "blah"
+    gdb_continue_to_breakpoint "blah"
+
+    gdb_test "backtrace" [multi_line \
+                             "#0  blah \[^\n\r\]+" \
+                             "#1  $hex in func \[^\n\r\]+" \
+                             "#2  $hex in main \[^\n\r\]+"]
+}
+
+foreach opt { O0 O1 O2 } {
+    with_test_prefix "optimize=-$opt" {
+       run_test $opt
+    }
+}
+
diff --git a/gdb/testsuite/gdb.base/large-frame.h b/gdb/testsuite/gdb.base/large-frame.h
new file mode 100644 (file)
index 0000000..17058a5
--- /dev/null
@@ -0,0 +1,24 @@
+/* This file is part of GDB, the GNU debugger.
+
+   Copyright 2018 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/>.  */
+
+#ifndef LARGE_FRAME_H
+#define LARGE_FRAME_H
+
+extern void blah (int *);
+extern int func (void);
+
+#endif /* LARGE_FRAME_H */