Add support for ELF indirect functions. These are symbols of
type STT_GNU_IFUNC which, instead of pointing directly at the
function, point at a routine which will return the address of
the real function. Redirection of indirect functions is handled
by valgrind as follows:

  - When a redirection specification matches an indirect
    function symbol an active redirection is added in the
    normal way, but with the isIFunc flag set.

  - When a call is made to an address which matches an
    active redirection with the isIFunc flag set the call
    is redirected, but not to the target address of the
    redirection - instead it is sent to a small wrapper
    routine that is preloaded into the client.

  - The wrapper routine calls the original client routine
    and collects the result, which it reports to valgrind
    using a client request, and then returns the result to
    the caller.

  - When valgrind gets the client request it looks up the
    active redirection for the indirect function and then
    adds a new active redirection which redirects from the
    address returned by the indirection function to the
    redirection target. This new redirection does not have
    the isIFunc flag set so behaves as a normal redirection.

In addition to the above we also add a few new redirections to
memcheck to capture internal calls made by glibc to things like
strlen, as these internal calls do not go through the indirect
function and instead go direct to the chosen implementation.

Based on a patch from Dodji Seketeli and comments from Jakub
Jelinek, this commit closes bug 206013.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@10920 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/m_debuginfo/debuginfo.c b/coregrind/m_debuginfo/debuginfo.c
index eed2ab1..66ae82b 100644
--- a/coregrind/m_debuginfo/debuginfo.c
+++ b/coregrind/m_debuginfo/debuginfo.c
@@ -3435,14 +3435,16 @@
                                   /*OUT*/Addr*   tocptr,
                                   /*OUT*/UInt*   size,
                                   /*OUT*/HChar** name,
-                                  /*OUT*/Bool*   isText )
+                                  /*OUT*/Bool*   isText,
+                                  /*OUT*/Bool*   isIFunc )
 {
    vg_assert(idx >= 0 && idx < si->symtab_used);
-   if (avma)   *avma   = si->symtab[idx].addr;
-   if (tocptr) *tocptr = si->symtab[idx].tocptr;
-   if (size)   *size   = si->symtab[idx].size;
-   if (name)   *name   = (HChar*)si->symtab[idx].name;
-   if (isText) *isText = si->symtab[idx].isText;
+   if (avma)    *avma    = si->symtab[idx].addr;
+   if (tocptr)  *tocptr  = si->symtab[idx].tocptr;
+   if (size)    *size    = si->symtab[idx].size;
+   if (name)    *name    = (HChar*)si->symtab[idx].name;
+   if (isText)  *isText  = si->symtab[idx].isText;
+   if (isIFunc) *isIFunc = si->symtab[idx].isIFunc;
 }
 
 
diff --git a/coregrind/m_debuginfo/priv_storage.h b/coregrind/m_debuginfo/priv_storage.h
index f6e6e82..cff91f7 100644
--- a/coregrind/m_debuginfo/priv_storage.h
+++ b/coregrind/m_debuginfo/priv_storage.h
@@ -48,15 +48,16 @@
 /* A structure to hold an ELF/XCOFF symbol (very crudely). */
 typedef 
    struct { 
-      Addr  addr;   /* lowest address of entity */
-      Addr  tocptr; /* ppc64-linux only: value that R2 should have */
-      UChar *name;  /* name */
+      Addr  addr;    /* lowest address of entity */
+      Addr  tocptr;  /* ppc64-linux only: value that R2 should have */
+      UChar *name;   /* name */
       // XXX: this could be shrunk (on 32-bit platforms) by using 31 bits for
       // the size and 1 bit for the isText.  If you do this, make sure that
       // all assignments to isText use 0 or 1 (or True or False), and that a
       // positive number larger than 1 is never used to represent True.
-      UInt  size;   /* size in bytes */
+      UInt  size;    /* size in bytes */
       Bool  isText;
+      Bool  isIFunc; /* symbol is an indirect function? */
    }
    DiSym;
 
