This is the mail archive of the gdb-cvs@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] Calling ifunc functions when resolver has debug info, user symbol same name


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=ca31ab1d675c1e20cee5f8cb213c52e3d7352496

commit ca31ab1d675c1e20cee5f8cb213c52e3d7352496
Author: Pedro Alves <palves@redhat.com>
Date:   Thu Apr 26 13:01:26 2018 +0100

    Calling ifunc functions when resolver has debug info, user symbol same name
    
    If the GNU ifunc resolver has the same name as the user visible
    symbol, and the resolver has debug info, then the DWARF info for the
    resolver masks the ifunc minsym.  In that scenario, if you try calling
    the ifunc from GDB, you call the resolver instead.  With the
    gnu-ifunc.exp testcase added in a following patch, you'd see:
    
      (gdb) p gnu_ifunc (3)
      $1 = (int (*)(int)) 0x400753 <final>
      (gdb) FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1: resolved_debug=0: p gnu_ifunc (3)
                                                           ^^^^^^^^^^^^^^^^
    
    That is, we called the ifunc resolver manually, which returned a
    pointer to the ifunc target function ("final").  The "final" symbol is
    the function that GDB should have called automatically,
    
      ~~~~~~~~~~~~
      int
      final (int arg)
      {
        return arg + 1;
      }
      ~~~~~~~~~
    
    which is what happens if you don't have debug info for the resolver:
    
      (gdb) p gnu_ifunc (3)
      $1 = 4
      (gdb) PASS: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0: resolved_debug=1: p gnu_ifunc (3)
                                                           ^^^^^^^^^^^^^^^^
    
    or if the resolver's symbol has a different name from the ifunc (as is
    the case with modern uses of ifunc via __attribute__ ifunc, such as
    glibc uses):
    
      (gdb) p gnu_ifunc (3)
      $1 = 4
      (gdb) PASS: gdb.base/gnu-ifunc.exp: resolver_attr=1: resolver_debug=1: resolved_debug=0: p gnu_ifunc (3)
                                          ^^^^^^^^^^^^^^^
    
    in which case after this patch, you can still call the resolver
    directly if you want:
    
      (gdb) p gnu_ifunc_resolver (3)
      $1 = (int (*)(int)) 0x400753 <final>
    
    gdb/ChangeLog:
    2018-04-26  Pedro Alves  <palves@redhat.com>
    
    	* c-exp.y (variable production): Prefer ifunc minsyms over
    	regular function symbols.
    	* symtab.c (find_gnu_ifunc): New function.
    	* minsyms.h (lookup_msym_prefer): New enum.
    	(lookup_minimal_symbol_by_pc_section): Replace 'want_trampoline'
    	parameter by a lookup_msym_prefer parameter.
    	* symtab.h (find_gnu_ifunc): New declaration.

Diff:
---
 gdb/ChangeLog  | 10 ++++++++++
 gdb/c-exp.y    | 20 ++++++++++++++++----
 gdb/linespec.c |  3 +++
 gdb/minsyms.c  |  8 +++++---
 gdb/minsyms.h  |  2 +-
 gdb/symtab.c   | 32 ++++++++++++++++++++++++++++++++
 gdb/symtab.h   |  3 +++
 7 files changed, 70 insertions(+), 8 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 8db2d8a..67e1bab 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,15 @@
 2018-04-26  Pedro Alves  <palves@redhat.com>
 
+	* c-exp.y (variable production): Prefer ifunc minsyms over
+	regular function symbols.
+	* symtab.c (find_gnu_ifunc): New function.
+	* minsyms.h (lookup_msym_prefer): New enum.
+	(lookup_minimal_symbol_by_pc_section): Replace 'want_trampoline'
+	parameter by a lookup_msym_prefer parameter.
+	* symtab.h (find_gnu_ifunc): New declaration.
+
+2018-04-26  Pedro Alves  <palves@redhat.com>
+
 	* blockframe.c (find_gnu_ifunc_target_type): New function.
 	(find_function_type): New.
 	* eval.c (evaluate_var_msym_value): For GNU ifunc types, always
diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index e2ea07c..723249c 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -1041,10 +1041,22 @@ variable:	name_not_typename
 			      if (symbol_read_needs_frame (sym.symbol))
 				innermost_block.update (sym);
 
