Handle function aliases better (PR gdb/19487, errno printing)
authorPedro Alves <palves@redhat.com>
Mon, 21 Aug 2017 10:34:32 +0000 (11:34 +0100)
committerPedro Alves <palves@redhat.com>
Mon, 21 Aug 2017 10:34:32 +0000 (11:34 +0100)
(Ref: https://sourceware.org/ml/gdb/2017-06/msg00048.html)

This patch improves GDB support for function aliases defined with
__attribute__ alias.  For example, in the test added by this commit,
there is no reference to "func_alias" in the debug info at all, only
to "func"'s definition:

 $ nm  ./testsuite/outputs/gdb.base/symbol-alias/symbol-alias  | grep " func"
 00000000004005ae t func
 00000000004005ae T func_alias

 $ readelf -w ./testsuite/outputs/gdb.base/symbol-alias/symbol-alias | grep func -B 1 -A 8
 <1><db>: Abbrev Number: 5 (DW_TAG_subprogram)
    <dc>   DW_AT_name        : (indirect string, offset: 0x111): func
    <e0>   DW_AT_decl_file   : 1
    <e1>   DW_AT_decl_line   : 27
    <e2>   DW_AT_prototyped  : 1
    <e2>   DW_AT_type        : <0xf8>
    <e6>   DW_AT_low_pc      : 0x4005ae
    <ee>   DW_AT_high_pc     : 0xb
    <f6>   DW_AT_frame_base  : 1 byte block: 9c         (DW_OP_call_frame_cfa)
    <f8>   DW_AT_GNU_all_call_sites: 1

So all GDB knows about "func_alias" is from the minsym (elf symbol):

 (gdb) p func_alias
 $1 = {<text variable, no debug info>} 0x4005ae <func>
 (gdb) ptype func_alias
 type = int ()

 (gdb) p func
 $2 = {struct S *(void)} 0x4005ae <func>
 (gdb) ptype func
 type = struct S {
     int field1;
     int field2;
 } *(void)

The result is that calling func_alias from the command line produces
incorrect results.

This is similar (though not exactly the same) to the glibc
errno/__errno_location/__GI___errno_location situation.  On glibc,
errno is defined like this:

  extern int *__errno_location (void);
  #define errno (*__errno_location ())

with __GI___errno_location being an internal alias for
__errno_location.  On my system's libc (F23), I do see debug info for
__errno_location, in the form of name vs linkage name:

 <1><95a5>: Abbrev Number: 18 (DW_TAG_subprogram)
    <95a6>   DW_AT_external    : 1
    <95a6>   DW_AT_name        : (indirect string, offset: 0x2c26): __errno_location
    <95aa>   DW_AT_decl_file   : 1
    <95ab>   DW_AT_decl_line   : 24
    <95ac>   DW_AT_linkage_name: (indirect string, offset: 0x2c21): __GI___errno_location
    <95b0>   DW_AT_prototyped  : 1
    <95b0>   DW_AT_type        : <0x9206>
    <95b4>   DW_AT_low_pc      : 0x20f40
    <95bc>   DW_AT_high_pc     : 0x11
    <95c4>   DW_AT_frame_base  : 1 byte block: 9c       (DW_OP_call_frame_cfa)
    <95c6>   DW_AT_GNU_all_call_sites: 1

however that doesn't matter in practice, because GDB doesn't record
demangled names anyway, and so we end up with the exact same situation
covered by the testcase.

So the fix is to make the expression parser find a debug symbol for
the same address as the just-found minsym, when a lookup by name
didn't find a debug symbol by name.  We now get:

 (gdb) p func_alias
 $1 = {struct S *(void)} 0x4005ae <func>
 (gdb) p __errno_location
 $2 = {int *(void)} 0x7ffff6e92830 <__errno_location>

I've made the test exercise variable aliases too, for completeness.
Those already work correctly, because unlike for function aliases, GCC
emits debug information for variable aliases.

Tested on GNU/Linux.

gdb/ChangeLog:
2017-08-21  Pedro Alves  <palves@redhat.com>

PR gdb/19487
* c-exp.y (variable production): Handle function aliases.
* minsyms.c (msymbol_is_text): New function.
* minsyms.h (msymbol_is_text): Declare.
* symtab.c (find_function_alias_target): New function.
* symtab.h (find_function_alias_target): Declare.

gdb/testsuite/ChangeLog:
2017-08-21  Pedro Alves  <palves@redhat.com>

PR gdb/19487
* gdb.base/symbol-alias.c: New.
* gdb.base/symbol-alias2.c: New.
* gdb.base/symbol-alias.exp: New.

gdb/ChangeLog
gdb/c-exp.y
gdb/minsyms.c
gdb/minsyms.h
gdb/symtab.c
gdb/symtab.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/symbol-alias.c [new file with mode: 0644]
gdb/testsuite/gdb.base/symbol-alias.exp [new file with mode: 0644]
gdb/testsuite/gdb.base/symbol-alias2.c [new file with mode: 0644]

index fae715041cbcb2136ddd8e2a455023506e683d18..db53fcb64525995ab060787194a0b85d167ca698 100644 (file)
@@ -1,3 +1,12 @@
+2017-08-21  Pedro Alves  <palves@redhat.com>
+
+       PR gdb/19487
+       * c-exp.y (variable production): Handle function aliases.
+       * minsyms.c (msymbol_is_text): New function.
+       * minsyms.h (msymbol_is_text): Declare.
+       * symtab.c (find_function_alias_target): New function.
+       * symtab.h (find_function_alias_target): Declare.
+
 2017-08-21  Pedro Alves  <palves@redhat.com>
 
        * eval.c (evaluate_subexp_standard) <OP_TYPE>: Don't dig past
index 24a2fbd76e684e284bc32878068567d99fef4ec8..a1f9feee71795dc6d90ef6bb2aca5eb1784946c1 100644 (file)
@@ -1038,18 +1038,38 @@ variable:       name_not_typename
                            }
                          else
                            {
-                             struct bound_minimal_symbol msymbol;
                              char *arg = copy_name ($1.stoken);
 
-                             msymbol =
-                               lookup_bound_minimal_symbol (arg);
-                             if (msymbol.minsym != NULL)
-                               write_exp_msymbol (pstate, msymbol);
-                             else if (!have_full_symbols () && !have_partial_symbols ())
-                               error (_("No symbol table is loaded.  Use the \"file\" command."));
+                             bound_minimal_symbol msymbol
+                               = lookup_bound_minimal_symbol (arg);
+                             if (msymbol.minsym == NULL)
+                               {
+                                 if (!have_full_symbols () && !have_partial_symbols ())
+                                   error (_("No symbol table is loaded.  Use the \"file\" command."));
+                                 else
+                                   error (_("No symbol \"%s\" in current context."),
+                                          copy_name ($1.stoken));
+                               }
+
+                             /* This minsym might be an alias for
+                                another function.  See if we can find
+                                the debug symbol for the target, and
+                                if so, use it instead, since it has
+                                return type / prototype info.  This
+                                is important for example for "p
+                                *__errno_location()".  */
+                             symbol *alias_target
+                               = find_function_alias_target (msymbol);
+                             if (alias_target != NULL)
+                               {
+                                 write_exp_elt_opcode (pstate, OP_VAR_VALUE);
+                                 write_exp_elt_block
+                                   (pstate, SYMBOL_BLOCK_VALUE (alias_target));
+                                 write_exp_elt_sym (pstate, alias_target);
+                                 write_exp_elt_opcode (pstate, OP_VAR_VALUE);
+                               }
                              else
-                               error (_("No symbol \"%s\" in current context."),
-                                      copy_name ($1.stoken));
+                               write_exp_msymbol (pstate, msymbol);
                            }
                        }
        ;
