]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add support for ELF indirect functions. These are symbols of
authorTom Hughes <tom@compton.nu>
Thu, 29 Oct 2009 09:27:11 +0000 (09:27 +0000)
committerTom Hughes <tom@compton.nu>
Thu, 29 Oct 2009 09:27:11 +0000 (09:27 +0000)
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

coregrind/m_debuginfo/debuginfo.c
coregrind/m_debuginfo/priv_storage.h
coregrind/m_debuginfo/readelf.c
coregrind/m_redir.c
coregrind/m_scheduler/scheduler.c
coregrind/pub_core_clreq.h
coregrind/pub_core_redir.h
coregrind/vg_preloaded.c
include/pub_tool_debuginfo.h
memcheck/mc_replace_strmem.c

index eed2ab1f46cd8a4f12cd0cebd124f9946089969a..66ae82b702ebc3b5ac03a9a6d830374921fd6c04 100644 (file)
@@ -3435,14 +3435,16 @@ void VG_(DebugInfo_syms_getidx) ( const DebugInfo *si,
                                   /*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;
 }
 
 
index f6e6e82b02ccebfdee585510ccd67f34c2b4c5fc..cff91f7d14c96c6bd9c6b4ad850649bf92938dfa 100644 (file)
 /* 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;
 
index 02cea02bb6a8e9e97954c9bf0da0d3d514d73655..4011852c6343b8de3869025ab573130050a1dc40 100644 (file)
@@ -214,7 +214,8 @@ Bool get_elf_symbol_info (
                                   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 @@ Bool get_elf_symbol_info (
    *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 @@ Bool get_elf_symbol_info (
         &&
         (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 @@ Bool get_elf_symbol_info (
       *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 @@ void read_elf_symtab__normal(
    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 @@ void read_elf_symtab__normal(
                               &sym_avma_really,
                               &sym_size,
                               &sym_tocptr,
-                              &from_opd, &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;
+                              &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.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 @@ typedef
       Int        size;
       Bool       from_opd;
       Bool       is_text;
+      Bool       is_ifunc;
    }
    TempSym;
 
@@ -671,7 +686,7 @@ void read_elf_symtab__ppc64_linux(
    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 @@ void read_elf_symtab__ppc64_linux(
                               &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 @@ void read_elf_symtab__ppc64_linux(
             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 @@ void read_elf_symtab__ppc64_linux(
    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 );
index 98a502fe765a7b040297b7a59ccaf272de0b4229..d4cab7efbb3d39ff32a5bd48896b7b7f56b2e8bd 100644 (file)
@@ -268,12 +268,15 @@ typedef
       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 @@ void VG_(redir_notify_new_DebugInfo)( DebugInfo* newsi )
 
    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 @@ void VG_(redir_notify_new_DebugInfo)( DebugInfo* newsi )
 
    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 @@ void VG_(redir_notify_new_DebugInfo)( DebugInfo* newsi )
 
 #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 @@ void generate_and_add_actives (
      )
 {
    Spec*  sp;
-   Bool   anyMark, isText;
+   Bool   anyMark, isText, isIFunc;
    Active act;
    Int    nsyms, i;
    Addr   sym_addr;
@@ -513,7 +540,7 @@ void generate_and_add_actives (
    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 @@ void generate_and_add_actives (
             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 @@ Addr VG_(redir_do_lookup) ( Addr orig, Bool* isWrap )
 
    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 @@ void handle_maybe_load_notifier( const UChar* soname,
 
    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);
 }
index 295e0ab03d3562a17156e450de98dc830313b339..c10456c5f6f988e6fd640be83ba01ada0671219e 100644 (file)
@@ -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 @@ void do_client_request ( ThreadId tid )
             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] );
index 563904bdfe71cb34e53f1e23ac6532d19c8e3c5b..412f89e1cf9db86b71f20c8d865109d78ba46595 100644 (file)
@@ -50,6 +50,9 @@ typedef
       /* 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
index c993c27277d40c476abc1fba4d9205f0516aea45..c931d85fcc9a2a25aff7774674ff37e9a518c337 100644 (file)
@@ -58,6 +58,8 @@ extern void VG_(redir_notify_delete_DebugInfo)( DebugInfo* );
 /* 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
index 6f0f04916d1d87625efdec9b80941a10a04d1893..1500e8052552869a4086b92ba92686d048b06c72 100644 (file)
 #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 @@ void VG_NOTIFY_ON_LOAD(freeres)( void )
    *(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)
 
 /* ---------------------------------------------------------------------
index a02b79015dcd434561a64f83ddc1ac2de7388663..6b5acd7eceecfb48c9c3c88f658ef95977a2f74a 100644 (file)
@@ -212,7 +212,8 @@ void VG_(DebugInfo_syms_getidx)  ( const DebugInfo *di,
                                    /*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. */
index c15717adbceb2cb0282a6db0dc8c2edf6ac6d3d5..abd838f9c26f7fd200e90cc50160378439d8ed3a 100644 (file)
@@ -116,6 +116,7 @@ Bool is_overlap ( void* dst, const void* src, SizeT dstlen, SizeT srclen )
 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 @@ STRRCHR(VG_Z_DYLD,          rindex)
 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 @@ STRCHR(VG_Z_DYLD,                 index)
    }
 
 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 @@ STRLCAT(VG_Z_DYLD,        strlcat)
    }
 
 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 @@ STRNLEN(VG_Z_LIBC_SONAME, strnlen)
 
 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 @@ STRLEN(VG_Z_LD_LINUX_X86_64_SO_2, strlen)
    }
 
 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 @@ STRCPY(VG_Z_DYLD,        strcpy)
    }
 
 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 @@ STRLCPY(VG_Z_DYLD,        strlcpy)
    }
 
 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 @@ STRNCMP(VG_Z_DYLD,        strncmp)
 
 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 @@ MEMCMP(VG_Z_DYLD,        bcmp)
 
 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_STRCHRNUL(VG_Z_LIBC_SONAME, strchrnul)
    }
 
 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. */