Pass HWCAP to ifunc resolver
authorAndreas Arnez <arnez@linux.vnet.ibm.com>
Fri, 9 Sep 2016 17:59:53 +0000 (19:59 +0200)
committerAndreas Arnez <arnez@linux.vnet.ibm.com>
Fri, 9 Sep 2016 17:59:53 +0000 (19:59 +0200)
On various GNU Elf architectures, including AArch64, ARM, s390/s390x,
ppc32/64, and sparc32/64, the dynamic loader passes HWCAP as a parameter
to each ifunc resolver.  Currently there is an open glibc Bugzilla that
requests this to be generalized to all architectures:

  https://sourceware.org/bugzilla/show_bug.cgi?id=19766

And various ifunc resolvers already rely on receiving HWCAP.  Currently
GDB always calls an ifunc resolver without any arguments; thus the
resolver may receive garbage, and based on that, the resolver may decide
to return a function that is not suited for the given platform.

This patch always passes HWCAP to ifunc resolvers, even on systems where
the dynamic loader currently behaves otherwise.  The rationale is
that (1) the dynamic loader may get adjusted on those systems as well in
the future; (2) passing an unused argument should not cause a problem
with existing resolvers; and (3) the logic is much simpler without such
a distinction.

gdb/ChangeLog:

* elfread.c (auxv.h): New include.
(elf_gnu_ifunc_resolve_addr): Pass HWCAP to ifunc resolver.

gdb/testsuite/ChangeLog:

* gdb.base/gnu-ifunc-lib.c (resolver_hwcap): New external
variable declaration.
(gnu_ifunc): Add parameter hwcap.  Store it in resolver_hwcap.
* gdb.base/gnu-ifunc.c (resolver_hwcap): New global variable.
* gdb.base/gnu-ifunc.exp: Add test to verify that the resolver
received HWCAP as its argument.

gdb/ChangeLog
gdb/elfread.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/gnu-ifunc-lib.c
gdb/testsuite/gdb.base/gnu-ifunc.c
gdb/testsuite/gdb.base/gnu-ifunc.exp

index e7cc7122d8c12dd820650e2472836d0835b6d3c2..b58591466a81dc8c064174e2e2b171b2d448c5f0 100644 (file)
@@ -1,3 +1,8 @@
+2016-09-09  Andreas Arnez  <arnez@linux.vnet.ibm.com>
+
+       * elfread.c (auxv.h): New include.
+       (elf_gnu_ifunc_resolve_addr): Pass HWCAP to ifunc resolver.
+
 2016-09-08  Tom Tromey  <tom@tromey.com>
 
        * remote.c (remote_notif_stop_ack, remote_wait_as)
index e90466b00cb43a6ad5f74bd1ca8be5cdeb27a68b..84355cf032f5f10ea403f4c3e9012ed7bf40a0ef 100644 (file)
@@ -46,6 +46,7 @@
 #include "gdb_bfd.h"
 #include "build-id.h"
 #include "location.h"
+#include "auxv.h"
 
 extern void _initialize_elfread (void);
 
@@ -860,6 +861,8 @@ elf_gnu_ifunc_resolve_addr (struct gdbarch *gdbarch, CORE_ADDR pc)
   CORE_ADDR start_at_pc, address;
   struct type *func_func_type = builtin_type (gdbarch)->builtin_func_func;
   struct value *function, *address_val;
+  CORE_ADDR hwcap = 0;
+  struct value *hwcap_val;
 
   /* Try first any non-intrusive methods without an inferior call.  */
 
@@ -875,10 +878,14 @@ elf_gnu_ifunc_resolve_addr (struct gdbarch *gdbarch, CORE_ADDR pc)
   function = allocate_value (func_func_type);
   set_value_address (function, pc);
 
-  /* STT_GNU_IFUNC resolver functions have no parameters.  FUNCTION is the
-     function entry address.  ADDRESS may be a function descriptor.  */
+  /* STT_GNU_IFUNC resolver functions usually receive the HWCAP vector as
+     parameter.  FUNCTION is the function entry address.  ADDRESS may be a
+     function descriptor.  */
 
-  address_val = call_function_by_hand (function, 0, NULL);
+  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 = value_as_address (address_val);
   address = gdbarch_convert_from_func_ptr_addr (gdbarch, address,
                                                &current_target);
index 958ec27b6264294f285316e137623011afc7002e..783e30fedb0968a70e4289df82ce949a8a0fef52 100644 (file)
@@ -1,3 +1,12 @@
+2016-09-09  Andreas Arnez  <arnez@linux.vnet.ibm.com>
+
+       * gdb.base/gnu-ifunc-lib.c (resolver_hwcap): New external
+       variable declaration.
+       (gnu_ifunc): Add parameter hwcap.  Store it in resolver_hwcap.
+       * gdb.base/gnu-ifunc.c (resolver_hwcap): New global variable.
+       * gdb.base/gnu-ifunc.exp: Add test to verify that the resolver
+       received HWCAP as its argument.
+
 2016-09-06  Pedro Alves  <palves@redhat.com>
 
        * gdb.base/new-ui-pending-input.c: New file.
index 8a55f600396f3864cff60e0237566b257e212eb4..0c0aeef364f15a93bc985d4666b0a873148de6ef 100644 (file)
@@ -16,6 +16,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 extern volatile int gnu_ifunc_initialized;
+extern volatile unsigned long resolver_hwcap;
 extern int init_stub (int arg);
 extern int final (int arg);
 
@@ -24,8 +25,9 @@ typedef int (*final_t) (int arg);
 asm (".type gnu_ifunc, %gnu_indirect_function");
 
 final_t
-gnu_ifunc (void)
+gnu_ifunc (unsigned long hwcap)
 {
+  resolver_hwcap = hwcap;
   if (! gnu_ifunc_initialized)
     return init_stub;
   else
index c68866ec84d737104f235d9c996ae5763e5710e2..77dea30d96b75ed854c98a3ef0a3c91e4d7479e2 100644 (file)
@@ -35,6 +35,10 @@ final (int arg)
 
 volatile int gnu_ifunc_initialized;
 
+/* This stores the argument received by the ifunc resolver.  */
+
+volatile unsigned long resolver_hwcap = -1;
+
 static void
 gnu_ifunc_pre (void)
 {
index 097e48a9a0c743e8df75a3b890ac1874ec6eaeea..3b2775b8c76e7ebdbd9886c0450490b2dfcda9a9 100644 (file)
@@ -76,6 +76,21 @@ gdb_continue_to_breakpoint "break-at-call" ".*break-at-call.*"
 
 gdb_test "p gnu_ifunc (3)" " = 4"
 
+# Test that the resolver received its argument.
+
+set actual_hwcap "0x0"
+set test "info auxv"
+gdb_test_multiple $test $test {
+    -re "\r\n\\d+\\s+AT_HWCAP\[^\r\n\]+($hex)\r\n.*$gdb_prompt $" {
+       set actual_hwcap $expect_out(1,string)
+    }
+    -re ".*$gdb_prompt $" {
+       pass "$test (no HWCAP)"
+    }
+}
+
+gdb_test "p/x resolver_hwcap" "= $actual_hwcap" "resolver received HWCAP"
+
 # Test GDB will skip the gnu_ifunc resolver on first call.
 
 gdb_test "step" "\r\nfinal .*"