public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] gdb: Respect the DW_CC_nocall attribute
@ 2022-01-19 13:30 Lancelot SIX
  2022-01-26 21:19 ` Simon Marchi
  0 siblings, 1 reply; 2+ messages in thread
From: Lancelot SIX @ 2022-01-19 13:30 UTC (permalink / raw)
  To: gdb-patches; +Cc: Lancelot SIX

It is possible for a compiler to optimize a function in a such ways that
the function does not follow the calling convention of the target.  In
such situation, the compiler can use the DW_AT_calling_convention
attribute with the value DW_CC_nocall to tell the debugger that it is
unsafe to call the function.  The DWARF5 standard states, in 3.3.1.1:

  > If the value of the calling convention attribute is the constant
  > DW_CC_nocall, the subroutine does not obey standard calling
  > conventions, and it may not be safe for the debugger to call this
  > subroutine.

Non standard calling convention can affect GDB's assumptions in multiple
ways, including how arguments are passed to the function, how values are
returned, and so on.  For this reason, it is unsafe for GDB to try to do
the following operations on a function with marked with DW_CC_nocall:

- call / print an expression requiring the function to be evaluated,
- inspect the value a function returns using the 'finish' command,
- force the value returned by a function using the 'return' command.

This patch ensures that if a command which relies on GDB's knowledge of
the target's calling convention is used on a function marked nocall, GDB
prints an appropriate message to the user and does not proceed with the
operation which is unreliable.

Note that it is still possible for someone to use a vendor specific
value for the DW_AT_calling_convention attribute for example to indicate
the use of an alternative calling convention.  This commit does not
prevent this, and target dependent code can be adjusted if one wanted to
support multiple calling conventions.

Tested on x86_64-Linux, with no regression observed.

Change-Id: I72970dae68234cb83edbc0cf71aa3d6002a4a540
---
 gdb/gdbtypes.c                                | 11 +++
 gdb/gdbtypes.h                                |  7 ++
 gdb/infcall.c                                 |  4 +
 gdb/infcmd.c                                  | 15 +++-
 gdb/stack.c                                   |  4 +
 gdb/testsuite/gdb.dwarf2/calling-convention.c | 36 +++++++++
 .../gdb.dwarf2/calling-convention.exp         | 73 +++++++++++++++++++
 7 files changed, 147 insertions(+), 3 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/calling-convention.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/calling-convention.exp

diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 8af96c79e6c..0c2586e28f2 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -3903,6 +3903,17 @@ type_byte_order (const struct type *type)
   return byteorder;
 }
 
+/* See gdbtypes.h.  */
+
+bool
+is_nocall_function (const struct type *type)
+{
+  gdb_assert (type->code () == TYPE_CODE_FUNC
+	      || type->code () == TYPE_CODE_METHOD);
+
+  return TYPE_CALLING_CONVENTION (type) == DW_CC_nocall;
+}
+
 \f
 /* Overload resolution.  */
 
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 7238873e4db..8c2828dd203 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -2852,4 +2852,11 @@ extern enum bfd_endian type_byte_order (const struct type *type);
 
 extern unsigned int overload_debug;
 
+/* The function is marked as unsafe to call by the debugger.
+
+   This usually indicates that the function does not follow the target's
+   standard calling convention.  */
+
+extern bool is_nocall_function (const struct type *type);
+
 #endif /* GDBTYPES_H */
diff --git a/gdb/infcall.c b/gdb/infcall.c
index a178051391c..332c5197f6c 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -813,6 +813,10 @@ call_function_by_hand_dummy (struct value *function,
   type *values_type;
   CORE_ADDR funaddr = find_function_addr (function, &values_type, &ftype);
 
+  if (is_nocall_function (ftype))
+    error (_("Cannot call a function which does not follow the target "
+	     "calling convention."));
+
   if (values_type == NULL)
     values_type = default_return_type;
   if (values_type == NULL)
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index af8b6c3eb46..60daa66175f 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1434,9 +1434,18 @@ get_return_value (struct value *function, struct type *value_type)
     case RETURN_VALUE_REGISTER_CONVENTION:
     case RETURN_VALUE_ABI_RETURNS_ADDRESS:
     case RETURN_VALUE_ABI_PRESERVES_ADDRESS:
-      value = allocate_value (value_type);
-      gdbarch_return_value (gdbarch, function, value_type, stop_regs,
-			    value_contents_raw (value).data (), NULL);
+      if (is_nocall_function (check_typedef (::value_type (function))))
+	{
+	  warning (_("Cannot access the return value of a function which "
+		     "does not follow the target calling convention."));
+	  value = nullptr;
+	}
+      else
+	{
+	  value = allocate_value (value_type);
+	  gdbarch_return_value (gdbarch, function, value_type, stop_regs,
+				value_contents_raw (value).data (), NULL);
+	}
       break;
     case RETURN_VALUE_STRUCT_CONVENTION:
       value = NULL;
diff --git a/gdb/stack.c b/gdb/stack.c
index b81be2962d4..03eb51891ec 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -2796,6 +2796,10 @@ return_command (const char *retval_exp, int from_tty)
 	return_value = NULL;
       else if (thisfun != NULL)
 	{
+	  if (is_nocall_function (check_typedef (value_type (function))))
+	    error (_("Cannot force the return value of a function which "
+		     "does not follow the target calling convention."));
+
 	  rv_conv = struct_return_convention (gdbarch, function, return_type);
 	  if (rv_conv == RETURN_VALUE_STRUCT_CONVENTION
 	      || rv_conv == RETURN_VALUE_ABI_RETURNS_ADDRESS)
diff --git a/gdb/testsuite/gdb.dwarf2/calling-convention.c b/gdb/testsuite/gdb.dwarf2/calling-convention.c
new file mode 100644
index 00000000000..48c71185a1f
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/calling-convention.c
@@ -0,0 +1,36 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022 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/>.  */
+
+/* Dummy foo function.  */
+
+int
+foo ()
+{
+  asm ("foo_label: .globl foo_label");
+  return 42;
+}
+
+/* Dummy main function.  */
+
+int
+main()
+{
+  asm ("main_label: .globl main_label");
+  foo ();
+  return 0;
+}
+
diff --git a/gdb/testsuite/gdb.dwarf2/calling-convention.exp b/gdb/testsuite/gdb.dwarf2/calling-convention.exp
new file mode 100644
index 00000000000..eae935a961d
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/calling-convention.exp
@@ -0,0 +1,73 @@
+# Copyright 2022 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/>.
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+standard_testfile .c .S
+
+# Make some DWARF for the test.
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    cu {} {
+	compile_unit {
+	    {language @DW_LANG_C}
+	    {name "calling-convention"}
+	} {
+	    declare_labels int_label
+
+	    int_label: base_type {
+		{byte_size 4 sdata}
+		{encoding @DW_ATE_signed}
+		{name "int"}
+	    }
+
+	    subprogram {
+		{MACRO_AT_func { foo }}
+		{type :$int_label}
+		{calling_convention @DW_CC_nocall}
+	    }
+
+	    subprogram {
+		{MACRO_AT_func { main }}
+		{type :$int_label}
+	    }
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_test "call foo ()" \
+    "Cannot call a function which does not follow the target calling convention."
+gdb_breakpoint "foo"
+gdb_continue_to_breakpoint "foo"
+gdb_test "return 35" \
+    "Cannot force the return value of a function which does not follow the target calling convention."
+gdb_test "finish" [multi_line \
+    "Run till exit from #0  $hex in foo \\(\\)" \
+    "warning: Cannot access the return value of a function which does not follow the target calling convention." \
+    "$hex in main \\(\\)" \
+    "Value returned has type: int. Cannot determine contents"]
-- 
2.25.1


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] gdb: Respect the DW_CC_nocall attribute
  2022-01-19 13:30 [PATCH] gdb: Respect the DW_CC_nocall attribute Lancelot SIX
@ 2022-01-26 21:19 ` Simon Marchi
  0 siblings, 0 replies; 2+ messages in thread
