diff --git a/ChangeLog b/ChangeLog
index fc8f60b..23c0484 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2012-12-28  Alexander Ivchenko  <alexander.ivchenko@intel.com>
+
+	* gcc/target.def (TARGET_HAS_IFUNC): New target hook.
+	* gcc/doc/tm.texi.in (TARGET_HAS_IFUNC): New.
+	* gcc/doc/tm.texi: Regenerate.
+	* gcc/targhooks.h (default_have_ifunc): New.
+	* gcc/targhooks.c (default_have_ifunc): Ditto.
+	* gcc/config/linux-protos.h: New file.
+	* gcc/config.gcc: Added include of linux-protos.h.
+	* gcc/config/linux.h (TARGET_HAS_IFUNC): Using version of this
+	hook for linux which disables support of indirect functions in
+	android.
+	* gcc/config/host-linux.c (linux_has_ifunc): New.
+	* gcc/config/i386/i386.c (ix86_get_function_versions_dispatcher):
+	Using TARGET_HAS_IFUNC hook instead of HAVE_GNU_INDIRECT_FUNCTION.
+	* gcc/varasm.c (do_assemble_alias): Likewise.
+
 2012-12-20  Matthias Klose  <doko@ubuntu.com>
 
 	* Makefile.def (install-target-libgfortran): Depend on
diff --git a/gcc/config.gcc b/gcc/config.gcc
index db56819..e535218 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -637,6 +637,11 @@ case ${target} in
       native_system_header_dir=/include
       ;;
   esac
+  case $target in
+    *linux*)
+      tm_p_file="${tm_p_file} linux-protos.h"
+      ;;
+  esac
   # glibc / uclibc / bionic switch.
   # uclibc and bionic aren't usable for GNU/Hurd and neither for GNU/k*BSD.
   case $target in
diff --git a/gcc/config/host-linux.c b/gcc/config/host-linux.c
index b535758..c72db79 100644
--- a/gcc/config/host-linux.c
+++ b/gcc/config/host-linux.c
@@ -22,7 +22,7 @@
 #include "coretypes.h"
 #include "hosthooks.h"
 #include "hosthooks-def.h"
-
+#include "tm.h"
 
 /* Linux has a feature called exec-shield-randomize that perturbs the
    address of non-fixed mapped segments by a (relatively) small amount.
@@ -222,5 +222,13 @@ linux_gt_pch_use_address (void *base, size_t size, int fd, size_t offset)
   return 1;
 }
 
+/* Android does not support GNU indirect functions.  */
+
+bool
+linux_has_ifunc (void)
+{
+  return TARGET_ANDROID ? false : HAVE_GNU_INDIRECT_FUNCTION;
+}
+
 
 const struct host_hooks host_hooks = HOST_HOOKS_INITIALIZER;
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index b466a4f..c422fd9 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -29146,7 +29146,7 @@ make_name (tree decl, const char *suffix, bool make_unique)
   return global_var_name;
 }
 
