public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 10/13] Handle "p S::method()::static_var" in the C++ parser
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
                   ` (2 preceding siblings ...)
  2017-07-13 15:32 ` [PATCH v2 03/13] Introduce OP_VAR_MSYM_VALUE Pedro Alves
@ 2017-07-13 15:32 ` Pedro Alves
  2017-07-13 15:32 ` [PATCH v2 02/13] Stop assuming no-debug-info functions return int Pedro Alves
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:32 UTC (permalink / raw)
  To: gdb-patches

This commit makes "print S::method()::static_var" actually find the
debug symbol for static_var.  Currently, you get:

  (gdb) print S::method()::static_var
  A syntax error in expression, near `'.

Quoting the whole string would seemingly work before the previous
patch that made GDB stop assuming int for no-debug-info variables:

  (gdb) p 'S::method()::static_var'
  $1 = 1

... except that's incorrect output, because:

  (gdb) ptype 'S::method()::static_var'
  type = <data variable, no debug info>

The way to make it work correctly currently is by quoting the
function/method part, like this:

  (gdb) print 'S::method()'::static_var
  $1 = {i1 = 1, i2 = 2, i3 = 3}
  (gdb) ptype 'S::method()'::static_var
  type = struct aggregate {
      int i1;
      int i2;
      int i3;
  }

At least after the "stop assuming int" patch, this is what we
now get:

  (gdb) p 'S::method()::static_var'
  'S::method()::static_var' has unknown type; cast it to its declared type
  (gdb) p (struct aggregate) 'S::method()::static_var'
  $1 = {i1 = 1, i2 = 2, i3 = 3}

However, IMO, users shouldn't really have to care about any of this.
GDB should Just Work, without quoting, IMO.

So here's a patch that implements support for that in the C++ parser.
With this patch, you now get:

  (gdb) p S::method()::S_M_s_var_aggregate
  $1 = {i1 = 1, i2 = 2, i3 = 3}
  (gdb) ptype S::method()::S_M_s_var_aggregate
  type = struct aggregate {
      int i1;
      int i2;
      int i3;
  }

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	(%type <voidval>): Add function_method.
	* c-exp.y (exp): New production for calls with no arguments.
	(function_method, function_method_void_or_typelist): New
	productions.
	(exp): New production for "method()::static_var".
	* eval.c (evaluate_subexp_standard): Handle OP_FUNC_STATIC_VAR.
	* expprint.c (print_subexp_standard, dump_subexp_body_standard):
	Handle OP_FUNC_STATIC_VAR.
	* parse.c (operator_length_standard):
	Handle OP_FUNC_STATIC_VAR.
	* std-operator.def (OP_FUNC_STATIC_VAR): New.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/local-static.c: New.
	* gdb.base/local-static.cc: New.
	* gdb.base/local-static.exp:  New.
---
 gdb/c-exp.y                           |  43 +++++++++-
 gdb/eval.c                            |  21 +++++
 gdb/expprint.c                        |  18 +++++
 gdb/parse.c                           |   6 ++
 gdb/std-operator.def                  |  21 +++++
 gdb/testsuite/gdb.cp/local-static.c   | 142 ++++++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.cp/local-static.cc  |   1 +
 gdb/testsuite/gdb.cp/local-static.exp | 136 ++++++++++++++++++++++++++++++++
 8 files changed, 386 insertions(+), 2 deletions(-)
 create mode 100644 gdb/testsuite/gdb.cp/local-static.c
 create mode 100644 gdb/testsuite/gdb.cp/local-static.cc
 create mode 100644 gdb/testsuite/gdb.cp/local-static.exp

diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index 48b5a53..5102470 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -127,7 +127,7 @@ static void c_print_token (FILE *file, int type, YYSTYPE value);
 #endif
 %}
 
-%type <voidval> exp exp1 type_exp start variable qualified_name lcurly
+%type <voidval> exp exp1 type_exp start variable qualified_name lcurly function_method
 %type <lval> rcurly
 %type <tval> type typebase
 %type <tvec> nonempty_typelist func_mod parameter_typelist
@@ -498,6 +498,18 @@ exp	:	exp '('
 			  write_exp_elt_opcode (pstate, OP_FUNCALL); }
 	;
 
+/* This is here to disambiguate with the production for
+   "func()::static_var" further below, which uses
+   function_method_void.  */
+exp	:	exp '(' ')' %prec ARROW
+			{ start_arglist ();
+			  write_exp_elt_opcode (pstate, OP_FUNCALL);
+			  write_exp_elt_longcst (pstate,
+						 (LONGEST) end_arglist ());
+			  write_exp_elt_opcode (pstate, OP_FUNCALL); }
+	;
+
+
 exp	:	UNKNOWN_CPP_NAME '('
 			{
 			  /* This could potentially be a an argument defined
@@ -539,7 +551,7 @@ arglist	:	arglist ',' exp   %prec ABOVE_COMMA
 			{ arglist_len++; }
 	;
 
-exp     :       exp '(' parameter_typelist ')' const_or_volatile
+function_method:       exp '(' parameter_typelist ')' const_or_volatile
 			{ int i;
 			  VEC (type_ptr) *type_list = $3;
 			  struct type *type_elt;
@@ -557,6 +569,33 @@ exp     :       exp '(' parameter_typelist ')' const_or_volatile
 			}
 	;
 
+function_method_void:	    exp '(' ')' const_or_volatile
+		       { write_exp_elt_opcode (pstate, TYPE_INSTANCE);
+			 write_exp_elt_longcst (pstate, 0);
+			 write_exp_elt_longcst (pstate, 0);
+			 write_exp_elt_opcode (pstate, TYPE_INSTANCE);
+		       }
+       ;
+
+exp     :       function_method
+	;
+
+/* Normally we must interpret "func()" as a function call, instead of
+   a type.  The user needs to write func(void) to disambiguate.
+   However, in the "func()::static_var" case, there's no
+   ambiguity.  */
+function_method_void_or_typelist: function_method
+	|               function_method_void
+	;
+
+exp     :       function_method_void_or_typelist COLONCOLON name
+			{
+			  write_exp_elt_opcode (pstate, OP_FUNC_STATIC_VAR);
+			  write_exp_string (pstate, $3);
+			  write_exp_elt_opcode (pstate, OP_FUNC_STATIC_VAR);
+			}
+	;
+
 rcurly	:	'}'
 			{ $$ = end_arglist () - 1; }
 	;
diff --git a/gdb/eval.c b/gdb/eval.c
index e70328b..7d129f0 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -847,6 +847,27 @@ evaluate_subexp_standard (struct type *expect_type,
 	return SYMBOL_COMPUTED_OPS (sym)->read_variable_at_entry (sym, frame);
       }
 
+    case OP_FUNC_STATIC_VAR:
+      tem = longest_to_int (exp->elts[pc + 1].longconst);
+      (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+      if (noside == EVAL_SKIP)
+	return eval_skip_value (exp);
+
+      {
+	value *func = evaluate_subexp_standard (NULL, exp, pos, noside);
+	CORE_ADDR addr = value_address (func);
+
+	const block *blk = block_for_pc (addr);
+	const char *var = &exp->elts[pc + 2].string;
+
+	struct block_symbol sym = lookup_symbol (var, blk, VAR_DOMAIN, NULL);
+
+	if (sym.symbol == NULL)
+	  error (_("No symbol \"%s\" in specified context."), var);
+
+	return evaluate_var_value (noside, sym.block, sym.symbol);
+      }
+
     case OP_LAST:
       (*pos) += 2;
       return
diff --git a/gdb/expprint.c b/gdb/expprint.c
index 0697a77..fad20e8 100644
--- a/gdb/expprint.c
+++ b/gdb/expprint.c
@@ -141,6 +141,14 @@ print_subexp_standard (struct expression *exp, int *pos,
       }
       return;
 
+    case OP_FUNC_STATIC_VAR:
+      {
+	tem = longest_to_int (exp->elts[pc + 1].longconst);
+	(*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+	fputs_filtered (&exp->elts[pc + 1].string, stream);
+      }
+      return;
+
     case OP_VAR_ENTRY_VALUE:
       {
 	(*pos) += 2;
@@ -999,6 +1007,16 @@ dump_subexp_body_standard (struct expression *exp,
 	elt += 4 + BYTES_TO_EXP_ELEM (len + 1);
       }
       break;
+
+    case OP_FUNC_STATIC_VAR:
+      {
+	int len = longest_to_int (exp->elts[elt].longconst);
+	const char *var_name = &exp->elts[elt + 1].string;
+	fprintf_filtered (stream, "Field name: `%.*s'", len, var_name);
+	elt += 3 + BYTES_TO_EXP_ELEM (len + 1);
+      }
+      break;
+
     case TYPE_INSTANCE:
       {
 	LONGEST len;
diff --git a/gdb/parse.c b/gdb/parse.c
index ee6b6f7..97ddc72 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -900,6 +900,12 @@ operator_length_standard (const struct expression *expr, int endpos,
       oplen = 4;
       break;
 
+    case OP_FUNC_STATIC_VAR:
+      oplen = longest_to_int (expr->elts[endpos - 2].longconst);
+      oplen = 4 + BYTES_TO_EXP_ELEM (oplen + 1);
+      args = 1;
+      break;
+
     case OP_TYPE:
     case OP_BOOL:
     case OP_LAST:
diff --git a/gdb/std-operator.def b/gdb/std-operator.def
index 3eed2cc..56a9af9 100644
--- a/gdb/std-operator.def
+++ b/gdb/std-operator.def
@@ -280,6 +280,27 @@ OP (OP_OBJC_SELECTOR)
    a string, which, of course, is variable length.  */
 OP (OP_SCOPE)
 
+/* OP_FUNC_STATIC_VAR refers to a function local static variable.  The
+   function is taken from the following subexpression.  The length of
+   the variable name as a string follows the opcode, followed by
+   BYTES_TO_EXP_ELEM(length) elements containing the data of the
+   string, followed by the length again and the opcode again.
+
+   Note this is used by C++, but not C.  The C parser handles local
+   static variables in the parser directly.  Also, this is only used
+   in C++ if the function/method name is not quoted, like e.g.:
+
+     p S:method()::var
+     p S:method() const::var
+
+   If the function/method is quoted like instead:
+
+     p 'S:method() const'::var
+
+   then the C-specific handling directly in the parser takes over (see
+   "block/variable productions).  */
+OP (OP_FUNC_STATIC_VAR)
+
 /* OP_TYPE is for parsing types, and used with the "ptype" command
    so we can look up types that are qualified by scope, either with
    the GDB "::" operator, or the Modula-2 '.' operator.  */
diff --git a/gdb/testsuite/gdb.cp/local-static.c b/gdb/testsuite/gdb.cp/local-static.c
new file mode 100644
index 0000000..5bfff8d
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/local-static.c
@@ -0,0 +1,142 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2002-2017 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/>.  */
+
+struct aggregate
+{
+  int i1;
+  int i2;
+  int i3;
+};
+
+void keepalive_float (double *var) { }
+void keepalive_int (int *var) { }
+void keepalive_aggregate (struct aggregate *var) { }
+
+#define PREFIXIFY(PREFIX, VAR)			\
+  PREFIX ## _ ## VAR
+
+#define DEF_STATICS(PREFIX)						\
+  static int PREFIXIFY(PREFIX, s_var_int) = 4;				\
+  static double PREFIXIFY(PREFIX, s_var_float) = 3.14f;			\
+  static struct aggregate PREFIXIFY(PREFIX, s_var_aggregate)		\
+    = { 1, 2, 3 };							\
+									\
+  keepalive_int (&PREFIXIFY(PREFIX, s_var_int));			\
+  keepalive_float (&PREFIXIFY(PREFIX, s_var_float));			\
+  keepalive_aggregate (&PREFIXIFY(PREFIX, s_var_aggregate));
+
+#ifdef __cplusplus
+
+struct S
+{
+  void inline_method ()
+  {
+    DEF_STATICS (S_IM);
+  }
+  static void static_inline_method ()
+  {
+    DEF_STATICS (S_SIM);
+  }
+
+  void method ();
+  static void static_method ();
+};
+
+S s;
+
+void
+S::method ()
+{
+  DEF_STATICS (S_M);
+}
+
+void
+S::static_method ()
+{
+  DEF_STATICS (S_SM);
+}
+
+template <typename T>
+struct S2
+{
+  void method ();
+  static void static_method ();
+
+  void inline_method ()
+  {
+    DEF_STATICS (S2_IM);
+  }
+
+  static void static_inline_method ()
+  {
+    DEF_STATICS (S2_SIM);
+  }
+};
+
+template<typename T>
+void
+S2<T>::method ()
+{
+  DEF_STATICS (S2_M);
+}
+
+template<typename T>
+void
+S2<T>::static_method ()
+{
+  DEF_STATICS (S2_SM);
+}
+
+S2<int> s2;
+
+#endif
+
+void
+free_func (void)
+{
+  DEF_STATICS (FF);
+}
+
+static inline void
+free_inline_func (void)
+{
+  DEF_STATICS (FIF);
+}
+
+int
+main ()
+{
+  for (int i = 0; i < 1000; i++)
+    {
+      free_func ();
+      free_inline_func ();
+
+#ifdef __cplusplus
+      s.method ();
+      s.inline_method ();
+      S::static_method ();
+      S::static_inline_method ();
+
+      s2.method ();
+      s2.inline_method ();
+      S2<int>::static_method ();
+      S2<int>::static_inline_method ();
+#endif
+    }
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.cp/local-static.cc b/gdb/testsuite/gdb.cp/local-static.cc
new file mode 100644
index 0000000..f2bffbe
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/local-static.cc
@@ -0,0 +1 @@
+#include "local-static.c"
diff --git a/gdb/testsuite/gdb.cp/local-static.exp b/gdb/testsuite/gdb.cp/local-static.exp
new file mode 100644
index 0000000..ba0c5ef
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/local-static.exp
@@ -0,0 +1,136 @@
+# Copyright 2017 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/>.
+
+# Tests for function local static variables, both C and C++.
+
+# This file is part of the gdb testsuite.
+
+standard_testfile .c
+
+# A list of scopes that have the static variables that we want to
+# print.  Each entry has, in order, the scope/function name, and the
+# prefix used by the static variables.  (The prefix exists to make it
+# easier to debug the test if something goes wrong.)
+
+     #SCOPE				#PREFIX
+set cxx_scopes_list {
+    {"S::method()"			"S_M"}
+    {"S::static_method()"		"S_SM"}
+    {"S::inline_method()"		"S_IM"}
+    {"S::static_inline_method()"	"S_SIM"}
+    {"S2<int>::method()"		"S2_M"}
+    {"S2<int>::static_method()"		"S2_SM"}
+    {"S2<int>::inline_method()"		"S2_IM"}
+    {"S2<int>::static_inline_method()"	"S2_SIM"}
+    {"free_func()"			"FF"}
+    {"free_inline_func()"		"FIF"}
+}
+
+set c_scopes_list {
+    {"free_func"			"FF"}
+    {"free_inline_func"			"FIF"}
+}
+
+# A list of all the static varibles defined in each scope.  The first
+# column is the name of the variable, without the prefix, and the
+# second column is a regex matching what printing the variable should
+# output.
+
+     #VAR		#PRINT
+set vars_list {
+    {"s_var_int"	" = 4"}
+    {"s_var_float"	" = 3.14.*"}
+    {"s_var_aggregate"	" = \\{i1 = 1, i2 = 2, i3 = 3\\}"}
+}
+
+proc do_test {lang} {
+    global c_scopes_list
+    global cxx_scopes_list
+    global vars_list
+    global srcfile testfile
+
+    set options {debug}
+
+    if {$lang == "c++"} {
+	if { [skip_cplus_tests] } {
+	    return
+	}
+	lappend options $lang
+	set src ${srcfile}c
+    } else {
+	set src ${srcfile}
+    }
+
+    if {[prepare_for_testing "failed to prepare" $testfile-$lang \
+	     [list $src] $options]} {
+	return -1
+    }
+
+    if ![runto_main] then {
+	fail "couldn't run to breakpoint"
+	return
+    }
+
+    gdb_test "show language" " currently [string_to_regexp $lang]\"\\."
+
+    if {$lang == "c"} {
+	set scopes_list $c_scopes_list
+    } else {
+	set scopes_list $cxx_scopes_list
+    }
+
+    # Print each variable using these syntaxes:
+    #
+    #  'func()'::var
+    #  func()::var
+
+    foreach scope_line $scopes_list  {
+	set scope [lindex $scope_line 0]
+	set var_prefix [lindex $scope_line 1]
+	foreach var_line $vars_list {
+	    set var [lindex $var_line 0]
+	    set print_re [lindex $var_line 1]
+
+	    gdb_test "print '${scope}'::${var_prefix}_${var}" $print_re
+	    gdb_test "print ${scope}::${var_prefix}_${var}" $print_re
+	}
+    }
+
+    # Now run to each function, and print its variables using the
+    # localy-visible name.
+    foreach scope_line $scopes_list {
+	set scope [lindex $scope_line 0]
+	set var_prefix [lindex $scope_line 1]
+
+	with_test_prefix "$scope" {
+	    delete_breakpoints
+	    gdb_breakpoint "$scope"
+	    gdb_continue_to_breakpoint "$scope"
+
+	    foreach var_line $vars_list {
+		set var [lindex $var_line 0]
+		set print_re [lindex $var_line 1]
+
+		gdb_test "print ${var_prefix}_${var}" $print_re
+	    }
+	}
+    }
+}
+
+foreach lang {"c" "c++"} {
+    with_test_prefix $lang {
+	do_test $lang
+    }
+}
-- 
2.5.5

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

* [PATCH v2 11/13] Handle "p 'S::method()::static_var'" (quoted) in symbol lookup
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
@ 2017-07-13 15:32 ` Pedro Alves
  2017-07-13 15:32 ` [PATCH v2 09/13] Eliminate UNOP_MEMVAL_TLS Pedro Alves
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:32 UTC (permalink / raw)
  To: gdb-patches

While the previous commit made "p method()::static_var" (no
single-quotes) Just Work, if users (or frontends) try wrapping the
expression with quotes, they'll get:

  (gdb) p 'S::method()::static_var'
  'S::method()::static_var' has unknown type; cast it to its declared type

even if we _do_ have debug info for that variable.  That's better than
the bogus/confusing value what GDB would print before the
stop-assuming-int patch:

  (gdb) p 'S::method()::static_var'
  $1 = 1

but I think it'd still be nice to make this case Just Work too.

In this case, due to the quoting, the C/C++ parser (c-exp.y)
interprets the whole expression/string as a single symbol name, and we
end up calling lookup_symbol on that name.  There's no debug symbol
with that fully-qualified name, but since the compiler gives the
static variable a mangled linkage name exactly like the above, it
appears in the mininal symbols:

  $ nm -A local-static | c++filt | grep static_var
  local-static:0000000000601040 d S::method()::static_var

... and that's what GDB happens to find/print.  This only happens in
C++, note, since for C the compiler uses different linkage names:

  local-static-c:0000000000601040 d static_var.1848

So while (in C++, not C) function local static variables are given a
mangled name that demangles to the same syntax that GDB
documents/expects as the way to access function local statics, there's
no global symbol in the debug info with that name at all.  The debug
info for a static local variable for a non-inline function looks like
this:

 <1><2a1>: Abbrev Number: 19 (DW_TAG_subprogram)
 ...
 <2><2f7>: Abbrev Number: 20 (DW_TAG_variable)
    <2f8>   DW_AT_name        : (indirect string, offset: 0x4e9): static_var
    <2fc>   DW_AT_decl_file   : 1
    <2fd>   DW_AT_decl_line   : 64
    <2fe>   DW_AT_type        : <0x25>
    <302>   DW_AT_location    : 9 byte block: 3 40 10 60 0 0 0 0 0      (DW_OP_addr: 601040)

and for an inline function, it looks like this (linkage name run
through c++filt for convenience):

 <2><21b>: Abbrev Number: 16 (DW_TAG_variable)
    <21c>   DW_AT_name        : (indirect string, offset: 0x21a): static_var
    <220>   DW_AT_decl_file   : 1
    <221>   DW_AT_decl_line   : 48
    <222>   DW_AT_linkage_name: (indirect string, offset: 0x200): S::inline_method()::static_var
    <226>   DW_AT_type        : <0x25>
    <22a>   DW_AT_external    : 1
    <22a>   DW_AT_location    : 9 byte block: 3 a0 10 60 0 0 0 0 0      (DW_OP_addr: 6010a0)

(The inline case makes the variable external so that the linker can
merge the different inlined copies.  It seems like GCC never outputs
the linkage name for non-extern globals.)

When we read the DWARF, we record the static_var variable as a regular
variable of the containing function's block.  This makes stopping in
the function and printing the variable as usual.  The variable just so
happens to have a memory address as location.

So one way to make "p 'S::method()::static_var'" work would be to
record _two_ copies of the symbols for these variables.  One in the
function's scope/block, with "static_var" as name, as we currently do,
and another in the static or global blocks (depending on whether the
symbol is external), with a fully-qualified name.  I wrote a prototype
patch for that, and it works.  For the non-inline case above, since
the debug info doesn't point to the linkage same, that patch built the
physname of the static local variable as the concat of the physname of
the containing function, plus "::", plus the variable's name.  We
could make that approach work for C too, though it kind of feels
awkward to record fake symbol names like that in C.

The other approach I tried is to change the C++ symbol lookup routines
instead.  This is the approach this commit takes.  We can already
lookup up symbol in namespaces and classes, so this feels like a good
fit, and was easy enough.  The advantage is that this doesn't require
recording extra symbols.

The test in gdb.cp/m-static.exp that exposed the need for this is
removed, since the same functionality is now covered by
gdb.cp/local-static.exp.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cp-namespace.c (cp_search_static_and_baseclasses): Handle
	function/method scopes; lookup the nested name as a function local
	static variable.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/local-static.exp: Also test with
	class::method::variable wholly quoted.
	* gdb.cp/m-static.exp (class::method::variable): Remove test.
---
 gdb/cp-namespace.c                    | 50 +++++++++++++++++++----------------
 gdb/std-operator.def                  | 11 +++++++-
 gdb/testsuite/gdb.cp/local-static.exp | 12 +++++++++
 gdb/testsuite/gdb.cp/m-static.exp     |  5 ----
 4 files changed, 49 insertions(+), 29 deletions(-)

diff --git a/gdb/cp-namespace.c b/gdb/cp-namespace.c
index b96c421..c7b5aa8 100644
--- a/gdb/cp-namespace.c
+++ b/gdb/cp-namespace.c
@@ -246,40 +246,44 @@ cp_search_static_and_baseclasses (const char *name,
 				  unsigned int prefix_len,
 				  int is_in_anonymous)
 {
-  struct block_symbol sym;
-  struct block_symbol klass_sym;
-  struct type *klass_type;
-
   /* Check for malformed input.  */
   if (prefix_len + 2 > strlen (name) || name[prefix_len + 1] != ':')
     return null_block_symbol;
 
-  /* Find the name of the class and the name of the method, variable, etc.  */
-
-  /* The class name is everything up to and including PREFIX_LEN.  */
-  std::string klass (name, prefix_len);
+  /* The class, namespace or function name is everything up to and
+     including PREFIX_LEN.  */
+  std::string scope (name, prefix_len);
 
   /* The rest of the name is everything else past the initial scope
      operator.  */
-  std::string nested (name + prefix_len + 2);
-
-  /* Lookup a class named KLASS.  If none is found, there is nothing
-     more that can be done.  KLASS could be a namespace, so always look
-     in VAR_DOMAIN.  This works for classes too because of
-     symbol_matches_domain (which should be replaced with something else,
-     but it's what we have today).  */
-  klass_sym = lookup_global_symbol (klass.c_str (), block, VAR_DOMAIN);
-  if (klass_sym.symbol == NULL)
+  const char *nested = name + prefix_len + 2;
+
+  /* Lookup the scope symbol.  If none is found, there is nothing more
+     that can be done.  SCOPE could be a namespace, so always look in
+     VAR_DOMAIN.  This works for classes too because of
+     symbol_matches_domain (which should be replaced with something
+     else, but it's what we have today).  */
+  block_symbol scope_sym = lookup_symbol_in_static_block (scope.c_str (),
+							  block, VAR_DOMAIN);
+  if (scope_sym.symbol == NULL)
+    scope_sym = lookup_global_symbol (scope.c_str (), block, VAR_DOMAIN);
+  if (scope_sym.symbol == NULL)
     return null_block_symbol;
-  klass_type = SYMBOL_TYPE (klass_sym.symbol);
 
-  /* Look for a symbol named NESTED in this class.
+  struct type *scope_type = SYMBOL_TYPE (scope_sym.symbol);
+
+  /* If the scope is a function/method, then look up NESTED as a local
+     static variable.  E.g., "print 'function()::static_var'".  */
+  if (TYPE_CODE (scope_type) == TYPE_CODE_FUNC
+      || TYPE_CODE (scope_type) == TYPE_CODE_METHOD)
+    return lookup_symbol (nested, SYMBOL_BLOCK_VALUE (scope_sym.symbol),
+			  VAR_DOMAIN, NULL);
+
+  /* Look for a symbol named NESTED in this class/namespace.
      The caller is assumed to have already have done a basic lookup of NAME.
      So we pass zero for BASIC_LOOKUP to cp_lookup_nested_symbol_1 here.  */
-  sym = cp_lookup_nested_symbol_1 (klass_type, nested.c_str (), name,
-				   block, domain, 0, is_in_anonymous);
-
-  return sym;
+  return cp_lookup_nested_symbol_1 (scope_type, nested, name,
+				    block, domain, 0, is_in_anonymous);
 }
 
 /* Look up NAME in the C++ namespace NAMESPACE.  Other arguments are
diff --git a/gdb/std-operator.def b/gdb/std-operator.def
index 56a9af9..344ba25 100644
--- a/gdb/std-operator.def
+++ b/gdb/std-operator.def
@@ -298,7 +298,16 @@ OP (OP_SCOPE)
      p 'S:method() const'::var
 
    then the C-specific handling directly in the parser takes over (see
-   "block/variable productions).  */
+   "block/variable productions).
+
+   Also, if the whole function+var is quoted like this:
+
+     p 'S:method() const::var'
+
+   then the whole quoted expression is interpreted as a single symbol
+   name and we don't use OP_FUNC_STATIC_VAR either.  In that case, the
+   C++-specific symbol lookup routines take care of the
+   function-local-static search.  */
 OP (OP_FUNC_STATIC_VAR)
 
 /* OP_TYPE is for parsing types, and used with the "ptype" command
diff --git a/gdb/testsuite/gdb.cp/local-static.exp b/gdb/testsuite/gdb.cp/local-static.exp
index ba0c5ef..581efa3 100644
--- a/gdb/testsuite/gdb.cp/local-static.exp
+++ b/gdb/testsuite/gdb.cp/local-static.exp
@@ -95,6 +95,11 @@ proc do_test {lang} {
     #
     #  'func()'::var
     #  func()::var
+    #  'func()::var'
+    #
+    # In C++, the latter case makes sure that symbol lookup finds the
+    # debug symbol instead of the minimal symbol with that exact same
+    # name.
 
     foreach scope_line $scopes_list  {
 	set scope [lindex $scope_line 0]
@@ -105,6 +110,13 @@ proc do_test {lang} {
 
 	    gdb_test "print '${scope}'::${var_prefix}_${var}" $print_re
 	    gdb_test "print ${scope}::${var_prefix}_${var}" $print_re
+
+	    set sym "${scope}::${var_prefix}_${var}"
+	    if {$lang == "c++"} {
+		gdb_test "print '${sym}'" $print_re
+	    } else {
+		gdb_test "print '${sym}'" "No symbol \"$sym\" in current context\\."
+	    }
 	}
     }
 
diff --git a/gdb/testsuite/gdb.cp/m-static.exp b/gdb/testsuite/gdb.cp/m-static.exp
index eeb88e9..10239a3 100644
--- a/gdb/testsuite/gdb.cp/m-static.exp
+++ b/gdb/testsuite/gdb.cp/m-static.exp
@@ -52,11 +52,6 @@ gdb_continue_to_breakpoint "end of constructors"
 
 # One.
 
-# simple object, static const int, accessing via 'class::method::variable'
-# Regression test for PR c++/15203 and PR c++/15210
-gdb_test "print (int) 'gnu_obj_1::method()::sintvar'" "\\$\[0-9\]+ = 4" \
-    "simple object, static int, accessing via 'class::method::variable'"
-
 # simple object, static const bool
 gdb_test "print test1.test" "\\$\[0-9\]* = true" "simple object, static const bool"
 
-- 
2.5.5

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

* [PATCH v2 05/13] evaluate_subexp_standard: Eliminate one goto
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
                   ` (5 preceding siblings ...)
  2017-07-13 15:32 ` [PATCH v2 06/13] evaluate_subexp_standard: Remove useless assignments Pedro Alves
@ 2017-07-13 15:32 ` Pedro Alves
  2017-07-13 15:32 ` [PATCH v2 01/13] Fix calling prototyped functions via function pointers Pedro Alves
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:32 UTC (permalink / raw)
  To: gdb-patches

A following patch will want to factor out a bit of
evaluate_subexp_standard, and it'd be handy to reuse the code under the
"nosideret:" label there too.  This commits moves it to a separate
function as preparation for that.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* eval.c (eval_skip_value): New function.
	(evaluate_subexp_standard): Use it.
---
 gdb/eval.c | 110 ++++++++++++++++++++++++++++++-------------------------------
 1 file changed, 55 insertions(+), 55 deletions(-)

diff --git a/gdb/eval.c b/gdb/eval.c
index 0e77f0a..639e06e 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -695,6 +695,14 @@ evaluate_var_msym_value (enum noside noside,
     }
 }
 
+/* Helper for returning a value when handling EVAL_SKIP.  */
+
+static value *
+eval_skip_value (expression *exp)
+{
+  return value_from_longest (builtin_type (exp->gdbarch)->builtin_int, 1);
+}
+
 struct value *
 evaluate_subexp_standard (struct type *expect_type,
 			  struct expression *exp, int *pos,
@@ -727,7 +735,7 @@ evaluate_subexp_standard (struct type *expect_type,
       tem = longest_to_int (exp->elts[pc + 2].longconst);
       (*pos) += 4 + BYTES_TO_EXP_ELEM (tem + 1);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       arg1 = value_aggregate_elt (exp->elts[pc + 1].type,
 				  &exp->elts[pc + 3].string,
 				  expect_type, 0, noside);
@@ -754,7 +762,7 @@ evaluate_subexp_standard (struct type *expect_type,
     case OP_VAR_VALUE:
       (*pos) += 3;
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
 
       /* JYG: We used to just return value_zero of the symbol type
 	 if we're asked to avoid side effects.  Otherwise we return
@@ -795,7 +803,7 @@ evaluate_subexp_standard (struct type *expect_type,
     case OP_VAR_ENTRY_VALUE:
       (*pos) += 2;
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
 
       {
 	struct symbol *sym = exp->elts[pc + 1].symbol;
@@ -860,7 +868,7 @@ evaluate_subexp_standard (struct type *expect_type,
       tem = longest_to_int (exp->elts[pc + 1].longconst);
       (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       type = language_string_char_type (exp->language_defn, exp->gdbarch);
       return value_string (&exp->elts[pc + 2].string, tem, type);
 
@@ -869,9 +877,7 @@ evaluate_subexp_standard (struct type *expect_type,
       tem = longest_to_int (exp->elts[pc + 1].longconst);
       (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
       if (noside == EVAL_SKIP)
-	{
-	  goto nosideret;
-	}
+	return eval_skip_value (exp);
       return value_nsstring (exp->gdbarch, &exp->elts[pc + 2].string, tem + 1);
 
     case OP_ARRAY:
@@ -1011,7 +1017,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	  argvec[tem] = evaluate_subexp_with_coercion (exp, pos, noside);
 	}
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       return value_array (tem2, tem3, argvec);
 
     case TERNOP_SLICE:
@@ -1023,7 +1029,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	  = value_as_long (evaluate_subexp (NULL_TYPE, exp, pos, noside));
 
 	if (noside == EVAL_SKIP)
-	  goto nosideret;
+	  return eval_skip_value (exp);
 	return value_slice (array, lowbound, upper - lowbound + 1);
       }
 
@@ -1050,7 +1056,7 @@ evaluate_subexp_standard (struct type *expect_type,
 
 	(*pos) += 3 + BYTES_TO_EXP_ELEM (len + 1);
 	if (noside == EVAL_SKIP)
-	  goto nosideret;
+	  return eval_skip_value (exp);
 
 	if (sel[len] != 0)
 	  sel[len] = 0;		/* Make sure it's terminated.  */
@@ -1292,7 +1298,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	  }
 
 	if (noside == EVAL_SKIP)
-	  goto nosideret;
+	  return eval_skip_value (exp);
 
 	if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	  {
@@ -1586,7 +1592,7 @@ evaluate_subexp_standard (struct type *expect_type,
       argvec[tem] = 0;
 
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
 
       if (op == OP_ADL_FUNC)
         {
@@ -1860,7 +1866,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	    argvec[tem] = evaluate_subexp_with_coercion (exp, pos, noside);
 	  argvec[tem] = 0;	/* signal end of arglist */
 	  if (noside == EVAL_SKIP)
-	    goto nosideret;
+	    return eval_skip_value (exp);
 	  goto do_call_it;
 
 	default:
@@ -1881,7 +1887,7 @@ evaluate_subexp_standard (struct type *expect_type,
       (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       arg3 = value_struct_elt (&arg1, NULL, &exp->elts[pc + 2].string,
 			       NULL, "structure");
       if (noside == EVAL_AVOID_SIDE_EFFECTS)
@@ -1893,7 +1899,7 @@ evaluate_subexp_standard (struct type *expect_type,
       (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
 
       /* Check to see if operator '->' has been overloaded.  If so replace
          arg1 with the value returned by evaluating operator->().  */
@@ -1954,7 +1960,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
 
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
 
       type = check_typedef (value_type (arg2));
       switch (TYPE_CODE (type))
@@ -2003,7 +2009,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
       arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (binop_user_defined_p (op, arg1, arg2))
 	return value_x_binop (arg1, arg2, op, OP_NULL, noside);
       else
@@ -2057,7 +2063,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
       arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (binop_user_defined_p (op, arg1, arg2))
 	return value_x_binop (arg1, arg2, op, OP_NULL, noside);
       else if (ptrmath_type_p (exp->language_defn, value_type (arg1))
@@ -2076,7 +2082,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
       arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (binop_user_defined_p (op, arg1, arg2))
 	return value_x_binop (arg1, arg2, op, OP_NULL, noside);
       else if (ptrmath_type_p (exp->language_defn, value_type (arg1))
@@ -2109,7 +2115,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (binop_user_defined_p (op, arg1, arg2))
 	return value_x_binop (arg1, arg2, op, OP_NULL, noside);
       else
@@ -2152,7 +2158,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (binop_user_defined_p (op, arg1, arg2))
 	return value_x_binop (arg1, arg2, op, OP_NULL, noside);
       else
@@ -2189,13 +2195,8 @@ evaluate_subexp_standard (struct type *expect_type,
 	  if (noside == EVAL_SKIP)
 	    {
 	      if (nargs > 0)
-		{
-		  continue;
-		}
-	      else
-		{
-		  goto nosideret;
-		}
+		continue;
+	      return eval_skip_value (exp);
 	    }
 	  /* FIXME:  EVAL_AVOID_SIDE_EFFECTS handling may not be correct.  */
 	  if (noside == EVAL_AVOID_SIDE_EFFECTS)
@@ -2294,7 +2295,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	{
 	  evaluate_subexp (NULL_TYPE, exp, pos, noside);
-	  goto nosideret;
+	  return eval_skip_value (exp);
 	}
 
       oldpos = *pos;
@@ -2321,7 +2322,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	{
 	  evaluate_subexp (NULL_TYPE, exp, pos, noside);
-	  goto nosideret;
+	  return eval_skip_value (exp);
 	}
 
       oldpos = *pos;
@@ -2347,7 +2348,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       arg2 = evaluate_subexp (value_type (arg1), exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (binop_user_defined_p (op, arg1, arg2))
 	{
 	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
@@ -2364,7 +2365,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       arg2 = evaluate_subexp (value_type (arg1), exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (binop_user_defined_p (op, arg1, arg2))
 	{
 	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
@@ -2381,7 +2382,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       arg2 = evaluate_subexp (value_type (arg1), exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (binop_user_defined_p (op, arg1, arg2))
 	{
 	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
@@ -2398,7 +2399,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       arg2 = evaluate_subexp (value_type (arg1), exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (binop_user_defined_p (op, arg1, arg2))
 	{
 	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
@@ -2415,7 +2416,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       arg2 = evaluate_subexp (value_type (arg1), exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (binop_user_defined_p (op, arg1, arg2))
 	{
 	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
@@ -2432,7 +2433,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       arg2 = evaluate_subexp (value_type (arg1), exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (binop_user_defined_p (op, arg1, arg2))
 	{
 	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
@@ -2449,7 +2450,7 @@ evaluate_subexp_standard (struct type *expect_type,
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       type = check_typedef (value_type (arg2));
       if (TYPE_CODE (type) != TYPE_CODE_INT
           && TYPE_CODE (type) != TYPE_CODE_ENUM)
@@ -2469,7 +2470,7 @@ evaluate_subexp_standard (struct type *expect_type,
     case UNOP_PLUS:
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (unop_user_defined_p (op, arg1))
 	return value_x_unop (arg1, op, noside);
       else
@@ -2481,7 +2482,7 @@ evaluate_subexp_standard (struct type *expect_type,
     case UNOP_NEG:
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (unop_user_defined_p (op, arg1))
 	return value_x_unop (arg1, op, noside);
       else
@@ -2496,7 +2497,7 @@ evaluate_subexp_standard (struct type *expect_type,
 
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (unop_user_defined_p (UNOP_COMPLEMENT, arg1))
 	return value_x_unop (arg1, UNOP_COMPLEMENT, noside);
       else
@@ -2508,7 +2509,7 @@ evaluate_subexp_standard (struct type *expect_type,
     case UNOP_LOGICAL_NOT:
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (unop_user_defined_p (op, arg1))
 	return value_x_unop (arg1, op, noside);
       else
@@ -2527,7 +2528,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	error (_("Attempt to dereference pointer "
 		 "to member without an object"));
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (unop_user_defined_p (op, arg1))
 	return value_x_unop (arg1, op, noside);
       else if (noside == EVAL_AVOID_SIDE_EFFECTS)
@@ -2565,7 +2566,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	{
 	  evaluate_subexp (NULL_TYPE, exp, pos, EVAL_SKIP);
-	  goto nosideret;
+	  return eval_skip_value (exp);
 	}
       else
 	{
@@ -2579,7 +2580,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	{
 	  evaluate_subexp (NULL_TYPE, exp, pos, EVAL_SKIP);
-	  goto nosideret;
+	  return eval_skip_value (exp);
 	}
       return evaluate_subexp_for_sizeof (exp, pos, noside);
 
@@ -2588,7 +2589,7 @@ evaluate_subexp_standard (struct type *expect_type,
       type = exp->elts[pc + 1].type;
       arg1 = evaluate_subexp (type, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (type != value_type (arg1))
 	arg1 = value_cast (type, arg1);
       return arg1;
@@ -2598,7 +2599,7 @@ evaluate_subexp_standard (struct type *expect_type,
       type = value_type (arg1);
       arg1 = evaluate_subexp (type, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (type != value_type (arg1))
 	arg1 = value_cast (type, arg1);
       return arg1;
@@ -2608,7 +2609,7 @@ evaluate_subexp_standard (struct type *expect_type,
       type = value_type (arg1);
       arg1 = evaluate_subexp (type, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       return value_dynamic_cast (type, arg1);
 
     case UNOP_REINTERPRET_CAST:
@@ -2616,14 +2617,14 @@ evaluate_subexp_standard (struct type *expect_type,
       type = value_type (arg1);
       arg1 = evaluate_subexp (type, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       return value_reinterpret_cast (type, arg1);
 
     case UNOP_MEMVAL:
       (*pos) += 2;
       arg1 = evaluate_subexp (expect_type, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	return value_zero (exp->elts[pc + 1].type, lval_memory);
       else
@@ -2635,7 +2636,7 @@ evaluate_subexp_standard (struct type *expect_type,
       type = value_type (arg1);
       arg1 = evaluate_subexp (expect_type, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	return value_zero (type, lval_memory);
       else
@@ -2645,7 +2646,7 @@ evaluate_subexp_standard (struct type *expect_type,
       (*pos) += 3;
       arg1 = evaluate_subexp (expect_type, exp, pos, noside);
       if (noside == EVAL_SKIP)
-	goto nosideret;
+	return eval_skip_value (exp);
       if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	return value_zero (exp->elts[pc + 2].type, lval_memory);
       else
@@ -2768,7 +2769,7 @@ evaluate_subexp_standard (struct type *expect_type,
          easier to accommodate expressions that contain types.  */
       (*pos) += 2;
       if (noside == EVAL_SKIP)
-        goto nosideret;
+	return eval_skip_value (exp);
       else if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	return allocate_value (exp->elts[pc + 1].type);
       else
@@ -2779,7 +2780,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	{
 	  evaluate_subexp (NULL_TYPE, exp, pos, EVAL_SKIP);
-	  goto nosideret;
+	  return eval_skip_value (exp);
 	}
       else if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	{
@@ -2844,8 +2845,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	       "evaluate that kind of expression"));
     }
 
-nosideret:
-  return value_from_longest (builtin_type (exp->gdbarch)->builtin_int, 1);
+  gdb_assert_not_reached ("missed return?");
 }
 \f
 /* Evaluate a subexpression of EXP, at index *POS,
-- 
2.5.5

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

* [PATCH v2 02/13] Stop assuming no-debug-info functions return int
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
                   ` (3 preceding siblings ...)
  2017-07-13 15:32 ` [PATCH v2 10/13] Handle "p S::method()::static_var" in the C++ parser Pedro Alves
@ 2017-07-13 15:32 ` Pedro Alves
  2017-07-13 15:32 ` [PATCH v2 06/13] evaluate_subexp_standard: Remove useless assignments Pedro Alves
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:32 UTC (permalink / raw)
  To: gdb-patches

New in v2:
  - The new simpler cast-return-type syntax now assumes the called
    function is prototyped instead of unprototyped.

The fact that GDB defaults to assuming that functions return int, when
it has no debug info for the function has been a recurring source of
user confusion.  Recently this came up on the errno pretty printer
discussions.  Shortly after, it came up again on IRC, with someone
wondering why does getenv() in GDB return a negative int:

  (gdb) p getenv("PATH")
  $1 = -6185

This question (with s/getenv/random-other-C-runtime-function) is a FAQ
on IRC.

The reason for the above is:

 (gdb) p getenv
 $2 = {<text variable, no debug info>} 0x7ffff7751d80 <getenv>
 (gdb) ptype getenv
 type = int ()

... which means that GDB truncated the 64-bit pointer that is actually
returned from getent to 32-bit, and then sign-extended it:

 (gdb) p /x -6185
 $6 = 0xffffe7d7

The workaround is to cast the function to the right type, like:

 (gdb) p ((char *(*) (const char *)) getenv) ("PATH")
 $3 = 0x7fffffffe7d7 "/usr/local/bin:/"...

IMO, we should do better than this.

I see the "assume-int" issue the same way I see printing bogus values
for optimized-out variables instead of "<optimized out>" -- I'd much
rather that the debugger tells me "I don't know" and tells me how to
fix it than showing me bogus misleading results, making me go around
tilting at windmills.

If GDB prints a signed integer when you're expecting a pointer or
aggregate, you at least have some sense that something is off, but
consider the case of the function actually returning a 64-bit integer.
For example, compile this without debug info:

 unsigned long long
 function ()
 {
   return 0x7fffffffffffffff;
 }

Currently, with pristine GDB, you get:

 (gdb) p function ()
 $1 = -1                      # incorrect
 (gdb) p /x function ()
 $2 = 0xffffffff              # incorrect

maybe after spending a few hours debugging you suspect something is
wrong with that -1, and do:

 (gdb) ptype function
 type = int ()

and maybe, just maybe, you realize that the function actually returns
unsigned long long.  And you try to fix it with:

(gdb) p /x (unsigned long long) function ()
 $3 = 0xffffffffffffffff      # incorrect

... which still produces the wrong result, because GDB simply applied
int to unsigned long long conversion.  Meaning, it sign-extended the
integer that it extracted from the return of the function, to 64-bits.

and then maybe, after asking around on IRC, you realize you have to
cast the function to a pointer of the right type, and call that.  It
won't be easy, but after a few missteps, you'll get to it:

.....  (gdb) p /x ((unsigned long long(*) ()) function) ()
 $666 = 0x7fffffffffffffff             # finally! :-)


So to improve on the user experience, this patch does the following
(interrelated) things:

 - makes no-debug-info functions no longer default to "int" as return
   type.  Instead, they're left with NULL/"<unknown return type>"
   return type.

    (gdb) ptype getenv
    type = <unknown return type> ()

 - makes calling a function with unknown return type an error.

    (gdb) p getenv ("PATH")
    'getenv' has unknown return type; cast the call to its declared return type

 - and then to make it easier to call the function, makes it possible
   to _only_ cast the return of the function to the right type,
   instead of having to cast the function to a function pointer:

    (gdb) p (char *) getenv ("PATH")                      # now Just Works
    $3 = 0x7fffffffe7d7 "/usr/local/bin:/"...

    (gdb) p ((char *(*) (const char *)) getenv) ("PATH")  # continues working
    $4 = 0x7fffffffe7d7 "/usr/local/bin:/"...

   I.e., it makes GDB default the function's return type to the type
   of the cast, and the function's parameters to the type of the
   arguments passed down.

After this patch, here's what you'll get for the "unsigned long long"
example above:

 (gdb) p function ()
 'function' has unknown return type; cast the call to its declared return type
 (gdb) p /x (unsigned long long) function ()
 $4 = 0x7fffffffffffffff     # correct!

Note that while with "print" GDB shows the name of the function that
has the problem:

  (gdb) p getenv ("PATH")
  'getenv' has unknown return type; cast the call to its declared return type

which can by handy in more complicated expressions, "ptype" does not:

  (gdb) ptype getenv ("PATH")
  function has unknown return type; cast the call to its declared return type

This will be fixed in the next patch.

gdb/ChangeLog:
yyyy-dd-mm  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_evaluate_subexp) <TYPE_CODE_FUNC>: Don't handle
	TYPE_GNU_IFUNC specially here.  Throw error if return type is
	unknown.
	* ada-typeprint.c (print_func_type): Handle functions with unknown
	return type.
	* c-typeprint.c (c_type_print_base): Handle functions and methods
	with unknown return type.
	* compile/compile-c-symbols.c (convert_symbol_bmsym)
	<mst_text_gnu_ifunc>: Use nodebug_text_gnu_ifunc_symbol.
	* compile/compile-c-types.c: Include "objfiles.h".
	(convert_func): For functions with unknown return type, warn and
	default to int.
	* compile/compile-object-run.c (compile_object_run): Adjust call
	to call_function_by_hand_dummy.
	* elfread.c (elf_gnu_ifunc_resolve_addr): Adjust call to
	call_function_by_hand.
	* eval.c (evaluate_subexp_standard): Adjust calls to
	call_function_by_hand.  Handle functions and methods with unknown
	return type.  Pass expect_type to call_function_by_hand.
	* f-typeprint.c (f_type_print_base): Handle functions with unknown
	return type.
	* gcore.c (call_target_sbrk): Adjust call to
	call_function_by_hand.
	* gdbtypes.c (objfile_type): Leave nodebug text symbol with NULL
	return type instead of int.  Make nodebug_text_gnu_ifunc_symbol be
	an integer address type instead of nodebug.
	* guile/scm-value.c (gdbscm_value_call): Adjust call to
	call_function_by_hand.
	* infcall.c (error_call_unknown_return_type): New function.
	(call_function_by_hand): New "default_return_type" parameter.
	Pass it down.
	(call_function_by_hand_dummy): New "default_return_type"
	parameter.  Use it instead of defaulting to int.  If there's no
	default and the return type is unknown, throw an error.  If
	there's a default return type, and the called function has no
	debug info, then assume the function is prototyped.
	* infcall.h (call_function_by_hand, call_function_by_hand_dummy):
	New "default_return_type" parameter.
	(error_call_unknown_return_type): New declaration.
	* linux-fork.c (call_lseek): Cast return type of lseek.
	(inferior_call_waitpid, checkpoint_command): Adjust calls to
	call_function_by_hand.
	* linux-tdep.c (linux_infcall_mmap, linux_infcall_munmap): Adjust
	calls to call_function_by_hand.
	* m2-typeprint.c (m2_procedure): Handle functions with unknown
	return type.
	* objc-lang.c (lookup_objc_class, lookup_child_selector)
	(value_nsstring, print_object_command): Adjust calls to
	call_function_by_hand.
	* p-typeprint.c (pascal_type_print_varspec_prefix): Handle
	functions with unknown return type.
	(pascal_type_print_func_varspec_suffix): New function.
	(pascal_type_print_varspec_suffix) <TYPE_CODE_FUNC,
	TYPE_CODE_METHOD>: Use it.
	* python/py-value.c (valpy_call): Adjust call to
	call_function_by_hand.
	* rust-lang.c (rust_evaluate_funcall): Adjust call to
	call_function_by_hand.
	* valarith.c (value_x_binop, value_x_unop): Adjust calls to
	call_function_by_hand.
	* valops.c (value_allocate_space_in_inferior): Adjust call to
	call_function_by_hand.
	* typeprint.c (type_print_unknown_return_type): New function.
	* typeprint.h (type_print_unknown_return_type): New declaration.

gdb/testsuite/ChangeLog:
yyyy-dd-mm  Pedro Alves  <palves@redhat.com>

	* gdb.base/break-main-file-remove-fail.exp (test_remove_bp): Cast
	return type of munmap in infcall.
	* gdb.base/break-probes.exp: Cast return type of foo in infcall.
	* gdb.base/checkpoint.exp: Simplify using for loop.  Cast return
	type of ftell in infcall.
	* gdb.base/dprintf-detach.exp (dprintf_detach_test): Cast return
	type of getpid in infcall.
	* gdb.base/infcall-exec.exp: Cast return type of execlp in
	infcall.
	* gdb.base/info-os.exp: Cast return type of getpid in infcall.
	Bail on failure to extract the pid.
	* gdb.base/nodebug.c: #include <stdint.h>.
	(multf, multf_noproto, mult, mult_noproto, add8, add8_noproto):
	New functions.
	* gdb.base/nodebug.exp (test_call_promotion): New procedure.
	Change expected output of print/whatis/ptype with functions with
	no debug info.  Test all supported languages.  Call
	test_call_promotion.
	* gdb.compile/compile.exp: Adjust expected output to expect
	warning.
	* gdb.threads/siginfo-threads.exp: Likewise.
---
 gdb/ada-lang.c                                     |  10 +-
 gdb/ada-typeprint.c                                |   7 +-
 gdb/c-typeprint.c                                  |  10 +-
 gdb/compile/compile-c-symbols.c                    |   4 +-
 gdb/compile/compile-c-types.c                      |  20 +++-
 gdb/compile/compile-object-run.c                   |   3 +-
 gdb/elfread.c                                      |   2 +-
 gdb/eval.c                                         |  41 ++++---
 gdb/f-typeprint.c                                  |   7 +-
 gdb/gcore.c                                        |   2 +-
 gdb/gdbtypes.c                                     |   6 +-
 gdb/guile/scm-value.c                              |   2 +-
 gdb/infcall.c                                      |  49 ++++++++-
 gdb/infcall.h                                      |  19 +++-
 gdb/linux-fork.c                                   |   6 +-
 gdb/linux-tdep.c                                   |   4 +-
 gdb/m2-typeprint.c                                 |  11 +-
 gdb/objc-lang.c                                    |  14 ++-
 gdb/p-typeprint.c                                  |  57 ++++++----
 gdb/python/py-value.c                              |   3 +-
 gdb/rust-lang.c                                    |   2 +-
 .../gdb.base/break-main-file-remove-fail.exp       |   2 +-
 gdb/testsuite/gdb.base/break-probes.exp            |   2 +-
 gdb/testsuite/gdb.base/checkpoint.exp              |  44 +-------
 gdb/testsuite/gdb.base/dprintf-detach.exp          |   2 +-
 gdb/testsuite/gdb.base/infcall-exec.exp            |   2 +-
 gdb/testsuite/gdb.base/info-os.exp                 |   8 +-
 gdb/testsuite/gdb.base/nodebug.c                   |  41 +++++++
 gdb/testsuite/gdb.base/nodebug.exp                 | 122 ++++++++++++++++++---
 gdb/testsuite/gdb.compile/compile.exp              |  10 +-
 gdb/testsuite/gdb.threads/siginfo-threads.exp      |   7 +-
 gdb/typeprint.c                                    |   9 +-
 gdb/typeprint.h                                    |   4 +
 gdb/valarith.c                                     |   4 +-
 gdb/valops.c                                       |   2 +-
 35 files changed, 384 insertions(+), 154 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index ea60df2..77c5866 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -10807,13 +10807,11 @@ ada_evaluate_subexp (struct type *expect_type, struct expression *exp,
         case TYPE_CODE_FUNC:
           if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	    {
-	      struct type *rtype = TYPE_TARGET_TYPE (type);
-
-	      if (TYPE_GNU_IFUNC (type))
-		return allocate_value (TYPE_TARGET_TYPE (rtype));
-	      return allocate_value (rtype);
+	      if (TYPE_TARGET_TYPE (type) == NULL)
+		error_call_unknown_return_type (NULL);
+	      return allocate_value (TYPE_TARGET_TYPE (type));
 	    }
-          return call_function_by_hand (argvec[0], nargs, argvec + 1);
+	  return call_function_by_hand (argvec[0], NULL, nargs, argvec + 1);
 	case TYPE_CODE_INTERNAL_FUNCTION:
 	  if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	    /* We don't know anything about what the internal
diff --git a/gdb/ada-typeprint.c b/gdb/ada-typeprint.c
index 3c33bdc..65b8084 100644
--- a/gdb/ada-typeprint.c
+++ b/gdb/ada-typeprint.c
@@ -775,7 +775,8 @@ print_func_type (struct type *type, struct ui_file *stream, const char *name,
 {
   int i, len = TYPE_NFIELDS (type);
 
-  if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_VOID)
+  if (TYPE_TARGET_TYPE (type) != NULL
+      && TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_VOID)
     fprintf_filtered (stream, "procedure");
   else
     fprintf_filtered (stream, "function");
@@ -800,7 +801,9 @@ print_func_type (struct type *type, struct ui_file *stream, const char *name,
       fprintf_filtered (stream, ")");
     }
 
-  if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
+  if (TYPE_TARGET_TYPE (type) == NULL)
+    fprintf_filtered (stream, " return <unknown return type>");
+  else if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
     {
       fprintf_filtered (stream, " return ");
       ada_print_type (TYPE_TARGET_TYPE (type), "", stream, 0, 0, flags);
diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c
index 9e197f5..64bb51c 100644
--- a/gdb/c-typeprint.c
+++ b/gdb/c-typeprint.c
@@ -893,13 +893,19 @@ c_type_print_base (struct type *type, struct ui_file *stream,
       fprintf_filtered (stream, _("<unnamed typedef>"));
       break;
 
+    case TYPE_CODE_FUNC:
+    case TYPE_CODE_METHOD:
+      if (TYPE_TARGET_TYPE (type) == NULL)
+	type_print_unknown_return_type (stream);
+      else
+	c_type_print_base (TYPE_TARGET_TYPE (type),
+			   stream, show, level, flags);
+      break;
     case TYPE_CODE_ARRAY:
     case TYPE_CODE_PTR:
     case TYPE_CODE_MEMBERPTR:
     case TYPE_CODE_REF:
     case TYPE_CODE_RVALUE_REF:
-    case TYPE_CODE_FUNC:
-    case TYPE_CODE_METHOD:
     case TYPE_CODE_METHODPTR:
       c_type_print_base (TYPE_TARGET_TYPE (type),
 			 stream, show, level, flags);
diff --git a/gdb/compile/compile-c-symbols.c b/gdb/compile/compile-c-symbols.c
index 15e1d6d..c5ec941 100644
--- a/gdb/compile/compile-c-symbols.c
+++ b/gdb/compile/compile-c-symbols.c
@@ -379,9 +379,7 @@ convert_symbol_bmsym (struct compile_c_instance *context,
       break;
 
     case mst_text_gnu_ifunc:
-      /* nodebug_text_gnu_ifunc_symbol would cause:
-	 function return type cannot be function  */
-      type = objfile_type (objfile)->nodebug_text_symbol;
+      type = objfile_type (objfile)->nodebug_text_gnu_ifunc_symbol;
       kind = GCC_C_SYMBOL_FUNCTION;
       addr = gnu_ifunc_resolve_addr (target_gdbarch (), addr);
       break;
diff --git a/gdb/compile/compile-c-types.c b/gdb/compile/compile-c-types.c
index 22aee78..7fbab13 100644
--- a/gdb/compile/compile-c-types.c
+++ b/gdb/compile/compile-c-types.c
@@ -21,6 +21,8 @@
 #include "defs.h"
 #include "gdbtypes.h"
 #include "compile-internal.h"
+#include "objfiles.h"
+
 /* An object that maps a gdb type to a gcc type.  */
 
 struct type_map_instance
@@ -234,9 +236,25 @@ convert_func (struct compile_c_instance *context, struct type *type)
   struct gcc_type_array array;
   int is_varargs = TYPE_VARARGS (type) || !TYPE_PROTOTYPED (type);
 
+  struct type *target_type = TYPE_TARGET_TYPE (type);
+
+  /* Functions with no debug info have no return type.  Ideally we'd
+     want to fallback to the type of the cast just before the
+     function, like GDB's built-in expression parser, but we don't
+     have access to that type here.  For now, fallback to int, like
+     GDB's parser used to do.  */
+  if (target_type == NULL)
+    {
+      if (TYPE_OBJFILE_OWNED (type))
+	target_type = objfile_type (TYPE_OWNER (type).objfile)->builtin_int;
+      else
+	target_type = builtin_type (TYPE_OWNER (type).gdbarch)->builtin_int;
+      warning (_("function has unknown return type; assuming int"));
+    }
+
   /* This approach means we can't make self-referential function
      types.  Those are impossible in C, though.  */
-  return_type = convert_type (context, TYPE_TARGET_TYPE (type));
+  return_type = convert_type (context, target_type);
 
   array.n_elements = TYPE_NFIELDS (type);
   array.elements = XNEWVEC (gcc_type, TYPE_NFIELDS (type));
diff --git a/gdb/compile/compile-object-run.c b/gdb/compile/compile-object-run.c
index de5fa16..43da59f 100644
--- a/gdb/compile/compile-object-run.c
+++ b/gdb/compile/compile-object-run.c
@@ -172,7 +172,8 @@ compile_object_run (struct compile_module *module)
 	  ++current_arg;
 	}
       gdb_assert (current_arg == TYPE_NFIELDS (func_type));
-      call_function_by_hand_dummy (func_val, TYPE_NFIELDS (func_type), vargs,
+      call_function_by_hand_dummy (func_val,
+				   NULL, TYPE_NFIELDS (func_type), vargs,
 				   do_module_cleanup, data);
     }
   CATCH (ex, RETURN_MASK_ERROR)
diff --git a/gdb/elfread.c b/gdb/elfread.c
index fba2026..b34cdd5 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -889,7 +889,7 @@ elf_gnu_ifunc_resolve_addr (struct gdbarch *gdbarch, CORE_ADDR pc)
   target_auxv_search (&current_target, AT_HWCAP, &hwcap);
   hwcap_val = value_from_longest (builtin_type (gdbarch)
 				  ->builtin_unsigned_long, hwcap);
-  address_val = call_function_by_hand (function, 1, &hwcap_val);
+  address_val = call_function_by_hand (function, NULL, 1, &hwcap_val);
   address = value_as_address (address_val);
   address = gdbarch_convert_from_func_ptr_addr (gdbarch, address,
 						&current_target);
diff --git a/gdb/eval.c b/gdb/eval.c
index 80dfb2e..1ec6751 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -1147,12 +1147,12 @@ evaluate_subexp_standard (struct type *expect_type,
 	argvec[3] = value_from_longest (long_type, selector);
 	argvec[4] = 0;
 
-	ret = call_function_by_hand (argvec[0], 3, argvec + 1);
+	ret = call_function_by_hand (argvec[0], NULL, 3, argvec + 1);
 	if (gnu_runtime)
 	  {
 	    /* Function objc_msg_lookup returns a pointer.  */
 	    argvec[0] = ret;
-	    ret = call_function_by_hand (argvec[0], 3, argvec + 1);
+	    ret = call_function_by_hand (argvec[0], NULL, 3, argvec + 1);
 	  }
 	if (value_as_long (ret) == 0)
 	  error (_("Target does not respond to this message selector."));
@@ -1169,11 +1169,11 @@ evaluate_subexp_standard (struct type *expect_type,
 	argvec[3] = value_from_longest (long_type, selector);
 	argvec[4] = 0;
 
-	ret = call_function_by_hand (argvec[0], 3, argvec + 1);
+	ret = call_function_by_hand (argvec[0], NULL, 3, argvec + 1);
 	if (gnu_runtime)
 	  {
 	    argvec[0] = ret;
-	    ret = call_function_by_hand (argvec[0], 3, argvec + 1);
+	    ret = call_function_by_hand (argvec[0], NULL, 3, argvec + 1);
 	  }
 
 	/* ret should now be the selector.  */
@@ -1315,10 +1315,10 @@ evaluate_subexp_standard (struct type *expect_type,
 	    deprecated_set_value_type (argvec[0],
 				       lookup_pointer_type (lookup_function_type (value_type (argvec[0]))));
 	    argvec[0]
-	      = call_function_by_hand (argvec[0], nargs + 2, argvec + 1);
+	      = call_function_by_hand (argvec[0], NULL, nargs + 2, argvec + 1);
 	  }
 
-	ret = call_function_by_hand (argvec[0], nargs + 2, argvec + 1);
+	ret = call_function_by_hand (argvec[0], NULL, nargs + 2, argvec + 1);
 	return ret;
       }
       break;
@@ -1705,12 +1705,9 @@ evaluate_subexp_standard (struct type *expect_type,
 	error (_("Cannot evaluate function -- may be inlined"));
       if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	{
-	  /* If the return type doesn't look like a function type, call an
-	     error.  This can happen if somebody tries to turn a variable into
-	     a function call.  This is here because people often want to
-	     call, eg, strcmp, which gdb doesn't know is a function.  If
-	     gdb isn't asked for it's opinion (ie. through "whatis"),
-	     it won't offer it.  */
+	  /* If the return type doesn't look like a function type,
+	     call an error.  This can happen if somebody tries to turn
+	     a variable into a function call.  */
 
 	  struct type *ftype = value_type (argvec[0]);
 
@@ -1731,10 +1728,19 @@ evaluate_subexp_standard (struct type *expect_type,
 		error (_("Xmethod is missing return type."));
 	      return value_zero (return_type, not_lval);
 	    }
-	  else if (TYPE_GNU_IFUNC (ftype))
-	    return allocate_value (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (ftype)));
-	  else if (TYPE_TARGET_TYPE (ftype))
-	    return allocate_value (TYPE_TARGET_TYPE (ftype));
+	  else if (TYPE_CODE (ftype) == TYPE_CODE_FUNC
+		   || TYPE_CODE (ftype) == TYPE_CODE_METHOD)
+	    {
+	      struct type *return_type = TYPE_TARGET_TYPE (ftype);
+
+	      if (return_type == NULL)
+		return_type = expect_type;
+
+	      if (return_type == NULL)
+		error_call_unknown_return_type (NULL);
+
+	      return allocate_value (return_type);
+	    }
 	  else
 	    error (_("Expression of type other than "
 		     "\"Function returning ...\" used as function"));
@@ -1747,7 +1753,8 @@ evaluate_subexp_standard (struct type *expect_type,
 	case TYPE_CODE_XMETHOD:
 	  return call_xmethod (argvec[0], nargs, argvec + 1);
 	default:
-	  return call_function_by_hand (argvec[0], nargs, argvec + 1);
+	  return call_function_by_hand (argvec[0],
+					expect_type, nargs, argvec + 1);
 	}
       /* pai: FIXME save value from call_function_by_hand, then adjust
 	 pc by adjust_fn_pc if +ve.  */
diff --git a/gdb/f-typeprint.c b/gdb/f-typeprint.c
index 7dbe093..2a858aa 100644
--- a/gdb/f-typeprint.c
+++ b/gdb/f-typeprint.c
@@ -304,9 +304,14 @@ f_type_print_base (struct type *type, struct ui_file *stream, int show,
       break;
 
     case TYPE_CODE_ARRAY:
-    case TYPE_CODE_FUNC:
       f_type_print_base (TYPE_TARGET_TYPE (type), stream, show, level);
       break;
+    case TYPE_CODE_FUNC:
+      if (TYPE_TARGET_TYPE (type) == NULL)
+	type_print_unknown_return_type (stream);
+      else
+	f_type_print_base (TYPE_TARGET_TYPE (type), stream, show, level);
+      break;
 
     case TYPE_CODE_PTR:
       fprintf_filtered (stream, "PTR TO -> ( ");
diff --git a/gdb/gcore.c b/gdb/gcore.c
index c32d2ff..687ad8c 100644
--- a/gdb/gcore.c
+++ b/gdb/gcore.c
@@ -302,7 +302,7 @@ call_target_sbrk (int sbrk_arg)
   target_sbrk_arg = value_from_longest (builtin_type (gdbarch)->builtin_int, 
 					sbrk_arg);
   gdb_assert (target_sbrk_arg);
-  ret = call_function_by_hand (sbrk_fn, 1, &target_sbrk_arg);
+  ret = call_function_by_hand (sbrk_fn, NULL, 1, &target_sbrk_arg);
   if (ret == NULL)
     return (bfd_vma) 0;
 
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index ceb4f0c..a22f0ca 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -5312,13 +5312,13 @@ objfile_type (struct objfile *objfile)
   objfile_type->nodebug_text_symbol
     = init_type (objfile, TYPE_CODE_FUNC, 1,
 		 "<text variable, no debug info>");
-  TYPE_TARGET_TYPE (objfile_type->nodebug_text_symbol)
-    = objfile_type->builtin_int;
   objfile_type->nodebug_text_gnu_ifunc_symbol
     = init_type (objfile, TYPE_CODE_FUNC, 1,
 		 "<text gnu-indirect-function variable, no debug info>");
+  /* Ifunc resolvers return a function address.  */
   TYPE_TARGET_TYPE (objfile_type->nodebug_text_gnu_ifunc_symbol)
-    = objfile_type->nodebug_text_symbol;
+    = init_integer_type (objfile, gdbarch_addr_bit (gdbarch), 1,
+			 "__IFUNC_RESOLVER_RET");
   TYPE_GNU_IFUNC (objfile_type->nodebug_text_gnu_ifunc_symbol) = 1;
   objfile_type->nodebug_got_plt_symbol
     = init_pointer_type (objfile, gdbarch_addr_bit (gdbarch),
diff --git a/gdb/guile/scm-value.c b/gdb/guile/scm-value.c
index f631032..0dc6630 100644
--- a/gdb/guile/scm-value.c
+++ b/gdb/guile/scm-value.c
@@ -864,7 +864,7 @@ gdbscm_value_call (SCM self, SCM args)
       struct cleanup *cleanup = make_cleanup_value_free_to_mark (mark);
       struct value *return_value;
 
-      return_value = call_function_by_hand (function, args_count, vargs);
+      return_value = call_function_by_hand (function, NULL, args_count, vargs);
       result = vlscm_scm_from_value (return_value);
       do_cleanups (cleanup);
     }
diff --git a/gdb/infcall.c b/gdb/infcall.c
index 38335a7..b09dc8f 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -340,6 +340,20 @@ push_dummy_code (struct gdbarch *gdbarch,
 				  regcache);
 }
 
+/* See infcall.h.  */
+
+void
+error_call_unknown_return_type (const char *func_name)
+{
+  if (func_name != NULL)
+    error (_("'%s' has unknown return type; "
+	     "cast the call to its declared return type"),
+	   func_name);
+  else
+    error (_("function has unknown return type; "
+	     "cast the call to its declared return type"));
+}
+
 /* Fetch the name of the function at FUNADDR.
    This is used in printing an error message for call_function_by_hand.
    BUF is used to print FUNADDR in hex if the function name cannot be
@@ -673,9 +687,12 @@ cleanup_delete_std_terminate_breakpoint (void *ignore)
 /* See infcall.h.  */
 
 struct value *
-call_function_by_hand (struct value *function, int nargs, struct value **args)
+call_function_by_hand (struct value *function,
+		       type *default_return_type,
+		       int nargs, struct value **args)
 {
-  return call_function_by_hand_dummy (function, nargs, args, NULL, NULL);
+  return call_function_by_hand_dummy (function, default_return_type,
+				      nargs, args, NULL, NULL);
 }
 
 /* All this stuff with a dummy frame may seem unnecessarily complicated
@@ -698,6 +715,7 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
 
 struct value *
 call_function_by_hand_dummy (struct value *function,
+			     type *default_return_type,
 			     int nargs, struct value **args,
 			     dummy_frame_dtor_ftype *dummy_dtor,
 			     void *dummy_dtor_data)
@@ -850,8 +868,16 @@ call_function_by_hand_dummy (struct value *function,
   }
 
   funaddr = find_function_addr (function, &values_type);
-  if (!values_type)
-    values_type = builtin_type (gdbarch)->builtin_int;
+  if (values_type == NULL)
+    values_type = default_return_type;
+  if (values_type == NULL)
+    {
+      const char *name = get_function_name (funaddr,
+					    name_buf, sizeof (name_buf));
+      error (_("'%s' has unknown return type; "
+	       "cast the call to its declared return type"),
+	     name);
+    }
 
   values_type = check_typedef (values_type);
 
@@ -955,6 +981,21 @@ call_function_by_hand_dummy (struct value *function,
 	   prototyped.  Can we respect TYPE_VARARGS?  Probably not.  */
 	if (TYPE_CODE (ftype) == TYPE_CODE_METHOD)
 	  prototyped = 1;
+	if (TYPE_TARGET_TYPE (ftype) == NULL && TYPE_NFIELDS (ftype) == 0
+	    && default_return_type != NULL)
+	  {
+	    /* Calling a no-debug function with the return type
+	       explicitly cast.  Assume the function is prototyped,
+	       with a prototype matching the types of the arguments.
+	       E.g., with:
+		 float mult (float v1, float v2) { return v1 * v2; }
+	       This:
+		 (gdb) p (float) mult (2.0f, 3.0f)
+	       Is a simpler alternative to:
+		 (gdb) p ((float (*) (float, float)) mult) (2.0f, 3.0f)
+	     */
+	    prototyped = 1;
+	  }
 	else if (i < TYPE_NFIELDS (ftype))
 	  prototyped = TYPE_PROTOTYPED (ftype);
 	else
diff --git a/gdb/infcall.h b/gdb/infcall.h
index cbaa943..a456d92 100644
--- a/gdb/infcall.h
+++ b/gdb/infcall.h
@@ -35,9 +35,15 @@ extern CORE_ADDR find_function_addr (struct value *function,
    representing what the function returned.  May fail to return, if a
    breakpoint or signal is hit during the execution of the function.
 
+   DFEAULT_RETURN_TYPE is used as function return type if the return
+   type is unknown.  This is used when calling functions with no debug
+   info.
+
    ARGS is modified to contain coerced values.  */
 
-extern struct value *call_function_by_hand (struct value *function, int nargs,
+extern struct value *call_function_by_hand (struct value *function,
+					    type *default_return_type,
+					    int nargs,
 					    struct value **args);
 
 /* Similar to call_function_by_hand and additional call
@@ -45,9 +51,18 @@ extern struct value *call_function_by_hand (struct value *function, int nargs,
    created inferior call dummy frame.  */
 
 extern struct value *
-  call_function_by_hand_dummy (struct value *function, int nargs,
+  call_function_by_hand_dummy (struct value *function,
+			       type *default_return_type,
+			       int nargs,
 			       struct value **args,
 			       dummy_frame_dtor_ftype *dummy_dtor,
 			       void *dummy_dtor_data);
 
+/* Throw an error indicating that the user tried to call a function
+   that has unknown return type.  FUNC_NAME is the name of the
+   function to be included in the error message; may be NULL, in which
+   case the error message doesn't include a function name.  */
+
+extern void error_call_unknown_return_type (const char *func_name);
+
 #endif
diff --git a/gdb/linux-fork.c b/gdb/linux-fork.c
index 573a3e9..16403c9 100644
--- a/gdb/linux-fork.c
+++ b/gdb/linux-fork.c
@@ -248,7 +248,7 @@ call_lseek (int fd, off_t offset, int whence)
 {
   char exp[80];
 
-  snprintf (&exp[0], sizeof (exp), "lseek (%d, %ld, %d)",
+  snprintf (&exp[0], sizeof (exp), "(long) lseek (%d, %ld, %d)",
 	    fd, (long) offset, whence);
   return (off_t) parse_and_eval_long (&exp[0]);
 }
@@ -492,7 +492,7 @@ inferior_call_waitpid (ptid_t pptid, int pid)
   argv[2] = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
   argv[3] = 0;
 
-  retv = call_function_by_hand (waitpid_fn, 3, argv);
+  retv = call_function_by_hand (waitpid_fn, NULL, 3, argv);
   if (value_as_long (retv) < 0)
     goto out;
 
@@ -707,7 +707,7 @@ checkpoint_command (char *args, int from_tty)
     scoped_restore save_pid
       = make_scoped_restore (&checkpointing_pid, ptid_get_pid (inferior_ptid));
 
-    ret = call_function_by_hand (fork_fn, 0, &ret);
+    ret = call_function_by_hand (fork_fn, NULL, 0, &ret);
   }
 
   if (!ret)	/* Probably can't happen.  */
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 2792cbd..37dd95e 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -2427,7 +2427,7 @@ linux_infcall_mmap (CORE_ADDR size, unsigned prot)
   arg[ARG_FD] = value_from_longest (builtin_type (gdbarch)->builtin_int, -1);
   arg[ARG_OFFSET] = value_from_longest (builtin_type (gdbarch)->builtin_int64,
 					0);
-  addr_val = call_function_by_hand (mmap_val, ARG_LAST, arg);
+  addr_val = call_function_by_hand (mmap_val, NULL, ARG_LAST, arg);
   retval = value_as_address (addr_val);
   if (retval == (CORE_ADDR) -1)
     error (_("Failed inferior mmap call for %s bytes, errno is changed."),
@@ -2456,7 +2456,7 @@ linux_infcall_munmap (CORE_ADDR addr, CORE_ADDR size)
   /* Assuming sizeof (unsigned long) == sizeof (size_t).  */
   arg[ARG_LENGTH] = value_from_ulongest
 		    (builtin_type (gdbarch)->builtin_unsigned_long, size);
-  retval_val = call_function_by_hand (munmap_val, ARG_LAST, arg);
+  retval_val = call_function_by_hand (munmap_val, NULL, ARG_LAST, arg);
   retval = value_as_long (retval_val);
   if (retval != 0)
     warning (_("Failed inferior munmap call at %s for %s bytes, "
diff --git a/gdb/m2-typeprint.c b/gdb/m2-typeprint.c
index 4cf1569..0dd6f19 100644
--- a/gdb/m2-typeprint.c
+++ b/gdb/m2-typeprint.c
@@ -280,7 +280,8 @@ m2_procedure (struct type *type, struct ui_file *stream,
 {
   fprintf_filtered (stream, "PROCEDURE ");
   m2_type_name (type, stream);
-  if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
+  if (TYPE_TARGET_TYPE (type) == NULL
+      || TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
     {
       int i, len = TYPE_NFIELDS (type);
 
@@ -294,11 +295,11 @@ m2_procedure (struct type *type, struct ui_file *stream,
 	    }
 	  m2_print_type (TYPE_FIELD_TYPE (type, i), "", stream, -1, 0, flags);
 	}
+      fprintf_filtered (stream, ") : ");
       if (TYPE_TARGET_TYPE (type) != NULL)
-	{
-	  fprintf_filtered (stream, " : ");
-	  m2_print_type (TYPE_TARGET_TYPE (type), "", stream, 0, 0, flags);
-	}
+	m2_print_type (TYPE_TARGET_TYPE (type), "", stream, 0, 0, flags);
+      else
+	type_print_unknown_return_type (stream);
     }
 }
 
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index 934d9c6..fbdd69f 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -130,7 +130,8 @@ lookup_objc_class (struct gdbarch *gdbarch, const char *classname)
 
   classval = value_string (classname, strlen (classname) + 1, char_type);
   classval = value_coerce_array (classval);
-  return (CORE_ADDR) value_as_long (call_function_by_hand (function, 
+  return (CORE_ADDR) value_as_long (call_function_by_hand (function,
+							   NULL,
 							   1, &classval));
 }
 
@@ -160,7 +161,7 @@ lookup_child_selector (struct gdbarch *gdbarch, const char *selname)
   selstring = value_coerce_array (value_string (selname, 
 						strlen (selname) + 1,
 						char_type));
-  return value_as_long (call_function_by_hand (function, 1, &selstring));
+  return value_as_long (call_function_by_hand (function, NULL, 1, &selstring));
 }
 
 struct value * 
@@ -181,12 +182,13 @@ value_nsstring (struct gdbarch *gdbarch, char *ptr, int len)
   if (lookup_minimal_symbol("_NSNewStringFromCString", 0, 0).minsym)
     {
       function = find_function_in_inferior("_NSNewStringFromCString", NULL);
-      nsstringValue = call_function_by_hand(function, 1, &stringValue[2]);
+      nsstringValue = call_function_by_hand(function,
+					    NULL, 1, &stringValue[2]);
     }
   else if (lookup_minimal_symbol("istr", 0, 0).minsym)
     {
       function = find_function_in_inferior("istr", NULL);
-      nsstringValue = call_function_by_hand(function, 1, &stringValue[2]);
+      nsstringValue = call_function_by_hand(function, NULL, 1, &stringValue[2]);
     }
   else if (lookup_minimal_symbol("+[NSString stringWithCString:]", 0, 0).minsym)
     {
@@ -198,7 +200,7 @@ value_nsstring (struct gdbarch *gdbarch, char *ptr, int len)
 	(type, lookup_objc_class (gdbarch, "NSString"));
       stringValue[1] = value_from_longest 
 	(type, lookup_child_selector (gdbarch, "stringWithCString:"));
-      nsstringValue = call_function_by_hand(function, 3, &stringValue[0]);
+      nsstringValue = call_function_by_hand(function, NULL, 3, &stringValue[0]);
     }
   else
     error (_("NSString: internal error -- no way to create new NSString"));
@@ -1209,7 +1211,7 @@ print_object_command (char *args, int from_tty)
   if (function == NULL)
     error (_("Unable to locate _NSPrintForDebugger in child process"));
 
-  description = call_function_by_hand (function, 1, &object);
+  description = call_function_by_hand (function, NULL, 1, &object);
 
   string_addr = value_as_long (description);
   if (string_addr == 0)
diff --git a/gdb/p-typeprint.c b/gdb/p-typeprint.c
index d08f6f7..33b4992 100644
--- a/gdb/p-typeprint.c
+++ b/gdb/p-typeprint.c
@@ -227,7 +227,8 @@ pascal_type_print_varspec_prefix (struct type *type, struct ui_file *stream,
     case TYPE_CODE_METHOD:
       if (passed_a_ptr)
 	fprintf_filtered (stream, "(");
-      if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
+      if (TYPE_TARGET_TYPE (type) != NULL
+	  && TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
 	{
 	  fprintf_filtered (stream, "function  ");
 	}
@@ -255,7 +256,8 @@ pascal_type_print_varspec_prefix (struct type *type, struct ui_file *stream,
       if (passed_a_ptr)
 	fprintf_filtered (stream, "(");
 
-      if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
+      if (TYPE_TARGET_TYPE (type) != NULL
+	  && TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
 	{
 	  fprintf_filtered (stream, "function  ");
 	}
@@ -334,6 +336,33 @@ pascal_print_func_args (struct type *type, struct ui_file *stream,
     }
 }
 
+/* Helper for pascal_type_print_varspec_suffix to print the suffix of
+   a function or method.  */
+
+static void
+pascal_type_print_func_varspec_suffix  (struct type *type, struct ui_file *stream,
+					int show, int passed_a_ptr,
+					int demangled_args,
+					const struct type_print_options *flags)
+{
+  if (TYPE_TARGET_TYPE (type) == NULL
+      || TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
+    {
+      fprintf_filtered (stream, " : ");
+      pascal_type_print_varspec_prefix (TYPE_TARGET_TYPE (type),
+					stream, 0, 0, flags);
+
+      if (TYPE_TARGET_TYPE (type) == NULL)
+	type_print_unknown_return_type (stream);
+      else
+	pascal_type_print_base (TYPE_TARGET_TYPE (type), stream, show, 0,
+				flags);
+
+      pascal_type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0,
+					passed_a_ptr, 0, flags);
+    }
+}
+
 /* Print any array sizes, function arguments or close parentheses
    needed after the variable name (to describe its type).
    Args work like pascal_type_print_varspec_prefix.  */
@@ -365,16 +394,8 @@ pascal_type_print_varspec_suffix (struct type *type, struct ui_file *stream,
       pascal_type_print_method_args ("",
 				     "",
 				     stream);
-      if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
-	{
-	  fprintf_filtered (stream, " : ");
-	  pascal_type_print_varspec_prefix (TYPE_TARGET_TYPE (type),
-					    stream, 0, 0, flags);
-	  pascal_type_print_base (TYPE_TARGET_TYPE (type), stream, show, 0,
-				  flags);
-	  pascal_type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0,
-					    passed_a_ptr, 0, flags);
-	}
+      pascal_type_print_func_varspec_suffix (type, stream, show,
+					     passed_a_ptr, 0, flags);
       break;
 
     case TYPE_CODE_PTR:
@@ -388,16 +409,8 @@ pascal_type_print_varspec_suffix (struct type *type, struct ui_file *stream,
 	fprintf_filtered (stream, ")");
       if (!demangled_args)
 	pascal_print_func_args (type, stream, flags);
-      if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
-	{
-	  fprintf_filtered (stream, " : ");
-	  pascal_type_print_varspec_prefix (TYPE_TARGET_TYPE (type),
-					    stream, 0, 0, flags);
-	  pascal_type_print_base (TYPE_TARGET_TYPE (type), stream, show, 0,
-				  flags);
-	  pascal_type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0,
-					    passed_a_ptr, 0, flags);
-	}
+      pascal_type_print_func_varspec_suffix (type, stream, show,
+					     passed_a_ptr, 0, flags);
       break;
 
     case TYPE_CODE_UNDEF:
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index b5a0ef8..cbbb936 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -925,7 +925,8 @@ valpy_call (PyObject *self, PyObject *args, PyObject *keywords)
       scoped_value_mark free_values;
       struct value *return_value;
 
-      return_value = call_function_by_hand (function, args_count, vargs);
+      return_value = call_function_by_hand (function, NULL,
+					    args_count, vargs);
       result = value_to_value_object (return_value);
     }
   CATCH (except, RETURN_MASK_ALL)
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index d4cda1f..14e7f9b 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -1292,7 +1292,7 @@ rust_evaluate_funcall (struct expression *exp, int *pos, enum noside noside)
   if (noside == EVAL_AVOID_SIDE_EFFECTS)
     result = value_zero (TYPE_TARGET_TYPE (fn_type), not_lval);
   else
-    result = call_function_by_hand (function, num_args + 1, args.data ());
+    result = call_function_by_hand (function, NULL, num_args + 1, args.data ());
   return result;
 }
 
diff --git a/gdb/testsuite/gdb.base/break-main-file-remove-fail.exp b/gdb/testsuite/gdb.base/break-main-file-remove-fail.exp
index eba72bc..080d193 100644
--- a/gdb/testsuite/gdb.base/break-main-file-remove-fail.exp
+++ b/gdb/testsuite/gdb.base/break-main-file-remove-fail.exp
@@ -88,7 +88,7 @@ proc test_remove_bp { initial_load } {
 	# should warn the user about it.
 	set pagesize [get_integer_valueof "pg_size" 0]
 	set align_addr [expr $bp_addr - $bp_addr % $pagesize]
-	set munmap [get_integer_valueof "munmap ($align_addr, $pagesize)" -1]
+	set munmap [get_integer_valueof "(int) munmap ($align_addr, $pagesize)" -1]
 
 	if {$munmap != 0} {
 	    unsupported "can't munmap foo's page"
diff --git a/gdb/testsuite/gdb.base/break-probes.exp b/gdb/testsuite/gdb.base/break-probes.exp
index 929509a..318a219 100644
--- a/gdb/testsuite/gdb.base/break-probes.exp
+++ b/gdb/testsuite/gdb.base/break-probes.exp
@@ -86,5 +86,5 @@ if { $using_probes } {
     }
 
     # Call something to ensure that relocation occurred
-    gdb_test "call foo(23)" "\\\$.* = 31.*\\\M.*"
+    gdb_test "call (int) foo(23)" "\\\$.* = 31.*\\\M.*"
 }
diff --git a/gdb/testsuite/gdb.base/checkpoint.exp b/gdb/testsuite/gdb.base/checkpoint.exp
index 12c1d8a..677c389 100644
--- a/gdb/testsuite/gdb.base/checkpoint.exp
+++ b/gdb/testsuite/gdb.base/checkpoint.exp
@@ -224,45 +224,11 @@ gdb_test "shell diff $pi_txt $copy1_txt" \
 delete_breakpoints
 gdb_breakpoint $break2_loc
 
-gdb_test "restart 1" "if .c == EOF.*" "restart 1 three"
-gdb_test "continue" "breakpoint 2.*" "break2 1 one"
-gdb_test "print ftell (out) > 100000" " = 1.*" "outfile still open 1"
-
-gdb_test "restart 2" "if .c == EOF.*" "restart 2 three"
-gdb_test "continue" "breakpoint 2.*" "break2 2 one"
-gdb_test "print ftell (out) > 100000" " = 1.*" "outfile still open 2"
-
-gdb_test "restart 3" "if .c == EOF.*" "restart 3 three"
-gdb_test "continue" "breakpoint 2.*" "break2 3 one"
-gdb_test "print ftell (out) > 100000" " = 1.*" "outfile still open 3"
-
-gdb_test "restart 4" "if .c == EOF.*" "restart 4 three"
-gdb_test "continue" "breakpoint 2.*" "break2 4 one"
-gdb_test "print ftell (out) > 100000" " = 1.*" "outfile still open 4"
-
-gdb_test "restart 5" "if .c == EOF.*" "restart 5 three"
-gdb_test "continue" "breakpoint 2.*" "break2 5 one"
-gdb_test "print ftell (out) > 100000" " = 1.*" "outfile still open 5"
-
-gdb_test "restart 6" "if .c == EOF.*" "restart 6 three"
-gdb_test "continue" "breakpoint 2.*" "break2 6 one"
-gdb_test "print ftell (out) > 100000" " = 1.*" "outfile still open 6"
-
-gdb_test "restart 7" "if .c == EOF.*" "restart 7 three"
-gdb_test "continue" "breakpoint 2.*" "break2 7 one"
-gdb_test "print ftell (out) > 100000" " = 1.*" "outfile still open 7"
-
-gdb_test "restart 8" "if .c == EOF.*" "restart 8 three"
-gdb_test "continue" "breakpoint 2.*" "break2 8 one"
-gdb_test "print ftell (out) > 100000" " = 1.*" "outfile still open 8"
-
-gdb_test "restart 9" "if .c == EOF.*" "restart 9 three"
-gdb_test "continue" "breakpoint 2.*" "break2 9 one"
-gdb_test "print ftell (out) > 100000" " = 1.*" "outfile still open 9"
-
-gdb_test "restart 10" "if .c == EOF.*" "restart 10 three"
-gdb_test "continue" "breakpoint 2.*" "break2 10 one"
-gdb_test "print ftell (out) > 100000" " = 1.*" "outfile still open 10"
+for {set num 1} {$num <= 10} {incr num} {
+    gdb_test "restart $num" "if .c == EOF.*" "restart $num three"
+    gdb_test "continue" "breakpoint 2.*" "break2 $num one"
+    gdb_test "print (long) ftell (out) > 100000" " = 1.*" "outfile still open $num"
+}
 
 #
 # Now confirm that if one fork exits, we automatically switch to another one.
diff --git a/gdb/testsuite/gdb.base/dprintf-detach.exp b/gdb/testsuite/gdb.base/dprintf-detach.exp
index 4c6ef1c..940a9ef 100644
--- a/gdb/testsuite/gdb.base/dprintf-detach.exp
+++ b/gdb/testsuite/gdb.base/dprintf-detach.exp
@@ -53,7 +53,7 @@ proc dprintf_detach_test { breakpoint_always_inserted dprintf_style disconnected
 	# Get PID of test program.
 	set inferior_pid -1
 	set test "get inferior process ID"
-	gdb_test_multiple "call getpid ()" $test {
+	gdb_test_multiple "call (int) getpid ()" $test {
 	    -re ".* = ($decimal).*$gdb_prompt $" {
 		set inferior_pid $expect_out(1,string)
 		pass $test
diff --git a/gdb/testsuite/gdb.base/infcall-exec.exp b/gdb/testsuite/gdb.base/infcall-exec.exp
index 3984076..8419905 100644
--- a/gdb/testsuite/gdb.base/infcall-exec.exp
+++ b/gdb/testsuite/gdb.base/infcall-exec.exp
@@ -44,5 +44,5 @@ append expected_result "\[\r\n\]+.*"
 append expected_result "Breakpoint 1, main .*at .*$srcfile2:$decimal"
 append expected_result ".*"
 
-gdb_test "call execlp \(\"$binfile2\", \"$binfile2\", \(char \*\)0\)" \
+gdb_test "call (int) execlp \(\"$binfile2\", \"$binfile2\", \(char \*\)0\)" \
     $expected_result "call execlp"
diff --git a/gdb/testsuite/gdb.base/info-os.exp b/gdb/testsuite/gdb.base/info-os.exp
index 0168ccb..574da26 100644
--- a/gdb/testsuite/gdb.base/info-os.exp
+++ b/gdb/testsuite/gdb.base/info-os.exp
@@ -39,14 +39,18 @@ if ![runto_main] then {
 }
 
 # Get PID of test program.
-set inferior_pid -1
+set inferior_pid ""
 set test "get inferior process ID"
-gdb_test_multiple "call getpid()" $test {
+gdb_test_multiple "call (int) getpid()" $test {
     -re ".* = ($decimal).*$gdb_prompt $" {
 	set inferior_pid $expect_out(1,string)
 	pass $test
     }
 }
+if {$inferior_pid == ""} {
+    untested "failed to get pid"
+    return
+}
 
 gdb_breakpoint ${srcfile}:[gdb_get_line_number "Set breakpoint here"]
 gdb_continue_to_breakpoint "Set breakpoint here"
diff --git a/gdb/testsuite/gdb.base/nodebug.c b/gdb/testsuite/gdb.base/nodebug.c
index eb5d661..99641e8 100644
--- a/gdb/testsuite/gdb.base/nodebug.c
+++ b/gdb/testsuite/gdb.base/nodebug.c
@@ -1,4 +1,6 @@
 #include <stdlib.h>
+#include <stdint.h>
+
 /* Test that things still (sort of) work when compiled without -g.  */
 
 int dataglobal = 3;			/* Should go in global data */
@@ -43,3 +45,42 @@ int array_index (char *arr, int i)
   free (x);
   return retval;
 }
+
+float
+multf (float v1, float v2)
+{
+  return v1 * v2;
+}
+
+float
+multf_noproto (v1, v2)
+  float v1, v2;
+{
+  return v1 * v2;
+}
+
+double
+mult (double v1, double v2)
+{
+  return v1 * v2;
+}
+
+double
+mult_noproto (v1, v2)
+  double v1, v2;
+{
+  return v1 * v2;
+}
+
+uint8_t
+add8 (uint8_t v1, uint8_t v2)
+{
+  return v1 + v2;
+}
+
+uint8_t
+add8_noproto (v1, v2)
+  uint8_t v1, v2;
+{
+  return v1 + v2;
+}
diff --git a/gdb/testsuite/gdb.base/nodebug.exp b/gdb/testsuite/gdb.base/nodebug.exp
index a54e5bb..7bdf9ad 100644
--- a/gdb/testsuite/gdb.base/nodebug.exp
+++ b/gdb/testsuite/gdb.base/nodebug.exp
@@ -39,6 +39,40 @@ if  { [gdb_compile $srcdir/$subdir/$srcfile $binfile executable $exec_opts] != "
 
 clean_restart $binfile
 
+# Test calling no-debug functions involving argument types that may
+# require coercion/promotion, both prototyped and unprototyped, both
+# return-type-cast style, and function-pointer-cast styles.
+proc test_call_promotion {} {
+    if [target_info exists gdb,cannot_call_functions] {
+	return
+    }
+
+    # Call prototyped function with float parameters via both
+    # return-type cast and function-pointer cast.  This checks that
+    # GDB doesn't do float->double coercion.
+    gdb_test "p (float) multf(2.0f, 3.0f)" " = 6"
+    gdb_test "p ((float (*) (float, float)) multf)(2, 3)" " = 6"
+    gdb_test "p ((float (*) (float, float)) multf)(2.0f, 3.0f)" " = 6"
+
+    # Call unprototyped function with float parameters via
+    # function-pointer cast, only.  return-type cast assumes
+    # protototyped.  Check that GDB does float->double coercion.
+    gdb_test "p ((float (*) ()) multf_noproto)(2.0f, 3.0f)" " = 6"
+    gdb_test "p ((float (*) ()) multf_noproto)(2.0, 3.0)" " = 6"
+
+    # Same, but for double.
+    gdb_test "p (double) mult (2.0, 3.0)" " = 6"
+    gdb_test "p ((double (*) (double, double)) mult)(2.0f, 3.0f)" " = 6"
+    gdb_test "p ((double (*) (double, double)) mult)(2, 3)" " = 6"
+    gdb_test "p ((double (*) ()) mult_noproto)(2.0f, 3.0f)" " = 6"
+    gdb_test "p ((double (*) ()) mult_noproto)(2.0, 3.0)" " = 6"
+
+    # Check that GDB promotes char->int correctly.
+    gdb_test "p /d (uint8) add8((uint8) 2, (uint8) 3)" " = 5"
+    gdb_test "p /d ((uint8 (*) (uint8, uint8)) add8)((uint8) 2, (uint8) 3)" " = 5"
+    gdb_test "p /d ((uint8 (*) ()) add8_noproto)((uint8) 2, (uint8) 3)" " = 5"
+}
+
 if [runto inner] then {
     
     # Expect to find global/local symbols in each of text/data/bss.
@@ -59,18 +93,66 @@ if [runto inner] then {
     # out debugging info for non-aggregate return values of functions
     # even without -g, which should be accepted.
 
-    gdb_test "p top" \
-	"\{(<(text variable|function), no debug info>|short \\(int\\)|short \\(\\))\} \[0-9a-fx]* <\\.?top(\\(int\\)|)>"
-    gdb_test "whatis top" \
-	"(<(text variable|function), no debug info>|short \\(int\\)|short \\(\\))"
-    gdb_test "ptype top" "(short|int) \\((|void|int|<non-float parameter>|<non-float parameter>, <non-float parameter>)\\)"
-    
-    gdb_test "p middle" \
-	"\{(<(text variable|function), no debug info>|short \\(int\\)|short \\(\\))\} \[0-9a-fx]* <\\.?middle(\\(int\\)|)>"
-    gdb_test "whatis middle" \
-	"(<(text variable|function), no debug info>|short \\(int\\)|short \\(\\))"
-    gdb_test "ptype middle" "(short|int) \\((|void|int|<non-float parameter>|<non-float parameter>, <non-float parameter>)\\)"
-    
+    with_test_prefix "func" {
+	# Most languages default to printing like C.
+	set c_print_re " = \\{<text variable, no debug info>\\} $hex <top>"
+	set c_whatis_re " = <text variable, no debug info>"
+	set c_ptype_re "= <unknown return type> \\(\\)"
+
+	set cxx_ptype_re "= <unknown return type> \\(void\\)"
+
+	set ada_ptype_re " = function return <unknown return type>"
+
+	set m2_print_re " = \\{PROCEDURE <text variable, no debug info> \\(\\) : <unknown return type>\\} $hex <top>"
+	set m2_whatis_re "PROCEDURE <text variable, no debug info> \\(\\) : <unknown return type>"
+	set m2_ptype_re $m2_whatis_re
+
+	# Rust can't access minsyms?
+	set rust_nosym "No symbol 'top' in current context"
+
+	set pascal_ptype_re "type = procedure  : <unknown return type>"
+
+	#LANG		#PRINT		#WHATIS		#PTYPE
+	foreach lang_line {
+	    {"ada"		$c_print_re	$c_whatis_re	$ada_ptype_re}
+	    {"asm"		$c_print_re	$c_whatis_re	$c_ptype_re}
+	    {"c"		$c_print_re	$c_whatis_re	$c_ptype_re}
+	    {"c++"		$c_print_re	$c_whatis_re	$cxx_ptype_re}
+	    {"d"		$c_print_re	$c_whatis_re	$c_ptype_re}
+	    {"fortran"	$c_print_re	$c_whatis_re	$c_ptype_re}
+	    {"go"		$c_print_re	$c_whatis_re	$c_ptype_re}
+	    {"minimal"	$c_print_re	$c_whatis_re	$c_ptype_re}
+	    {"modula-2"	$m2_print_re	$m2_whatis_re	$m2_ptype_re}
+	    {"objective-c"	$c_print_re	$c_whatis_re	$c_ptype_re}
+	    {"opencl"	$c_print_re	$c_whatis_re	$c_ptype_re}
+	    {"pascal"	$c_print_re	$c_whatis_re	$pascal_ptype_re}
+	    {"rust"		$rust_nosym	$rust_nosym	$rust_nosym}
+	} {
+	    set lang [lindex $lang_line 0]
+	    set print_re [lindex $lang_line 1]
+	    set whatis_re [lindex $lang_line 2]
+	    set ptype_re [lindex $lang_line 3]
+
+	    set print_re [subst "$print_re"]
+	    set whatis_re [subst "$whatis_re"]
+	    set ptype_re [subst "$ptype_re"]
+
+	    with_test_prefix "$lang" {
+		gdb_test_no_output "set language $lang"
+		gdb_test "p top" $print_re
+		gdb_test "whatis top" $whatis_re
+		gdb_test "ptype top" $ptype_re
+	    }
+	}
+    }
+
+    gdb_test_no_output "set language auto"
+
+    # We can't rely on uintXX_t being available/known to GDB because
+    # we may or may not have debug info for those (depending on
+    # whether we have debug info for the C runtime, for example).
+    gdb_test_no_output "macro define uint8 unsigned char"
+
     gdb_test "p dataglobal" "= 3"
     gdb_test "whatis dataglobal" \
 	"<(data variable|variable), no debug info>|int"
@@ -122,17 +204,27 @@ if [runto inner] then {
     
     # This test is not as obscure as it might look.  `p getenv ("TERM")'
     # is a real-world example, at least on many systems.
+
+    gdb_test {p/c array_index("abcdef",2)} \
+	"'array_index' has unknown return type; cast the call to its declared return type"
+    gdb_test {ptype array_index("abcdef",2)} \
+	"function has unknown return type; cast the call to its declared return type"
+    gdb_test {whatis array_index("abcdef",2)} \
+	"function has unknown return type; cast the call to its declared return type"
+
     if [target_info exists gdb,cannot_call_functions] {
-	unsupported "p/c array_index(\"abcdef\",2)"
+	unsupported "p/c (int) array_index(\"abcdef\",2)"
     } else {
 	# We need to up this because this can be really slow on some boards.
 	# (malloc() is called as part of the test).
 	set prev_timeout $timeout
 	set timeout 60
-	gdb_test {p/c array_index("abcdef",2)} " = 99 'c'"
+	gdb_test {p/c (int) array_index("abcdef",2)} " = 99 'c'"
 	set timeout $prev_timeout
     }
-    
+
+    test_call_promotion
+
     # Now, try that we can give names of file-local symbols which happen
     # to be unique, and have it still work
     if [runto middle] then {
diff --git a/gdb/testsuite/gdb.compile/compile.exp b/gdb/testsuite/gdb.compile/compile.exp
index b221870..c23ff6a 100644
--- a/gdb/testsuite/gdb.compile/compile.exp
+++ b/gdb/testsuite/gdb.compile/compile.exp
@@ -296,12 +296,14 @@ gdb_test "p globalvar" " = 77" "expect 77"
 
 # Test reference to minimal_symbol, not (full) symbol.
 
-gdb_test_no_output "compile code globalvar = func_nodebug (75);" \
+gdb_test "compile code globalvar = func_nodebug (75);" \
+    "warning: function has unknown return type; assuming int" \
     "call func_nodebug"
 gdb_test "p globalvar" " = -75" "expect -75"
-gdb_test_no_output \
-  "compile code int (*funcp) (int) = func_nodebug; globalvar = funcp (76);" \
-  "call func_nodebug indirectly"
+gdb_test \
+    "compile code int (*funcp) (int) = func_nodebug; globalvar = funcp (76);" \
+    "warning: function has unknown return type; assuming int" \
+    "call func_nodebug indirectly"
 gdb_test "p globalvar" " = -76" "expect -76"
 
 
diff --git a/gdb/testsuite/gdb.threads/siginfo-threads.exp b/gdb/testsuite/gdb.threads/siginfo-threads.exp
index 328168c..8ee4c36 100644
--- a/gdb/testsuite/gdb.threads/siginfo-threads.exp
+++ b/gdb/testsuite/gdb.threads/siginfo-threads.exp
@@ -40,12 +40,17 @@ gdb_test "handle SIGUSR2 stop print pass" \
 gdb_breakpoint [gdb_get_line_number "break-at-exit"]
 
 set test "get pid"
-gdb_test_multiple "p getpid ()" $test {
+set pid ""
+gdb_test_multiple "p (int) getpid ()" $test {
     -re " = (\[0-9\]+)\r\n$gdb_prompt $" {
 	set pid $expect_out(1,string)
 	pass $test
     }
 }
+if {$pid == ""} {
+    untested "failed to get pid"
+    return
+}
 
 for {set sigcount 0} {$sigcount < 4} {incr sigcount} {
     set test "catch signal $sigcount"
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index 045271a..8e58bf9 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -386,6 +386,14 @@ type_to_string (struct type *type)
   return {};
 }
 
+/* See typeprint.h.  */
+
+void
+type_print_unknown_return_type (struct ui_file *stream)
+{
+  fprintf_filtered (stream, _("<unknown return type>"));
+}
+
 /* Print type of EXP, or last thing in value history if EXP == NULL.
    show is passed to type_print.  */
 
@@ -757,4 +765,3 @@ val_print_not_associated (struct ui_file *stream)
 {
   fprintf_filtered (stream, _("<not associated>"));
 }
-
diff --git a/gdb/typeprint.h b/gdb/typeprint.h
index 72da7f4..d4e19e5b 100644
--- a/gdb/typeprint.h
+++ b/gdb/typeprint.h
@@ -74,6 +74,10 @@ void c_type_print_varspec_suffix (struct type *, struct ui_file *, int,
 void c_type_print_args (struct type *, struct ui_file *, int, enum language,
 			const struct type_print_options *);
 
+/* Print <unknown return type> to stream STREAM.  */
+
+void type_print_unknown_return_type (struct ui_file *stream);
+
 extern void val_print_not_allocated (struct ui_file *stream);
 
 extern void val_print_not_associated (struct ui_file *stream);
diff --git a/gdb/valarith.c b/gdb/valarith.c
index 985233c..6c765df 100644
--- a/gdb/valarith.c
+++ b/gdb/valarith.c
@@ -505,7 +505,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
 	    = TYPE_TARGET_TYPE (check_typedef (value_type (argvec[0])));
 	  return value_zero (return_type, VALUE_LVAL (arg1));
 	}
-      return call_function_by_hand (argvec[0], 2 - static_memfuncp,
+      return call_function_by_hand (argvec[0], NULL, 2 - static_memfuncp,
 				    argvec + 1);
     }
   throw_error (NOT_FOUND_ERROR,
@@ -624,7 +624,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
 	    = TYPE_TARGET_TYPE (check_typedef (value_type (argvec[0])));
 	  return value_zero (return_type, VALUE_LVAL (arg1));
 	}
-      return call_function_by_hand (argvec[0], nargs, argvec + 1);
+      return call_function_by_hand (argvec[0], NULL, nargs, argvec + 1);
     }
   throw_error (NOT_FOUND_ERROR,
                _("member function %s not found"), tstr);
diff --git a/gdb/valops.c b/gdb/valops.c
index f3f5308..66678e5 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -191,7 +191,7 @@ value_allocate_space_in_inferior (int len)
   struct value *blocklen;
 
   blocklen = value_from_longest (builtin_type (gdbarch)->builtin_int, len);
-  val = call_function_by_hand (val, 1, &blocklen);
+  val = call_function_by_hand (val, NULL, 1, &blocklen);
   if (value_logical_not (val))
     {
       if (!target_has_execution)
-- 
2.5.5

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

* [PATCH v2 01/13] Fix calling prototyped functions via function pointers
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
                   ` (6 preceding siblings ...)
  2017-07-13 15:32 ` [PATCH v2 05/13] evaluate_subexp_standard: Eliminate one goto Pedro Alves
@ 2017-07-13 15:32 ` Pedro Alves
  2017-07-13 15:37 ` [PATCH v2 12/13] Make "p S::method() const::static_var" work too Pedro Alves
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:32 UTC (permalink / raw)
  To: gdb-patches

Calling a prototyped function via a function pointer with the right
prototype doesn't work correctly, if the called function requires
argument coercion...  Like, e.g., with:

  float mult (float f1, float f2) { return f1 * f2; }

  (gdb) p mult (2, 3.5)
  $1 = 7
  (gdb) p ((float (*) (float, float)) mult) (2, 3.5)
  $2 = 0

both calls should have returned the same, of course.  The problem is
that GDB misses marking the type of the function pointer target as
prototyped...

Without the fix, the new test fails like this:

 (gdb) p ((int (*) (float, float)) t_float_values2)(3.14159,float_val2)
 $30 = 0
 (gdb) FAIL: gdb.base/callfuncs.exp: p ((int (*) (float, float)) t_float_values2)(3.14159,float_val2)

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdbtypes.c (lookup_function_type_with_arguments): Mark function
	types with more than one parameter as prototyped.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/callfuncs.exp (do_function_calls): New parameter
	"prototypes".  Test calling float functions via prototyped and
	unprototyped function pointers.
	(perform_all_tests): New parameter "prototypes".  Pass it down.
	(top level): Pass down "prototypes" parameter to
	perform_all_tests.
---
 gdb/gdbtypes.c                       |  2 ++
 gdb/testsuite/gdb.base/callfuncs.exp | 26 +++++++++++++++++++++-----
 2 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index a686923..ceb4f0c 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -547,6 +547,8 @@ lookup_function_type_with_arguments (struct type *type,
 	  gdb_assert (nparams == 0);
 	  TYPE_PROTOTYPED (fn) = 1;
 	}
+      else
+	TYPE_PROTOTYPED (fn) = 1;
     }
 
   TYPE_NFIELDS (fn) = nparams;
diff --git a/gdb/testsuite/gdb.base/callfuncs.exp b/gdb/testsuite/gdb.base/callfuncs.exp
index 3651963..a537ebd 100644
--- a/gdb/testsuite/gdb.base/callfuncs.exp
+++ b/gdb/testsuite/gdb.base/callfuncs.exp
@@ -39,7 +39,7 @@ set skip_float_test [gdb_skip_float_test]
 # specifies that the numeric value of a relational or logical expression
 # (computed in the inferior) is 1 for true and 0 for false.
 
-proc do_function_calls {} {
+proc do_function_calls {prototypes} {
     global gdb_prompt skip_float_test
 
     # We need to up this because this can be really slow on some boards.
@@ -95,11 +95,25 @@ proc do_function_calls {} {
 	setup_xfail "mn10300-*-*"
 	if { [test_compiler_info "armcc-*"] } { setup_xfail "*-*-*" }
 	gdb_test "p t_float_values(float_val1,-2.3765)" " = 1"
+	# Same, via unprototyped function pointer (t_float_values is
+	# always unprototyped).
+	gdb_test "p ((int (*) ()) t_float_values)(float_val1,-2.3765)" " = 1"
 
 	# Test passing of arguments which might not be widened.
 	gdb_test "p t_float_values2(0.0,0.0)" " = 0"
+	# Same, via function pointer.
+	if {$prototypes} {
+	    gdb_test "p ((int (*) (float, float)) t_float_values2)(0.0,0.0)" " = 0"
+	} else {
+	    gdb_test "p ((int (*) ()) t_float_values2)(0.0,0.0)" " = 0"
+	}
 
 	gdb_test "p t_float_values2(3.14159,float_val2)" " = 1"
+	if {$prototypes} {
+	    gdb_test "p ((int (*) (float, float)) t_float_values2)(3.14159,float_val2)" " = 1"
+	} else {
+	    gdb_test "p ((int (*) ()) t_float_values2)(3.14159,float_val2)" " = 1"
+	}
 
 	gdb_test "p t_float_many_args (float_val1, float_val2, float_val3, float_val4, float_val5, float_val6, float_val7, float_val8, float_val9, float_val10, float_val11, float_val12, float_val13, float_val14, float_val15)" " = 1" "call function with many float arguments."
 
@@ -315,7 +329,7 @@ proc rerun_and_prepare {} {
 	"next to t_structs_c"
 }
 
-proc perform_all_tests {} {
+proc perform_all_tests {prototypes} {
     gdb_test_no_output "set print sevenbit-strings"
     gdb_test_no_output "set print address off"
     gdb_test_no_output "set width 0"
@@ -326,7 +340,7 @@ proc perform_all_tests {} {
     set old_reg_content [fetch_all_registers "retrieve original register contents"]
 
     # Perform function calls.
-    do_function_calls
+    do_function_calls $prototypes
 
     # Check if all registers still have the same value.
     set new_reg_content [fetch_all_registers \
@@ -500,9 +514,11 @@ proc perform_all_tests {} {
 # Perform all tests with and without function prototypes.
 
 if { ![prepare_for_testing "failed to prepare" $testfile $srcfile "$compile_flags additional_flags=-DPROTOTYPES"] } {
-    perform_all_tests
+    perform_all_tests 1
 }
 
 if { ![prepare_for_testing "failed to prepare" $testfile $srcfile "$compile_flags additional_flags=-DNO_PROTOTYPES"] } {
-    with_test_prefix "noproto" perform_all_tests
+    with_test_prefix "noproto" {
+	perform_all_tests 0
+    }
 }
-- 
2.5.5

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

* [PATCH v2 03/13] Introduce OP_VAR_MSYM_VALUE
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
  2017-07-13 15:32 ` [PATCH v2 11/13] Handle "p 'S::method()::static_var'" (quoted) in symbol lookup Pedro Alves
  2017-07-13 15:32 ` [PATCH v2 09/13] Eliminate UNOP_MEMVAL_TLS Pedro Alves
@ 2017-07-13 15:32 ` Pedro Alves
  2017-07-13 15:32 ` [PATCH v2 10/13] Handle "p S::method()::static_var" in the C++ parser Pedro Alves
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:32 UTC (permalink / raw)
  To: gdb-patches

The previous patch left GDB with an inconsistency.  While with normal
expression evaluation the "unknown return type" error shows the name
of the function that misses debug info:

  (gdb) p getenv ("PATH")
  'getenv' has unknown return type; cast the call to its declared return type
   ^^^^^^

which can by handy in more complicated expressions, "ptype" does not:

  (gdb) ptype getenv ("PATH")
  function has unknown return type; cast the call to its declared return type
  ^^^^^^^^

This commit is a step toward fixing it.

The problem is that while evaluating the expression above, we have no
reference to the minimal symbol where we could extract the name from.
This is because the resulting expression tree has no reference to the
minsym at all.  During parsing, the type and address of the minsym are
extracted and an UNOP_MEMVAL / UNOP_MEMVAL_TLS operator is generated
(see write_exp_elt_msym).  With "set debug expression", here's what
you see:

            0  OP_FUNCALL            Number of args: 0
            3    UNOP_MEMVAL           Type @0x565334a51930 (<text variable, no debug info>)
            6      OP_LONG               Type @0x565334a51c60 (__CORE_ADDR), value 140737345035648 (0x7ffff7751d80)

The "print" case finds the function name, because
call_function_by_hand looks up the function by address again.
However, for "ptype", we don't reach that code, because obviously we
don't really call the function.

Unlike minsym references, references to variables with debug info have
a pointer to the variable's symbol in the expression tree, with
OP_VAR_VALUE:

  (gdb) ptype main()
  ...
            0  OP_FUNCALL            Number of args: 0
            3    OP_VAR_VALUE          Block @0x0, symbol @0x559bbbd9b358 (main(int, char**))
  ...

so I don't see why do minsyms need to be different.  So to prepare for
fixing the missing function name issue, this commit adds a new
OP_VAR_MSYM_VALUE operator that mimics OP_VAR_VALUE, except that it's
for minsyms instead of debug symbols.  For infcalls, we now get
expressions like these:

            0  OP_FUNCALL            Number of args: 0
            3    OP_VAR_MSYM_VALUE     Objfile @0x1e41bf0, msymbol @0x7fffe599b000 (getenv)

In the following patch, we'll make OP_FUNCALL extract the function
name from the symbol stored in OP_VAR_VALUE/OP_VAR_MSYM_VALUE.

OP_VAR_MSYM_VALUE will be used more in a later patch in the series
too.

gdb/ChangeLog:
yyyy-dd-yy  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (resolve_subexp): Handle OP_VAR_MSYM_VALUE.
	* ax-gdb.c (gen_msym_var_ref): New function.
	(gen_expr): Handle OP_VAR_MSYM_VALUE.
	* eval.c (evaluate_var_msym_value): New function.
	* eval.c (evaluate_subexp_standard): Handle OP_VAR_MSYM_VALUE.
	<OP_FUNCALL>: Extract function name from symbol/minsym and pass it
	to call_function_by_hand.
	* expprint.c (print_subexp_standard, dump_subexp_body_standard):
	Handle OP_VAR_MSYM_VALUE.
	(union exp_element) <msymbol>: New field.
	* minsyms.h (struct type): Forward declare.
	(find_minsym_type_and_address): Declare.
	* parse.c (write_exp_elt_msym): New function.
	(write_exp_msymbol): Delete, refactored as ...
	(find_minsym_type_and_address): ... this new function.
	(write_exp_msymbol): Reimplement using OP_VAR_MSYM_VALUE.
	(operator_length_standard, operator_check_standard): Handle
	OP_VAR_MSYM_VALUE.
	* std-operator.def (OP_VAR_MSYM_VALUE): New.
---
 gdb/ada-lang.c       |  1 +
 gdb/ax-gdb.c         | 22 ++++++++++++++
 gdb/eval.c           | 24 ++++++++++++++++
 gdb/expprint.c       | 16 +++++++++++
 gdb/expression.h     |  1 +
 gdb/minsyms.h        |  9 ++++++
 gdb/parse.c          | 81 +++++++++++++++++++++++++++++++---------------------
 gdb/std-operator.def |  6 ++++
 8 files changed, 128 insertions(+), 32 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 77c5866..d4dde83 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -3387,6 +3387,7 @@ resolve_subexp (struct expression **expp, int *pos, int deprocedure_p,
     case OP_LONG:
     case OP_DOUBLE:
     case OP_VAR_VALUE:
+    case OP_VAR_MSYM_VALUE:
       *pos += 4;
       break;
 
diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c
index fae2e2d..70e7980 100644
--- a/gdb/ax-gdb.c
+++ b/gdb/ax-gdb.c
@@ -742,6 +742,23 @@ gen_var_ref (struct gdbarch *gdbarch, struct agent_expr *ax,
       break;
     }
 }
+
+/* Generate code for a minimal symbol variable reference to AX.  The
+   variable is the symbol MINSYM, of OBJFILE.  Set VALUE to describe
+   the result.  */
+
+static void
+gen_msym_var_ref (agent_expr *ax, axs_value *value,
+		  minimal_symbol *msymbol, objfile *objf)
+{
+  CORE_ADDR address;
+  type *t = find_minsym_type_and_address (msymbol, objf, &address);
+  value->type = t;
+  value->optimized_out = false;
+  ax_const_l (ax, address);
+  value->kind = axs_lvalue_memory;
+}
+
 \f
 
 
@@ -1987,6 +2004,11 @@ gen_expr (struct expression *exp, union exp_element **pc,
       (*pc) += 4;
       break;
 
+    case OP_VAR_MSYM_VALUE:
+      gen_msym_var_ref (ax, value, (*pc)[2].msymbol, (*pc)[1].objfile);
+      (*pc) += 4;
+      break;
+
     case OP_REGISTER:
       {
 	const char *name = &(*pc)[2].string;
diff --git a/gdb/eval.c b/gdb/eval.c
index 1ec6751..457e280 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -676,6 +676,25 @@ make_params (int num_types, struct type **param_types)
   return type;
 }
 
+/* Helper for evaluating an OP_VAR_MSYM_VALUE.  */
+
+static value *
+evaluate_var_msym_value (enum noside noside,
+			 struct objfile *objfile, minimal_symbol *msymbol)
+{
+  if (noside == EVAL_AVOID_SIDE_EFFECTS)
+    {
+      type *the_type = find_minsym_type_and_address (msymbol, objfile, NULL);
+      return value_zero (the_type, not_lval);
+    }
+  else
+    {
+      CORE_ADDR address;
+      type *the_type = find_minsym_type_and_address (msymbol, objfile, &address);
+      return value_at_lazy (the_type, address);
+    }
+}
+
 struct value *
 evaluate_subexp_standard (struct type *expect_type,
 			  struct expression *exp, int *pos,
@@ -766,6 +785,11 @@ evaluate_subexp_standard (struct type *expect_type,
 
 	return ret;
       }
+    case OP_VAR_MSYM_VALUE:
+      (*pos) += 3;
+      return evaluate_var_msym_value (noside,
+				      exp->elts[pc + 1].objfile,
+				      exp->elts[pc + 2].msymbol);
 
     case OP_VAR_ENTRY_VALUE:
       (*pos) += 2;
diff --git a/gdb/expprint.c b/gdb/expprint.c
index 9b8ac4c..4939c01 100644
--- a/gdb/expprint.c
+++ b/gdb/expprint.c
@@ -134,6 +134,13 @@ print_subexp_standard (struct expression *exp, int *pos,
       }
       return;
 
+    case OP_VAR_MSYM_VALUE:
+      {
+	(*pos) += 3;
+	fputs_filtered (MSYMBOL_PRINT_NAME (exp->elts[pc + 2].msymbol), stream);
+      }
+      return;
+
     case OP_VAR_ENTRY_VALUE:
       {
 	(*pos) += 2;
@@ -876,6 +883,15 @@ dump_subexp_body_standard (struct expression *exp,
 			SYMBOL_PRINT_NAME (exp->elts[elt + 1].symbol));
       elt += 3;
       break;
+    case OP_VAR_MSYM_VALUE:
+      fprintf_filtered (stream, "Objfile @");
+      gdb_print_host_address (exp->elts[elt].objfile, stream);
+      fprintf_filtered (stream, ", msymbol @");
+      gdb_print_host_address (exp->elts[elt + 1].msymbol, stream);
+      fprintf_filtered (stream, " (%s)",
+			MSYMBOL_PRINT_NAME (exp->elts[elt + 1].msymbol));
+      elt += 3;
+      break;
     case OP_VAR_ENTRY_VALUE:
       fprintf_filtered (stream, "Entry value of symbol @");
       gdb_print_host_address (exp->elts[elt].symbol, stream);
diff --git a/gdb/expression.h b/gdb/expression.h
index 8fe6b07..9e4ddf5 100644
--- a/gdb/expression.h
+++ b/gdb/expression.h
@@ -64,6 +64,7 @@ union exp_element
   {
     enum exp_opcode opcode;
     struct symbol *symbol;
+    struct minimal_symbol *msymbol;
     LONGEST longconst;
     DOUBLEST doubleconst;
     gdb_byte decfloatconst[16];
diff --git a/gdb/minsyms.h b/gdb/minsyms.h
index e763f62..dc51725 100644
--- a/gdb/minsyms.h
+++ b/gdb/minsyms.h
@@ -20,6 +20,8 @@
 #ifndef MINSYMS_H
 #define MINSYMS_H
 
+struct type;
+
 /* Several lookup functions return both a minimal symbol and the
    objfile in which it is found.  This structure is used in these
    cases.  */
@@ -275,4 +277,11 @@ void iterate_over_minimal_symbols (struct objfile *objf,
 
 CORE_ADDR minimal_symbol_upper_bound (struct bound_minimal_symbol minsym);
 
+/* Return the type of MSYMBOL, a minimal symbol of OBJFILE.  If
+   ADDRESS_P is not NULL, set it to the MSYMBOL's resolved
+   address.  */
+
+type *find_minsym_type_and_address (minimal_symbol *msymbol, objfile *objf,
+				    CORE_ADDR *address_p);
+
 #endif /* MINSYMS_H */
diff --git a/gdb/parse.c b/gdb/parse.c
index 3dd7075..4889c1a 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -255,6 +255,16 @@ write_exp_elt_sym (struct parser_state *ps, struct symbol *expelt)
 }
 
 void
+write_exp_elt_msym (struct parser_state *ps, minimal_symbol *expelt)
+{
+  union exp_element tmp;
+
+  memset (&tmp, 0, sizeof (union exp_element));
+  tmp.msymbol = expelt;
+  write_exp_elt (ps, &tmp);
+}
+
+void
 write_exp_elt_block (struct parser_state *ps, const struct block *b)
 {
   union exp_element tmp;
@@ -469,17 +479,17 @@ write_exp_bitstring (struct parser_state *ps, struct stoken str)
   write_exp_elt_longcst (ps, (LONGEST) bits);
 }
 
-/* Add the appropriate elements for a minimal symbol to the end of
-   the expression.  */
+/* Return the type of MSYMBOL, a minimal symbol of OBJFILE.  If
+   ADDRESS_P is not NULL, set it to the MSYMBOL's resolved
+   address.  */
 
-void
-write_exp_msymbol (struct parser_state *ps,
-		   struct bound_minimal_symbol bound_msym)
+type *
+find_minsym_type_and_address (minimal_symbol *msymbol,
+			      struct objfile *objfile,
+			      CORE_ADDR *address_p)
 {
-  struct minimal_symbol *msymbol = bound_msym.minsym;
-  struct objfile *objfile = bound_msym.objfile;
+  bound_minimal_symbol bound_msym = {msymbol, objfile};
   struct gdbarch *gdbarch = get_objfile_arch (objfile);
-
   CORE_ADDR addr = BMSYMBOL_VALUE_ADDRESS (bound_msym);
   struct obj_section *section = MSYMBOL_OBJ_SECTION (objfile, msymbol);
   enum minimal_symbol_type type = MSYMBOL_TYPE (msymbol);
@@ -514,51 +524,54 @@ write_exp_msymbol (struct parser_state *ps,
   if (overlay_debugging)
     addr = symbol_overlayed_address (addr, section);
 
-  write_exp_elt_opcode (ps, OP_LONG);
-  /* Let's make the type big enough to hold a 64-bit address.  */
-  write_exp_elt_type (ps, objfile_type (objfile)->builtin_core_addr);
-  write_exp_elt_longcst (ps, (LONGEST) addr);
-  write_exp_elt_opcode (ps, OP_LONG);
-
   if (section && section->the_bfd_section->flags & SEC_THREAD_LOCAL)
     {
-      write_exp_elt_opcode (ps, UNOP_MEMVAL_TLS);
-      write_exp_elt_objfile (ps, objfile);
-      write_exp_elt_type (ps, objfile_type (objfile)->nodebug_tls_symbol);
-      write_exp_elt_opcode (ps, UNOP_MEMVAL_TLS);
-      return;
+      /* Skip translation if caller does not need the address.  */
+      if (address_p != NULL)
+	*address_p = target_translate_tls_address (objfile, addr);
+      return objfile_type (objfile)->nodebug_tls_symbol;
     }
 
-  write_exp_elt_opcode (ps, UNOP_MEMVAL);
+  if (address_p != NULL)
+    *address_p = addr;
+
+  struct type *the_type;
+
   switch (type)
     {
     case mst_text:
     case mst_file_text:
     case mst_solib_trampoline:
-      write_exp_elt_type (ps, objfile_type (objfile)->nodebug_text_symbol);
-      break;
+      return objfile_type (objfile)->nodebug_text_symbol;
 
     case mst_text_gnu_ifunc:
-      write_exp_elt_type (ps, objfile_type (objfile)
-			  ->nodebug_text_gnu_ifunc_symbol);
-      break;
+      return objfile_type (objfile)->nodebug_text_gnu_ifunc_symbol;
 
     case mst_data:
     case mst_file_data:
     case mst_bss:
     case mst_file_bss:
-      write_exp_elt_type (ps, objfile_type (objfile)->nodebug_data_symbol);
-      break;
+      return objfile_type (objfile)->nodebug_data_symbol;
 
     case mst_slot_got_plt:
-      write_exp_elt_type (ps, objfile_type (objfile)->nodebug_got_plt_symbol);
-      break;
+      return objfile_type (objfile)->nodebug_got_plt_symbol;
 
     default:
-      write_exp_elt_type (ps, objfile_type (objfile)->nodebug_unknown_symbol);
-      break;
+      return objfile_type (objfile)->nodebug_unknown_symbol;
     }
-  write_exp_elt_opcode (ps, UNOP_MEMVAL);
+}
+
+/* Add the appropriate elements for a minimal symbol to the end of
+   the expression.  */
+
+void
+write_exp_msymbol (struct parser_state *ps,
+		   struct bound_minimal_symbol bound_msym)
+{
+  write_exp_elt_opcode (ps, OP_VAR_MSYM_VALUE);
+  write_exp_elt_objfile (ps, bound_msym.objfile);
+  write_exp_elt_msym (ps, bound_msym.minsym);
+  write_exp_elt_opcode (ps, OP_VAR_MSYM_VALUE);
 }
 
 /* Mark the current index as the starting location of a structure
@@ -883,6 +896,7 @@ operator_length_standard (const struct expression *expr, int endpos,
     case OP_DOUBLE:
     case OP_DECFLOAT:
     case OP_VAR_VALUE:
+    case OP_VAR_MSYM_VALUE:
       oplen = 4;
       break;
 
@@ -1844,6 +1858,9 @@ operator_check_standard (struct expression *exp, int pos,
 	type = SYMBOL_TYPE (symbol);
       }
       break;
+    case OP_VAR_MSYM_VALUE:
+      objfile = elts[pos + 1].objfile;
+      break;
     }
 
   /* Invoke callbacks for TYPE and OBJFILE if they were set as non-NULL.  */
diff --git a/gdb/std-operator.def b/gdb/std-operator.def
index 4650726..9007dd4 100644
--- a/gdb/std-operator.def
+++ b/gdb/std-operator.def
@@ -132,6 +132,12 @@ OP (OP_VAR_VALUE)
    current function.  Implemented via DW_OP_entry_value.  */
 OP (OP_VAR_ENTRY_VALUE)
 
+/* OP_VAR_MSYM_VALUE takes one struct objfile * in the following
+   element, and one struct minimal_symbol * in the following
+   exp_element, followed by another OP_VAR_MSYM_VALUE, making four
+   exp_elements.  */
+OP (OP_VAR_MSYM_VALUE)
+
 /* OP_LAST is followed by an integer in the next exp_element.
    The integer is zero for the last value printed,
    or it is the absolute number of a history element.
-- 
2.5.5

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

* [PATCH v2 06/13] evaluate_subexp_standard: Remove useless assignments
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
                   ` (4 preceding siblings ...)
  2017-07-13 15:32 ` [PATCH v2 02/13] Stop assuming no-debug-info functions return int Pedro Alves
@ 2017-07-13 15:32 ` Pedro Alves
  2017-07-13 15:32 ` [PATCH v2 05/13] evaluate_subexp_standard: Eliminate one goto Pedro Alves
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:32 UTC (permalink / raw)
  To: gdb-patches

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* eval.c (evaluate_subexp_standard) <UNOP_COMPLEMENT, UNOP_ADDR>:
	Remove useless assignments to 'op'.
---
 gdb/eval.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/gdb/eval.c b/gdb/eval.c
index 639e06e..cb41a4a 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -2493,7 +2493,6 @@ evaluate_subexp_standard (struct type *expect_type,
 
     case UNOP_COMPLEMENT:
       /* C++: check for and handle destructor names.  */
-      op = exp->elts[*pos].opcode;
 
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       if (noside == EVAL_SKIP)
@@ -2561,8 +2560,6 @@ evaluate_subexp_standard (struct type *expect_type,
     case UNOP_ADDR:
       /* C++: check for and handle pointer to members.  */
 
-      op = exp->elts[*pos].opcode;
-
       if (noside == EVAL_SKIP)
 	{
 	  evaluate_subexp (NULL_TYPE, exp, pos, EVAL_SKIP);
-- 
2.5.5

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

* [PATCH v2 00/13] No-debug-info debugging improvements
@ 2017-07-13 15:32 Pedro Alves
  2017-07-13 15:32 ` [PATCH v2 11/13] Handle "p 'S::method()::static_var'" (quoted) in symbol lookup Pedro Alves
                   ` (13 more replies)
  0 siblings, 14 replies; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:32 UTC (permalink / raw)
  To: gdb-patches

New in v2:
  - The new simpler cast-return-type syntax now assumes the called
    function is prototyped instead of unprototyped:
     https://sourceware.org/ml/gdb-patches/2017-07/msg00128.html
  - Documentation patch adjusted accordingly.
  - The fix for calling prototyped functions via function pointers is
    now patch #1, because the v2 of patch #2 includes new tests that
    depend on the fix.


This series stops GDB from assuming that no-debug-info functions
return int, and that no-debug-info variables have type int.

E.g. instead of this, if you have no debug info for glibc:

  (gdb) p getenv("PATH")
  $1 = -6185

You'll get:

  (gdb) p getenv ("PATH")
  'getenv' has unknown return type; cast the call to its declared return type
  (gdb) p (char *) getenv ("PATH")                      # now Just Works
  $1 = 0x7fffffffe7d7 "/usr/local/bin:/"...
  (gdb) p ((char *(*) (const char *)) getenv) ("PATH")  # continues working
  $1 = 0x7fffffffe7d7 "/usr/local/bin:/"...

And similarly for variables.

While implementing and writing tests for the above, I ran into a few
other problems (as usual...), and fixed them too.  Most of the
problems were related to printing static local variables, with the
documented "p klass::method()::static_var" syntax.

One other problem I ran into today while writing the documentation
patch...  I wanted to document when to call a no-debug-info function
by casting it to the right function pointer type, and calling that
pointer, and noticed that that doesn't actually work correctly...  GDB
always leaves the target function type of the function pointer type as
an unprototyped function, meaning that it applies argument coercion
incorrectly.  W00t!

See many more details and rationale on the description of each patch.
Documentation is in the last patch.

Tested on x86-64 GNU/Linux, native and gdbserver, both with and
without glibc debug info.  Adds over 3700 new passes:

  -# of expected passes           39661
  +# of expected passes           43405

I see this series as follow up to the recent "whatis" fixes for Zack's
errno pretty printer:

  https://sourceware.org/ml/gdb-patches/2017-06/msg00827.html
  https://sourceware.org/ml/gdb-patches/2017-07/msg00018.html

so I put this series on top of those patches in the same branch:

  users/palves/whatis

Pedro Alves (13):
  Fix calling prototyped functions via function pointers
  Stop assuming no-debug-info functions return int
  Introduce OP_VAR_MSYM_VALUE
  Make ptype/whatis print function name of functions with no debug info
    too
  evaluate_subexp_standard: Eliminate one goto
  evaluate_subexp_standard: Remove useless assignments
  evaluate_subexp_standard: Factor out OP_VAR_VALUE handling.
  Stop assuming no-debug-info variables have type int
  Eliminate UNOP_MEMVAL_TLS
  Handle "p S::method()::static_var" in the C++ parser
  Handle "p 'S::method()::static_var'" (quoted) in symbol lookup
  Make "p S::method() const::static_var" work too
  Document "no debug info debugging" improvements

 gdb/doc/gdb.texinfo                                | 114 +++++-
 gdb/NEWS                                           |  26 ++
 gdb/ada-lang.c                                     |  11 +-
 gdb/ada-typeprint.c                                |   7 +-
 gdb/ax-gdb.c                                       |  72 +++-
 gdb/c-exp.y                                        |  51 ++-
 gdb/c-typeprint.c                                  |  10 +-
 gdb/compile/compile-c-symbols.c                    |   4 +-
 gdb/compile/compile-c-types.c                      |  35 +-
 gdb/compile/compile-object-run.c                   |   3 +-
 gdb/cp-namespace.c                                 |  50 +--
 gdb/dwarf2read.c                                   |  46 ++-
 gdb/elfread.c                                      |   2 +-
 gdb/eval.c                                         | 439 ++++++++++++++-------
 gdb/expprint.c                                     |  92 +++--
 gdb/expression.h                                   |   1 +
 gdb/f-typeprint.c                                  |   7 +-
 gdb/gcore.c                                        |   2 +-
 gdb/gdbtypes.c                                     |  27 +-
 gdb/gdbtypes.h                                     |  15 +-
 gdb/guile/scm-value.c                              |   2 +-
 gdb/infcall.c                                      |  49 ++-
 gdb/infcall.h                                      |  19 +-
 gdb/linux-fork.c                                   |   6 +-
 gdb/linux-tdep.c                                   |   4 +-
 gdb/m2-typeprint.c                                 |  11 +-
 gdb/minsyms.h                                      |   9 +
 gdb/objc-lang.c                                    |  14 +-
 gdb/p-typeprint.c                                  |  57 +--
 gdb/parse.c                                        | 130 +++---
 gdb/parser-defs.h                                  |   2 +
 gdb/python/py-value.c                              |   3 +-
 gdb/rust-lang.c                                    |   2 +-
 gdb/std-operator.def                               |  43 +-
 gdb/testsuite/gdb.asm/asm-source.exp               |   8 +-
 .../gdb.base/break-main-file-remove-fail.exp       |   2 +-
 gdb/testsuite/gdb.base/break-probes.exp            |   2 +-
 gdb/testsuite/gdb.base/callfuncs.exp               |  26 +-
 gdb/testsuite/gdb.base/checkpoint.exp              |  44 +--
 gdb/testsuite/gdb.base/dprintf-detach.exp          |   2 +-
 gdb/testsuite/gdb.base/infcall-exec.exp            |   2 +-
 gdb/testsuite/gdb.base/info-os.exp                 |   8 +-
 gdb/testsuite/gdb.base/nodebug.c                   |  48 +++
 gdb/testsuite/gdb.base/nodebug.exp                 | 194 +++++++--
 gdb/testsuite/gdb.base/solib-display.exp           |  16 +-
 gdb/testsuite/gdb.compile/compile-ifunc.exp        |   5 +-
 gdb/testsuite/gdb.compile/compile.exp              |  10 +-
 gdb/testsuite/gdb.cp/local-static.c                | 170 ++++++++
 gdb/testsuite/gdb.cp/local-static.cc               |   1 +
 gdb/testsuite/gdb.cp/local-static.exp              | 241 +++++++++++
 gdb/testsuite/gdb.cp/m-static.exp                  |   5 -
 gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp     |   2 +-
 gdb/testsuite/gdb.threads/siginfo-threads.exp      |   7 +-
 gdb/testsuite/gdb.threads/tls-nodebug.exp          |   5 +-
 gdb/testsuite/gdb.trace/entry-values.exp           |   6 +-
 gdb/typeprint.c                                    |  18 +-
 gdb/typeprint.h                                    |   9 +
 gdb/valarith.c                                     |   4 +-
 gdb/valops.c                                       |   7 +-
 59 files changed, 1753 insertions(+), 454 deletions(-)
 create mode 100644 gdb/testsuite/gdb.cp/local-static.c
 create mode 100644 gdb/testsuite/gdb.cp/local-static.cc
 create mode 100644 gdb/testsuite/gdb.cp/local-static.exp

-- 
2.5.5

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

* [PATCH v2 09/13] Eliminate UNOP_MEMVAL_TLS
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
  2017-07-13 15:32 ` [PATCH v2 11/13] Handle "p 'S::method()::static_var'" (quoted) in symbol lookup Pedro Alves
@ 2017-07-13 15:32 ` Pedro Alves
  2017-07-13 15:32 ` [PATCH v2 03/13] Introduce OP_VAR_MSYM_VALUE Pedro Alves
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:32 UTC (permalink / raw)
  To: gdb-patches

Since minsym references now go via OP_VAR_MSYM_VALUE, UNOP_MEMVAL_TLS
is no longer used anywhere.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* eval.c (evaluate_subexp_standard): Remove UNOP_MEMVAL_TLS
	handling.
	* expprint.c (print_subexp_standard, dump_subexp_body_standard):
	Ditto.
	* parse.c (operator_length_standard, operator_check_standard):
	Ditto.
	* std-operator.def (UNOP_MEMVAL_TLS): Delete.
---
 gdb/eval.c           | 16 ----------------
 gdb/expprint.c       | 22 ----------------------
 gdb/parse.c          | 10 ----------
 gdb/std-operator.def |  7 -------
 4 files changed, 55 deletions(-)

diff --git a/gdb/eval.c b/gdb/eval.c
index 75d3c86..e70328b 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -2655,22 +2655,6 @@ evaluate_subexp_standard (struct type *expect_type,
       else
 	return value_at_lazy (type, value_as_address (arg1));
 
-    case UNOP_MEMVAL_TLS:
-      (*pos) += 3;
-      arg1 = evaluate_subexp (expect_type, exp, pos, noside);
-      if (noside == EVAL_SKIP)
-	return eval_skip_value (exp);
-      if (noside == EVAL_AVOID_SIDE_EFFECTS)
-	return value_zero (exp->elts[pc + 2].type, lval_memory);
-      else
-	{
-	  CORE_ADDR tls_addr;
-
-	  tls_addr = target_translate_tls_address (exp->elts[pc + 1].objfile,
-						   value_as_address (arg1));
-	  return value_at_lazy (exp->elts[pc + 2].type, tls_addr);
-	}
-
     case UNOP_PREINCREMENT:
       arg1 = evaluate_subexp (expect_type, exp, pos, noside);
       if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
diff --git a/gdb/expprint.c b/gdb/expprint.c
index 4939c01..0697a77 100644
--- a/gdb/expprint.c
+++ b/gdb/expprint.c
@@ -478,18 +478,6 @@ print_subexp_standard (struct expression *exp, int *pos,
 	fputs_filtered (")", stream);
       return;
 
-    case UNOP_MEMVAL_TLS:
-      (*pos) += 3;
-      if ((int) prec > (int) PREC_PREFIX)
-	fputs_filtered ("(", stream);
-      fputs_filtered ("{", stream);
-      type_print (exp->elts[pc + 2].type, "", stream, 0);
-      fputs_filtered ("} ", stream);
-      print_subexp (exp, pos, stream, PREC_PREFIX);
-      if ((int) prec > (int) PREC_PREFIX)
-	fputs_filtered (")", stream);
-      return;
-
     case BINOP_ASSIGN_MODIFY:
       opcode = exp->elts[pc + 1].opcode;
       (*pos) += 2;
@@ -961,16 +949,6 @@ dump_subexp_body_standard (struct expression *exp,
       fprintf_filtered (stream, ")");
       elt = dump_subexp (exp, stream, elt + 2);
       break;
-    case UNOP_MEMVAL_TLS:
-      fprintf_filtered (stream, "TLS type @");
-      gdb_print_host_address (exp->elts[elt + 1].type, stream);
-      fprintf_filtered (stream, " (__thread /* \"%s\" */ ",
-                        (exp->elts[elt].objfile == NULL ? "(null)"
-			 : objfile_name (exp->elts[elt].objfile)));
-      type_print (exp->elts[elt + 1].type, NULL, stream, 0);
-      fprintf_filtered (stream, ")");
-      elt = dump_subexp (exp, stream, elt + 3);
-      break;
     case OP_TYPE:
       fprintf_filtered (stream, "Type @");
       gdb_print_host_address (exp->elts[elt].type, stream);
diff --git a/gdb/parse.c b/gdb/parse.c
index 4889c1a..ee6b6f7 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -949,11 +949,6 @@ operator_length_standard (const struct expression *expr, int endpos,
       args = 1;
       break;
 
-    case UNOP_MEMVAL_TLS:
-      oplen = 4;
-      args = 1;
-      break;
-
     case UNOP_ABS:
     case UNOP_CAP:
     case UNOP_CHR:
@@ -1837,11 +1832,6 @@ operator_check_standard (struct expression *exp, int pos,
       }
       break;
 
-    case UNOP_MEMVAL_TLS:
-      objfile = elts[pos + 1].objfile;
-      type = elts[pos + 2].type;
-      break;
-
     case OP_VAR_VALUE:
       {
 	const struct block *const block = elts[pos + 1].block;
diff --git a/gdb/std-operator.def b/gdb/std-operator.def
index 9007dd4..3eed2cc 100644
--- a/gdb/std-operator.def
+++ b/gdb/std-operator.def
@@ -218,13 +218,6 @@ OP (UNOP_REINTERPRET_CAST)
    following subexpression.  */
 OP (UNOP_MEMVAL)
 
-/* UNOP_MEMVAL_TLS is followed by a `struct objfile' pointer in the next
-   exp_element and a type pointer in the following exp_element.
-   With another UNOP_MEMVAL_TLS at the end, this makes four exp_elements.
-   It casts the contents of the word offsetted by the value of the
-   following subexpression from the TLS specified by `struct objfile'.  */
-OP (UNOP_MEMVAL_TLS)
-
 /* Like UNOP_MEMVAL, but the type is supplied as a subexpression.  */
 OP (UNOP_MEMVAL_TYPE)
 
-- 
2.5.5

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

* [PATCH v2 12/13] Make "p S::method() const::static_var" work too
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
                   ` (7 preceding siblings ...)
  2017-07-13 15:32 ` [PATCH v2 01/13] Fix calling prototyped functions via function pointers Pedro Alves
@ 2017-07-13 15:37 ` Pedro Alves
  2017-07-13 15:38 ` [PATCH v2 13/13] Document "no debug info debugging" improvements Pedro Alves
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:37 UTC (permalink / raw)
  To: gdb-patches

Trying to print a function local static variable of a const-qualified
method still doesn't work after the previous fixes:

  (gdb) p 'S::method() const'::static_var
  $1 = {i1 = 1, i2 = 2, i3 = 3}
  (gdb) p S::method() const::static_var
  No symbol "static_var" in specified context.

The reason is that the expression parser/evaluator loses the "const",
and the above unquoted case is just like trying to print a variable of
the non-const overload, if it exists, even.  As if the above unquoted
case had been written as:

  (gdb) p S::method()::static_var
  No symbol "static_var" in specified context.

We can see the problem without static vars in the picture.  With:

 struct S
 {
    void method ();
    void method () const;
 };

Compare:

  (gdb) print 'S::method(void) const'
  $1 = {void (const S * const)} 0x400606 <S::method() const>
  (gdb) print S::method(void) const
  $2 = {void (S * const)} 0x4005d8 <S::method()>   # wrong method!

That's what we need to fix.  If we fix that, the function local static
case starts working.

The grammar production for function/method types is this one:

  exp:       exp '(' parameter_typelist ')' const_or_volatile

This results in a TYPE_INSTANCE expression evaluator operator.  For
the example above, we get something like this ("set debug expression 1"):

...
            0  TYPE_INSTANCE         1 TypeInstance: Type @0x560fda958be0 (void)
            5    OP_SCOPE              Type @0x560fdaa544d8 (S) Field name: `method'
...

While evaluating TYPE_INSTANCE, we end up in
value_struct_elt_for_reference, trying to find the method named
"method" that has the prototype recorded in TYPE_INSTANCE.  In this
case, TYPE_INSTANCE says that we're looking for a method that has
"(void)" as parameters (that's what "1 TypeInstance: Type
@0x560fda958be0 (void)" above means.  The trouble is that nowhere in
this mechanism do we communicate to value_struct_elt_for_reference
that we're looking for the _const_ overload.
value_struct_elt_for_reference only compared parameters, and the
non-const "method()" overload has matching parameters, so it's
considered the right match...

Conveniently, the "const_or_volatile" production in the grammar
already records "const" and "volatile" info in the type stack.  The
type stack is not used in this code path, but we can borrow the
information.  The patch converts the info in the type stack to an
"instance flags" enum, and adds that as another element in
TYPE_INSTANCE operators.  This type instance flags is then applied to
the temporary type that is passed to value_struct_elt_for_reference
for matching.

The other side of the problem is that methods in the debug info aren't
marked const/volatile, so with that in place, the matching never finds
const/volatile-qualified methods.

The problem is that in the DWARF, there's no indication at all whether
a method is const/volatile qualified...  For example (c++filt applied
to the linkage name for convenience):

   <2><d3>: Abbrev Number: 6 (DW_TAG_subprogram)
      <d4>   DW_AT_external    : 1
      <d4>   DW_AT_name        : (indirect string, offset: 0x3df): method
      <d8>   DW_AT_decl_file   : 1
      <d9>   DW_AT_decl_line   : 58
      <da>   DW_AT_linkage_name: (indirect string, offset: 0x5b2): S::method() const
      <de>   DW_AT_declaration : 1
      <de>   DW_AT_object_pointer: <0xe6>
      <e2>   DW_AT_sibling     : <0xec>

I see the same with both GCC and Clang.  The patch works around this
by extracting the cv qualification from the "const" and "volatile" in
the demangled name.  This will need further tweaking for "&" and
"const &" overloads, but we don't support them in the parser yet,
anyway.

The TYPE_CONST changes were necessary otherwise the comparisons in valops.c:

  if (TYPE_CONST (intype) != TYPE_FN_FIELD_CONST (f, j))
    continue;

would fail, because when both TYPE_CONST() TYPE_FN_FIELD_CONST() were
true, their values were different.

BTW, I'm recording the const/volatile-ness of methods in the
TYPE_FN_FIELD info because #1 - I'm not sure it's kosher to change the
method's type directly (vs having to call make_cv_type to create a new
type), and #2 it's what stabsread.c does:

...
	    case 'A':		/* Normal functions.  */
	      new_sublist->fn_field.is_const = 0;
	      new_sublist->fn_field.is_volatile = 0;
	      (*pp)++;
	      break;
	    case 'B':		/* `const' member functions.  */
	      new_sublist->fn_field.is_const = 1;
	      new_sublist->fn_field.is_volatile = 0;
...

After all this, this finally all works:

  print S::method(void) const
  $1 = {void (const S * const)} 0x400606 <S::method() const>
  (gdb) p S::method() const::static_var
  $2 = {i1 = 1, i2 = 2, i3 = 3}

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* c-exp.y (function_method, function_method_void): Add current
	instance flags to TYPE_INSTANCE.
	* dwarf2read.c (check_modifier): New.
	(compute_delayed_physnames): Assert that only C++ adds delayed
	physnames.  Mark fn_fields as const/volatile depending on
	physname.
	* eval.c (make_params): New type_instance_flags parameter.  Use
	it as the new type's instance flags.
	(evaluate_subexp_standard) <TYPE_INSTANCE>: Extract the instance
	flags element and pass it to make_params.
	* expprint.c (print_subexp_standard) <TYPE_INSTANCE>: Handle
	instance flags element.
	(dump_subexp_body_standard) <TYPE_INSTANCE>: Likewise.
	* gdbtypes.h: Include "enum-flags.h".
	(type_instance_flags): New enum-flags type.
	(TYPE_CONST, TYPE_VOLATILE, TYPE_RESTRICT, TYPE_ATOMIC)
	(TYPE_CODE_SPACE, TYPE_DATA_SPACE): Return boolean.
	* parse.c (operator_length_standard) <TYPE_INSTANCE>: Adjust.
	(follow_type_instance_flags): New function.
	(operator_check_standard) <TYPE_INSTANCE>: Adjust.
	* parser-defs.h (follow_type_instance_flags): Declare.
	* valops.c (value_struct_elt_for_reference): const/volatile must
	match too.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/func-static.c (S::method const, S::method volatile)
	(S::method volatile const): New methods.
	(c_s, v_s, cv_s): New instances.
	(main): Call method() on them.
	* gdb.base/func-static.exp (syntax_re, cannot_resolve_re): New variables.
	(cannot_resolve): New procedure.
	(cxx_scopes_list): Test cv methods.  Add print-scope-quote and
	print-quote-unquoted columns.
	(do_test): Test printing each scope too.
---
 gdb/c-exp.y                           |   8 +++
 gdb/dwarf2read.c                      |  43 ++++++++++++
 gdb/eval.c                            |  40 ++++++-----
 gdb/expprint.c                        |  36 ++++++++--
 gdb/gdbtypes.h                        |  15 ++--
 gdb/parse.c                           |  33 ++++++++-
 gdb/parser-defs.h                     |   2 +
 gdb/testsuite/gdb.cp/local-static.c   |  28 ++++++++
 gdb/testsuite/gdb.cp/local-static.exp | 125 +++++++++++++++++++++++++++++-----
 gdb/valops.c                          |   5 ++
 10 files changed, 289 insertions(+), 46 deletions(-)

diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index 5102470..05c92ac 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -558,6 +558,11 @@ function_method:       exp '(' parameter_typelist ')' const_or_volatile
 			  LONGEST len = VEC_length (type_ptr, type_list);
 
 			  write_exp_elt_opcode (pstate, TYPE_INSTANCE);
+			  /* Save the const/volatile qualifiers as
+			     recorded by the const_or_volatile
+			     production's actions.  */
+			  write_exp_elt_longcst (pstate,
+						 follow_type_instance_flags ());
 			  write_exp_elt_longcst (pstate, len);
 			  for (i = 0;
 			       VEC_iterate (type_ptr, type_list, i, type_elt);
@@ -571,6 +576,9 @@ function_method:       exp '(' parameter_typelist ')' const_or_volatile
 
 function_method_void:	    exp '(' ')' const_or_volatile
 		       { write_exp_elt_opcode (pstate, TYPE_INSTANCE);
+			 /* See above.  */
+			 write_exp_elt_longcst (pstate,
+						follow_type_instance_flags ());
 			 write_exp_elt_longcst (pstate, 0);
 			 write_exp_elt_longcst (pstate, 0);
 			 write_exp_elt_opcode (pstate, TYPE_INSTANCE);
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 6845fd7..108296d 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -7947,6 +7947,23 @@ free_delayed_list (void *ptr)
     }
 }
 
+/* Check whether [PHYSNAME, PHYSNAME+LEN) ends with a modifier like
+   "const" / "volatile".  If so, decrements LEN by the length of the
+   modifier and return true.  Otherwise return false.  */
+
+template<size_t N>
+static bool
+check_modifier (const char *physname, size_t &len, const char (&mod)[N])
+{
+  size_t mod_len = sizeof (mod) - 1;
+  if (len > mod_len && startswith (physname + (len - mod_len), mod))
+    {
+      len -= mod_len;
+      return true;
+    }
+  return false;
+}
+
 /* Compute the physnames of any methods on the CU's method list.
 
    The computation of method physnames is delayed in order to avoid the
@@ -7958,6 +7975,12 @@ compute_delayed_physnames (struct dwarf2_cu *cu)
 {
   int i;
   struct delayed_method_info *mi;
+
+  /* Only C++ delays computing physnames.  */
+  if (VEC_empty (delayed_method_info, cu->method_list))
+    return;
+  gdb_assert (cu->language == language_cplus);
+
   for (i = 0; VEC_iterate (delayed_method_info, cu->method_list, i, mi) ; ++i)
     {
       const char *physname;
@@ -7966,6 +7989,26 @@ compute_delayed_physnames (struct dwarf2_cu *cu)
       physname = dwarf2_physname (mi->name, mi->die, cu);
       TYPE_FN_FIELD_PHYSNAME (fn_flp->fn_fields, mi->index)
 	= physname ? physname : "";
+
+      /* Since there's no tag to indicate whether a method is a
+	 const/volatile overload, extract that information out of the
+	 demangled name.  */
+      if (physname != NULL)
+	{
+	  size_t len = strlen (physname);
+
+	  while (1)
+	    {
+	      if (physname[len] == ')') /* shortcut */
+		break;
+	      else if (check_modifier (physname, len, " const"))
+		TYPE_FN_FIELD_CONST (fn_flp->fn_fields, mi->index) = 1;
+	      else if (check_modifier (physname, len, " volatile"))
+		TYPE_FN_FIELD_VOLATILE (fn_flp->fn_fields, mi->index) = 1;
+	      else
+		break;
+	    }
+	}
     }
 }
 
diff --git a/gdb/eval.c b/gdb/eval.c
index 7d129f0..5a434b9 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -642,18 +642,22 @@ ptrmath_type_p (const struct language_defn *lang, struct type *type)
     }
 }
 
-/* Constructs a fake method with the given parameter types.
-   This function is used by the parser to construct an "expected"
-   type for method overload resolution.  */
+/* Constructs a fake method with the given parameter types.  This
+   function is used by the parser to construct an "expected" type for
+   method overload resolution.  FLAGS is used as instance flags of the
+   new type, in order to be able to make the new type represent a
+   const/volatile overload.  */
 
 static struct type *
-make_params (int num_types, struct type **param_types)
+make_params (type_instance_flags flags,
+	     int num_types, struct type **param_types)
 {
   struct type *type = XCNEW (struct type);
   TYPE_MAIN_TYPE (type) = XCNEW (struct main_type);
   TYPE_LENGTH (type) = 1;
   TYPE_CODE (type) = TYPE_CODE_METHOD;
   TYPE_CHAIN (type) = type;
+  TYPE_INSTANCE_FLAGS (type) = flags;
   if (num_types > 0)
     {
       if (param_types[num_types - 1] == NULL)
@@ -2039,18 +2043,22 @@ evaluate_subexp_standard (struct type *expect_type,
 	}
 
     case TYPE_INSTANCE:
-      nargs = longest_to_int (exp->elts[pc + 1].longconst);
-      arg_types = (struct type **) alloca (nargs * sizeof (struct type *));
-      for (ix = 0; ix < nargs; ++ix)
-	arg_types[ix] = exp->elts[pc + 1 + ix + 1].type;
-
-      expect_type = make_params (nargs, arg_types);
-      *(pos) += 3 + nargs;
-      arg1 = evaluate_subexp_standard (expect_type, exp, pos, noside);
-      xfree (TYPE_FIELDS (expect_type));
-      xfree (TYPE_MAIN_TYPE (expect_type));
-      xfree (expect_type);
-      return arg1;
+      {
+	type_instance_flags flags
+	  = (type_instance_flag_value) longest_to_int (exp->elts[pc + 1].longconst);
+	nargs = longest_to_int (exp->elts[pc + 2].longconst);
+	arg_types = (struct type **) alloca (nargs * sizeof (struct type *));
+	for (ix = 0; ix < nargs; ++ix)
+	  arg_types[ix] = exp->elts[pc + 2 + ix + 1].type;
+
+	expect_type = make_params (flags, nargs, arg_types);
+	*(pos) += 4 + nargs;
+	arg1 = evaluate_subexp_standard (expect_type, exp, pos, noside);
+	xfree (TYPE_FIELDS (expect_type));
+	xfree (TYPE_MAIN_TYPE (expect_type));
+	xfree (expect_type);
+	return arg1;
+      }
 
     case BINOP_CONCAT:
       arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
diff --git a/gdb/expprint.c b/gdb/expprint.c
index fad20e8..2c16b49 100644
--- a/gdb/expprint.c
+++ b/gdb/expprint.c
@@ -543,11 +543,15 @@ print_subexp_standard (struct expression *exp, int *pos,
 
     case TYPE_INSTANCE:
       {
-	LONGEST count = exp->elts[pc + 1].longconst;
+	type_instance_flags flags
+	  = (type_instance_flag_value) longest_to_int (exp->elts[pc + 1].longconst);
+	LONGEST count = exp->elts[pc + 2].longconst;
 
+	/* The FLAGS.  */
+	(*pos)++;
 	/* The COUNT.  */
 	(*pos)++;
-	fputs_unfiltered ("TypesInstance(", stream);
+	fputs_unfiltered ("TypeInstance(", stream);
 	while (count-- > 0)
 	  {
 	    type_print (exp->elts[(*pos)++].type, "", stream, 0);
@@ -558,6 +562,12 @@ print_subexp_standard (struct expression *exp, int *pos,
 	/* Ending COUNT and ending TYPE_INSTANCE.  */
 	(*pos) += 2;
 	print_subexp (exp, pos, stream, PREC_PREFIX);
+
+	if (flags & TYPE_INSTANCE_FLAG_CONST)
+	  fputs_unfiltered (",const", stream);
+	if (flags & TYPE_INSTANCE_FLAG_VOLATILE)
+	  fputs_unfiltered (",volatile", stream);
+
 	fputs_unfiltered (")", stream);
 	return;
       }
@@ -1019,9 +1029,9 @@ dump_subexp_body_standard (struct expression *exp,
 
     case TYPE_INSTANCE:
       {
-	LONGEST len;
-
-	len = exp->elts[elt++].longconst;
+	type_instance_flags flags
+	  = (type_instance_flag_value) longest_to_int (exp->elts[elt++].longconst);
+	LONGEST len = exp->elts[elt++].longconst;
 	fprintf_filtered (stream, "%s TypeInstance: ", plongest (len));
 	while (len-- > 0)
 	  {
@@ -1034,6 +1044,22 @@ dump_subexp_body_standard (struct expression *exp,
 	    if (len > 0)
 	      fputs_filtered (", ", stream);
 	  }
+
+	fprintf_filtered (stream, " Flags: %s (", hex_string (flags));
+	bool space = false;
+	auto print_one = [&] (const char *mod)
+	  {
+	    if (space)
+	      fputs_filtered (" ", stream);
+	    space = true;
+	    fprintf_filtered (stream, mod);
+	  };
+	if (flags & TYPE_INSTANCE_FLAG_CONST)
+	  print_one ("const");
+	if (flags & TYPE_INSTANCE_FLAG_VOLATILE)
+	  print_one ("volatile");
+	fprintf_filtered (stream, ")");
+
 	/* Ending LEN and ending TYPE_INSTANCE.  */
 	elt += 2;
 	elt = dump_subexp (exp, stream, elt);
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index d2018a8..9d9b09f 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -46,6 +46,7 @@
 
 #include "hashtab.h"
 #include "common/offset-type.h"
+#include "common/enum-flags.h"
 
 /* Forward declarations for prototypes.  */
 struct field;
@@ -196,6 +197,8 @@ enum type_instance_flag_value
   TYPE_INSTANCE_FLAG_ATOMIC = (1 << 8)
 };
 
+DEF_ENUM_FLAGS_TYPE (enum type_instance_flag_value, type_instance_flags);
+
 /* * Unsigned integer type.  If this is not set for a TYPE_CODE_INT,
    the type is signed (unless TYPE_NOSIGN (below) is set).  */
 
@@ -304,25 +307,25 @@ enum type_instance_flag_value
 /* * Constant type.  If this is set, the corresponding type has a
    const modifier.  */
 
-#define TYPE_CONST(t) (TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_CONST)
+#define TYPE_CONST(t) ((TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_CONST) != 0)
 
 /* * Volatile type.  If this is set, the corresponding type has a
    volatile modifier.  */
 
 #define TYPE_VOLATILE(t) \
-  (TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_VOLATILE)
+  ((TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_VOLATILE) != 0)
 
 /* * Restrict type.  If this is set, the corresponding type has a
    restrict modifier.  */
 
 #define TYPE_RESTRICT(t) \
-  (TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_RESTRICT)
+  ((TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_RESTRICT) != 0)
 
 /* * Atomic type.  If this is set, the corresponding type has an
    _Atomic modifier.  */
 
 #define TYPE_ATOMIC(t) \
-  (TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_ATOMIC)
+  ((TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_ATOMIC) != 0)
 
 /* * True if this type represents either an lvalue or lvalue reference type.  */
 
@@ -349,10 +352,10 @@ enum type_instance_flag_value
    is instruction space, and for data objects is data memory.  */
 
 #define TYPE_CODE_SPACE(t) \
-  (TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_CODE_SPACE)
+  ((TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_CODE_SPACE) != 0)
 
 #define TYPE_DATA_SPACE(t) \
-  (TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_DATA_SPACE)
+  ((TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_DATA_SPACE) != 0)
 
 /* * Address class flags.  Some environments provide for pointers
    whose size is different from that of a normal pointer or address
diff --git a/gdb/parse.c b/gdb/parse.c
index 97ddc72..6f85eba 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -926,7 +926,7 @@ operator_length_standard (const struct expression *expr, int endpos,
       break;
 
     case TYPE_INSTANCE:
-      oplen = 4 + longest_to_int (expr->elts[endpos - 2].longconst);
+      oplen = 5 + longest_to_int (expr->elts[endpos - 2].longconst);
       args = 1;
       break;
 
@@ -1646,6 +1646,33 @@ push_typelist (VEC (type_ptr) *list)
   push_type (tp_function_with_arguments);
 }
 
+/* Pop the type stack and return a type_instance_flags that
+   corresponds the const/volatile qualifiers on the stack.  This is
+   called by the C++ parser when parsing methods types, and as such no
+   other kind of type in the type stack is expected.  */
+
+type_instance_flags
+follow_type_instance_flags ()
+{
+  type_instance_flags flags = 0;
+
+  for (;;)
+    switch (pop_type ())
+      {
+      case tp_end:
+	return flags;
+      case tp_const:
+	flags |= TYPE_INSTANCE_FLAG_CONST;
+	break;
+      case tp_volatile:
+	flags |= TYPE_INSTANCE_FLAG_VOLATILE;
+	break;
+      default:
+	gdb_assert_not_reached ("unrecognized tp_ value in follow_types");
+      }
+}
+
+
 /* Pop the type stack and return the type which corresponds to FOLLOW_TYPE
    as modified by all the stuff on the stack.  */
 struct type *
@@ -1825,11 +1852,11 @@ operator_check_standard (struct expression *exp, int pos,
 
     case TYPE_INSTANCE:
       {
-	LONGEST arg, nargs = elts[pos + 1].longconst;
+	LONGEST arg, nargs = elts[pos + 2].longconst;
 
 	for (arg = 0; arg < nargs; arg++)
 	  {
-	    struct type *type = elts[pos + 2 + arg].type;
+	    struct type *type = elts[pos + 3 + arg].type;
 	    struct objfile *objfile = TYPE_OBJFILE (type);
 
 	    if (objfile && (*objfile_func) (objfile, data))
diff --git a/gdb/parser-defs.h b/gdb/parser-defs.h
index fadc2ef..f7ba7f0 100644
--- a/gdb/parser-defs.h
+++ b/gdb/parser-defs.h
@@ -266,6 +266,8 @@ extern const char *op_name_standard (enum exp_opcode);
 
 extern struct type *follow_types (struct type *);
 
+extern type_instance_flags follow_type_instance_flags ();
+
 extern void null_post_parser (struct expression **, int);
 
 extern int parse_float (const char *p, int len, DOUBLEST *d,
diff --git a/gdb/testsuite/gdb.cp/local-static.c b/gdb/testsuite/gdb.cp/local-static.c
index 5bfff8d..0f4c4f0 100644
--- a/gdb/testsuite/gdb.cp/local-static.c
+++ b/gdb/testsuite/gdb.cp/local-static.c
@@ -53,10 +53,17 @@ struct S
   }
 
   void method ();
+  void method () const;
+  void method () volatile;
+  void method () volatile const;
+
   static void static_method ();
 };
 
 S s;
+const S c_s = {};
+volatile S v_s = {};
+const volatile S cv_s = {};
 
 void
 S::method ()
@@ -65,6 +72,24 @@ S::method ()
 }
 
 void
+S::method () const
+{
+  DEF_STATICS (S_M_C);
+}
+
+void
+S::method () volatile
+{
+  DEF_STATICS (S_M_V);
+}
+
+void
+S::method () const volatile
+{
+  DEF_STATICS (S_M_CV);
+}
+
+void
 S::static_method ()
 {
   DEF_STATICS (S_SM);
@@ -127,6 +152,9 @@ main ()
 
 #ifdef __cplusplus
       s.method ();
+      c_s.method ();
+      v_s.method ();
+      cv_s.method ();
       s.inline_method ();
       S::static_method ();
       S::static_inline_method ();
diff --git a/gdb/testsuite/gdb.cp/local-static.exp b/gdb/testsuite/gdb.cp/local-static.exp
index 581efa3..5e8eaaa 100644
--- a/gdb/testsuite/gdb.cp/local-static.exp
+++ b/gdb/testsuite/gdb.cp/local-static.exp
@@ -19,28 +19,98 @@
 
 standard_testfile .c
 
+# A few expected errors.
+set syntax_re "A syntax error in expression, near.*"
+set cannot_resolve_re "Cannot resolve method S::method to any overloaded instance"
+
+# Build an "Cannot resolve method ..." expected error string for
+# method METH.
+#
+proc cannot_resolve {meth} {
+    return "Cannot resolve method $meth to any overloaded instance"
+}
+
 # A list of scopes that have the static variables that we want to
 # print.  Each entry has, in order, the scope/function name, and the
-# prefix used by the static variables.  (The prefix exists to make it
-# easier to debug the test if something goes wrong.)
-
-     #SCOPE				#PREFIX
+# prefix used by the static variables.  The prefix exists both to make
+# it easier to debug the test if something goes wrong, and, to make
+# sure that printing the static local of one method overload doesn't
+# find the variables of the wrong overload.
+#
+# While at it, we also try printing each scope without the static
+# local, to check that the parse copes with cv overloads without
+# quoting.  That's what the third and forth columns are for.  Note
+# that printing "func()" is different from "func(void)".  The former
+# is an inferior function call, while the latter is a reference to the
+# function.
+
+     #SCOPE				#PREFIX         #PRINT-SCOPE-QUOTED
+							#PRINT-SCOPE-UNQUOTED (opt)
 set cxx_scopes_list {
-    {"S::method()"			"S_M"}
-    {"S::static_method()"		"S_SM"}
-    {"S::inline_method()"		"S_IM"}
-    {"S::static_inline_method()"	"S_SIM"}
-    {"S2<int>::method()"		"S2_M"}
-    {"S2<int>::static_method()"		"S2_SM"}
-    {"S2<int>::inline_method()"		"S2_IM"}
-    {"S2<int>::static_inline_method()"	"S2_SIM"}
-    {"free_func()"			"FF"}
-    {"free_inline_func()"		"FIF"}
+    {"S::method()"			"S_M"		{= \\{void \\(S \\* const\\)\\} $hex <S::method\\(\\)>}
+							{[cannot_resolve "S::method"]}}
+
+    {"S::method() const"		"S_M_C"		{= \\{void \\(const S \\* const\\)\\} $hex <S::method\\(\\) const>}
+							$syntax_re}
+
+    {"S::method() volatile"		"S_M_V"		{= \\{void \\(volatile S \\* const\\)\\} $hex <S::method\\(\\) volatile>}
+							$syntax_re}
+
+    {"S::method() const volatile"	"S_M_CV"	{= \\{void \\(const volatile S \\* const\\)\\} $hex <S::method\\(\\) const volatile>}
+							$syntax_re}
+
+    {"S::method() volatile const"	"S_M_CV"	{= \\{void \\(const volatile S \\* const\\)\\} $hex <S::method\\(\\) const volatile>}
+							$syntax_re}
+
+    {"S::method(void)"			"S_M"		{= \\{void \\(S \\* const\\)\\} $hex <S::method\\(\\)>}}
+    {"S::method(void) const"		"S_M_C"		{= \\{void \\(const S \\* const\\)\\} $hex <S::method\\(\\) const>}}
+    {"S::method(void) volatile"		"S_M_V"		{= \\{void \\(volatile S \\* const\\)\\} $hex <S::method\\(\\) volatile>}}
+    {"S::method(void) const volatile"	"S_M_CV"	{= \\{void \\(const volatile S \\* const\\)\\} $hex <S::method\\(\\) const volatile>}}
+    {"S::method(void) volatile const"	"S_M_CV"	{= \\{void \\(const volatile S \\* const\\)\\} $hex <S::method\\(\\) const volatile>}}
+
+    {"S::static_method()"		"S_SM"		{= \\{void \\(void\\)\\} $hex <S::static_method\\(\\)>}
+							"void"}
+
+    {"S::static_method(void)"		"S_SM"		{= \\{void \\(void\\)\\} $hex <S::static_method\\(\\)>}}
+
+    {"S::inline_method()"		"S_IM"		{= \\{void \\(S \\* const\\)\\} $hex <S::inline_method\\(\\)>}
+							{[cannot_resolve "S::inline_method"]}}
+
+    {"S::inline_method(void)"		"S_IM"		{= \\{void \\(S \\* const\\)\\} $hex <S::inline_method\\(\\)>}}
+
+    {"S::static_inline_method()"	"S_SIM"		{= \\{void \\(void\\)\\} $hex <S::static_inline_method\\(\\)>}
+							"void"}
+
+    {"S::static_inline_method(void)"	"S_SIM"		{= \\{void \\(void\\)\\} $hex <S::static_inline_method\\(\\)>}}
+
+    {"S2<int>::method()"		"S2_M"		{= \\{void \\(S2<int> \\* const\\)\\} $hex <S2<int>::method\\(\\)>}
+							{[cannot_resolve "S2<int>::method"]}}
+
+    {"S2<int>::static_method()"		"S2_SM"		{= \\{void \\(void\\)\\} $hex <S2<int>::static_method\\(\\)>}
+							"void"}
+
+    {"S2<int>::inline_method()"		"S2_IM"		{= \\{void \\(S2<int> \\* const\\)\\} $hex <S2<int>::inline_method\\(\\)>}
+							{[cannot_resolve "S2<int>::inline_method"]}}
+
+    {"S2<int>::static_inline_method()"	"S2_SIM"	{= \\{void \\(void\\)\\} $hex <S2<int>::static_inline_method\\(\\)>}
+							"void"}
+
+    {"free_func"			"FF"		{= \\{void \\(void\\)\\} $hex <free_func\\(\\)>}}
+
+    {"free_func()"			"FF"		{= \\{void \\(void\\)\\} $hex <free_func\\(\\)>}
+							"void"}
+
+    {"free_func(void)"			"FF"		{= \\{void \\(void\\)\\} $hex <free_func\\(\\)>}}
+
+    {"free_inline_func()"		"FIF"		{= \\{void \\(void\\)\\} $hex <free_inline_func\\(\\)>}
+							"void"}
+
+    {"free_inline_func(void)"		"FIF"		{= \\{void \\(void\\)\\} $hex <free_inline_func\\(\\)>}}
 }
 
 set c_scopes_list {
-    {"free_func"			"FF"}
-    {"free_inline_func"			"FIF"}
+    {"free_func"			"FF"		{= \\{void \\(void\\)\\} $hex <free_func>}}
+    {"free_inline_func"			"FIF"		{= \\{void \\(void\\)\\} $hex <free_inline_func>}}
 }
 
 # A list of all the static varibles defined in each scope.  The first
@@ -91,6 +161,29 @@ proc do_test {lang} {
 	set scopes_list $cxx_scopes_list
     }
 
+    # Print each scope/function using these syntaxes:
+    #
+    #  "(gdb) p 'S::method() const'"    # quoted
+    #  "(gdb) p S::method() const"      # unquoted
+    #
+    foreach scope_line $scopes_list  {
+	set scope [lindex $scope_line 0]
+
+	set print_quoted_re [lindex $scope_line 2]
+	set print_quoted_re [uplevel 1 "subst -nobackslashes -nocommands \"$print_quoted_re\""]
+
+	set print_unquoted_re [lindex $scope_line 3]
+	set print_unquoted_re [uplevel 1 "subst -nobackslashes -nocommands \"$print_unquoted_re\""]
+
+	gdb_test "print '${scope}'" $print_quoted_re
+
+	if {$print_unquoted_re != ""} {
+	    gdb_test "print ${scope}" $print_unquoted_re
+	} else {
+	    gdb_test "print ${scope}" $print_quoted_re
+	}
+    }
+
     # Print each variable using these syntaxes:
     #
     #  'func()'::var
diff --git a/gdb/valops.c b/gdb/valops.c
index 66678e5..f928eea 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -3427,6 +3427,11 @@ value_struct_elt_for_reference (struct type *domain, int offset,
 	    {
 	      for (j = 0; j < len; ++j)
 		{
+		  if (TYPE_CONST (intype) != TYPE_FN_FIELD_CONST (f, j))
+		    continue;
+		  if (TYPE_VOLATILE (intype) != TYPE_FN_FIELD_VOLATILE (f, j))
+		    continue;
+
 		  if (compare_parameters (TYPE_FN_FIELD_TYPE (f, j), intype, 0)
 		      || compare_parameters (TYPE_FN_FIELD_TYPE (f, j),
 					     intype, 1))
-- 
2.5.5

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

* [PATCH v2 07/13] evaluate_subexp_standard: Factor out OP_VAR_VALUE handling.
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
                   ` (9 preceding siblings ...)
  2017-07-13 15:38 ` [PATCH v2 13/13] Document "no debug info debugging" improvements Pedro Alves
@ 2017-07-13 15:38 ` Pedro Alves
  2017-07-13 15:38 ` [PATCH v2 08/13] Stop assuming no-debug-info variables have type int Pedro Alves
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:38 UTC (permalink / raw)
  To: gdb-patches

A following patch will want to call the new evaluate_var_value
function in another spot.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* eval.c (evaluate_var_value): New function, factored out from ...
	(evaluate_subexp_standard): ... here.
---
 gdb/eval.c | 67 +++++++++++++++++++++++++++++++++-----------------------------
 1 file changed, 36 insertions(+), 31 deletions(-)

diff --git a/gdb/eval.c b/gdb/eval.c
index cb41a4a..a97f4a9 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -676,6 +676,39 @@ make_params (int num_types, struct type **param_types)
   return type;
 }
 
+/* Helper for evaluating an OP_VAR_VALUE.  */
+
+static value *
+evaluate_var_value (enum noside noside, const block *blk, symbol *var)
+{
+  /* JYG: We used to just return value_zero of the symbol type if
+     we're asked to avoid side effects.  Otherwise we return
+     value_of_variable (...).  However I'm not sure if
+     value_of_variable () has any side effect.  We need a full value
+     object returned here for whatis_exp () to call evaluate_type ()
+     and then pass the full value to value_rtti_target_type () if we
+     are dealing with a pointer or reference to a base class and print
+     object is on.  */
+
+  struct value *ret = NULL;
+
+  TRY
+    {
+      ret = value_of_variable (var, blk);
+    }
+
+  CATCH (except, RETURN_MASK_ERROR)
+    {
+      if (noside != EVAL_AVOID_SIDE_EFFECTS)
+	throw_exception (except);
+
+      ret = value_zero (SYMBOL_TYPE (var), not_lval);
+    }
+  END_CATCH
+
+  return ret;
+}
+
 /* Helper for evaluating an OP_VAR_MSYM_VALUE.  */
 
 static value *
@@ -763,37 +796,9 @@ evaluate_subexp_standard (struct type *expect_type,
       (*pos) += 3;
       if (noside == EVAL_SKIP)
 	return eval_skip_value (exp);
-
-      /* JYG: We used to just return value_zero of the symbol type
-	 if we're asked to avoid side effects.  Otherwise we return
-	 value_of_variable (...).  However I'm not sure if
-	 value_of_variable () has any side effect.
-	 We need a full value object returned here for whatis_exp ()
-	 to call evaluate_type () and then pass the full value to
-	 value_rtti_target_type () if we are dealing with a pointer
-	 or reference to a base class and print object is on.  */
-
-      {
-	struct value *ret = NULL;
-
-	TRY
-	  {
-	    ret = value_of_variable (exp->elts[pc + 2].symbol,
-				     exp->elts[pc + 1].block);
-	  }
-
-	CATCH (except, RETURN_MASK_ERROR)
-	  {
-	    if (noside == EVAL_AVOID_SIDE_EFFECTS)
-	      ret = value_zero (SYMBOL_TYPE (exp->elts[pc + 2].symbol),
-				not_lval);
-	    else
-	      throw_exception (except);
-	  }
-	END_CATCH
-
-	return ret;
-      }
+      return evaluate_var_value (noside,
+				 exp->elts[pc + 1].block,
+				 exp->elts[pc + 2].symbol);
     case OP_VAR_MSYM_VALUE:
       (*pos) += 3;
       return evaluate_var_msym_value (noside,
-- 
2.5.5

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

* [PATCH v2 08/13] Stop assuming no-debug-info variables have type int
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
                   ` (10 preceding siblings ...)
  2017-07-13 15:38 ` [PATCH v2 07/13] evaluate_subexp_standard: Factor out OP_VAR_VALUE handling Pedro Alves
@ 2017-07-13 15:38 ` Pedro Alves
  2017-12-07 23:30   ` [RFA] Adjust gdb.arch/i386-sse-stack-align.exp print statement Sergio Durigan Junior
  2017-07-13 15:39 ` [PATCH v2 04/13] Make ptype/whatis print function name of functions with no debug info too Pedro Alves
  2017-09-04 19:21 ` [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
  13 siblings, 1 reply; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:38 UTC (permalink / raw)
  To: gdb-patches

An earlier commit made GDB no longer assume no-debug-info functions
return int.  This commit gives the same treatment to variables.

Currently, you can end misled by GDB over output like this:

  (gdb) p var
  $1 = -1
  (gdb) p /x var
  $2 = 0xffffffff

until you realize that GDB is assuming that the variable is an "int",
because:

  (gdb) ptype var
  type = <data variable, no debug info>

You may try to fix it by casting, but that doesn't really help:

  (gdb) p /x (unsigned long long) var
  $3 = 0xffffffffffffffff            # incorrect
         ^^

That's incorrect output, because the variable was defined like this:

  uint64_t var = 0x7fffffffffffffff;
                   ^^

What happened is that with the cast, GDB did an int -> 'unsigned long
long' conversion instead of reinterpreting the variable as the cast-to
type.  To get at the variable properly you have to reinterpret the
variable's address manually instead, with either:

  (gdb) p /x *(unsigned long long *) &var
  $4 = 0x7fffffffffffffff
  (gdb) p /x {unsigned long long} &var
  $5 = 0x7fffffffffffffff

After this commit GDB does it for you.  This is what you'll get
instead:

  (gdb) p var
  'var' has unknown type; cast it to its declared type
  (gdb) p /x (unsigned long long) var
  $1 = 0x7fffffffffffffff

As in the functions patch, the "compile" machinery doesn't currently
have the cast-to type handy, so it continues assuming no-debug
variables have int type, though now at least it warns.

The change to gdb.cp/m-static.exp deserves an explanation:

 - gdb_test "print 'gnu_obj_1::method()::sintvar'" "\\$\[0-9\]+ = 4" \
 + gdb_test "print (int) 'gnu_obj_1::method()::sintvar'" "\\$\[0-9\]+ = 4" \

That's printing the "sintvar" function local static of the
"gnu_obj_1::method()" method.

The problem with that test is that that "'S::method()::static_var'"
syntax doesn't really work in C++ as you'd expect.  The way to make it
work correctly currently is to quote the method part, not the whole
expression, like:

  (gdb) print 'gnu_obj_1::method()'::sintvar

If you wrap the whole expression in quotes, like in m-static.exp, what
really happens is that the parser considers the whole string as a
symbol name, but there's no debug symbol with that name.  However,
local statics have linkage and are given a mangled name that demangles
to the same string as the full expression, so that's what GDB prints.
After this commit, and without the cast, the print in m-static.exp
would error out saying that the variable has unknown type:

  (gdb) p 'gnu_obj_1::method()::sintvar'
  'gnu_obj_1::method()::sintvar' has unknown type; cast it to its declared type

TBC, if currently (even before this series) you try to print any
function local static variable of type other than int, you'll get
bogus results.  You can see that with m-static.cc as is, even.
Printing the "svar" local, which is a boolean (1 byte) still prints as
"int" (4 bytes):

  (gdb) p 'gnu_obj_1::method()::svar'
  $1 = 1
  (gdb) ptype 'gnu_obj_1::method()::svar'
  type = <data variable, no debug info>

This probably prints some random bogus value on big endian machines.

If 'svar' was of some aggregate type (etc.) we'd still print it as
int, so the problem would have been more obvious...  After this
commit, you'll get instead:

  (gdb) p 'gnu_obj_1::method()::svar'
  'gnu_obj_1::method()::svar' has unknown type; cast it to its declared type

... so at least GDB is no longer misleading.  Making GDB find the real
local static debug symbol is the subject of the following patches.  In
the end, it'll all "Just Work".

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* ax-gdb.c: Include "typeprint.h".
	(gen_expr_for_cast): New function.
	(gen_expr) <OP_CAST, OP_CAST_TYPE>: Use it.
	<OP_VAR_VALUE, OP_MSYM_VAR_VALUE>: Error out if the variable's
	type is unknown.
	* dwarf2read.c (new_symbol_full): Fallback to int instead of
	nodebug_data_symbol.
	* eval.c: Include "typeprint.h".
	(evaluate_subexp_standard) <OP_VAR_VALUE, OP_VAR_MSYM_VALUE>:
	Error out if symbol has unknown type.
	<UNOP_CAST, UNOP_CAST_TYPE>: Common bits factored out to
	evaluate_subexp_for_cast.
	(evaluate_subexp_for_address, evaluate_subexp_for_sizeof): Handle
	OP_VAR_MSYM_VALUE.
	(evaluate_subexp_for_cast): New function.
	* gdbtypes.c (init_nodebug_var_type): New function.
	(objfile_type): Use it to initialize types of variables with no
	debug info.
	* typeprint.c (error_unknown_type): New.
	* typeprint.h (error_unknown_type): New declaration.
	* compile/compile-c-types.c (convert_type_basic): Handle
	TYPE_CODE_ERROR; warn and fallback to int for variables with
	unknown type.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.asm/asm-source.exp: Add casts to int.
	* gdb.base/nodebug.c (dataglobal8, dataglobal32_1, dataglobal32_2)
	(dataglobal64_1, dataglobal64_2): New globals.
	* gdb.base/nodebug.exp: Test different expressions involving the
	new globals, with print, whatis and ptype.  Add casts to int.
	* gdb.base/solib-display.exp: Add casts to int.
	* gdb.compile/compile-ifunc.exp: Expect warning.  Add cast to int.
	* gdb.cp/m-static.exp: Add cast to int.
	* gdb.dwarf2/dw2-skip-prologue.exp: Add cast to int.
	* gdb.threads/tls-nodebug.exp: Check that gdb errors out printing
	tls variable with no debug info without a cast.  Test with a cast
	to int too.
	* gdb.trace/entry-values.exp: Add casts.
---
 gdb/ax-gdb.c                                   |  50 +++++++--
 gdb/compile/compile-c-types.c                  |  15 +++
 gdb/dwarf2read.c                               |   3 +-
 gdb/eval.c                                     | 137 +++++++++++++++++++++----
 gdb/gdbtypes.c                                 |  19 ++--
 gdb/testsuite/gdb.asm/asm-source.exp           |   8 +-
 gdb/testsuite/gdb.base/nodebug.c               |   7 ++
 gdb/testsuite/gdb.base/nodebug.exp             |  78 +++++++++++---
 gdb/testsuite/gdb.base/solib-display.exp       |  16 +--
 gdb/testsuite/gdb.compile/compile-ifunc.exp    |   5 +-
 gdb/testsuite/gdb.cp/m-static.exp              |   4 +-
 gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp |   2 +-
 gdb/testsuite/gdb.threads/tls-nodebug.exp      |   5 +-
 gdb/testsuite/gdb.trace/entry-values.exp       |   6 +-
 gdb/typeprint.c                                |   9 ++
 gdb/typeprint.h                                |   5 +
 16 files changed, 301 insertions(+), 68 deletions(-)

diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c
index 70e7980..e843983 100644
--- a/gdb/ax-gdb.c
+++ b/gdb/ax-gdb.c
@@ -42,7 +42,7 @@
 #include "linespec.h"
 #include "location.h"
 #include "objfiles.h"
-
+#include "typeprint.h"
 #include "valprint.h"
 #include "c-lang.h"
 
@@ -1788,6 +1788,40 @@ gen_sizeof (struct expression *exp, union exp_element **pc,
 }
 \f
 
+/* Generate bytecode for a cast to TO_TYPE.  Advance *PC over the
+   subexpression.  */
+
+static void
+gen_expr_for_cast (struct expression *exp, union exp_element **pc,
+		   struct agent_expr *ax, struct axs_value *value,
+		   struct type *to_type)
+{
+  enum exp_opcode op = (*pc)[0].opcode;
+
+  /* Don't let symbols be handled with gen_expr because that throws an
+     "unknown type" error for no-debug data symbols.  Instead, we want
+     the cast to reinterpret such symbols.  */
+  if (op == OP_VAR_MSYM_VALUE || op == OP_VAR_VALUE)
+    {
+      if (op == OP_VAR_VALUE)
+	{
+	  gen_var_ref (exp->gdbarch, ax, value, (*pc)[2].symbol);
+
+	  if (value->optimized_out)
+	    error (_("`%s' has been optimized out, cannot use"),
+		   SYMBOL_PRINT_NAME ((*pc)[2].symbol));
+	}
+      else
+	gen_msym_var_ref (ax, value, (*pc)[2].msymbol, (*pc)[1].objfile);
+      if (TYPE_CODE (value->type) == TYPE_CODE_ERROR)
+	value->type = to_type;
+      (*pc) += 4;
+    }
+  else
+    gen_expr (exp, pc, ax, value);
+  gen_cast (ax, value, to_type);
+}
+
 /* Generating bytecode from GDB expressions: general recursive thingy  */
 
 /* XXX: i18n */
@@ -2001,11 +2035,18 @@ gen_expr (struct expression *exp, union exp_element **pc,
 	error (_("`%s' has been optimized out, cannot use"),
 	       SYMBOL_PRINT_NAME ((*pc)[2].symbol));
 
+      if (TYPE_CODE (value->type) == TYPE_CODE_ERROR)
+	error_unknown_type (SYMBOL_PRINT_NAME ((*pc)[2].symbol));
+
       (*pc) += 4;
       break;
 
     case OP_VAR_MSYM_VALUE:
       gen_msym_var_ref (ax, value, (*pc)[2].msymbol, (*pc)[1].objfile);
+
+      if (TYPE_CODE (value->type) == TYPE_CODE_ERROR)
+	error_unknown_type (MSYMBOL_PRINT_NAME ((*pc)[2].msymbol));
+
       (*pc) += 4;
       break;
 
@@ -2066,8 +2107,7 @@ gen_expr (struct expression *exp, union exp_element **pc,
 	struct type *type = (*pc)[1].type;
 
 	(*pc) += 3;
-	gen_expr (exp, pc, ax, value);
-	gen_cast (ax, value, type);
+	gen_expr_for_cast (exp, pc, ax, value, type);
       }
       break;
 
@@ -2082,9 +2122,7 @@ gen_expr (struct expression *exp, union exp_element **pc,
 	val = evaluate_subexp (NULL, exp, &offset, EVAL_AVOID_SIDE_EFFECTS);
 	type = value_type (val);
 	*pc = &exp->elts[offset];
-
-	gen_expr (exp, pc, ax, value);
-	gen_cast (ax, value, type);
+	gen_expr_for_cast (exp, pc, ax, value, type);
       }
       break;
 
diff --git a/gdb/compile/compile-c-types.c b/gdb/compile/compile-c-types.c
index 7fbab13..ce1b487 100644
--- a/gdb/compile/compile-c-types.c
+++ b/gdb/compile/compile-c-types.c
@@ -384,6 +384,21 @@ convert_type_basic (struct compile_c_instance *context, struct type *type)
 
     case TYPE_CODE_COMPLEX:
       return convert_complex (context, type);
+
+    case TYPE_CODE_ERROR:
+      {
+	/* Ideally, if we get here due to a cast expression, we'd use
+	   the cast-to type as the variable's type, like GDB's
+	   built-in parser does.  For now, assume "int" like GDB's
+	   built-in parser used to do, but at least warn.  */
+	struct type *fallback;
+	if (TYPE_OBJFILE_OWNED (type))
+	  fallback = objfile_type (TYPE_OWNER (type).objfile)->builtin_int;
+	else
+	  fallback = builtin_type (TYPE_OWNER (type).gdbarch)->builtin_int;
+	warning (_("variable has unknown type; assuming int"));
+	return convert_int (context, fallback);
+      }
     }
 
   return C_CTX (context)->c_ops->error (C_CTX (context),
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 2f70bd2..6845fd7 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -19017,8 +19017,7 @@ new_symbol_full (struct die_info *die, struct type *type, struct dwarf2_cu *cu,
 	     variables with missing type entries.  Change the
 	     misleading `void' type to something sensible.  */
 	  if (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_VOID)
-	    SYMBOL_TYPE (sym)
-	      = objfile_type (objfile)->nodebug_data_symbol;
+	    SYMBOL_TYPE (sym) = objfile_type (objfile)->builtin_int;
 
 	  attr = dwarf2_attr (die, DW_AT_const_value, cu);
 	  /* In the case of DW_TAG_member, we should only be called for
diff --git a/gdb/eval.c b/gdb/eval.c
index a97f4a9..75d3c86 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -39,6 +39,7 @@
 #include "valprint.h"
 #include "gdb_obstack.h"
 #include "objfiles.h"
+#include "typeprint.h"
 #include <ctype.h>
 
 /* This is defined in valops.c */
@@ -52,6 +53,10 @@ static struct value *evaluate_subexp_for_sizeof (struct expression *, int *,
 static struct value *evaluate_subexp_for_address (struct expression *,
 						  int *, enum noside);
 
+static value *evaluate_subexp_for_cast (expression *exp, int *pos,
+					enum noside noside,
+					struct type *type);
+
 static struct value *evaluate_struct_tuple (struct value *,
 					    struct expression *, int *,
 					    enum noside, int);
@@ -796,14 +801,30 @@ evaluate_subexp_standard (struct type *expect_type,
       (*pos) += 3;
       if (noside == EVAL_SKIP)
 	return eval_skip_value (exp);
-      return evaluate_var_value (noside,
-				 exp->elts[pc + 1].block,
-				 exp->elts[pc + 2].symbol);
+
+      {
+	symbol *var = exp->elts[pc + 2].symbol;
+	if (TYPE_CODE (SYMBOL_TYPE (var)) == TYPE_CODE_ERROR)
+	  error_unknown_type (SYMBOL_PRINT_NAME (var));
+
+	return evaluate_var_value (noside, exp->elts[pc + 1].block, var);
+      }
+
     case OP_VAR_MSYM_VALUE:
-      (*pos) += 3;
-      return evaluate_var_msym_value (noside,
-				      exp->elts[pc + 1].objfile,
-				      exp->elts[pc + 2].msymbol);
+      {
+	(*pos) += 3;
+
+	minimal_symbol *msymbol = exp->elts[pc + 2].msymbol;
+	value *val = evaluate_var_msym_value (noside,
+					      exp->elts[pc + 1].objfile,
+					      msymbol);
+
+	type = value_type (val);
+	if (TYPE_CODE (type) == TYPE_CODE_ERROR
+	    && (noside != EVAL_AVOID_SIDE_EFFECTS || pc != 0))
+	  error_unknown_type (MSYMBOL_PRINT_NAME (msymbol));
+	return val;
+      }
 
     case OP_VAR_ENTRY_VALUE:
       (*pos) += 2;
@@ -2589,22 +2610,12 @@ evaluate_subexp_standard (struct type *expect_type,
     case UNOP_CAST:
       (*pos) += 2;
       type = exp->elts[pc + 1].type;
-      arg1 = evaluate_subexp (type, exp, pos, noside);
-      if (noside == EVAL_SKIP)
-	return eval_skip_value (exp);
-      if (type != value_type (arg1))
-	arg1 = value_cast (type, arg1);
-      return arg1;
+      return evaluate_subexp_for_cast (exp, pos, noside, type);
 
     case UNOP_CAST_TYPE:
       arg1 = evaluate_subexp (NULL, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
       type = value_type (arg1);
-      arg1 = evaluate_subexp (type, exp, pos, noside);
-      if (noside == EVAL_SKIP)
-	return eval_skip_value (exp);
-      if (type != value_type (arg1))
-	arg1 = value_cast (type, arg1);
-      return arg1;
+      return evaluate_subexp_for_cast (exp, pos, noside, type);
 
     case UNOP_DYNAMIC_CAST:
       arg1 = evaluate_subexp (NULL, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
@@ -2927,6 +2938,22 @@ evaluate_subexp_for_address (struct expression *exp, int *pos,
       else
 	return address_of_variable (var, exp->elts[pc + 1].block);
 
+    case OP_VAR_MSYM_VALUE:
+      {
+	(*pos) += 4;
+
+	value *val = evaluate_var_msym_value (noside,
+					      exp->elts[pc + 1].objfile,
+					      exp->elts[pc + 2].msymbol);
+	if (noside == EVAL_AVOID_SIDE_EFFECTS)
+	  {
+	    struct type *type = lookup_pointer_type (value_type (val));
+	    return value_zero (type, not_lval);
+	  }
+	else
+	  return value_addr (val);
+      }
+
     case OP_SCOPE:
       tem = longest_to_int (exp->elts[pc + 2].longconst);
       (*pos) += 5 + BYTES_TO_EXP_ELEM (tem + 1);
@@ -3065,6 +3092,23 @@ evaluate_subexp_for_sizeof (struct expression *exp, int *pos,
 	(*pos) += 4;
       break;
 
+    case OP_VAR_MSYM_VALUE:
+      {
+	(*pos) += 4;
+
+	minimal_symbol *msymbol = exp->elts[pc + 2].msymbol;
+	value *val = evaluate_var_msym_value (noside,
+					      exp->elts[pc + 1].objfile,
+					      msymbol);
+
+	type = value_type (val);
+	if (TYPE_CODE (type) == TYPE_CODE_ERROR)
+	  error_unknown_type (MSYMBOL_PRINT_NAME (msymbol));
+
+	return value_from_longest (size_type, TYPE_LENGTH (type));
+      }
+      break;
+
       /* Deal with the special case if NOSIDE is EVAL_NORMAL and the resulting
 	 type of the subscript is a variable length array type. In this case we
 	 must re-evaluate the right hand side of the subcription to allow
@@ -3112,6 +3156,61 @@ evaluate_subexp_for_sizeof (struct expression *exp, int *pos,
   return value_from_longest (size_type, (LONGEST) TYPE_LENGTH (type));
 }
 
+/* Evaluate a subexpression of EXP, at index *POS, and return a value
+   for that subexpression cast to TO_TYPE.  Advance *POS over the
+   subexpression.  */
+
+static value *
+evaluate_subexp_for_cast (expression *exp, int *pos,
+			  enum noside noside,
+			  struct type *to_type)
+{
+  int pc = *pos;
+
+  /* Don't let symbols be evaluated with evaluate_subexp because that
+     throws an "unknown type" error for no-debug data symbols.
+     Instead, we want the cast to reinterpret the symbol.  */
+  if (exp->elts[pc].opcode == OP_VAR_MSYM_VALUE
+      || exp->elts[pc].opcode == OP_VAR_VALUE)
+    {
+      (*pos) += 4;
+
+      value *val;
+      if (exp->elts[pc].opcode == OP_VAR_MSYM_VALUE)
+	{
+	  if (noside == EVAL_AVOID_SIDE_EFFECTS)
+	    return value_zero (to_type, not_lval);
+
+	  val = evaluate_var_msym_value (noside,
+					 exp->elts[pc + 1].objfile,
+					 exp->elts[pc + 2].msymbol);
+	}
+      else
+	val = evaluate_var_value (noside,
+				  exp->elts[pc + 1].block,
+				  exp->elts[pc + 2].symbol);
+
+      if (noside == EVAL_SKIP)
+	return eval_skip_value (exp);
+
+      val = value_cast (to_type, val);
+
+      /* Don't allow e.g. '&(int)var_with_no_debug_info'.  */
+      if (VALUE_LVAL (val) == lval_memory)
+	{
+	  if (value_lazy (val))
+	    value_fetch_lazy (val);
+	  VALUE_LVAL (val) = not_lval;
+	}
+      return val;
+    }
+
+  value *val = evaluate_subexp (to_type, exp, pos, noside);
+  if (noside == EVAL_SKIP)
+    return eval_skip_value (exp);
+  return value_cast (to_type, val);
+}
+
 /* Parse a type expression in the string [P..P+LENGTH).  */
 
 struct type *
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index a22f0ca..fc86225 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -2770,6 +2770,16 @@ init_type (struct objfile *objfile, enum type_code code, int length,
   return type;
 }
 
+/* Allocate a TYPE_CODE_ERROR type structure associated with OBJFILE,
+   to use with variables that have no debug info.  NAME is the type
+   name.  */
+
+static struct type *
+init_nodebug_var_type (struct objfile *objfile, const char *name)
+{
+  return init_type (objfile, TYPE_CODE_ERROR, 0, name);
+}
+
 /* Allocate a TYPE_CODE_INT type structure associated with OBJFILE.
    BIT is the type size in bits.  If UNSIGNED_P is non-zero, set
    the type's TYPE_UNSIGNED flag.  NAME is the type name.  */
@@ -5325,14 +5335,11 @@ objfile_type (struct objfile *objfile)
 			 "<text from jump slot in .got.plt, no debug info>",
 			 objfile_type->nodebug_text_symbol);
   objfile_type->nodebug_data_symbol
-    = init_integer_type (objfile, gdbarch_int_bit (gdbarch), 0,
-			 "<data variable, no debug info>");
+    = init_nodebug_var_type (objfile, "<data variable, no debug info>");
   objfile_type->nodebug_unknown_symbol
-    = init_integer_type (objfile, TARGET_CHAR_BIT, 0,
-			 "<variable (not text or data), no debug info>");
+    = init_nodebug_var_type (objfile, "<variable (not text or data), no debug info>");
   objfile_type->nodebug_tls_symbol
-    = init_integer_type (objfile, gdbarch_int_bit (gdbarch), 0,
-			 "<thread local variable, no debug info>");
+    = init_nodebug_var_type (objfile, "<thread local variable, no debug info>");
 
   /* NOTE: on some targets, addresses and pointers are not necessarily
      the same.
diff --git a/gdb/testsuite/gdb.asm/asm-source.exp b/gdb/testsuite/gdb.asm/asm-source.exp
index e07e554..138609a 100644
--- a/gdb/testsuite/gdb.asm/asm-source.exp
+++ b/gdb/testsuite/gdb.asm/asm-source.exp
@@ -465,14 +465,14 @@ proc test_dis { command var } {
 }
 
 # See if we can look at a global variable, three ways
-gdb_test "print globalvar" ".* = 11" "look at global variable"
+gdb_test "print (int) globalvar" ".* = 11" "look at global variable"
 test_dis "x/i &globalvar" "globalvar"
-test_dis "disassem &globalvar, &globalvar+1" "globalvar"
+test_dis "disassem &globalvar, (int *) &globalvar+1" "globalvar"
 
 # See if we can look at a static variable, three ways
-gdb_test "print staticvar" ".* = 5" "look at static variable"
+gdb_test "print (int) staticvar" ".* = 5" "look at static variable"
 test_dis "x/i &staticvar" "staticvar"
-test_dis "disassem &staticvar, &staticvar+1" "staticvar"
+test_dis "disassem &staticvar, (int *) &staticvar+1" "staticvar"
 
 # See if we can look at a static function
 gdb_test "disassem foostatic" ".*<\\+0>:.*End of assembler dump." \
diff --git a/gdb/testsuite/gdb.base/nodebug.c b/gdb/testsuite/gdb.base/nodebug.c
index 99641e8..c7bc939 100644
--- a/gdb/testsuite/gdb.base/nodebug.c
+++ b/gdb/testsuite/gdb.base/nodebug.c
@@ -8,6 +8,13 @@ static int datalocal = 4;		/* Should go in local data */
 int bssglobal;				/* Should go in global bss */
 static int bsslocal;			/* Should go in local bss */
 
+/* Non-int-sized global data variables.  */
+uint8_t dataglobal8 = 0xff;
+uint32_t dataglobal32_1 = 0x7fffffff;
+uint32_t dataglobal32_2 = 0x000000ff;
+uint64_t dataglobal64_1 = 0x7fffffffffffffff;
+uint64_t dataglobal64_2 = 0x00000000000000ff;
+
 int
 inner (int x)
 {
diff --git a/gdb/testsuite/gdb.base/nodebug.exp b/gdb/testsuite/gdb.base/nodebug.exp
index da704f1..2099456 100644
--- a/gdb/testsuite/gdb.base/nodebug.exp
+++ b/gdb/testsuite/gdb.base/nodebug.exp
@@ -152,11 +152,57 @@ if [runto inner] then {
     # we may or may not have debug info for those (depending on
     # whether we have debug info for the C runtime, for example).
     gdb_test_no_output "macro define uint8 unsigned char"
-
-    gdb_test "p dataglobal" "= 3"
-    gdb_test "whatis dataglobal" \
-	"<(data variable|variable), no debug info>|int"
-    gdb_test "ptype dataglobal" "<(data variable|variable), no debug info>|int"
+    gdb_test_no_output "macro define uint32 unsigned int"
+    gdb_test_no_output "macro define uint64 unsigned long long"
+
+    set data_var_type "<data variable, no debug info>"
+    set unk_type_re "has unknown type.*to its declared type"
+    set ptr_math_re "Cannot perform pointer math on incomplete type \"$data_var_type\", try casting to a known type, or void \\*\\."
+    set not_mem_re "Attempt to take address of value not located in memory\\."
+
+    set dataglobal_unk_re "dataglobal.*$unk_type_re"
+
+	 #exp				#fmt #print						#ptype/whatis
+    foreach line {
+	{"dataglobal"			""   $dataglobal_unk_re					" = $data_var_type"}
+	{"(int) dataglobal"		""   "= 3"						" = int"}
+	{"sizeof(dataglobal)"		""   $dataglobal_unk_re					$dataglobal_unk_re}
+	{"sizeof(dataglobal + 1)"	""   $dataglobal_unk_re					$dataglobal_unk_re}
+	{"sizeof((int) dataglobal)"	""   " = $decimal"					" = int"}
+	{"dataglobal + 1"		""   $dataglobal_unk_re					$dataglobal_unk_re}
+	{"&dataglobal"			""   "\\($data_var_type \\*\\) $hex <dataglobal>"	" = $data_var_type \\*"}
+	{"&dataglobal + 1"		""   $ptr_math_re					$ptr_math_re}
+	{"(int *) &dataglobal + 1"	""   " = \\(int \\*\\) $hex <datalocal>"		"int \\*"}
+	{"&(int) dataglobal + 1"	""   $not_mem_re					$not_mem_re}
+	{"&dataglobal, &dataglobal"	""   "\\($data_var_type \\*\\) $hex <dataglobal>"	" = $data_var_type \\*"}
+	{"*dataglobal"			""   $dataglobal_unk_re					$dataglobal_unk_re}
+
+	{"dataglobal8"			"/x" $dataglobal_unk_re					" = $data_var_type"}
+	{"(uint8) dataglobal8"		"/x" " = 0xff"						"unsigned char"}
+
+	{"dataglobal32_1"		"/x" $dataglobal_unk_re					" = $data_var_type"}
+	{"(uint32) dataglobal32_1"	"/x" " = 0x7fffffff"					"unsigned int"}
+	{"dataglobal32_2"		"/x" $dataglobal_unk_re					" = $data_var_type"}
+	{"(uint32) dataglobal32_2"	"/x" " = 0xff"						"unsigned int"}
+
+	{"dataglobal64_1"		"/x" $dataglobal_unk_re					" = $data_var_type"}
+	{"(uint64) dataglobal64_1"	"/x" " = 0x7fffffffffffffff"				"unsigned long long"}
+	{"dataglobal64_2"		"/x" $dataglobal_unk_re					" = $data_var_type"}
+	{"(uint64) dataglobal64_2"	"/x" " = 0xff"						"unsigned long long"}
+    } {
+	set exp [lindex $line 0]
+	# Expand variables.
+	set fmt [subst -nobackslashes [lindex $line 1]]
+	set print [subst  -nobackslashes [lindex $line 2]]
+	set whatis [subst -nobackslashes [lindex $line 3]]
+	if {$fmt == ""} {
+	    gdb_test "p $exp" $print
+	} else {
+	    gdb_test "p $fmt $exp" $print
+	}
+	gdb_test "whatis $exp" $whatis
+	gdb_test "ptype $exp" $whatis
+    }
     
     # The only symbol xcoff puts out for statics is for the TOC entry.
     # Possible, but hairy, for gdb to deal.  Right now it doesn't, it
@@ -164,35 +210,39 @@ if [runto inner] then {
     setup_xfail "rs6000*-*-aix*"
     setup_xfail "powerpc*-*-aix*"
 
-    gdb_test "p datalocal" "= 4"
+    gdb_test "p datalocal" "datalocal.*$unk_type_re"
+    gdb_test "p (int) datalocal" "= 4"
 
     setup_xfail "rs6000*-*-aix*"
     setup_xfail "powerpc*-*-aix*"
 
-    gdb_test "whatis datalocal" "<(data variable|variable), no debug info>"
+    gdb_test "whatis datalocal" "datalocal.*$data_var_type"
 
     setup_xfail "rs6000*-*-aix*"
     setup_xfail "powerpc*-*-aix*"
 
-    gdb_test "ptype datalocal" "<(data variable|variable), no debug info>"
-    gdb_test "p bssglobal" "= 0"
-    gdb_test "whatis bssglobal" "<(data variable|variable), no debug info>|int"
-    gdb_test "ptype bssglobal" "<(data variable|variable), no debug info>|int"
+    gdb_test "ptype datalocal" "datalocal.*$data_var_type"
+
+    gdb_test "p bssglobal" "bssglobal.*$unk_type_re"
+    gdb_test "p (int) bssglobal" "= 0"
+    gdb_test "whatis bssglobal" $data_var_type
+    gdb_test "ptype bssglobal" $data_var_type
 
     setup_xfail "rs6000*-*-aix*"
     setup_xfail "powerpc*-*-aix*"
 
-    gdb_test "p bsslocal" "= 0"
+    gdb_test "p bsslocal" "bsslocal.*$unk_type_re"
+    gdb_test "p (int) bsslocal" "= 0"
 
     setup_xfail "rs6000*-*-aix*"
     setup_xfail "powerpc*-*-aix*"
 
-    gdb_test "whatis bsslocal" "<(data variable|variable), no debug info>"
+    gdb_test "whatis bsslocal" $data_var_type
 
     setup_xfail "rs6000*-*-aix*"
     setup_xfail "powerpc*-*-aix*"
 
-    gdb_test "ptype bsslocal" "<(data variable|variable), no debug info>"
+    gdb_test "ptype bsslocal" $data_var_type
 
     gdb_test "backtrace 10" "#0.*inner.*#1.*middle.*#2.*top.*#3.*main.*" \
 	"backtrace from inner in nodebug.exp"
diff --git a/gdb/testsuite/gdb.base/solib-display.exp b/gdb/testsuite/gdb.base/solib-display.exp
index 1e26853..6df9f26 100644
--- a/gdb/testsuite/gdb.base/solib-display.exp
+++ b/gdb/testsuite/gdb.base/solib-display.exp
@@ -94,9 +94,9 @@ foreach libsepdebug {NO IN SEP} { with_test_prefix "$libsepdebug" {
       return 0
     }
 
-    gdb_test "display a_global" "1: a_global = 41"
-    gdb_test "display b_global" "2: b_global = 42"
-    gdb_test "display c_global" "3: c_global = 43"
+    gdb_test "display (int) a_global" "1: \\(int\\) a_global = 41"
+    gdb_test "display (int) b_global" "2: \\(int\\) b_global = 42"
+    gdb_test "display (int) c_global" "3: \\(int\\) c_global = 43"
 
     if { [gdb_start_cmd] < 0 } {
 	fail "can't run to main (2)"
@@ -104,9 +104,9 @@ foreach libsepdebug {NO IN SEP} { with_test_prefix "$libsepdebug" {
     }
 
     gdb_test "" [multi_line \
-		     "1: a_global = 41" \
-		     "2: b_global = 42"  \
-		     "3: c_global = 43" \
+		     "1: \\(int\\) a_global = 41" \
+		     "2: \\(int\\) b_global = 42"  \
+		     "3: \\(int\\) c_global = 43" \
 		    ] "after rerun"
 
     # Now rebuild the library without b_global
@@ -132,9 +132,9 @@ foreach libsepdebug {NO IN SEP} { with_test_prefix "$libsepdebug" {
 
 
     gdb_test "" [multi_line \
-		     "1: a_global = 41" \
+		     "1: \\(int\\) a_global = 41" \
 		     "warning: .*b_global.*"  \
-		     "3: c_global = 43" \
+		     "3: \\(int\\) c_global = 43" \
 		    ] "after rerun (2)"
 
     # Now verify that displays which are not in the shared library
diff --git a/gdb/testsuite/gdb.compile/compile-ifunc.exp b/gdb/testsuite/gdb.compile/compile-ifunc.exp
index dc442ff..b591939 100644
--- a/gdb/testsuite/gdb.compile/compile-ifunc.exp
+++ b/gdb/testsuite/gdb.compile/compile-ifunc.exp
@@ -36,9 +36,10 @@ with_test_prefix "nodebug" {
 	return -1
     }
 
-    gdb_test_no_output "compile code resultvar = gnu_ifunc (10);"
+    gdb_test "compile code resultvar = gnu_ifunc (10);" \
+	"warning: variable has unknown type; assuming int"
 
-    gdb_test "p resultvar" " = 11"
+    gdb_test "p (int) resultvar" " = 11"
 
 }
 
diff --git a/gdb/testsuite/gdb.cp/m-static.exp b/gdb/testsuite/gdb.cp/m-static.exp
index 52eea1a..eeb88e9 100644
--- a/gdb/testsuite/gdb.cp/m-static.exp
+++ b/gdb/testsuite/gdb.cp/m-static.exp
@@ -54,8 +54,8 @@ gdb_continue_to_breakpoint "end of constructors"
 
 # simple object, static const int, accessing via 'class::method::variable'
 # Regression test for PR c++/15203 and PR c++/15210
-gdb_test "print 'gnu_obj_1::method()::sintvar'" "\\$\[0-9\]+ = 4" \
-    "simple object, static const int, accessing via 'class::method::variable"
+gdb_test "print (int) 'gnu_obj_1::method()::sintvar'" "\\$\[0-9\]+ = 4" \
+    "simple object, static int, accessing via 'class::method::variable'"
 
 # simple object, static const bool
 gdb_test "print test1.test" "\\$\[0-9\]* = true" "simple object, static const bool"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp b/gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp
index 458809b..6338e6d 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp
@@ -79,4 +79,4 @@ gdb_continue_to_breakpoint "func"
 # Sanity check GDB has really found 2 locations
 gdb_test {info break $bpnum} "\r\n2\\.1\[ \t\]\[^\n\]*\r\n2\\.2\[ \t\]\[^\n\]*" "2 locations found"
 
-gdb_test "p v" " = 0" "no statement got executed"
+gdb_test "p (int) v" " = 0" "no statement got executed"
diff --git a/gdb/testsuite/gdb.threads/tls-nodebug.exp b/gdb/testsuite/gdb.threads/tls-nodebug.exp
index 8dc9444..e718736 100644
--- a/gdb/testsuite/gdb.threads/tls-nodebug.exp
+++ b/gdb/testsuite/gdb.threads/tls-nodebug.exp
@@ -33,7 +33,10 @@ if ![runto_main] then {
 }
 
 # Formerly: Cannot access memory at address 0x0
-gdb_test "p thread_local" "= 42" "thread local storage"
+gdb_test "p thread_local" "'thread_local' has unknown type; cast it to its declared type" \
+    "thread local storage, unknown type"
+gdb_test "p (int) thread_local" "= 42" \
+    "thread local storage, cast"
 
 # Done!
 #
diff --git a/gdb/testsuite/gdb.trace/entry-values.exp b/gdb/testsuite/gdb.trace/entry-values.exp
index b2650c6..baf9371 100644
--- a/gdb/testsuite/gdb.trace/entry-values.exp
+++ b/gdb/testsuite/gdb.trace/entry-values.exp
@@ -194,8 +194,8 @@ gdb_test_sequence "bt" "bt (1)" {
 # Update global variables 'global1' and 'global2' and test that the
 # entry values are updated too.
 
-gdb_test_no_output "set var global1=10"
-gdb_test_no_output "set var global2=11"
+gdb_test_no_output "set var *(int *) &global1=10"
+gdb_test_no_output "set var *(int *) &global2=11"
 
 gdb_test_sequence "bt" "bt (2)" {
     "\[\r\n\]#0 .* foo \\(i=[-]?[0-9]+, i@entry=10, j=[-]?[0-9]+, j@entry=11\\)"
@@ -226,7 +226,7 @@ gdb_test "trace foo" "Tracepoint $decimal at .*"
 # argument j.
 
 gdb_trace_setactions "set action for tracepoint 1" "" \
-    "collect i, j, global1, \(\*\(void \*\*\) \(\$$spreg\)\) @ 128" "^$"
+    "collect i, j, (int) global1, \(\*\(void \*\*\) \(\$$spreg\)\) @ 128" "^$"
 
 gdb_test_no_output "tstart"
 
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index 8e58bf9..0cc6821 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -394,6 +394,15 @@ type_print_unknown_return_type (struct ui_file *stream)
   fprintf_filtered (stream, _("<unknown return type>"));
 }
 
+/* See typeprint.h.  */
+
+void
+error_unknown_type (const char *sym_print_name)
+{
+  error (_("'%s' has unknown type; cast it to its declared type"),
+	 sym_print_name);
+}
+
 /* Print type of EXP, or last thing in value history if EXP == NULL.
    show is passed to type_print.  */
 
diff --git a/gdb/typeprint.h b/gdb/typeprint.h
index d4e19e5b..a458aa4 100644
--- a/gdb/typeprint.h
+++ b/gdb/typeprint.h
@@ -78,6 +78,11 @@ void c_type_print_args (struct type *, struct ui_file *, int, enum language,
 
 void type_print_unknown_return_type (struct ui_file *stream);
 
+/* Throw an error indicating that the user tried to use a symbol that
+   has unknown type.  SYM_PRINT_NAME is the name of the symbol, to be
+   included in the error message.  */
+extern void error_unknown_type (const char *sym_print_name);
+
 extern void val_print_not_allocated (struct ui_file *stream);
 
 extern void val_print_not_associated (struct ui_file *stream);
-- 
2.5.5

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

* [PATCH v2 13/13] Document "no debug info debugging" improvements
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
                   ` (8 preceding siblings ...)
  2017-07-13 15:37 ` [PATCH v2 12/13] Make "p S::method() const::static_var" work too Pedro Alves
@ 2017-07-13 15:38 ` Pedro Alves
  2017-07-13 16:11   ` Eli Zaretskii
  2017-07-13 15:38 ` [PATCH v2 07/13] evaluate_subexp_standard: Factor out OP_VAR_VALUE handling Pedro Alves
                   ` (3 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:38 UTC (permalink / raw)
  To: gdb-patches

New in v2: updated/rewritten to describe that the simpler cast syntax
assumes the function is prototyped.

Here's the documentation bits for all the improvements.

Note that the original "weak alias functions" paragraph ends up
disappearing, because this patch, which I'm considering kind of part
of this series, makes the alias case Just Work:
  https://sourceware.org/ml/gdb-patches/2017-07/msg00018.html

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* NEWS (Safer support for debugging with no debug info): New.

gdb/doc/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.texinfo (Variables) <Program Variables>: Document inspecting
	no-debug-info variables.
	(Symbols) <Examining the Symbol Table>: Document inspecting
	no-debug-info types.
	(Calling) <Calling functions with no debug info>: New subsection,
	documenting calling no-debug-info functions.
	(Non-debug DLL Symbols) <Working with Minimal Symbols>: Update.
---
 gdb/doc/gdb.texinfo | 114 +++++++++++++++++++++++++++++++++++++++++++++++-----
 gdb/NEWS            |  26 ++++++++++++
 2 files changed, 130 insertions(+), 10 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index c167a86..e72ecb3 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -9114,6 +9114,22 @@ If you ask to print an object whose contents are unknown to
 by the debug information, @value{GDBN} will say @samp{<incomplete
 type>}.  @xref{Symbols, incomplete type}, for more about this.
 
+@cindex no debug info variables
+If you try to examine or use the value of a (global) variable for
+which @value{GDBN} has no type information, e.g., because the program
+includes no debug information, @value{GDBN} says @w{@samp{'var' has
+unknown type; cast it to its declared type}}.  @xref{Symbols, unknown
+type}, for more about unknown types.  If you cast the variable to its
+declared type, @value{GDBN} gets the variable's value using the
+cast-to type as the variable's type.  For example, in a C program:
+
+@smallexample
+  (@value{GDBP}) p var
+  'var' has unknown type; cast it to its declared type
+  (@value{GDBP}) p (float) var
+  $1 = 3.14
+@end smallexample
+
 If you append @kbd{@@entry} string to a function parameter name you get its
 value at the time the function got called.  If the value is not available an
 error message is printed.  Entry values are available only with some compilers.
@@ -17083,6 +17099,25 @@ but no definition for @code{struct foo} itself, @value{GDBN} will say:
 ``Incomplete type'' is C terminology for data types that are not
 completely specified.
 
+@cindex unknown type
+Othertimes, information about a variable's type is completely absent
+from the debug information included in the program.  This most often
+happens when the program or library where the variable is defined
+includes no debug information at all.  @value{GDBN} knows the variable
+exists from inspecting the linker/loader symbol table (e.g., the ELF
+dynamic symbol table), but such symbols do not contain type
+information.  If you try to inspect the type of a (global) variable
+for which @value{GDBN} has no type information, @value{GDBN} says
+@w{@samp{<data variable, no debug info>}}.  For example:
+
+@smallexample
+  (@value{GDBP}) ptype var
+  type = <data variable, no debug info>
+@end smallexample
+
+@xref{Variables, no debug info variables}, for how to print the values
+of such variables.
+
 @kindex info types
 @item info types @var{regexp}
 @itemx info types
@@ -17798,14 +17833,73 @@ Show the current setting of stack unwinding in the functions called by
 
 @end table
 
-@cindex weak alias functions
-Sometimes, a function you wish to call is actually a @dfn{weak alias}
-for another function.  In such case, @value{GDBN} might not pick up
-the type information, including the types of the function arguments,
-which causes @value{GDBN} to call the inferior function incorrectly.
-As a result, the called function will function erroneously and may
-even crash.  A solution to that is to use the name of the aliased
-function instead.
+@subsection Calling functions with no debug info
+
+@cindex no debug info functions
+Sometimes, a function you wish to call is missing debug information.
+In such case, @value{GDBN} does not know the type of the function,
+including the types of the function's parameters.  To avoid calling
+the inferior function incorrectly, which could result in the called
+function functioning erroneously and even crash, @value{GDBN} refuses
+to call the function unless you tell it the type of the function.
+
+For prototyped (i.e.@: ANSI/ISO style) functions, there are two ways
+to do that.  The simplest is to cast the call to the function's
+declared return type.  For example:
+
+@smallexample
+(@value{GDBP}) p getenv ("PATH")
+'getenv' has unknown return type; cast the call to its declared return type
+(@value{GDBP}) p (char *) getenv ("PATH")
+$1 = 0x7fffffffe7ba "/usr/local/bin:/"...
+@end smallexample
+
+Casting the return type of a no-debug function is equivalent to
+casting the function to a pointer to a prototyped function that has a
+prototype that matches the types of the passed-in arguments, and
+calling that.  I.e., the call above is equivalent to:
+
+@smallexample
+(@value{GDBP}) p ((char * (*) (const char *)) getenv) ("PATH")
+@end smallexample
+
+@noindent
+and given this prototyped C or C++ function with float parameters:
+
+@smallexample
+float multiply (float v1, float v2) @{ return v1 * v2; @}
+@end smallexample
+
+@noindent
+these calls are equivalent:
+
+@smallexample
+(@value{GDBP}) p (float) multiply (2.0f, 3.0f)
+(@value{GDBP}) p ((float (*) (float, float)) multiply) (2.0f, 3.0f)
+@end smallexample
+
+If the function you wish to call is declared as unprototyped (i.e.@:
+old K&R style), you must use the cast-to-function-pointer syntax, so
+that @value{GDBN} knows that it needs to apply default argument
+promotions (promote float arguments to double).  @xref{ABI, float
+promotion}.  For example, given this unprototyped C function with
+float parameters, and no debug info:
+
+@smallexample
+float
+multiply_noproto (v1, v2)
+  float v1, v2;
+@{
+  return v1 * v2;
+@}
+@end smallexample
+
+@noindent
+you call it like this:
+
+@smallexample
+  (@value{GDBP}) p ((float (*) ()) multiply_noproto) (2.0f, 3.0f)
+@end smallexample
 
 @node Patching
 @section Patching Programs
@@ -21792,12 +21886,12 @@ problem:
 
 @smallexample
 (@value{GDBP}) print 'cygwin1!__argv'
-$1 = 268572168
+'cygwin1!__argv' has unknown type; cast it to its declared type
 @end smallexample
 
 @smallexample
 (@value{GDBP}) x 'cygwin1!__argv'
-0x10021610:      "\230y\""
+'cygwin1!__argv' has unknown type; cast it to its declared type
 @end smallexample
 
 And two possible solutions:
diff --git a/gdb/NEWS b/gdb/NEWS
index 7c8a8f6..92bd530 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -23,6 +23,32 @@ set debug separate-debug-file
 show debug separate-debug-file
   Control the display of debug output about separate debug file search.
 
+* Safer/improved support for debugging with no debug info
+
+  GDB no longer assumes functions with no debug information
+  information return 'int'.
+
+  This means that GDB now refuses the call such functions unless you
+  tell it the function's type, by either casting the call to the
+  declared return type, or by casting the function to a function
+  pointer of the right type, and calling that:
+
+    (gdb) p getenv ("PATH")
+    'getenv' has unknown return type; cast the call to its declared return type
+    (gdb) p (char *) getenv ("PATH")
+    $1 = 0x7fffffffe "/usr/local/bin:/"...
+    (gdb) p ((char * (*) (const char *)) getenv) ("PATH")
+    $2 = 0x7fffffffe "/usr/local/bin:/"...
+
+  Similarly, GDB no longer assumes that global variables with no debug
+  info have type 'int', and refuses to print the variable's value
+  unless you tell it the variable's type:
+
+    (gdb) p var
+    'var' has unknown type; cast it to its declared type
+    (gdb) p (float) var
+    $3 = 3.14
+
 *** Changes in GDB 8.0
 
 * GDB now supports access to the PKU register on GNU/Linux. The register is
-- 
2.5.5

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

* [PATCH v2 04/13] Make ptype/whatis print function name of functions with no debug info too
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
                   ` (11 preceding siblings ...)
  2017-07-13 15:38 ` [PATCH v2 08/13] Stop assuming no-debug-info variables have type int Pedro Alves
@ 2017-07-13 15:39 ` Pedro Alves
  2017-09-04 19:21 ` [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
  13 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 15:39 UTC (permalink / raw)
  To: gdb-patches

The patch to make GDB stop assuming functions return int left GDB with
an inconsistency.  While with normal expression evaluation the
"unknown return type" error shows the name of the function that misses
debug info:

  (gdb) p getenv ("PATH")
  'getenv' has unknown return type; cast the call to its declared return type
   ^^^^^^

which is handy in more complicated expressions, "ptype" does not:

  (gdb) ptype getenv ("PATH")
  function has unknown return type; cast the call to its declared return type
  ^^^^^^^^

This commit builds on the new OP_VAR_MSYM_VALUE to fix it, by making
OP_FUNCALL extract the function name from the symbol stored in
OP_VAR_VALUE/OP_VAR_MSYM_VALUE.  We now get the same error in "print"
vs "ptype":

  (gdb) ptype getenv()
  'getenv' has unknown return type; cast the call to its declared return type
  (gdb) p getenv()
  'getenv' has unknown return type; cast the call to its declared return type

gdb/ChangeLog:
yyyy-dd-yy  Pedro Alves  <palves@redhat.com>

	* eval.c (evaluate_subexp_standard): <OP_FUNCALL>: Extract
	function name from symbol/minsym and pass it to
	error_call_unknown_return_type.

gdb/testsuite/ChangeLog:
yyyy-dd-yy  Pedro Alves  <palves@redhat.com>

	* gdb.base/nodebug.exp: Test that ptype's error about functions
	with unknown return type includes the function name too.
---
 gdb/eval.c                         | 14 +++++++++++++-
 gdb/testsuite/gdb.base/nodebug.exp | 12 ++++--------
 2 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/gdb/eval.c b/gdb/eval.c
index 457e280..0e77f0a 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -716,6 +716,7 @@ evaluate_subexp_standard (struct type *expect_type,
   int save_pos1;
   struct symbol *function = NULL;
   char *function_name = NULL;
+  const char *var_func_name = NULL;
 
   pc = (*pos)++;
   op = exp->elts[pc].opcode;
@@ -1545,6 +1546,17 @@ evaluate_subexp_standard (struct type *expect_type,
 	    }
 	  else
 	    {
+	      if (op == OP_VAR_MSYM_VALUE)
+		{
+		  symbol *sym = exp->elts[*pos + 2].symbol;
+		  var_func_name = SYMBOL_PRINT_NAME (sym);
+		}
+	      else if (op == OP_VAR_VALUE)
+		{
+		  minimal_symbol *msym = exp->elts[*pos + 2].msymbol;
+		  var_func_name = MSYMBOL_PRINT_NAME (msym);
+		}
+
 	      argvec[0] = evaluate_subexp_with_coercion (exp, pos, noside);
 	      type = value_type (argvec[0]);
 	      if (type && TYPE_CODE (type) == TYPE_CODE_PTR)
@@ -1761,7 +1773,7 @@ evaluate_subexp_standard (struct type *expect_type,
 		return_type = expect_type;
 
 	      if (return_type == NULL)
-		error_call_unknown_return_type (NULL);
+		error_call_unknown_return_type (var_func_name);
 
 	      return allocate_value (return_type);
 	    }
diff --git a/gdb/testsuite/gdb.base/nodebug.exp b/gdb/testsuite/gdb.base/nodebug.exp
index 7bdf9ad..da704f1 100644
--- a/gdb/testsuite/gdb.base/nodebug.exp
+++ b/gdb/testsuite/gdb.base/nodebug.exp
@@ -204,14 +204,10 @@ if [runto inner] then {
     
     # This test is not as obscure as it might look.  `p getenv ("TERM")'
     # is a real-world example, at least on many systems.
-
-    gdb_test {p/c array_index("abcdef",2)} \
-	"'array_index' has unknown return type; cast the call to its declared return type"
-    gdb_test {ptype array_index("abcdef",2)} \
-	"function has unknown return type; cast the call to its declared return type"
-    gdb_test {whatis array_index("abcdef",2)} \
-	"function has unknown return type; cast the call to its declared return type"
-
+    foreach cmd {"p/c" "ptype" "whatis"} {
+	gdb_test "$cmd array_index(\"abcdef\",2)" \
+	    "'array_index' has unknown return type; cast the call to its declared return type"
+    }
     if [target_info exists gdb,cannot_call_functions] {
 	unsupported "p/c (int) array_index(\"abcdef\",2)"
     } else {
-- 
2.5.5

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

* Re: [PATCH v2 13/13] Document "no debug info debugging" improvements
  2017-07-13 15:38 ` [PATCH v2 13/13] Document "no debug info debugging" improvements Pedro Alves
@ 2017-07-13 16:11   ` Eli Zaretskii
  2017-07-13 16:31     ` Pedro Alves
  0 siblings, 1 reply; 21+ messages in thread
From: Eli Zaretskii @ 2017-07-13 16:11 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

> From: Pedro Alves <palves@redhat.com>
> Date: Thu, 13 Jul 2017 16:32:09 +0100
> 
> New in v2: updated/rewritten to describe that the simpler cast syntax
> assumes the function is prototyped.
> 
> Here's the documentation bits for all the improvements.
> 
> Note that the original "weak alias functions" paragraph ends up
> disappearing, because this patch, which I'm considering kind of part
> of this series, makes the alias case Just Work:
>   https://sourceware.org/ml/gdb-patches/2017-07/msg00018.html
> 
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* NEWS (Safer support for debugging with no debug info): New.
> 
> gdb/doc/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* gdb.texinfo (Variables) <Program Variables>: Document inspecting
> 	no-debug-info variables.
> 	(Symbols) <Examining the Symbol Table>: Document inspecting
> 	no-debug-info types.
> 	(Calling) <Calling functions with no debug info>: New subsection,
> 	documenting calling no-debug-info functions.
> 	(Non-debug DLL Symbols) <Working with Minimal Symbols>: Update.

Same comments as before.

Thanks.

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

* Re: [PATCH v2 13/13] Document "no debug info debugging" improvements
  2017-07-13 16:11   ` Eli Zaretskii
@ 2017-07-13 16:31     ` Pedro Alves
  2017-07-13 17:54       ` Eli Zaretskii
  0 siblings, 1 reply; 21+ messages in thread
From: Pedro Alves @ 2017-07-13 16:31 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

On 07/13/2017 05:11 PM, Eli Zaretskii wrote:

> Same comments as before.
> 
> Thanks.
> 

Thanks, did that tweak to the error case, but I realized that
the ptype example isn't showing an error message, but really
the variable's "untyped" type:

 (gdb) ptype dataglobal
 type = <data variable, no debug info>
 (gdb) ptype &dataglobal
 type = <data variable, no debug info> *

(note this type name in particular is not new, it just
wasn't documented.)

so I used wording that didn't say "error" instead, like this:

~~~
Inspecting the type of a (global) variable for which
@value{GDBN} has no type information shows:

@smallexample
  (@value{GDBP}) ptype var
  type = <data variable, no debug info>
@end smallexample
~~~

Here's the result.  WDYT?

From a76492152f9d1e910972f91bcc865c1ab66135f2 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Wed, 12 Jul 2017 14:21:30 +0100
Subject: [PATCH] Document "no debug info debugging" improvements

New in v2.1: addressed Eli's comments.
New in v2: updated/rewritten to describe that the simpler cast syntax
assumes the function is prototyped.

Here's the documentation bits for all the improvements.

Note that the original "weak alias functions" paragraph ends up
disappearing, because this patch, which I'm considering kind of part
of this series, makes the alias case Just Work:
  https://sourceware.org/ml/gdb-patches/2017-07/msg00018.html

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* NEWS (Safer support for debugging with no debug info): New.

gdb/doc/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.texinfo (Variables) <Program Variables>: Document inspecting
	no-debug-info variables.
	(Symbols) <Examining the Symbol Table>: Document inspecting
	no-debug-info types.
	(Calling) <Calling functions with no debug info>: New subsection,
	documenting calling no-debug-info functions.
	(Non-debug DLL Symbols) <Working with Minimal Symbols>: Update.
---
 gdb/doc/gdb.texinfo | 113 +++++++++++++++++++++++++++++++++++++++++++++++-----
 gdb/NEWS            |  26 ++++++++++++
 2 files changed, 129 insertions(+), 10 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index c167a86..7190a70 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -9114,6 +9114,22 @@ If you ask to print an object whose contents are unknown to
 by the debug information, @value{GDBN} will say @samp{<incomplete
 type>}.  @xref{Symbols, incomplete type}, for more about this.
 
+@cindex no debug info variables
+If you try to examine or use the value of a (global) variable for
+which @value{GDBN} has no type information, e.g., because the program
+includes no debug information, @value{GDBN} displays an error message.
+@xref{Symbols, unknown type}, for more about unknown types.  If you
+cast the variable to its declared type, @value{GDBN} gets the
+variable's value using the cast-to type as the variable's type.  For
+example, in a C program:
+
+@smallexample
+  (@value{GDBP}) p var
+  'var' has unknown type; cast it to its declared type
+  (@value{GDBP}) p (float) var
+  $1 = 3.14
+@end smallexample
+
 If you append @kbd{@@entry} string to a function parameter name you get its
 value at the time the function got called.  If the value is not available an
 error message is printed.  Entry values are available only with some compilers.
@@ -17083,6 +17099,24 @@ but no definition for @code{struct foo} itself, @value{GDBN} will say:
 ``Incomplete type'' is C terminology for data types that are not
 completely specified.
 
+@cindex unknown type
+Othertimes, information about a variable's type is completely absent
+from the debug information included in the program.  This most often
+happens when the program or library where the variable is defined
+includes no debug information at all.  @value{GDBN} knows the variable
+exists from inspecting the linker/loader symbol table (e.g., the ELF
+dynamic symbol table), but such symbols do not contain type
+information.  Inspecting the type of a (global) variable for which
+@value{GDBN} has no type information shows:
+
+@smallexample
+  (@value{GDBP}) ptype var
+  type = <data variable, no debug info>
+@end smallexample
+
+@xref{Variables, no debug info variables}, for how to print the values
+of such variables.
+
 @kindex info types
 @item info types @var{regexp}
 @itemx info types
@@ -17798,14 +17832,73 @@ Show the current setting of stack unwinding in the functions called by
 
 @end table
 
-@cindex weak alias functions
-Sometimes, a function you wish to call is actually a @dfn{weak alias}
-for another function.  In such case, @value{GDBN} might not pick up
-the type information, including the types of the function arguments,
-which causes @value{GDBN} to call the inferior function incorrectly.
-As a result, the called function will function erroneously and may
-even crash.  A solution to that is to use the name of the aliased
-function instead.
+@subsection Calling functions with no debug info
+
+@cindex no debug info functions
+Sometimes, a function you wish to call is missing debug information.
+In such case, @value{GDBN} does not know the type of the function,
+including the types of the function's parameters.  To avoid calling
+the inferior function incorrectly, which could result in the called
+function functioning erroneously and even crash, @value{GDBN} refuses
+to call the function unless you tell it the type of the function.
+
+For prototyped (i.e.@: ANSI/ISO style) functions, there are two ways
+to do that.  The simplest is to cast the call to the function's
+declared return type.  For example:
+
+@smallexample
+(@value{GDBP}) p getenv ("PATH")
+'getenv' has unknown return type; cast the call to its declared return type
+(@value{GDBP}) p (char *) getenv ("PATH")
+$1 = 0x7fffffffe7ba "/usr/local/bin:/"...
+@end smallexample
+
+Casting the return type of a no-debug function is equivalent to
+casting the function to a pointer to a prototyped function that has a
+prototype that matches the types of the passed-in arguments, and
+calling that.  I.e., the call above is equivalent to:
+
+@smallexample
+(@value{GDBP}) p ((char * (*) (const char *)) getenv) ("PATH")
+@end smallexample
+
+@noindent
+and given this prototyped C or C++ function with float parameters:
+
+@smallexample
+float multiply (float v1, float v2) @{ return v1 * v2; @}
+@end smallexample
+
+@noindent
+these calls are equivalent:
+
+@smallexample
+(@value{GDBP}) p (float) multiply (2.0f, 3.0f)
+(@value{GDBP}) p ((float (*) (float, float)) multiply) (2.0f, 3.0f)
+@end smallexample
+
+If the function you wish to call is declared as unprototyped (i.e.@:
+old K&R style), you must use the cast-to-function-pointer syntax, so
+that @value{GDBN} knows that it needs to apply default argument
+promotions (promote float arguments to double).  @xref{ABI, float
+promotion}.  For example, given this unprototyped C function with
+float parameters, and no debug info:
+
+@smallexample
+float
+multiply_noproto (v1, v2)
+  float v1, v2;
+@{
+  return v1 * v2;
+@}
+@end smallexample
+
+@noindent
+you call it like this:
+
+@smallexample
+  (@value{GDBP}) p ((float (*) ()) multiply_noproto) (2.0f, 3.0f)
+@end smallexample
 
 @node Patching
 @section Patching Programs
@@ -21792,12 +21885,12 @@ problem:
 
 @smallexample
 (@value{GDBP}) print 'cygwin1!__argv'
-$1 = 268572168
+'cygwin1!__argv' has unknown type; cast it to its declared type
 @end smallexample
 
 @smallexample
 (@value{GDBP}) x 'cygwin1!__argv'
-0x10021610:      "\230y\""
+'cygwin1!__argv' has unknown type; cast it to its declared type
 @end smallexample
 
 And two possible solutions:
diff --git a/gdb/NEWS b/gdb/NEWS
index 7c8a8f6..92bd530 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -23,6 +23,32 @@ set debug separate-debug-file
 show debug separate-debug-file
   Control the display of debug output about separate debug file search.
 
+* Safer/improved support for debugging with no debug info
+
+  GDB no longer assumes functions with no debug information
+  information return 'int'.
+
+  This means that GDB now refuses the call such functions unless you
+  tell it the function's type, by either casting the call to the
+  declared return type, or by casting the function to a function
+  pointer of the right type, and calling that:
+
+    (gdb) p getenv ("PATH")
+    'getenv' has unknown return type; cast the call to its declared return type
+    (gdb) p (char *) getenv ("PATH")
+    $1 = 0x7fffffffe "/usr/local/bin:/"...
+    (gdb) p ((char * (*) (const char *)) getenv) ("PATH")
+    $2 = 0x7fffffffe "/usr/local/bin:/"...
+
+  Similarly, GDB no longer assumes that global variables with no debug
+  info have type 'int', and refuses to print the variable's value
+  unless you tell it the variable's type:
+
+    (gdb) p var
+    'var' has unknown type; cast it to its declared type
+    (gdb) p (float) var
+    $3 = 3.14
+
 *** Changes in GDB 8.0
 
 * GDB now supports access to the PKU register on GNU/Linux. The register is
-- 
2.5.5

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

* Re: [PATCH v2 13/13] Document "no debug info debugging" improvements
  2017-07-13 16:31     ` Pedro Alves
@ 2017-07-13 17:54       ` Eli Zaretskii
  0 siblings, 0 replies; 21+ messages in thread
From: Eli Zaretskii @ 2017-07-13 17:54 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

> Cc: gdb-patches@sourceware.org
> From: Pedro Alves <palves@redhat.com>
> Date: Thu, 13 Jul 2017 17:30:56 +0100
> 
> so I used wording that didn't say "error" instead, like this:
> 
> ~~~
> Inspecting the type of a (global) variable for which
> @value{GDBN} has no type information shows:
> 
> @smallexample
>   (@value{GDBP}) ptype var
>   type = <data variable, no debug info>
> @end smallexample
> ~~~
> 
> Here's the result.  WDYT?

LGTM, thanks.

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

* Re: [PATCH v2 00/13] No-debug-info debugging improvements
  2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
                   ` (12 preceding siblings ...)
  2017-07-13 15:39 ` [PATCH v2 04/13] Make ptype/whatis print function name of functions with no debug info too Pedro Alves
@ 2017-09-04 19:21 ` Pedro Alves
  13 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2017-09-04 19:21 UTC (permalink / raw)
  To: GDB Patches

On 07/13/2017 04:31 PM, Pedro Alves wrote:
> New in v2:
>   - The new simpler cast-return-type syntax now assumes the called
>     function is prototyped instead of unprototyped:
>      https://sourceware.org/ml/gdb-patches/2017-07/msg00128.html
>   - Documentation patch adjusted accordingly.
>   - The fix for calling prototyped functions via function pointers is
>     now patch #1, because the v2 of patch #2 includes new tests that
>     depend on the fix.
> 
> 
> This series stops GDB from assuming that no-debug-info functions
> return int, and that no-debug-info variables have type int.

I'm pushing this to master now.

Thanks,
Pedro Alves

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

* [RFA] Adjust gdb.arch/i386-sse-stack-align.exp print statement
  2017-07-13 15:38 ` [PATCH v2 08/13] Stop assuming no-debug-info variables have type int Pedro Alves
@ 2017-12-07 23:30   ` Sergio Durigan Junior
  2017-12-08 10:47     ` Pedro Alves
  0 siblings, 1 reply; 21+ messages in thread
From: Sergio Durigan Junior @ 2017-12-07 23:30 UTC (permalink / raw)
  To: GDB Patches; +Cc: Pedro Alves, Sergio Durigan Junior

Since:

commit 46a4882b3c7d9ec981568b8b13a3c9c39c8f8e61
Author: Pedro Alves <palves@redhat.com>
Date:   Mon Sep 4 20:21:15 2017 +0100

    Stop assuming no-debug-info variables have type int

We now have to explicitly tell GDB the type of the non-debug-info
variable we want to print (by casting).  This commit adjusts the
"print" statement on gdb.arch/i386-sse-stack-align.exp to do the
proper cast, fixing a failure that started to happen after the
mentioned commit.

gdb/testsuite/ChangeLog:
2017-12-07  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb.arch/i386-sse-stack-align.exp: Cast "print" variable to
	"int".
---
 gdb/testsuite/ChangeLog                         | 5 +++++
 gdb/testsuite/gdb.arch/i386-sse-stack-align.exp | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 53f114aa15..81d23d1bb1 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2017-12-07  Sergio Durigan Junior  <sergiodj@redhat.com>
+
+	* gdb.arch/i386-sse-stack-align.exp: Cast "print" variable to
+	"int".
+
 2017-12-07  Keith Seitz  <keiths@redhat.com>
 
 	* gdb.cp/nested-types.cc: New file.
diff --git a/gdb/testsuite/gdb.arch/i386-sse-stack-align.exp b/gdb/testsuite/gdb.arch/i386-sse-stack-align.exp
index 47fb9f42d2..946f217ce1 100644
--- a/gdb/testsuite/gdb.arch/i386-sse-stack-align.exp
+++ b/gdb/testsuite/gdb.arch/i386-sse-stack-align.exp
@@ -43,7 +43,7 @@ if ![runto_main] then {
 
 set args ""
 foreach i {0 1 2 3 4} {
-    set test "print g$i ($args)"
+    set test "print (int) g$i ($args)"
     gdb_test_multiple $test $test {
 	-re " = 2\r\n$gdb_prompt $" {
 	    pass $test
-- 
2.14.3

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

* Re: [RFA] Adjust gdb.arch/i386-sse-stack-align.exp print statement
  2017-12-07 23:30   ` [RFA] Adjust gdb.arch/i386-sse-stack-align.exp print statement Sergio Durigan Junior
@ 2017-12-08 10:47     ` Pedro Alves
  2017-12-08 16:28       ` Sergio Durigan Junior
  0 siblings, 1 reply; 21+ messages in thread
From: Pedro Alves @ 2017-12-08 10:47 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches

On 12/07/2017 11:30 PM, Sergio Durigan Junior wrote:
> 
> commit 46a4882b3c7d9ec981568b8b13a3c9c39c8f8e61
> Author: Pedro Alves <palves@redhat.com>
> Date:   Mon Sep 4 20:21:15 2017 +0100
> 
>     Stop assuming no-debug-info variables have type int
> 
> We now have to explicitly tell GDB the type of the non-debug-info
> variable we want to print (by casting).  This commit adjusts the
> "print" statement on gdb.arch/i386-sse-stack-align.exp to do the
> proper cast, fixing a failure that started to happen after the
> mentioned commit.
> 
> gdb/testsuite/ChangeLog:
> 2017-12-07  Sergio Durigan Junior  <sergiodj@redhat.com>
> 
> 	* gdb.arch/i386-sse-stack-align.exp: Cast "print" variable to
> 	"int".

This is OK, except...

> ---
>  gdb/testsuite/ChangeLog                         | 5 +++++
>  gdb/testsuite/gdb.arch/i386-sse-stack-align.exp | 2 +-
>  2 files changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
> index 53f114aa15..81d23d1bb1 100644
> --- a/gdb/testsuite/ChangeLog
> +++ b/gdb/testsuite/ChangeLog
> @@ -1,3 +1,8 @@
> +2017-12-07  Sergio Durigan Junior  <sergiodj@redhat.com>
> +
> +	* gdb.arch/i386-sse-stack-align.exp: Cast "print" variable to
> +	"int".
> +
>  2017-12-07  Keith Seitz  <keiths@redhat.com>
>  
>  	* gdb.cp/nested-types.cc: New file.
> diff --git a/gdb/testsuite/gdb.arch/i386-sse-stack-align.exp b/gdb/testsuite/gdb.arch/i386-sse-stack-align.exp
> index 47fb9f42d2..946f217ce1 100644
> --- a/gdb/testsuite/gdb.arch/i386-sse-stack-align.exp
> +++ b/gdb/testsuite/gdb.arch/i386-sse-stack-align.exp
> @@ -43,7 +43,7 @@ if ![runto_main] then {
>  
>  set args ""
>  foreach i {0 1 2 3 4} {
> -    set test "print g$i ($args)"
> +    set test "print (int) g$i ($args)"

... this is printing/calling a function, not a variable:

 (gdb) print g0 ()
 'g0' has unknown return type; cast the call to its declared return type

So the git log and ChangeLog are inaccurate and should
reference 7022349d5c86 ("Stop assuming no-debug-info functions
return int") instead.

OK with that fixed.

Thanks,
Pedro Alves

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

* Re: [RFA] Adjust gdb.arch/i386-sse-stack-align.exp print statement
  2017-12-08 10:47     ` Pedro Alves
@ 2017-12-08 16:28       ` Sergio Durigan Junior
  0 siblings, 0 replies; 21+ messages in thread
From: Sergio Durigan Junior @ 2017-12-08 16:28 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches

On Friday, December 08 2017, Pedro Alves wrote:

> On 12/07/2017 11:30 PM, Sergio Durigan Junior wrote:
>> 
>> commit 46a4882b3c7d9ec981568b8b13a3c9c39c8f8e61
>> Author: Pedro Alves <palves@redhat.com>
>> Date:   Mon Sep 4 20:21:15 2017 +0100
>> 
>>     Stop assuming no-debug-info variables have type int
>> 
>> We now have to explicitly tell GDB the type of the non-debug-info
>> variable we want to print (by casting).  This commit adjusts the
>> "print" statement on gdb.arch/i386-sse-stack-align.exp to do the
>> proper cast, fixing a failure that started to happen after the
>> mentioned commit.
>> 
>> gdb/testsuite/ChangeLog:
>> 2017-12-07  Sergio Durigan Junior  <sergiodj@redhat.com>
>> 
>> 	* gdb.arch/i386-sse-stack-align.exp: Cast "print" variable to
>> 	"int".
>
> This is OK, except...
>
>> ---
>>  gdb/testsuite/ChangeLog                         | 5 +++++
>>  gdb/testsuite/gdb.arch/i386-sse-stack-align.exp | 2 +-
>>  2 files changed, 6 insertions(+), 1 deletion(-)
>> 
>> diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
>> index 53f114aa15..81d23d1bb1 100644
>> --- a/gdb/testsuite/ChangeLog
>> +++ b/gdb/testsuite/ChangeLog
>> @@ -1,3 +1,8 @@
>> +2017-12-07  Sergio Durigan Junior  <sergiodj@redhat.com>
>> +
>> +	* gdb.arch/i386-sse-stack-align.exp: Cast "print" variable to
>> +	"int".
>> +
>>  2017-12-07  Keith Seitz  <keiths@redhat.com>
>>  
>>  	* gdb.cp/nested-types.cc: New file.
>> diff --git a/gdb/testsuite/gdb.arch/i386-sse-stack-align.exp b/gdb/testsuite/gdb.arch/i386-sse-stack-align.exp
>> index 47fb9f42d2..946f217ce1 100644
>> --- a/gdb/testsuite/gdb.arch/i386-sse-stack-align.exp
>> +++ b/gdb/testsuite/gdb.arch/i386-sse-stack-align.exp
>> @@ -43,7 +43,7 @@ if ![runto_main] then {
>>  
>>  set args ""
>>  foreach i {0 1 2 3 4} {
>> -    set test "print g$i ($args)"
>> +    set test "print (int) g$i ($args)"
>
> ... this is printing/calling a function, not a variable:
>
>  (gdb) print g0 ()
>  'g0' has unknown return type; cast the call to its declared return type
>
> So the git log and ChangeLog are inaccurate and should
> reference 7022349d5c86 ("Stop assuming no-debug-info functions
> return int") instead.
>
> OK with that fixed.

Thanks for the catch, I adjusted the ChangeLog entry and the commit
message accordingly.

Pushed:
1cd9a73b4280cb133b305ee31a0e87f114bd1be8

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

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

end of thread, other threads:[~2017-12-08 16:28 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-13 15:32 [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves
2017-07-13 15:32 ` [PATCH v2 11/13] Handle "p 'S::method()::static_var'" (quoted) in symbol lookup Pedro Alves
2017-07-13 15:32 ` [PATCH v2 09/13] Eliminate UNOP_MEMVAL_TLS Pedro Alves
2017-07-13 15:32 ` [PATCH v2 03/13] Introduce OP_VAR_MSYM_VALUE Pedro Alves
2017-07-13 15:32 ` [PATCH v2 10/13] Handle "p S::method()::static_var" in the C++ parser Pedro Alves
2017-07-13 15:32 ` [PATCH v2 02/13] Stop assuming no-debug-info functions return int Pedro Alves
2017-07-13 15:32 ` [PATCH v2 06/13] evaluate_subexp_standard: Remove useless assignments Pedro Alves
2017-07-13 15:32 ` [PATCH v2 05/13] evaluate_subexp_standard: Eliminate one goto Pedro Alves
2017-07-13 15:32 ` [PATCH v2 01/13] Fix calling prototyped functions via function pointers Pedro Alves
2017-07-13 15:37 ` [PATCH v2 12/13] Make "p S::method() const::static_var" work too Pedro Alves
2017-07-13 15:38 ` [PATCH v2 13/13] Document "no debug info debugging" improvements Pedro Alves
2017-07-13 16:11   ` Eli Zaretskii
2017-07-13 16:31     ` Pedro Alves
2017-07-13 17:54       ` Eli Zaretskii
2017-07-13 15:38 ` [PATCH v2 07/13] evaluate_subexp_standard: Factor out OP_VAR_VALUE handling Pedro Alves
2017-07-13 15:38 ` [PATCH v2 08/13] Stop assuming no-debug-info variables have type int Pedro Alves
2017-12-07 23:30   ` [RFA] Adjust gdb.arch/i386-sse-stack-align.exp print statement Sergio Durigan Junior
2017-12-08 10:47     ` Pedro Alves
2017-12-08 16:28       ` Sergio Durigan Junior
2017-07-13 15:39 ` [PATCH v2 04/13] Make ptype/whatis print function name of functions with no debug info too Pedro Alves
2017-09-04 19:21 ` [PATCH v2 00/13] No-debug-info debugging improvements Pedro Alves

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).