diff --git a/coregrind/m_debuginfo/readelf.c b/coregrind/m_debuginfo/readelf.c
index 02cea02..4011852 100644
--- a/coregrind/m_debuginfo/readelf.c
+++ b/coregrind/m_debuginfo/readelf.c
@@ -214,7 +214,8 @@
                                   used on entry */
         Bool*  from_opd_out,   /* ppc64-linux only: did we deref an
                                   .opd entry? */
-        Bool*  is_text_out     /* is this a text symbol? */
+        Bool*  is_text_out,    /* is this a text symbol? */
+        Bool*  is_ifunc        /* is this a  STT_GNU_IFUNC function ?*/
      )
 {
    Bool plausible;
@@ -232,6 +233,7 @@
    *sym_size_out   = (Int)sym->st_size;
    *sym_tocptr_out = 0; /* unknown/inapplicable */
    *from_opd_out   = False;
+   *is_ifunc       = False;
 
    /* Figure out if we're interested in the symbol.  Firstly, is it of
       the right flavour?  */
@@ -243,6 +245,9 @@
         &&
         (ELFXX_ST_TYPE(sym->st_info) == STT_FUNC 
          || ELFXX_ST_TYPE(sym->st_info) == STT_OBJECT
+#ifdef STT_GNU_IFUNC
+         || ELFXX_ST_TYPE(sym->st_info) == STT_GNU_IFUNC
+#endif
         );
 
    /* Work out the svma and bias for each section as it will appear in
@@ -325,6 +330,14 @@
       *sym_avma_out += text_bias;
    }
 
+#  ifdef STT_GNU_IFUNC
+   /* Check for indirect functions. */
+   if (*is_text_out
+       && ELFXX_ST_TYPE(sym->st_info) == STT_GNU_IFUNC) {
+       *is_ifunc = True;
+   }
+#  endif
+
 #  if defined(VGP_ppc64_linux)
    /* Allow STT_NOTYPE in the very special case where we're running on
       ppc64-linux and the symbol is one which the .opd-chasing hack
@@ -570,7 +583,7 @@
    Char      *sym_name, *sym_name_really;
    Int        sym_size;
    Addr       sym_tocptr;
-   Bool       from_opd, is_text;
+   Bool       from_opd, is_text, is_ifunc;
    DiSym      risym;
    ElfXX_Sym *sym;
 
@@ -602,13 +615,14 @@
                               &sym_avma_really,
                               &sym_size,
                               &sym_tocptr,
-                              &from_opd, &is_text)) {
+                              &from_opd, &is_text, &is_ifunc)) {
 
-         risym.addr   = sym_avma_really;
-         risym.size   = sym_size;
-         risym.name   = ML_(addStr) ( di, sym_name_really, -1 );
-         risym.tocptr = sym_tocptr;
-         risym.isText = is_text;
+         risym.addr    = sym_avma_really;
+         risym.size    = sym_size;
+         risym.name    = ML_(addStr) ( di, sym_name_really, -1 );
+         risym.tocptr  = sym_tocptr;
+         risym.isText  = is_text;
+         risym.isIFunc = is_ifunc;
          vg_assert(risym.name != NULL);
          vg_assert(risym.tocptr == 0); /* has no role except on ppc64-linux */
          ML_(addSym) ( di, &risym );
@@ -646,6 +660,7 @@
       Int        size;
       Bool       from_opd;
       Bool       is_text;
+      Bool       is_ifunc;
    }
    TempSym;
 
@@ -671,7 +686,7 @@
    Char       *sym_name, *sym_name_really;
    Int         sym_size;
    Addr        sym_tocptr;
-   Bool        from_opd, modify_size, modify_tocptr, is_text;
+   Bool        from_opd, modify_size, modify_tocptr, is_text, is_ifunc;
    DiSym       risym;
    ElfXX_Sym  *sym;
    OSet       *oset;
@@ -713,7 +728,7 @@
                               &sym_avma_really,
                               &sym_size,
                               &sym_tocptr,
