gdb/
authorJan Kratochvil <jan.kratochvil@redhat.com>
Mon, 28 Mar 2011 20:29:51 +0000 (20:29 +0000)
committerJan Kratochvil <jan.kratochvil@redhat.com>
Mon, 28 Mar 2011 20:29:51 +0000 (20:29 +0000)
Support resolution of STT_GNU_IFUNC via breakpoints.
* breakpoint.c (print_it_typical): Support bp_gnu_ifunc_resolver and
bp_gnu_ifunc_resolver_return.
(bpstat_what): Rename parameter to bs_head, new variable bs, adjust
the loop.  Support bp_gnu_ifunc_resolver and
bp_gnu_ifunc_resolver_return.  New comment after the loop.  New loop
for bp_gnu_ifunc_resolver and bp_gnu_ifunc_resolver_return
breakpoints.
(bptype_string, print_one_breakpoint_location): Support
bp_gnu_ifunc_resolver and bp_gnu_ifunc_resolver_return.
(user_settable_breakpoint): Return true also for
bp_gnu_ifunc_resolver.
(allocate_bp_location): Support bp_gnu_ifunc_resolver and
bp_gnu_ifunc_resolver_return.
(set_breakpoint_location_function): New parameter explicit_loc,
describe it.  Call find_pc_partial_function_gnu_ifunc with new
variable IS_GNU_IFUNC and adjust the address for STT_GNU_IFUNC if
EXPLICIT_LOC is not set.
(set_raw_breakpoint): Set EXPLICIT_LOC for
set_breakpoint_location_function.
(clone_momentary_breakpoint): Use true for EXPLICIT_LOC of
set_breakpoint_location_function.
(mention): Support bp_gnu_ifunc_resolver and
bp_gnu_ifunc_resolver_return.
(add_location_to_breakpoint): Set EXPLICIT_LOC for
set_breakpoint_location_function.
(update_breakpoint_locations): Remove static.
(breakpoint_re_set_one): Support bp_gnu_ifunc_resolver and
bp_gnu_ifunc_resolver_return.
* breakpoint.h (enum bptype): New fields bp_gnu_ifunc_resolver and
bp_gnu_ifunc_resolver_return.
(update_breakpoint_locations): New declaration.
* elfread.c: Include gdbthread.h and regcache.h.
(elf_gnu_ifunc_resolver_stop, elf_gnu_ifunc_resolver_return_stop): New
functions.
(elf_gnu_ifunc_fns): Install them.
* minsyms.c (stub_gnu_ifunc_resolver_stop)
(stub_gnu_ifunc_resolver_return_stop): New functions.
(stub_gnu_ifunc_fns): Install them.
* symtab.h (struct gnu_ifunc_fns): New fields gnu_ifunc_resolver_stop
and gnu_ifunc_resolver_return_stop.
(gnu_ifunc_resolver_stop, gnu_ifunc_resolver_return_stop): New.

gdb/ChangeLog
gdb/breakpoint.c
gdb/breakpoint.h
gdb/elfread.c
gdb/minsyms.c
gdb/symtab.h