index c93eaa34dddc6d229bbec54e99b4b9e47742f816..69b9e403eaa6afe84c486def341e7aa9cf753678 100644 (file)
 #include "cli/cli-utils.h"
 #include "symbol.h"
 
+/* See minsyms.h.  */
+
+bool
+msymbol_is_text (minimal_symbol *msymbol)
+{
+  switch (MSYMBOL_TYPE (msymbol))
+    {
+    case mst_text:
+    case mst_text_gnu_ifunc:
+    case mst_solib_trampoline:
+    case mst_file_text:
+      return true;
+    default:
+      return false;
+    }
+}
+
 /* Accumulate the minimal symbols for each objfile in bunches of BUNCH_SIZE.
    At the end, copy them all into one newly allocated location on an objfile's
    per-BFD storage obstack.  */
index b82a22af71b74d974d820652cea049fd8ce854d3..e763f62aa36b7c5934bd86f2997a1787968dada9 100644 (file)
@@ -157,6 +157,10 @@ void terminate_minimal_symbol_table (struct objfile *objfile);
 
 \f
 
+/* Return whether MSYMBOL is a function/method.  */
+
+bool msymbol_is_text (minimal_symbol *msymbol);
+
 /* Compute a hash code for the string argument.  */
 
 unsigned int msymbol_hash (const char *);
index ccf31cc7c392dc07eb27cc4fefd0302d812c83ab..2c19b5339e567fc79d97905890c6ab7b23cc5b4a 100644 (file)
@@ -3830,6 +3830,25 @@ skip_prologue_using_sal (struct gdbarch *gdbarch, CORE_ADDR func_addr)
     /* Don't return END_PC, which is past the end of the function.  */
     return prologue_sal.pc;
 }
