Calling ifunc functions when resolver has debug info, user symbol same name
authorPedro Alves <palves@redhat.com>
Thu, 26 Apr 2018 12:01:26 +0000 (13:01 +0100)
committerPedro Alves <palves@redhat.com>
Thu, 26 Apr 2018 12:05:29 +0000 (13:05 +0100)
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.

gdb/ChangeLog
gdb/c-exp.y
gdb/linespec.c
gdb/minsyms.c
gdb/minsyms.h
gdb/symtab.c
gdb/symtab.h

index 8db2d8aeb6d59d3faaa515f5af3d368904b882b5..67e1babcba2889add2c8bfcf65516d76b1d08beb 100644 (file)
@@ -1,3 +1,13 @@
+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.
index e2ea07cd792836f1b3b08ba6b5b3391e863e1633..723249c1f58d58df32ab12366c38f40767da8659 100644 (file)
@@ -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)
                            {
index 7ef8012ab562f628c24e919b462634d055af4c4d..8951c1e8c4736026f247dd85120bf4d8b037fe14 100644 (file)
@@ -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;
               });
        }
     }
index 9d23c4fd4fd269f8cbd34b14c1fe0c921a3c0458..7ca3fcccbea9a6781199e2bed75382f7a71dd17b 100644 (file)
@@ -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;
     }
 }
 
index a2b7ddd7035e0d00c65c8ff0184993dbc26d269a..29d8283c214823a6ad1c500f89b0e4889bd1cf65 100644 (file)
@@ -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
index c1ead701eceea3dc7beb78e3298850f5026c88e8..92b7ed7538cae4ed94086b15f83f2602556066bd 100644 (file)
@@ -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
index 83ff6f226d85cd7b2e9347ded0f81c0b27955c99..94b6b24bd5dd87b74eb1a17fcebcd364332d2b33 100644 (file)
@@ -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.  */