index 08952867b299bbf6c360f934d49c08e020a44071..03885dd7beba70c3a3f8e370865b962630167913 100644 (file)
@@ -1,3 +1,48 @@
+2011-03-28  Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+       Support resolution of STT_GNU_IFUNC via breakpoints.
+       * breakpoint.c (print_it_typical): Support bp_gnu_ifunc_resolver and
+       bp_gnu_ifunc_resolver_return.
+       (bpstat_what): Rename parameter to bs_head, new variable bs, adjust
+       the loop.  Support bp_gnu_ifunc_resolver and
+       bp_gnu_ifunc_resolver_return.  New comment after the loop.  New loop
+       for bp_gnu_ifunc_resolver and bp_gnu_ifunc_resolver_return
+       breakpoints.
+       (bptype_string, print_one_breakpoint_location): Support
+       bp_gnu_ifunc_resolver and bp_gnu_ifunc_resolver_return.
+       (user_settable_breakpoint): Return true also for
+       bp_gnu_ifunc_resolver.
+       (allocate_bp_location): Support bp_gnu_ifunc_resolver and
+       bp_gnu_ifunc_resolver_return.
+       (set_breakpoint_location_function): New parameter explicit_loc,
+       describe it.  Call find_pc_partial_function_gnu_ifunc with new
+       variable IS_GNU_IFUNC and adjust the address for STT_GNU_IFUNC if
+       EXPLICIT_LOC is not set.
+       (set_raw_breakpoint): Set EXPLICIT_LOC for
+       set_breakpoint_location_function.
+       (clone_momentary_breakpoint): Use true for EXPLICIT_LOC of
+       set_breakpoint_location_function.
+       (mention): Support bp_gnu_ifunc_resolver and
+       bp_gnu_ifunc_resolver_return.
+       (add_location_to_breakpoint): Set EXPLICIT_LOC for
+       set_breakpoint_location_function.
+       (update_breakpoint_locations): Remove static.
+       (breakpoint_re_set_one): Support bp_gnu_ifunc_resolver and
+       bp_gnu_ifunc_resolver_return.
+       * breakpoint.h (enum bptype): New fields bp_gnu_ifunc_resolver and
+       bp_gnu_ifunc_resolver_return.
+       (update_breakpoint_locations): New declaration.
+       * elfread.c: Include gdbthread.h and regcache.h.
+       (elf_gnu_ifunc_resolver_stop, elf_gnu_ifunc_resolver_return_stop): New
+       functions.
+       (elf_gnu_ifunc_fns): Install them.
+       * minsyms.c (stub_gnu_ifunc_resolver_stop)
+       (stub_gnu_ifunc_resolver_return_stop): New functions.
+       (stub_gnu_ifunc_fns): Install them.
+       * symtab.h (struct gnu_ifunc_fns): New fields gnu_ifunc_resolver_stop
+       and gnu_ifunc_resolver_return_stop.
+       (gnu_ifunc_resolver_stop, gnu_ifunc_resolver_return_stop): New.
+
 2011-03-28  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        STT_GNU_IFUNC reader implementation.
index 1a9d9639d46cc8e3942734acad12f3f9e6573f09..c300df96ffa26e84858ef6e34d652bb846e055be 100644 (file)
@@ -3504,6 +3504,8 @@ print_it_typical (bpstat bs)
     case bp_tracepoint:
     case bp_fast_tracepoint:
     case bp_jit_event:
+    case bp_gnu_ifunc_resolver:
+    case bp_gnu_ifunc_resolver_return:
     default:
       result = PRINT_UNKNOWN;
       break;
@@ -4378,7 +4380,7 @@ handle_jit_event (void)
 /* Decide what infrun needs to do with this bpstat.  */
 
 struct bpstat_what
-bpstat_what (bpstat bs)
+bpstat_what (bpstat bs_head)
 {
   struct bpstat_what retval;
   /* We need to defer calling `solib_add', as adding new symbols
@@ -4386,12 +4388,13 @@ bpstat_what (bpstat bs)
      and hence may clear unprocessed entries in the BS chain.  */
   int shlib_event = 0;
   int jit_event = 0;
+  bpstat bs;
 
   retval.main_action = BPSTAT_WHAT_KEEP_CHECKING;
   retval.call_dummy = STOP_NONE;
   retval.is_longjmp = 0;
 
-  for (; bs != NULL; bs = bs->next)
+  for (bs = bs_head; bs != NULL; bs = bs->next)
     {
       /* Extract this BS's action.  After processing each BS, we check
         if its action overrides all we've seem so far.  */
@@ -4521,6 +4524,16 @@ bpstat_what (bpstat bs)
             out already.  */
          internal_error (__FILE__, __LINE__,
                          _("bpstat_what: tracepoint encountered"));
+         break;
+       case bp_gnu_ifunc_resolver:
+         /* Step over it (and insert bp_gnu_ifunc_resolver_return).  */
+         this_action = BPSTAT_WHAT_SINGLE;
+         break;
+       case bp_gnu_ifunc_resolver_return:
+         /* The breakpoint will be removed, execution will restart from the
+            PC of the former breakpoint.  */
+         this_action = BPSTAT_WHAT_KEEP_CHECKING;
+         break;
        default:
          internal_error (__FILE__, __LINE__,
                          _("bpstat_what: unhandled bptype %d"), (int) bptype);
@@ -4529,6 +4542,9 @@ bpstat_what (bpstat bs)
       retval.main_action = max (retval.main_action, this_action);
     }
 
+  /* These operations may affect the bs->breakpoint_at state so they are
+     delayed after MAIN_ACTION is decided above.  */
+
   if (shlib_event)
     {
       if (debug_infrun)
@@ -4558,6 +4574,23 @@ bpstat_what (bpstat bs)
       handle_jit_event ();
     }
 
+  for (bs = bs_head; bs != NULL; bs = bs->next)
+    {
+      struct breakpoint *b = bs->breakpoint_at;
+
+      if (b == NULL)
+       continue;
+      switch (b->type)
+       {
+       case bp_gnu_ifunc_resolver:
+         gnu_ifunc_resolver_stop (b);
+         break;
+       case bp_gnu_ifunc_resolver_return:
+         gnu_ifunc_resolver_return_stop (b);
+         break;
+       }
+    }
+
   return retval;
 }
 