-                              &from_opd, &is_text)) {
+                              &from_opd, &is_text, &is_ifunc)) {
 
          /* Check if we've seen this (name,addr) key before. */
          key.addr = sym_avma_really;
@@ -785,6 +800,7 @@
             elem->size     = sym_size;
             elem->from_opd = from_opd;
             elem->is_text  = is_text;
+            elem->is_ifunc = is_ifunc;
             VG_(OSetGen_Insert)(oset, elem);
             if (di->trace_symtab) {
                VG_(printf)("   to-oset [%4ld]:          "
@@ -808,11 +824,12 @@
    VG_(OSetGen_ResetIter)( oset );
 
    while ( (elem = VG_(OSetGen_Next)(oset)) ) {
-      risym.addr   = elem->key.addr;
-      risym.size   = elem->size;
-      risym.name   = ML_(addStr) ( di, elem->key.name, -1 );
-      risym.tocptr = elem->tocptr;
-      risym.isText = elem->is_text;
+      risym.addr    = elem->key.addr;
+      risym.size    = elem->size;
+      risym.name    = ML_(addStr) ( di, elem->key.name, -1 );
+      risym.tocptr  = elem->tocptr;
+      risym.isText  = elem->is_text;
+      risym.isIFunc = elem->is_ifunc;
       vg_assert(risym.name != NULL);
 
       ML_(addSym) ( di, &risym );
diff --git a/coregrind/m_redir.c b/coregrind/m_redir.c
index 98a502f..d4cab7e 100644
--- a/coregrind/m_redir.c
+++ b/coregrind/m_redir.c
@@ -268,12 +268,15 @@
       TopSpec* parent_spec; /* the TopSpec which supplied the Spec */
       TopSpec* parent_sym;  /* the TopSpec which supplied the symbol */
       Bool     isWrap;      /* wrap or replacement? */
+      Bool     isIFunc;     /* indirect function? */
    }
    Active;
 
 /* The active set is a fast lookup table */
 static OSet* activeSet = NULL;
 
+/* Wrapper routine for indirect functions */
+static Addr iFuncWrapper;
 
 /*------------------------------------------------------------*/
 /*--- FWDses                                               ---*/
@@ -350,8 +353,8 @@
 
    nsyms = VG_(DebugInfo_syms_howmany)( newsi );
    for (i = 0; i < nsyms; i++) {
-      VG_(DebugInfo_syms_getidx)( newsi, i, &sym_addr, &sym_toc, 
-                                            NULL, &sym_name, &isText );
+      VG_(DebugInfo_syms_getidx)( newsi, i, &sym_addr, &sym_toc,
+                                  NULL, &sym_name, &isText, NULL );
       ok = VG_(maybe_Z_demangle)( sym_name, demangled_sopatt, N_DEMANGLED,
                                   demangled_fnpatt, N_DEMANGLED, &isWrap );
       /* ignore data symbols */
@@ -388,8 +391,8 @@
 
    if (check_ppcTOCs) {
       for (i = 0; i < nsyms; i++) {
-         VG_(DebugInfo_syms_getidx)( newsi, i, &sym_addr, &sym_toc, 
-                                               NULL, &sym_name, &isText );
+         VG_(DebugInfo_syms_getidx)( newsi, i, &sym_addr, &sym_toc,
+                                     NULL, &sym_name, &isText, NULL );
          ok = isText
               && VG_(maybe_Z_demangle)( 
                     sym_name, demangled_sopatt, N_DEMANGLED,
@@ -470,6 +473,30 @@
 
 #undef N_DEMANGLED
 
+/* Add a new target for an indirect function. Adds a new redirection
+   for the indirection function with address old_from that redirects
+   the ordinary function with address new_from to the target address
+   of the original redirection. */
+
+void VG_(redir_add_ifunc_target)( Addr old_from, Addr new_from )
+{
+    Active *old, new;
+
+    old = VG_(OSetGen_Lookup)(activeSet, &old_from);
+    vg_assert(old);
+    vg_assert(old->isIFunc);
+
+    new = *old;
+    new.from_addr = new_from;
+    new.isIFunc = False;
+    maybe_add_active (new);
+
+    if (VG_(clo_trace_redir)) {
+       VG_(message)( Vg_DebugMsg,
+                     "Adding redirect for indirect function 0x%llx from 0x%llx -> 0x%llx\n",
+                     (ULong)old_from, (ULong)new_from, (ULong)new.to_addr );
+    }
+}
 
 /* Do one element of the basic cross product: add to the active set,
    all matches resulting from comparing all the given specs against
@@ -487,7 +514,7 @@
      )
 {
    Spec*  sp;
-   Bool   anyMark, isText;
+   Bool   anyMark, isText, isIFunc;
    Active act;
    Int    nsyms, i;
    Addr   sym_addr;
@@ -513,7 +540,7 @@
    nsyms = VG_(DebugInfo_syms_howmany)( di );
    for (i = 0; i < nsyms; i++) {
       VG_(DebugInfo_syms_getidx)( di, i, &sym_addr, NULL, NULL,
-                                         &sym_name, &isText );
+                                  &sym_name, &isText, &isIFunc );
 
       /* ignore data symbols */
       if (!isText)
@@ -539,6 +566,7 @@
             act.parent_spec = parent_spec;
             act.parent_sym  = parent_sym;
             act.isWrap      = sp->isWrap;
+            act.isIFunc     = isIFunc;
             sp->done = True;
             maybe_add_active( act );
          }
@@ -780,7 +808,9 @@
 
    vg_assert(r->to_addr != 0);
    if (isWrap)
-      *isWrap = r->isWrap;
+      *isWrap = r->isWrap || r->isIFunc;
+   if (r->isIFunc)
+      return iFuncWrapper;
    return r->to_addr;
 }
 
@@ -1096,6 +1126,8 @@
 
    if (VG_(strcmp)(symbol, VG_STRINGIFY(VG_NOTIFY_ON_LOAD(freeres))) == 0)
       VG_(client___libc_freeres_wrapper) = addr;
+   else if (VG_(strcmp)(symbol, VG_STRINGIFY(VG_NOTIFY_ON_LOAD(ifunc_wrapper))) == 0)
+      iFuncWrapper = addr;
    else
       vg_assert2(0, "unrecognised load notification function: %s", symbol);
 }
diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c
index 295e0ab..c10456c 100644
--- a/coregrind/m_scheduler/scheduler.c
+++ b/coregrind/m_scheduler/scheduler.c
@@ -89,6 +89,7 @@
 #include "pub_core_debuginfo.h"     // VG_(di_notify_pdb_debuginfo)
 #include "priv_sema.h"
 #include "pub_core_scheduler.h"     // self
+#include "pub_core_redir.h"
 
 
 /* ---------------------------------------------------------------------
@@ -1399,6 +1400,11 @@
             SET_CLREQ_RETVAL( tid, count );
          break; }
 
+      case VG_USERREQ__ADD_IFUNC_TARGET: {
+         VG_(redir_add_ifunc_target)( arg[1], arg[2] );
+         SET_CLREQ_RETVAL( tid, 0);
+         break; }
+
       case VG_USERREQ__PRINTF_BACKTRACE: {
          Int count =
             VG_(vmessage)( Vg_ClientMsg, (char *)arg[1], (void*)arg[2] );
diff --git a/coregrind/pub_core_clreq.h b/coregrind/pub_core_clreq.h
index 563904b..412f89e 100644
--- a/coregrind/pub_core_clreq.h
+++ b/coregrind/pub_core_clreq.h
@@ -50,6 +50,9 @@
       /* Internal equivalent of VALGRIND_PRINTF . */
       VG_USERREQ__INTERNAL_PRINTF   = 0x3103,
 
+      /* Add a target for an indirect function redirection. */
+      VG_USERREQ__ADD_IFUNC_TARGET  = 0x3104,
+
    } Vg_InternalClientRequest;
 
 // Function for printing from code within Valgrind, but which runs on the
diff --git a/coregrind/pub_core_redir.h b/coregrind/pub_core_redir.h
index c993c27..c931d85 100644
--- a/coregrind/pub_core_redir.h
+++ b/coregrind/pub_core_redir.h
@@ -58,6 +58,8 @@
 /* Initialise the module, and load initial "hardwired" redirects. */
 extern void VG_(redir_initialise)( void );
 
+/* Notify the module of a new target for an indirect function. */
+extern void VG_(redir_add_ifunc_target)( Addr old_from, Addr new_from );
 
 //--------------------------------------------------------------------
 // Queries
diff --git a/coregrind/vg_preloaded.c b/coregrind/vg_preloaded.c
index 6f0f049..1500e80 100644
--- a/coregrind/vg_preloaded.c
+++ b/coregrind/vg_preloaded.c
@@ -47,12 +47,12 @@
 #include "pub_core_debuginfo.h"  // Needed for pub_core_redir.h
 #include "pub_core_redir.h"      // For VG_NOTIFY_ON_LOAD
 
+#if defined(VGO_linux) || defined(VGO_aix5)
+
 /* ---------------------------------------------------------------------
    Hook for running __libc_freeres once the program exits.
    ------------------------------------------------------------------ */
 
-#if defined(VGO_linux) || defined(VGO_aix5)
-
 void VG_NOTIFY_ON_LOAD(freeres)( void );
 void VG_NOTIFY_ON_LOAD(freeres)( void )
 {
@@ -68,6 +68,31 @@
    *(int *)0 = 'x';
 }
 
+/* ---------------------------------------------------------------------
+   Wrapper for indirect functions which need to be redirected.
+   ------------------------------------------------------------------ */
+
+void * VG_NOTIFY_ON_LOAD(ifunc_wrapper) (void);
+void * VG_NOTIFY_ON_LOAD(ifunc_wrapper) (void)
+{
+    OrigFn fn;
+    Addr result = 0;
+    int res;
+
+    /* Call the original indirect function and get it's result */
+    VALGRIND_GET_ORIG_FN(fn);
+    CALL_FN_W_v(result, fn);
+
+    /* Ask the valgrind core running on the real CPU (as opposed to this
+       code which runs on the emulated CPU) to update the redirection that
+       led to this function. This client request eventually gives control to
+       the function VG_(redir_add_ifunc_target) in m_redir.c  */
+    VALGRIND_DO_CLIENT_REQUEST(res, 0,
+                               VG_USERREQ__ADD_IFUNC_TARGET,
+                               fn.nraddr, result, 0, 0, 0);
+    return result;
+}
+
 #elif defined(VGO_darwin)
 
 /* ---------------------------------------------------------------------
diff --git a/include/pub_tool_debuginfo.h b/include/pub_tool_debuginfo.h
index a02b790..6b5acd7 100644
--- a/include/pub_tool_debuginfo.h
+++ b/include/pub_tool_debuginfo.h
@@ -212,7 +212,8 @@
                                    /*OUT*/Addr*   tocptr,
                                    /*OUT*/UInt*   size,
                                    /*OUT*/HChar** name,
-                                   /*OUT*/Bool*   isText );
+                                   /*OUT*/Bool*   isText,
+                                   /*OUT*/Bool*   isIFunc );
 
 /* A simple enumeration to describe the 'kind' of various kinds of
    segments that arise from the mapping of object files. */
diff --git a/memcheck/mc_replace_strmem.c b/memcheck/mc_replace_strmem.c
index c15717a..abd838f 100644
--- a/memcheck/mc_replace_strmem.c
+++ b/memcheck/mc_replace_strmem.c
@@ -116,6 +116,7 @@
 STRRCHR(VG_Z_LIBC_SONAME,   strrchr)
 STRRCHR(VG_Z_LIBC_SONAME,   rindex)
 #if defined(VGO_linux)
+STRRCHR(VG_Z_LIBC_SONAME,   __GI_strrchr)
 STRRCHR(VG_Z_LD_LINUX_SO_2, rindex)
 #elif defined(VGO_darwin)
 STRRCHR(VG_Z_DYLD,          strrchr)
@@ -140,6 +141,7 @@
 STRCHR(VG_Z_LIBC_SONAME,          strchr)
 STRCHR(VG_Z_LIBC_SONAME,          index)
 #if defined(VGO_linux)
+STRCHR(VG_Z_LIBC_SONAME,          __GI_strchr)
 STRCHR(VG_Z_LD_LINUX_SO_2,        strchr)
 STRCHR(VG_Z_LD_LINUX_SO_2,        index)
 STRCHR(VG_Z_LD_LINUX_X86_64_SO_2, strchr)
@@ -172,7 +174,9 @@
    }
 
 STRCAT(VG_Z_LIBC_SONAME, strcat)