-#if defined (ASM_OUTPUT_TYPE_DIRECTIVE) && HAVE_GNU_INDIRECT_FUNCTION
+#if defined (ASM_OUTPUT_TYPE_DIRECTIVE)
 
 /* Make a dispatcher declaration for the multi-versioned function DECL.
    Calls to DECL function will be replaced with calls to the dispatcher
@@ -29213,7 +29213,7 @@ ix86_get_function_versions_dispatcher (void *decl)
 
   tree dispatch_decl = NULL;
 
-#if defined (ASM_OUTPUT_TYPE_DIRECTIVE) && HAVE_GNU_INDIRECT_FUNCTION
+#if defined (ASM_OUTPUT_TYPE_DIRECTIVE)
   struct cgraph_function_version_info *it_v = NULL;
   struct cgraph_node *dispatcher_node = NULL;
   struct cgraph_function_version_info *dispatcher_version_info = NULL;
@@ -29263,24 +29263,33 @@ ix86_get_function_versions_dispatcher (void *decl)
 
   default_node = default_version_info->this_node;
 
-#if defined (ASM_OUTPUT_TYPE_DIRECTIVE) && HAVE_GNU_INDIRECT_FUNCTION
-  /* Right now, the dispatching is done via ifunc.  */
-  dispatch_decl = make_dispatcher_decl (default_node->symbol.decl); 
-
-  dispatcher_node = cgraph_get_create_node (dispatch_decl);
-  gcc_assert (dispatcher_node != NULL);
-  dispatcher_node->dispatcher_function = 1;
-  dispatcher_version_info
-    = insert_new_cgraph_node_version (dispatcher_node);
-  dispatcher_version_info->next = default_version_info;
-  dispatcher_node->local.finalized = 1;
- 
-  /* Set the dispatcher for all the versions.  */ 
-  it_v = default_version_info;
-  while (it_v->next != NULL)
+#if defined (ASM_OUTPUT_TYPE_DIRECTIVE)
+  if (targetm.has_ifunc ())
+    {
+      /* Right now, the dispatching is done via ifunc.  */
+      dispatch_decl = make_dispatcher_decl (default_node->symbol.decl);
+
+      dispatcher_node = cgraph_get_create_node (dispatch_decl);
+      gcc_assert (dispatcher_node != NULL);
+      dispatcher_node->dispatcher_function = 1;
+      dispatcher_version_info
+	= insert_new_cgraph_node_version (dispatcher_node);
+      dispatcher_version_info->next = default_version_info;
+      dispatcher_node->local.finalized = 1;
+
+      /* Set the dispatcher for all the versions.  */
+      it_v = default_version_info;
+      while (it_v->next != NULL)
+	{
+	  it_v->dispatcher_resolver = dispatch_decl;
+	  it_v = it_v->next;
+	}
+    }
+  else
     {
-      it_v->dispatcher_resolver = dispatch_decl;
-      it_v = it_v->next;
+      error_at (DECL_SOURCE_LOCATION (default_node->symbol.decl),
+		"multiversioning needs ifunc which is not supported "
+		"on this target");
     }
 #else
   error_at (DECL_SOURCE_LOCATION (default_node->symbol.decl),
diff --git a/gcc/config/linux-protos.h b/gcc/config/linux-protos.h
new file mode 100644
index 0000000..ea3d77d
--- /dev/null
+++ b/gcc/config/linux-protos.h
@@ -0,0 +1,21 @@
+/* Prototypes.
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2010, 2011, 2012
+   Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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, or (at your option)
+any later version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+extern bool linux_has_ifunc (void);
diff --git a/gcc/config/linux.h b/gcc/config/linux.h
index fb459e6..bdfbc0d 100644
--- a/gcc/config/linux.h
+++ b/gcc/config/linux.h
@@ -108,3 +108,6 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Whether we have Bionic libc runtime */
 #undef TARGET_HAS_BIONIC
 #define TARGET_HAS_BIONIC (OPTION_BIONIC)
+
+#undef TARGET_HAS_IFUNC
+#define TARGET_HAS_IFUNC linux_has_ifunc
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 75aa867..2d7121f 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -11343,3 +11343,9 @@ memory model bits are allowed.
 @deftypevr {Target Hook} {unsigned char} TARGET_ATOMIC_TEST_AND_SET_TRUEVAL
 This value should be set if the result written by @code{atomic_test_and_set} is not exactly 1, i.e. the @code{bool} @code{true}.
 @end deftypevr
+
+@deftypefn {Target Hook} bool TARGET_HAS_IFUNC (void)
+It returns true if the target supports GNU indirect functions.
+The support includes the assembler, linker and dynamic linker.
+The default value of this hook is defined as for the host machine.
+@end deftypefn
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 95fab18..40dba77 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -11179,3 +11179,5 @@ memory model bits are allowed.
 @end deftypefn
 
 @hook TARGET_ATOMIC_TEST_AND_SET_TRUEVAL
+
+@hook TARGET_HAS_IFUNC
diff --git a/gcc/target.def b/gcc/target.def
index c5bbfae..627ae2d 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -1520,6 +1520,15 @@ DEFHOOK
  bool, (const_rtx x),
  default_use_anchors_for_symbol_p)
 
+/* True if target supports indirect functions.  */
+DEFHOOK
+(has_ifunc,
+ "It returns true if the target supports GNU indirect functions.\n\
+The support includes the assembler, linker and dynamic linker.\n\
+The default value of this hook is defined as for the host machine.",
+ bool, (void),
+ default_have_ifunc)
+
 /* True if it is OK to do sibling call optimization for the specified
    call expression EXP.  DECL will be the called function, or NULL if
    this is an indirect call.  */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 954cdb9..ea0f9e2 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -451,6 +451,14 @@ default_fixed_point_supported_p (void)
   return ENABLE_FIXED_POINT;
 }
 
+/* True if the target supports GNU indirect functions.  */
+
+bool
+default_have_ifunc (void)
+{
+  return HAVE_GNU_INDIRECT_FUNCTION;
+}
+
 /* NULL if INSN insn is valid within a low-overhead loop, otherwise returns
    an error message.
 
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index bd7d4bc..8622d58 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -73,6 +73,8 @@ extern bool targhook_float_words_big_endian (void);
 extern bool default_decimal_float_supported_p (void);
 extern bool default_fixed_point_supported_p (void);
 
+extern bool default_have_ifunc (void);
+
 extern const char * default_invalid_within_doloop (const_rtx);
 
 extern tree default_builtin_vectorized_function (tree, tree, tree);
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 7d083fd..dd677a4 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -5489,14 +5489,15 @@ do_assemble_alias (tree decl, tree target)
     }
   if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl)))
     {
-#if defined (ASM_OUTPUT_TYPE_DIRECTIVE) && HAVE_GNU_INDIRECT_FUNCTION
-      ASM_OUTPUT_TYPE_DIRECTIVE
-	(asm_out_file, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
-	 IFUNC_ASM_TYPE);
-#else
-      error_at (DECL_SOURCE_LOCATION (decl),
-		"ifunc is not supported in this configuration");
+#if defined (ASM_OUTPUT_TYPE_DIRECTIVE)
+      if (targetm.has_ifunc ())
+	ASM_OUTPUT_TYPE_DIRECTIVE
+	  (asm_out_file, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
+	   IFUNC_ASM_TYPE);
+      else
 #endif
+	error_at (DECL_SOURCE_LOCATION (decl),
+		  "ifunc is not supported on this target");
     }
 
 # ifdef ASM_OUTPUT_DEF_FROM_DECLS