@@ -4715,6 +4748,8 @@ bptype_string (enum bptype type)
     {bp_fast_tracepoint, "fast tracepoint"},
     {bp_static_tracepoint, "static tracepoint"},
     {bp_jit_event, "jit events"},
+    {bp_gnu_ifunc_resolver, "STT_GNU_IFUNC resolver"},
+    {bp_gnu_ifunc_resolver_return, "STT_GNU_IFUNC resolver return"},
   };
 
   if (((int) type >= (sizeof (bptypes) / sizeof (bptypes[0])))
@@ -4849,6 +4884,8 @@ print_one_breakpoint_location (struct breakpoint *b,
       case bp_fast_tracepoint:
       case bp_static_tracepoint:
       case bp_jit_event:
+      case bp_gnu_ifunc_resolver:
+      case bp_gnu_ifunc_resolver_return:
        if (opts.addressprint)
          {
            annotate_field (4);
@@ -5124,7 +5161,8 @@ user_settable_breakpoint (const struct breakpoint *b)
          || b->type == bp_catchpoint
          || b->type == bp_hardware_breakpoint
          || is_tracepoint (b)
-         || is_watchpoint (b));
+         || is_watchpoint (b)
+         || b->type == bp_gnu_ifunc_resolver);
 }
 
 /* Return true if this breakpoint was set by the user, false if it is
@@ -5620,6 +5658,8 @@ allocate_bp_location (struct breakpoint *bpt)
     case bp_longjmp_master:
     case bp_std_terminate_master:
     case bp_exception_master:
+    case bp_gnu_ifunc_resolver:
+    case bp_gnu_ifunc_resolver_return:
       loc->loc_type = bp_loc_software_breakpoint;
       break;
     case bp_hardware_breakpoint:
@@ -5726,9 +5766,12 @@ set_raw_breakpoint_without_location (struct gdbarch *gdbarch,
   return b;
 }
 
-/* Initialize loc->function_name.  */
+/* Initialize loc->function_name.  EXPLICIT_LOC says no indirect function
+   resolutions should be made as the user specified the location explicitly
+   enough.  */
+
 static void
-set_breakpoint_location_function (struct bp_location *loc)
+set_breakpoint_location_function (struct bp_location *loc, int explicit_loc)
 {
   gdb_assert (loc->owner != NULL);
 
@@ -5736,8 +5779,33 @@ set_breakpoint_location_function (struct bp_location *loc)
       || loc->owner->type == bp_hardware_breakpoint
       || is_tracepoint (loc->owner))
     {
-      find_pc_partial_function (loc->address, &(loc->function_name), 
-                               NULL, NULL);
+      int is_gnu_ifunc;
+
+      find_pc_partial_function_gnu_ifunc (loc->address, &loc->function_name,
+                                         NULL, NULL, &is_gnu_ifunc);
+
+      if (is_gnu_ifunc && !explicit_loc)
+       {
+         struct breakpoint *b = loc->owner;
+
+         gdb_assert (loc->pspace == current_program_space);
+         if (gnu_ifunc_resolve_name (loc->function_name,
+                                     &loc->requested_address))
+           {
+             /* Recalculate ADDRESS based on new REQUESTED_ADDRESS.  */
+             loc->address = adjust_breakpoint_address (loc->gdbarch,
+                                                       loc->requested_address,
+                                                       b->type);
+           }
+         else if (b->type == bp_breakpoint && b->loc == loc
+                  && loc->next == NULL && b->related_breakpoint == b)
+           {
+             /* Create only the whole new breakpoint of this type but do not
+                mess more complicated breakpoints with multiple locations.  */
+             b->type = bp_gnu_ifunc_resolver;
+           }
+       }
+
       if (loc->function_name)
        loc->function_name = xstrdup (loc->function_name);
     }