-
+#if defined(VGO_linux)
+STRCAT(VG_Z_LIBC_SONAME, __GI_strcat)
+#endif
 
 #define STRNCAT(soname, fnname) \
    char* VG_REPLACE_FUNCTION_ZU(soname,fnname) \
@@ -257,6 +261,9 @@
    }
 
 STRNLEN(VG_Z_LIBC_SONAME, strnlen)
+#if defined(VGO_linux)
+STRNLEN(VG_Z_LIBC_SONAME, __GI_strnlen)
+#endif
    
 
 // Note that this replacement often doesn't get used because gcc inlines
@@ -274,6 +281,7 @@
 
 STRLEN(VG_Z_LIBC_SONAME,          strlen)
 #if defined(VGO_linux)
+STRLEN(VG_Z_LIBC_SONAME,          __GI_strlen)
 STRLEN(VG_Z_LD_LINUX_SO_2,        strlen)
 STRLEN(VG_Z_LD_LINUX_X86_64_SO_2, strlen)
 #endif
@@ -301,7 +309,9 @@
    }
 
 STRCPY(VG_Z_LIBC_SONAME, strcpy)
-#if defined(VGO_darwin)
+#if defined(VGO_linux)
+STRCPY(VG_Z_LIBC_SONAME, __GI_strcpy)
+#elif defined(VGO_darwin)
 STRCPY(VG_Z_DYLD,        strcpy)
 #endif
 