-			      write_exp_elt_opcode (pstate, OP_VAR_VALUE);
-			      write_exp_elt_block (pstate, sym.block);
-			      write_exp_elt_sym (pstate, sym.symbol);
-			      write_exp_elt_opcode (pstate, OP_VAR_VALUE);
+			      /* If we found a function, see if it's
+				 an ifunc resolver that has the same
+				 address as the ifunc symbol itself.
+				 If so, prefer the ifunc symbol.  */
+
+			      bound_minimal_symbol resolver
+				= find_gnu_ifunc (sym.symbol);
+			      if (resolver.minsym != NULL)
+				write_exp_msymbol (pstate, resolver);
+			      else
+				{
+				  write_exp_elt_opcode (pstate, OP_VAR_VALUE);
+				  write_exp_elt_block (pstate, sym.block);
+				  write_exp_elt_sym (pstate, sym.symbol);
+				  write_exp_elt_opcode (pstate, OP_VAR_VALUE);
+				}
 			    }
 			  else if ($1.is_a_field_of_this)
 			    {
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 7ef8012..8951c1e 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -4343,6 +4343,7 @@ add_minsym (struct minimal_symbol *minsym, struct objfile *objfile,
 
   struct bound_minimal_symbol mo = {minsym, objfile};
   msyms->push_back (mo);
+  return;
 }
 
 /* Search for minimal symbols called NAME.  If SEARCH_PSPACE
@@ -4383,6 +4384,7 @@ search_minsyms_for_name (struct collect_info *info,
 					    add_minsym (msym, objfile, nullptr,
 							info->state->list_mode,
 							&minsyms);
+					    return false;
 					  });
 	}
       }
@@ -4398,6 +4400,7 @@ search_minsyms_for_name (struct collect_info *info,
 	       {
 		 add_minsym (msym, SYMTAB_OBJFILE (symtab), symtab,
 			     info->state->list_mode, &minsyms);
+		 return false;
 	       });
 	}
     }
diff --git a/gdb/minsyms.c b/gdb/minsyms.c
index 9d23c4f..7ca3fcc 100644
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -471,7 +471,7 @@ linkage_name_str (const lookup_name_info &lookup_name)
 void
 iterate_over_minimal_symbols
     (struct objfile *objf, const lookup_name_info &lookup_name,
-     gdb::function_view<void (struct minimal_symbol *)> callback)
+     gdb::function_view<bool (struct minimal_symbol *)> callback)
 {
   /* The first pass is over the ordinary hash table.  */
     {
@@ -487,7 +487,8 @@ iterate_over_minimal_symbols
 	   iter = iter->hash_next)
 	{
 	  if (mangled_cmp (MSYMBOL_LINKAGE_NAME (iter), name) == 0)
-	    callback (iter);
+	    if (callback (iter))
+	      return;
 	}
     }
 
@@ -506,7 +507,8 @@ iterate_over_minimal_symbols
 	   iter != NULL;
 	   iter = iter->demangled_hash_next)
 	if (name_match (MSYMBOL_SEARCH_NAME (iter), lookup_name, NULL))
-	  callback (iter);
+	  if (callback (iter))
+	    return;
     }
 }
 
diff --git a/gdb/minsyms.h b/gdb/minsyms.h
index a2b7ddd..29d8283 100644
--- a/gdb/minsyms.h
+++ b/gdb/minsyms.h
@@ -269,7 +269,7 @@ struct bound_minimal_symbol lookup_minimal_symbol_by_pc (CORE_ADDR);
 
 void iterate_over_minimal_symbols
     (struct objfile *objf, const lookup_name_info &name,
-     gdb::function_view<void (struct minimal_symbol *)> callback);
+     gdb::function_view<bool (struct minimal_symbol *)> callback);
 
 /* Compute the upper bound of MINSYM.  The upper bound is the last
    address thought to be part of the symbol.  If the symbol has a
diff --git a/gdb/symtab.c b/gdb/symtab.c
index c1ead70..92b7ed7 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4953,6 +4953,38 @@ symbol_is_function_or_method (minimal_symbol *msymbol)
     }
 }
 
+/* See symtab.h.  */
+
+bound_minimal_symbol
+find_gnu_ifunc (const symbol *sym)
+{
+  if (SYMBOL_CLASS (sym) != LOC_BLOCK)
+    return {};
+
+  lookup_name_info lookup_name (SYMBOL_SEARCH_NAME (sym),
+				symbol_name_match_type::SEARCH_NAME);
+  struct objfile *objfile = symbol_objfile (sym);
+
+  CORE_ADDR address = BLOCK_START (SYMBOL_BLOCK_VALUE (sym));
+  minimal_symbol *ifunc = NULL;
+
+  iterate_over_minimal_symbols (objfile, lookup_name,
+				[&] (minimal_symbol *minsym)
+    {
+      if (MSYMBOL_TYPE (minsym) == mst_text_gnu_ifunc
+	  && MSYMBOL_VALUE_ADDRESS (objfile, minsym) == address)
+	{
+	  ifunc = minsym;
+	  return true;
+	}
+      return false;
+    });
+
+  if (ifunc != NULL)
+    return {ifunc, objfile};
+  return {};
+}
+
 /* Add matching symbols from SYMTAB to the current completion list.  */
 
 static void
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 83ff6f2..94b6b24 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1686,6 +1686,9 @@ extern struct type *find_function_type (CORE_ADDR pc);
 
 extern struct type *find_gnu_ifunc_target_type (CORE_ADDR resolver_funaddr);
 
+/* Find the GNU ifunc minimal symbol that matches SYM.  */
+extern bound_minimal_symbol find_gnu_ifunc (const symbol *sym);
+
 extern void clear_pc_function_cache (void);
 
 /* Expand symtab containing PC, SECTION if not already expanded.  */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]