@@ -5812,7 +5880,8 @@ set_raw_breakpoint (struct gdbarch *gdbarch,
   b->loc->section = sal.section;
   b->line_number = sal.line;
 
-  set_breakpoint_location_function (b->loc);
+  set_breakpoint_location_function (b->loc,
+                                   sal.explicit_pc || sal.explicit_line);
 
   breakpoints_changed ();
 
@@ -6929,7 +6998,7 @@ clone_momentary_breakpoint (struct breakpoint *orig)
 
   copy = set_raw_breakpoint_without_location (orig->gdbarch, orig->type);
   copy->loc = allocate_bp_location (copy);
-  set_breakpoint_location_function (copy->loc);
+  set_breakpoint_location_function (copy->loc, 1);
 
   copy->loc->gdbarch = orig->loc->gdbarch;
   copy->loc->requested_address = orig->loc->requested_address;
@@ -7029,6 +7098,7 @@ mention (struct breakpoint *b)
        do_cleanups (ui_out_chain);
        break;
       case bp_breakpoint:
+      case bp_gnu_ifunc_resolver:
        if (ui_out_is_mi_like_p (uiout))
          {
            say_where = 0;
@@ -7039,6 +7109,8 @@ mention (struct breakpoint *b)
        else
          printf_filtered (_("Breakpoint"));
        printf_filtered (_(" %d"), b->number);
+       if (b->type == bp_gnu_ifunc_resolver)
+         printf_filtered (_(" at gnu-indirect-function resolver"));
        say_where = 1;
        break;
       case bp_hardware_breakpoint:
@@ -7098,6 +7170,7 @@ mention (struct breakpoint *b)
       case bp_longjmp_master:
       case bp_std_terminate_master:
       case bp_exception_master:
+      case bp_gnu_ifunc_resolver_return:
        break;
       }
 
@@ -7158,7 +7231,8 @@ add_location_to_breakpoint (struct breakpoint *b,
   gdb_assert (loc->pspace != NULL);
   loc->section = sal->section;
 
-  set_breakpoint_location_function (loc);
+  set_breakpoint_location_function (loc,
+                                   sal->explicit_pc || sal->explicit_line);
   return loc;
 }
 \f
@@ -10399,7 +10473,7 @@ update_static_tracepoint (struct breakpoint *b, struct symtab_and_line sal)
   return sal;
 }
 
-static void
+void
 update_breakpoint_locations (struct breakpoint *b,
                             struct symtabs_and_lines sals)
 {
@@ -10531,6 +10605,7 @@ breakpoint_re_set_one (void *bint)
     case bp_tracepoint:
     case bp_fast_tracepoint:
     case bp_static_tracepoint:
+    case bp_gnu_ifunc_resolver:
       /* Do not attempt to re-set breakpoints disabled during startup.  */
       if (b->enable_state == bp_startup_disabled)
        return 0;
@@ -10701,6 +10776,7 @@ breakpoint_re_set_one (void *bint)
     case bp_exception:
     case bp_exception_resume:
     case bp_jit_event:
+    case bp_gnu_ifunc_resolver_return:
       break;
     }
 
index d5af928f7d324defe38aa4b0a9b00230b4c665d0..2cb56b744884916e2b33b09a198d4ef65c3039f5 100644 (file)
@@ -149,6 +149,19 @@ enum bptype
 
     /* Event for JIT compiled code generation or deletion.  */
     bp_jit_event,
+
+    /* Breakpoint is placed at the STT_GNU_IFUNC resolver.  When hit GDB
+       inserts new bp_gnu_ifunc_resolver_return at the caller.
+       bp_gnu_ifunc_resolver is still being kept here as a different thread
+       may still hit it before bp_gnu_ifunc_resolver_return is hit by the
+       original thread.  */
+    bp_gnu_ifunc_resolver,
+
+    /* On its hit GDB now know the resolved address of the target
+       STT_GNU_IFUNC function.  Associated bp_gnu_ifunc_resolver can be
+       deleted now and the breakpoint moved to the target function entry
+       point.  */
+    bp_gnu_ifunc_resolver_return,
   };
 
 /* States of enablement of breakpoint.  */