@@ -327,7 +337,9 @@
    }
 
 STRNCPY(VG_Z_LIBC_SONAME, strncpy)
-#if defined(VGO_darwin)
+#if defined(VGO_linux)
+STRNCPY(VG_Z_LIBC_SONAME, __GI_strncpy)
+#elif defined(VGO_darwin)
 STRNCPY(VG_Z_DYLD,        strncpy)
 #endif
 
@@ -384,7 +396,9 @@
    }
 
 STRNCMP(VG_Z_LIBC_SONAME, strncmp)
-#if defined(VGO_darwin)
+#if defined(VGO_linux)
+STRNCMP(VG_Z_LIBC_SONAME, __GI_strncmp)
+#elif defined(VGO_darwin)
 STRNCMP(VG_Z_DYLD,        strncmp)
 #endif
 
@@ -411,6 +425,7 @@
 
 STRCMP(VG_Z_LIBC_SONAME,          strcmp)
 #if defined(VGO_linux)
+STRCMP(VG_Z_LIBC_SONAME,          __GI_strcmp)
 STRCMP(VG_Z_LD_LINUX_X86_64_SO_2, strcmp)
 STRCMP(VG_Z_LD64_SO_1,            strcmp)
 #endif
@@ -557,6 +572,7 @@
 
 STPCPY(VG_Z_LIBC_SONAME,          stpcpy)
 #if defined(VGO_linux)
+STPCPY(VG_Z_LIBC_SONAME,          __GI_stpcpy)
 STPCPY(VG_Z_LD_LINUX_SO_2,        stpcpy)
 STPCPY(VG_Z_LD_LINUX_X86_64_SO_2, stpcpy)
 #elif defined(VGO_darwin)
@@ -709,7 +725,9 @@
    }
 
 GLIBC232_RAWMEMCHR(VG_Z_LIBC_SONAME, rawmemchr)
-
+#if defined (VGO_linux)
+GLIBC232_RAWMEMCHR(VG_Z_LIBC_SONAME, __GI___rawmemchr)
+#endif
 
 /* glibc variant of strcpy that checks the dest is big enough.
    Copied from glibc-2.5/debug/test-strcpy_chk.c. */