From: Simon Marchi @ 2022-01-26 21:19 UTC (permalink / raw)
  To: Lancelot SIX, gdb-patches



On 2022-01-19 08:30, Lancelot SIX via Gdb-patches wrote:
> It is possible for a compiler to optimize a function in a such ways that
> the function does not follow the calling convention of the target.  In
> such situation, the compiler can use the DW_AT_calling_convention
> attribute with the value DW_CC_nocall to tell the debugger that it is
> unsafe to call the function.  The DWARF5 standard states, in 3.3.1.1:
> 
>   > If the value of the calling convention attribute is the constant
>   > DW_CC_nocall, the subroutine does not obey standard calling
>   > conventions, and it may not be safe for the debugger to call this
>   > subroutine.
> 
> Non standard calling convention can affect GDB's assumptions in multiple
> ways, including how arguments are passed to the function, how values are
> returned, and so on.  For this reason, it is unsafe for GDB to try to do
> the following operations on a function with marked with DW_CC_nocall:

One spurious "with".

> 
> - call / print an expression requiring the function to be evaluated,
> - inspect the value a function returns using the 'finish' command,
> - force the value returned by a function using the 'return' command.
> 
> This patch ensures that if a command which relies on GDB's knowledge of
> the target's calling convention is used on a function marked nocall, GDB
> prints an appropriate message to the user and does not proceed with the
> operation which is unreliable.
> 
> Note that it is still possible for someone to use a vendor specific
> value for the DW_AT_calling_convention attribute for example to indicate
> the use of an alternative calling convention.  This commit does not
> prevent this, and target dependent code can be adjusted if one wanted to
> support multiple calling conventions.
> 
> Tested on x86_64-Linux, with no regression observed.
> 
> Change-Id: I72970dae68234cb83edbc0cf71aa3d6002a4a540
> ---
>  gdb/gdbtypes.c                                | 11 +++
>  gdb/gdbtypes.h                                |  7 ++
>  gdb/infcall.c                                 |  4 +
>  gdb/infcmd.c                                  | 15 +++-
>  gdb/stack.c                                   |  4 +
>  gdb/testsuite/gdb.dwarf2/calling-convention.c | 36 +++++++++
>  .../gdb.dwarf2/calling-convention.exp         | 73 +++++++++++++++++++
>  7 files changed, 147 insertions(+), 3 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.dwarf2/calling-convention.c
>  create mode 100644 gdb/testsuite/gdb.dwarf2/calling-convention.exp
> 
> diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
> index 8af96c79e6c..0c2586e28f2 100644
> --- a/gdb/gdbtypes.c
> +++ b/gdb/gdbtypes.c
> @@ -3903,6 +3903,17 @@ type_byte_order (const struct type *type)
>    return byteorder;
>  }
>  
> +/* See gdbtypes.h.  */
> +
> +bool
> +is_nocall_function (const struct type *type)
> +{
> +  gdb_assert (type->code () == TYPE_CODE_FUNC
> +	      || type->code () == TYPE_CODE_METHOD);
> +
> +  return TYPE_CALLING_CONVENTION (type) == DW_CC_nocall;
> +}
> +
>  \f
>  /* Overload resolution.  */
>  
> diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
> index 7238873e4db..8c2828dd203 100644
> --- a/gdb/gdbtypes.h
> +++ b/gdb/gdbtypes.h
> @@ -2852,4 +2852,11 @@ extern enum bfd_endian type_byte_order (const struct type *type);
>  
>  extern unsigned int overload_debug;
>  
> +/* The function is marked as unsafe to call by the debugger.

Maybe write:

Return whether the function type represented by TYPE is marked as unsafe
to call by the debugger.

> +
> +   This usually indicates that the function does not follow the target's
> +   standard calling convention.  */

Please mentionp the precondition for that function, that TYPE must be of
code TYPE_CODE_FUNC or TYPE_CODE_METHOD.

> +
> +extern bool is_nocall_function (const struct type *type);
> +
>  #endif /* GDBTYPES_H */
> diff --git a/gdb/infcall.c b/gdb/infcall.c
> index a178051391c..332c5197f6c 100644
> --- a/gdb/infcall.c
> +++ b/gdb/infcall.c
> @@ -813,6 +813,10 @@ call_function_by_hand_dummy (struct value *function,
>    type *values_type;
>    CORE_ADDR funaddr = find_function_addr (function, &values_type, &ftype);
>  
> +  if (is_nocall_function (ftype))
> +    error (_("Cannot call a function which does not follow the target "
> +	     "calling convention."));

To make the message clearer, can we mention the name of the function
(type) that is problematic?  The same would apply for the other messages
introduced in this patch.

Simon

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2022-01-26 21:19 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-19 13:30 [PATCH] gdb: Respect the DW_CC_nocall attribute Lancelot SIX
2022-01-26 21:19 ` Simon Marchi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).