@@ -890,6 +903,9 @@ extern int breakpoint_thread_match (struct address_space *,
 
 extern void until_break_command (char *, int, int);
 
+extern void update_breakpoint_locations (struct breakpoint *b,
+                                        struct symtabs_and_lines sals);
+
 extern void breakpoint_re_set (void);
 
 extern void breakpoint_re_set_thread (struct breakpoint *);
index 115251c036f00420f35e8178dea35672091fc12c..68bed7ebca7312628b189ec7a6b94d23e741bc42 100644 (file)
@@ -41,6 +41,8 @@
 #include "gdbtypes.h"
 #include "value.h"
 #include "infcall.h"
+#include "gdbthread.h"
+#include "regcache.h"
 
 extern void _initialize_elfread (void);
 
@@ -948,6 +950,111 @@ elf_gnu_ifunc_resolve_addr (struct gdbarch *gdbarch, CORE_ADDR pc)
   return address;
 }
 
+/* Handle inferior hit of bp_gnu_ifunc_resolver, see its definition.  */
+
+static void
+elf_gnu_ifunc_resolver_stop (struct breakpoint *b)
+{
+  struct breakpoint *b_return;
+  struct frame_info *prev_frame = get_prev_frame (get_current_frame ());
+  struct frame_id prev_frame_id = get_stack_frame_id (prev_frame);
+  CORE_ADDR prev_pc = get_frame_pc (prev_frame);
+  int thread_id = pid_to_thread_id (inferior_ptid);
+
+  gdb_assert (b->type == bp_gnu_ifunc_resolver);
+
+  for (b_return = b->related_breakpoint; b_return != b;
+       b_return = b_return->related_breakpoint)
+    {
+      gdb_assert (b_return->type == bp_gnu_ifunc_resolver_return);
+      gdb_assert (b_return->loc != NULL && b_return->loc->next == NULL);
+      gdb_assert (frame_id_p (b_return->frame_id));
+
+      if (b_return->thread == thread_id
+         && b_return->loc->requested_address == prev_pc
+         && frame_id_eq (b_return->frame_id, prev_frame_id))
+       break;
+    }
+
+  if (b_return == b)
+    {
+      struct symtab_and_line sal;
+
+      /* No need to call find_pc_line for symbols resolving as this is only
+        a helper breakpointer never shown to the user.  */
+
+      init_sal (&sal);
+      sal.pspace = current_inferior ()->pspace;
+      sal.pc = prev_pc;
+      sal.section = find_pc_overlay (sal.pc);
+      sal.explicit_pc = 1;
+      b_return = set_momentary_breakpoint (get_frame_arch (prev_frame), sal,
+                                          prev_frame_id,
+                                          bp_gnu_ifunc_resolver_return);
+
+      /* Add new b_return to the ring list b->related_breakpoint.  */
+      gdb_assert (b_return->related_breakpoint == b_return);
+      b_return->related_breakpoint = b->related_breakpoint;
+      b->related_breakpoint = b_return;
+    }
+}
+
+/* Handle inferior hit of bp_gnu_ifunc_resolver_return, see its definition.  */
+
+static void
+elf_gnu_ifunc_resolver_return_stop (struct breakpoint *b)
+{
+  struct gdbarch *gdbarch = get_frame_arch (get_current_frame ());
+  struct type *func_func_type = builtin_type (gdbarch)->builtin_func_func;
+  struct type *value_type = TYPE_TARGET_TYPE (func_func_type);
+  struct regcache *regcache = get_thread_regcache (inferior_ptid);
+  struct value *value;
+  CORE_ADDR resolved_address, resolved_pc;
+  struct symtab_and_line sal;
+  struct symtabs_and_lines sals;
+
+  gdb_assert (b->type == bp_gnu_ifunc_resolver_return);
+
+  value = allocate_value (value_type);
+  gdbarch_return_value (gdbarch, func_func_type, value_type, regcache,
+                       value_contents_raw (value), NULL);
+  resolved_address = value_as_address (value);
+  resolved_pc = gdbarch_convert_from_func_ptr_addr (gdbarch,
+                                                   resolved_address,
+                                                   &current_target);
+
+  while (b->related_breakpoint != b)
+    {
+      struct breakpoint *b_next = b->related_breakpoint;
+
+      switch (b->type)
+       {
+       case bp_gnu_ifunc_resolver:
+         break;
+       case bp_gnu_ifunc_resolver_return:
+         delete_breakpoint (b);
+         break;
+       default:
+         internal_error (__FILE__, __LINE__,
+                         _("handle_inferior_event: Invalid "
+                           "gnu-indirect-function breakpoint type %d"),
+                         (int) b->type);
+       }
+      b = b_next;
+    }
+  gdb_assert (b->type == bp_gnu_ifunc_resolver);
+
+  gdb_assert (current_program_space == b->pspace);
+  elf_gnu_ifunc_record_cache (b->addr_string, resolved_pc);
+
+  sal = find_pc_line (resolved_pc, 0);
+  sals.nelts = 1;
+  sals.sals = &sal;
+
+  b->type = bp_breakpoint;
+  update_breakpoint_locations (b, sals);
+}
+
 struct build_id
   {
     size_t size;
@@ -1502,6 +1609,8 @@ static const struct gnu_ifunc_fns elf_gnu_ifunc_fns =
 {
   elf_gnu_ifunc_resolve_addr,
   elf_gnu_ifunc_resolve_name,
+  elf_gnu_ifunc_resolver_stop,
+  elf_gnu_ifunc_resolver_return_stop
 };
 
 void
index 80f9497922da1a66f044445e549813fa407057c7..b054e3ff9874ee7a6b9dda953b4cfd815b7bbe06 100644 (file)
@@ -729,12 +729,32 @@ stub_gnu_ifunc_resolve_name (const char *function_name,
         function_name);
 }
 