+
+/* See symtab.h.  */
+
+symbol *
+find_function_alias_target (bound_minimal_symbol msymbol)
+{
+  if (!msymbol_is_text (msymbol.minsym))
+    return NULL;
+
+  CORE_ADDR addr = BMSYMBOL_VALUE_ADDRESS (msymbol);
+  symbol *sym = find_pc_function (addr);
+  if (sym != NULL
+      && SYMBOL_CLASS (sym) == LOC_BLOCK
+      && BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) == addr)
+    return sym;
+
+  return NULL;
+}
+
 \f
 /* If P is of the form "operator[ \t]+..." where `...' is
    some legitimate operator text, return a pointer to the
index 35949f08674e36313f20cee90c2f41c5d3a8fbf7..8cbf4d6d9422b823deb07c961a6712e0c902fb3c 100644 (file)
@@ -1556,6 +1556,13 @@ extern CORE_ADDR skip_prologue_using_sal (struct gdbarch *gdbarch,
 extern struct symbol *fixup_symbol_section (struct symbol *,
                                            struct objfile *);
 
+/* If MSYMBOL is an text symbol, look for a function debug symbol with
+   the same address.  Returns NULL if not found.  This is necessary in
+   case a function is an alias to some other function, because debug
+   information is only emitted for the alias target function's
+   definition, not for the alias.  */
+extern symbol *find_function_alias_target (bound_minimal_symbol msymbol);
+
 /* Symbol searching */
 /* Note: struct symbol_search, search_symbols, et.al. are declared here,
    instead of making them local to symtab.c, for gdbtk's sake.  */
index 03e825e046201f2e0edb11e53c7fbcd5c0c42390..f1477313e6a2e37fdd26e8cfffb276f26e18b61d 100644 (file)
@@ -1,3 +1,10 @@
+2017-08-21  Pedro Alves  <palves@redhat.com>
+
+       PR gdb/19487
+       * gdb.base/symbol-alias.c: New.
+       * gdb.base/symbol-alias2.c: New.
+       * gdb.base/symbol-alias.exp: New.
+
 2017-08-21  Pedro Alves  <palves@redhat.com>
 
        * gdb.base/dfp-test.c
diff --git a/gdb/testsuite/gdb.base/symbol-alias.c b/gdb/testsuite/gdb.base/symbol-alias.c
new file mode 100644 (file)
index 0000000..cc5d104
--- /dev/null
@@ -0,0 +1,31 @@
+/* This test is part of GDB, the GNU debugger.
+
+   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/>.  */
+
+struct S
+{
+  int field1;
+  int field2;
+};
+
+extern struct S *func_alias (void);
+
+int
+main (void)
+{
+  struct S *s = func_alias ();
+  return s->field1 - s->field1;
+}
diff --git a/gdb/testsuite/gdb.base/symbol-alias.exp b/gdb/testsuite/gdb.base/symbol-alias.exp
new file mode 100644 (file)
index 0000000..dc63eb1
--- /dev/null
@@ -0,0 +1,37 @@
+# Test for printing alias symbols.
+# 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/>.
+
+standard_testfile symbol-alias.c symbol-alias2.c
+
+if { [prepare_for_testing "failed to prepare" ${testfile} [list $srcfile $srcfile2]] } {
+    return -1
+}
+
+if ![runto_main] then {
+    fail "can't run to main"
+    continue
+}
+
+# Functions.
+foreach f {"func" "func_alias"} {
+    gdb_test "p $f" " = {struct S \\*\\(void\\)} $hex <func>"
+    gdb_test "p *${f}()" "= {field1 = 1, field2 = 2}"
+}
+
+# Variables.
+foreach v {"g_var_s" "g_var_s_alias"} {
+    gdb_test "p $v" "= {field1 = 1, field2 = 2}"
+}
diff --git a/gdb/testsuite/gdb.base/symbol-alias2.c b/gdb/testsuite/gdb.base/symbol-alias2.c
new file mode 100644 (file)
index 0000000..aedd520
--- /dev/null
@@ -0,0 +1,34 @@
+/* This test is part of GDB, the GNU debugger.
+
+   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/>.  */
+
+struct S
+{
+  int field1;
+  int field2;
+};
+
+struct S g_var_s = { 1, 2 };
+
+static struct S *
+func (void)
+{
+  return &g_var_s;
+}
+
+struct S *func_alias (void) __attribute__ ((alias ("func")));
+
+extern struct S g_var_s_alias __attribute__ ((alias ("g_var_s")));