+/* See elf_gnu_ifunc_resolver_stop for its real implementation.  */
+
+static void
+stub_gnu_ifunc_resolver_stop (struct breakpoint *b)
+{
+  internal_error (__FILE__, __LINE__,
+                 _("elf_gnu_ifunc_resolver_stop cannot be reached."));
+}
+
+/* See elf_gnu_ifunc_resolver_return_stop for its real implementation.  */
+
+static void
+stub_gnu_ifunc_resolver_return_stop (struct breakpoint *b)
+{
+  internal_error (__FILE__, __LINE__,
+                 _("elf_gnu_ifunc_resolver_return_stop cannot be reached."));
+}
+
 /* See elf_gnu_ifunc_fns for its real implementation.  */
 
 static const struct gnu_ifunc_fns stub_gnu_ifunc_fns =
 {
   stub_gnu_ifunc_resolve_addr,
   stub_gnu_ifunc_resolve_name,
+  stub_gnu_ifunc_resolver_stop,
+  stub_gnu_ifunc_resolver_return_stop,
 };
 
 /* A placeholder for &elf_gnu_ifunc_fns.  */
index bc48d3c9b221317862158d3c3aad5806299fad29..abe5e8610ef112c0fa3c2a668576ca7cb41598df 100644 (file)
@@ -1055,10 +1055,19 @@ struct gnu_ifunc_fns
   /* See elf_gnu_ifunc_resolve_name for its real implementation.  */
   int (*gnu_ifunc_resolve_name) (const char *function_name,
                                 CORE_ADDR *function_address_p);
+
+  /* See elf_gnu_ifunc_resolver_stop for its real implementation.  */
+  void (*gnu_ifunc_resolver_stop) (struct breakpoint *b);
+
+  /* See elf_gnu_ifunc_resolver_return_stop for its real implementation.  */
+  void (*gnu_ifunc_resolver_return_stop) (struct breakpoint *b);
 };
 
 #define gnu_ifunc_resolve_addr gnu_ifunc_fns_p->gnu_ifunc_resolve_addr
 #define gnu_ifunc_resolve_name gnu_ifunc_fns_p->gnu_ifunc_resolve_name
+#define gnu_ifunc_resolver_stop gnu_ifunc_fns_p->gnu_ifunc_resolver_stop
+#define gnu_ifunc_resolver_return_stop \
+  gnu_ifunc_fns_p->gnu_ifunc_resolver_return_stop
 
 extern const struct gnu_ifunc_fns *gnu_ifunc_fns_p;