Extend the behavioural-equivalence-class mechanism for redirection
functions to include the ability to give a priority to each function,
as well as a tag indicating its behavioural class.  Add logic in
m_redir.c to resolve conflicting redirections with the same eclass but
different priorities by preferring the redirection with the higher
priority.  Use all of the above in mc_replace_strmem.c, to cause a
conflict between redirections for "memcpy" and "memcpy@GLIBC_2.2.5" to
be resolved in favour of the latter (the non-overlap-checking
version).

This is all related to the massive swamp that is #275284.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11991 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/m_demangle/demangle.c b/coregrind/m_demangle/demangle.c
index ccc5926..5fc47f3 100644
--- a/coregrind/m_demangle/demangle.c
+++ b/coregrind/m_demangle/demangle.c
@@ -100,7 +100,7 @@
       interested in that). */
    if (do_z_demangling) {
       if (VG_(maybe_Z_demangle)( orig, NULL,0,/*soname*/
-                                 z_demangled, N_ZBUF, NULL, NULL )) {
+                                 z_demangled, N_ZBUF, NULL, NULL, NULL )) {
          orig = z_demangled;
       }
    }
@@ -147,7 +147,8 @@
                              /*OUT*/HChar* so, Int soLen,
                              /*OUT*/HChar* fn, Int fnLen,
                              /*OUT*/Bool* isWrap,
-                             /*OUT*/Int*  eclassTag )
+                             /*OUT*/Int*  eclassTag,
+                             /*OUT*/Int*  eclassPrio )
 {
 #  define EMITSO(ch)                           \
       do {                                     \
@@ -181,24 +182,28 @@
    valid =     sym[0] == '_'
            &&  sym[1] == 'v'
            &&  sym[2] == 'g'
-           && (sym[3] == 'r' || sym[3] == 'w' || sym[3] == 'n')
+           && (sym[3] == 'r' || sym[3] == 'w')
            &&  VG_(isdigit)(sym[4])
            &&  VG_(isdigit)(sym[5])
            &&  VG_(isdigit)(sym[6])
            &&  VG_(isdigit)(sym[7])
-           &&  sym[8] == 'Z'
-           && (sym[9] == 'Z' || sym[9] == 'U')
-           &&  sym[10] == '_';
-   if (valid && sym[3] == 'n') {
-      /* for _vgn (notify-on-load symbols), the equivalence class has
-         no meaning; hence ensure it is the default 0000 value. */
-      if (sym[4] != '0' || sym[5] != '0' || sym[6] != '0' || sym[7] != '0')
-         valid = False;
+           &&  VG_(isdigit)(sym[8])
+           &&  sym[9] == 'Z'
+           && (sym[10] == 'Z' || sym[10] == 'U')
+           &&  sym[11] == '_';
+
+   if (valid
+       && sym[4] == '0' && sym[5] == '0' && sym[6] == '0' && sym[7] == '0'
+       && sym[8] != '0') {
+      /* If the eclass tag is 0000 (meaning "no eclass"), the priority
+         must be 0 too. */
+      valid = False;
    }
+
    if (!valid)
       return False;
 
-   fn_is_encoded = sym[9] == 'Z';
+   fn_is_encoded = sym[10] == 'Z';
 
    if (isWrap)
       *isWrap = sym[3] == 'w';
@@ -211,21 +216,26 @@
       vg_assert(*eclassTag >= 0 && *eclassTag <= 9999);
    }
 
+   if (eclassPrio) {
+      *eclassPrio = ((Int)sym[8]) - '0';
+      vg_assert(*eclassPrio >= 0 && *eclassPrio <= 9);
+   }
+
    /* Now check the soname prefix isn't "VG_Z_", as described in
       pub_tool_redir.h. */
    is_VG_Z_prefixed =
-      sym[11] == 'V' &&
-      sym[12] == 'G' &&
-      sym[13] == '_' &&
-      sym[14] == 'Z' &&
-      sym[15] == '_';
+      sym[12] == 'V' &&
+      sym[13] == 'G' &&
+      sym[14] == '_' &&
+      sym[15] == 'Z' &&
+      sym[16] == '_';
    if (is_VG_Z_prefixed) {
       vg_assert2(0, "symbol with a 'VG_Z_' prefix: %s.\n"
                     "see pub_tool_redir.h for an explanation.", sym);
    }
 
    /* Now scan the Z-encoded soname. */
-   i = 11;
+   i = 12;
    while (True) {
 
       if (sym[i] == '_')
diff --git a/coregrind/m_redir.c b/coregrind/m_redir.c
index f22088b..5b59144 100644
--- a/coregrind/m_redir.c
+++ b/coregrind/m_redir.c
@@ -233,6 +233,9 @@
       Int    becTag; /* 0 through 9999.  Behavioural equivalance class tag.
                         If two wrappers have the same (non-zero) tag, they
                         are promising that they behave identically. */
+      Int    becPrio; /* 0 through 9.  Behavioural equivalence class prio.
+                         Used to choose between competing wrappers with
+                         the same (non-zero) tag. */
       const HChar** mandatory; /* non-NULL ==> abort V and print the
                                   strings if from_sopatt is loaded but
                                   from_fnpatt cannot be found */
@@ -278,6 +281,7 @@
       TopSpec* parent_spec; /* the TopSpec which supplied the Spec */
       TopSpec* parent_sym;  /* the TopSpec which supplied the symbol */
       Int      becTag;      /* behavioural eclass tag for ::to_addr */
+      Int      becPrio;     /* and its priority */
       Bool     isWrap;      /* wrap or replacement? */
       Bool     isIFunc;     /* indirect function? */
    }
@@ -371,7 +375,7 @@
 void VG_(redir_notify_new_DebugInfo)( DebugInfo* newdi )
 {
    Bool         ok, isWrap;
-   Int          i, nsyms, becTag;
+   Int          i, nsyms, becTag, becPrio;
    Spec*        specList;
    Spec*        spec;
    TopSpec*     ts;
@@ -416,7 +420,7 @@
          ok = VG_(maybe_Z_demangle)( *names,
                                      demangled_sopatt, N_DEMANGLED,
                                      demangled_fnpatt, N_DEMANGLED,
-                                     &isWrap, &becTag );
+                                     &isWrap, &becTag, &becPrio );
          /* ignore data symbols */
          if (!isText)
             continue;
@@ -442,6 +446,7 @@
          spec->to_addr = sym_addr;
          spec->isWrap = isWrap;
          spec->becTag = becTag;
+         spec->becPrio = becPrio;
          /* check we're not adding manifestly stupid destinations */
          vg_assert(is_plausible_guest_addr(sym_addr));
          spec->next = specList;
@@ -465,7 +470,7 @@
             ok = isText
                  && VG_(maybe_Z_demangle)( 
                        *names, demangled_sopatt, N_DEMANGLED,
-                       demangled_fnpatt, N_DEMANGLED, &isWrap, NULL );
+                       demangled_fnpatt, N_DEMANGLED, &isWrap, NULL, NULL );
             if (!ok)
                /* not a redirect.  Ignore. */
                continue;
@@ -640,6 +645,7 @@
                act.parent_spec = parent_spec;
                act.parent_sym  = parent_sym;
                act.becTag      = sp->becTag;
+               act.becPrio     = sp->becPrio;
                act.isWrap      = sp->isWrap;
                act.isIFunc     = isIFunc;
                sp->done = True;
@@ -707,8 +713,9 @@
    conflicting bindings. */
 static void maybe_add_active ( Active act )
 {
-   HChar*  what = NULL;
-   Active* old  = NULL;
+   HChar*  what    = NULL;
+   Active* old     = NULL;
+   Bool    add_act = False;
 
    /* Complain and ignore manifestly bogus 'from' addresses.
 
@@ -744,26 +751,63 @@
             nonzero, then that's fine.  But if not, we can't show they
             are equivalent, so we have to complain, and ignore the new
             binding. */
-         vg_assert(old->becTag >= 0 && old->becTag <= 9999);
-         vg_assert(act.becTag  >= 0 && act.becTag  <= 9999);
-         if (old->becTag != 0 && act.becTag != 0 && old->becTag == act.becTag) {
-            /* the replacements are behaviourally equivalent, so we can
-               safely ignore this conflict, and not add the new one. */
+         vg_assert(old->becTag  >= 0 && old->becTag  <= 9999);
+         vg_assert(old->becPrio >= 0 && old->becPrio <= 9);
+         vg_assert(act.becTag   >= 0 && act.becTag   <= 9999);
+         vg_assert(act.becPrio  >= 0 && act.becPrio  <= 9);
+         if (old->becTag == 0)
+            vg_assert(old->becPrio == 0);
+         if (act.becTag == 0)
+            vg_assert(act.becPrio == 0);
+
+         if (old->becTag == 0 || act.becTag == 0 || old->becTag != act.becTag) {
+            /* We can't show that they are equivalent.  Complain and
+               ignore. */
+            what = "new redirection conflicts with existing -- ignoring it";
+            goto bad;
+         }
+         /* They have the same eclass tag.  Use the priorities to
+            resolve the ambiguity. */
+         if (act.becPrio <= old->becPrio) {
+            /* The new one doesn't have a higher priority, so just
+               ignore it. */
             if (VG_(clo_verbosity) > 2) {
-               VG_(message)(Vg_UserMsg, "Ignoring duplicate redirection:\n");
+               VG_(message)(Vg_UserMsg, "Ignoring %s redirection:\n",
+                            act.becPrio < old->becPrio ? "lower priority" 
+                                                       : "duplicate");
                show_active(             "    old: ", old);
                show_active(             "    new: ", &act);
             }
          } else {
-            what = "new redirection conflicts with existing -- ignoring it";
-            goto bad;
+            /* The tricky case.  The new one has a higher priority, so
+               we need to get the old one out of the OSet and install
+               this one in its place. */
+            if (VG_(clo_verbosity) > 1) {
+               VG_(message)(Vg_UserMsg, 
+                           "Preferring higher priority redirection:\n");
+               show_active(             "    old: ", old);
+               show_active(             "    new: ", &act);
+            }
+            add_act = True;
+            void* oldNd = VG_(OSetGen_Remove)( activeSet, &act.from_addr );
+            vg_assert(oldNd == old);
+            VG_(OSetGen_FreeNode)( activeSet, old );
+            old = NULL;
          }
       } else {
          /* This appears to be a duplicate of an existing binding.
             Safe(ish) -- ignore. */
          /* XXXXXXXXXXX COMPLAIN if new and old parents differ */
       }
+
    } else {
+      /* There's no previous binding for this from_addr, so we must
+         add 'act' to the active set. */
+      add_act = True;
+   }
+
+   /* So, finally, actually add it. */
+   if (add_act) {
       Active* a = VG_(OSetGen_AllocNode)(activeSet, sizeof(Active));
       vg_assert(a);
       *a = act;
@@ -786,6 +830,7 @@
 
   bad:
    vg_assert(what);
+   vg_assert(!add_act);
    if (VG_(clo_verbosity) > 1) {
       VG_(message)(Vg_UserMsg, "WARNING: %s\n", what);
       if (old) {
@@ -936,6 +981,7 @@
    act.parent_spec = NULL;
    act.parent_sym  = NULL;
    act.becTag      = 0; /* "not equivalent to any other fn" */
+   act.becPrio     = 0; /* mandatory when becTag == 0 */
    act.isWrap      = False;
    act.isIFunc     = False;
    maybe_add_active( act );
@@ -1248,7 +1294,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)
+   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);
@@ -1381,11 +1428,11 @@
 static void show_spec ( HChar* left, Spec* spec )
 {
    VG_(message)( Vg_DebugMsg, 
-                 "%s%25s %30s %s-> (%04d) 0x%08llx\n",
+                 "%s%25s %30s %s-> (%04d.%d) 0x%08llx\n",
                  left,
                  spec->from_sopatt, spec->from_fnpatt,
                  spec->isWrap ? "W" : "R",
-                 spec->becTag,
+                 spec->becTag, spec->becPrio,
                  (ULong)spec->to_addr );
 }
 
@@ -1400,11 +1447,12 @@
    ok = VG_(get_fnname_w_offset)(act->to_addr, name2, 64);
    if (!ok) VG_(strcpy)(name2, "???");
 
-   VG_(message)(Vg_DebugMsg, "%s0x%08llx (%20s) %s-> (%04d) 0x%08llx %s\n", 
+   VG_(message)(Vg_DebugMsg, "%s0x%08llx (%20s) %s-> (%04d.%d) 0x%08llx %s\n", 
                              left, 
                              (ULong)act->from_addr, name1,
                              act->isWrap ? "W" : "R",
-                             act->becTag, (ULong)act->to_addr, name2 );
+                             act->becTag, act->becPrio,
+                             (ULong)act->to_addr, name2 );
 }
 
 static void show_redir_state ( HChar* who )
diff --git a/coregrind/m_replacemalloc/vg_replace_malloc.c b/coregrind/m_replacemalloc/vg_replace_malloc.c
index 53170d8..38553ec 100644
--- a/coregrind/m_replacemalloc/vg_replace_malloc.c
+++ b/coregrind/m_replacemalloc/vg_replace_malloc.c
@@ -73,28 +73,28 @@
 /* Assignment of behavioural equivalence class tags: 1NNN is intended
    to be reserved for the Valgrind core.  Current usage:
 
-   1001 ALLOC_or_NULL
-   1002 ZONEALLOC_or_NULL
-   1003 ALLOC_or_BOMB
-   1004 ZONEFREE
-   1005 FREE
-   1006 ZONECALLOC
-   1007 CALLOC
-   1008 ZONEREALLOC
-   1009 REALLOC
-   1010 ZONEMEMALIGN
-   1011 MEMALIGN
-   1012 VALLOC
-   1013 ZONEVALLOC
-   1014 MALLOPT
-   1015 MALLOC_TRIM
-   1016 POSIX_MEMALIGN
-   1017 MALLOC_USABLE_SIZE
-   1018 PANIC
-   1019 MALLOC_STATS
-   1020 MALLINFO
-   1021 DEFAULT_ZONE
-   1022 ZONE_CHECK
+   10010 ALLOC_or_NULL
+   10020 ZONEALLOC_or_NULL
+   10030 ALLOC_or_BOMB
+   10040 ZONEFREE
+   10050 FREE
+   10060 ZONECALLOC
+   10070 CALLOC
+   10080 ZONEREALLOC
+   10090 REALLOC
+   10100 ZONEMEMALIGN
+   10110 MEMALIGN
+   10120 VALLOC
+   10130 ZONEVALLOC
+   10140 MALLOPT
+   10150 MALLOC_TRIM
+   10160 POSIX_MEMALIGN
+   10170 MALLOC_USABLE_SIZE
+   10180 PANIC
+   10190 MALLOC_STATS
+   10200 MALLINFO
+   10210 DEFAULT_ZONE
+   10220 ZONE_CHECK
 */
 
 /* 2 Apr 05: the Portland Group compiler, which uses cfront/ARM style
@@ -198,8 +198,8 @@
 */
 #define ALLOC_or_NULL(soname, fnname, vg_replacement) \
    \
-   void* VG_REPLACE_FUNCTION_EZU(1001,soname,fnname) (SizeT n); \
-   void* VG_REPLACE_FUNCTION_EZU(1001,soname,fnname) (SizeT n)  \
+   void* VG_REPLACE_FUNCTION_EZU(10010,soname,fnname) (SizeT n); \
+   void* VG_REPLACE_FUNCTION_EZU(10010,soname,fnname) (SizeT n)  \
    { \
       void* v; \
       \
@@ -213,8 +213,8 @@
 
 #define ZONEALLOC_or_NULL(soname, fnname, vg_replacement) \
    \
-   void* VG_REPLACE_FUNCTION_EZU(1002,soname,fnname) (void *zone, SizeT n); \
-   void* VG_REPLACE_FUNCTION_EZU(1002,soname,fnname) (void *zone, SizeT n)  \
+   void* VG_REPLACE_FUNCTION_EZU(10020,soname,fnname) (void *zone, SizeT n); \
+   void* VG_REPLACE_FUNCTION_EZU(10020,soname,fnname) (void *zone, SizeT n)  \
    { \
       void* v; \
       \
@@ -233,8 +233,8 @@
 */
 #define ALLOC_or_BOMB(soname, fnname, vg_replacement)  \
    \
-   void* VG_REPLACE_FUNCTION_EZU(1003,soname,fnname) (SizeT n); \
-   void* VG_REPLACE_FUNCTION_EZU(1003,soname,fnname) (SizeT n)  \
+   void* VG_REPLACE_FUNCTION_EZU(10030,soname,fnname) (SizeT n); \
+   void* VG_REPLACE_FUNCTION_EZU(10030,soname,fnname) (SizeT n)  \
    { \
       void* v; \
       \
@@ -342,8 +342,8 @@
 */
 #define ZONEFREE(soname, fnname, vg_replacement) \
    \
-   void VG_REPLACE_FUNCTION_EZU(1004,soname,fnname) (void *zone, void *p); \
-   void VG_REPLACE_FUNCTION_EZU(1004,soname,fnname) (void *zone, void *p)  \
+   void VG_REPLACE_FUNCTION_EZU(10040,soname,fnname) (void *zone, void *p); \
+   void VG_REPLACE_FUNCTION_EZU(10040,soname,fnname) (void *zone, void *p)  \
    { \
       if (!init_done) init(); \
       MALLOC_TRACE(#vg_replacement "(%p, %p)\n", zone, p ); \
@@ -354,8 +354,8 @@
 
 #define FREE(soname, fnname, vg_replacement) \
    \
-   void VG_REPLACE_FUNCTION_EZU(1005,soname,fnname) (void *p); \
-   void VG_REPLACE_FUNCTION_EZU(1005,soname,fnname) (void *p)  \
+   void VG_REPLACE_FUNCTION_EZU(10050,soname,fnname) (void *p); \
+   void VG_REPLACE_FUNCTION_EZU(10050,soname,fnname) (void *p)  \
    { \
       if (!init_done) init(); \
       MALLOC_TRACE(#vg_replacement "(%p)\n", p ); \
@@ -417,9 +417,9 @@
 
 #define ZONECALLOC(soname, fnname) \
    \
-   void* VG_REPLACE_FUNCTION_EZU(1006,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10060,soname,fnname) \
             ( void *zone, SizeT nmemb, SizeT size ); \
-   void* VG_REPLACE_FUNCTION_EZU(1006,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10060,soname,fnname) \
             ( void *zone, SizeT nmemb, SizeT size )  \
    { \
       void* v; \
@@ -434,9 +434,9 @@
 
 #define CALLOC(soname, fnname) \
    \
-   void* VG_REPLACE_FUNCTION_EZU(1007,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10070,soname,fnname) \
             ( SizeT nmemb, SizeT size ); \
-   void* VG_REPLACE_FUNCTION_EZU(1007,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10070,soname,fnname) \
             ( SizeT nmemb, SizeT size )  \
    { \
       void* v; \
@@ -469,9 +469,9 @@
 
 #define ZONEREALLOC(soname, fnname) \
    \
-   void* VG_REPLACE_FUNCTION_EZU(1008,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10080,soname,fnname) \
             ( void *zone, void* ptrV, SizeT new_size ); \
-   void* VG_REPLACE_FUNCTION_EZU(1008,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10080,soname,fnname) \
             ( void *zone, void* ptrV, SizeT new_size ) \
    { \
       void* v; \
@@ -482,10 +482,10 @@
       if (ptrV == NULL) \
          /* We need to call a malloc-like function; so let's use \
             one which we know exists. GrP fixme use zonemalloc instead? */ \
-         return VG_REPLACE_FUNCTION_EZU(1001,VG_Z_LIBC_SONAME,malloc) \
+         return VG_REPLACE_FUNCTION_EZU(10010,VG_Z_LIBC_SONAME,malloc) \
                    (new_size); \
       if (new_size <= 0) { \
-         VG_REPLACE_FUNCTION_EZU(1005,VG_Z_LIBC_SONAME,free)(ptrV); \
+         VG_REPLACE_FUNCTION_EZU(10050,VG_Z_LIBC_SONAME,free)(ptrV); \
          MALLOC_TRACE(" = 0\n"); \
          return NULL; \
       } \
@@ -496,9 +496,9 @@
 
 #define REALLOC(soname, fnname) \
    \
-   void* VG_REPLACE_FUNCTION_EZU(1009,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10090,soname,fnname) \
             ( void* ptrV, SizeT new_size );\
-   void* VG_REPLACE_FUNCTION_EZU(1009,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10090,soname,fnname) \
             ( void* ptrV, SizeT new_size ) \
    { \
       void* v; \
@@ -509,10 +509,10 @@
       if (ptrV == NULL) \
          /* We need to call a malloc-like function; so let's use \
             one which we know exists. */ \
-         return VG_REPLACE_FUNCTION_EZU(1001,VG_Z_LIBC_SONAME,malloc) \
+         return VG_REPLACE_FUNCTION_EZU(10010,VG_Z_LIBC_SONAME,malloc) \
                    (new_size); \
       if (new_size <= 0) { \
-         VG_REPLACE_FUNCTION_EZU(1005,VG_Z_LIBC_SONAME,free)(ptrV); \
+         VG_REPLACE_FUNCTION_EZU(10050,VG_Z_LIBC_SONAME,free)(ptrV); \
          MALLOC_TRACE(" = 0\n"); \
          return NULL; \
       } \
@@ -531,9 +531,9 @@
 
 #define ZONEMEMALIGN(soname, fnname) \
    \
-   void* VG_REPLACE_FUNCTION_EZU(1010,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10100,soname,fnname) \
             ( void *zone, SizeT alignment, SizeT n ); \
-   void* VG_REPLACE_FUNCTION_EZU(1010,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10100,soname,fnname) \
             ( void *zone, SizeT alignment, SizeT n ) \
    { \
       void* v; \
@@ -556,9 +556,9 @@
 
 #define MEMALIGN(soname, fnname) \
    \
-   void* VG_REPLACE_FUNCTION_EZU(1011,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10110,soname,fnname) \
             ( SizeT alignment, SizeT n ); \
-   void* VG_REPLACE_FUNCTION_EZU(1011,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10110,soname,fnname) \
             ( SizeT alignment, SizeT n )  \
    { \
       void* v; \
@@ -589,27 +589,27 @@
 
 #define VALLOC(soname, fnname) \
    \
-   void* VG_REPLACE_FUNCTION_EZU(1012,soname,fnname) ( SizeT size ); \
-   void* VG_REPLACE_FUNCTION_EZU(1012,soname,fnname) ( SizeT size ) \
+   void* VG_REPLACE_FUNCTION_EZU(10120,soname,fnname) ( SizeT size ); \
+   void* VG_REPLACE_FUNCTION_EZU(10120,soname,fnname) ( SizeT size ) \
    { \
       static int pszB = 0; \
       if (pszB == 0) \
          pszB = my_getpagesize(); \
-      return VG_REPLACE_FUNCTION_EZU(1011,VG_Z_LIBC_SONAME,memalign) \
+      return VG_REPLACE_FUNCTION_EZU(10110,VG_Z_LIBC_SONAME,memalign) \
                 ((SizeT)pszB, size); \
    }
 
 #define ZONEVALLOC(soname, fnname) \
    \
-   void* VG_REPLACE_FUNCTION_EZU(1013,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10130,soname,fnname) \
             ( void *zone, SizeT size ); \
-   void* VG_REPLACE_FUNCTION_EZU(1013,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(10130,soname,fnname) \
             ( void *zone, SizeT size )  \
    { \
       static int pszB = 0; \
       if (pszB == 0) \
          pszB = my_getpagesize(); \
-      return VG_REPLACE_FUNCTION_EZU(1011,VG_Z_LIBC_SONAME,memalign) \
+      return VG_REPLACE_FUNCTION_EZU(10110,VG_Z_LIBC_SONAME,memalign) \
                 ((SizeT)pszB, size); \
    }
 
@@ -625,8 +625,8 @@
 
 #define MALLOPT(soname, fnname) \
    \
-   int VG_REPLACE_FUNCTION_EZU(1014,soname,fnname) ( int cmd, int value ); \
-   int VG_REPLACE_FUNCTION_EZU(1014,soname,fnname) ( int cmd, int value ) \
+   int VG_REPLACE_FUNCTION_EZU(10140,soname,fnname) ( int cmd, int value ); \
+   int VG_REPLACE_FUNCTION_EZU(10140,soname,fnname) ( int cmd, int value ) \
    { \
       /* In glibc-2.2.4, 1 denotes a successful return value for \
          mallopt */ \
@@ -661,8 +661,8 @@
 // For simplicity, we always return 0.
 #define MALLOC_TRIM(soname, fnname) \
    \
-   int VG_REPLACE_FUNCTION_EZU(1015,soname,fnname) ( SizeT pad ); \
-   int VG_REPLACE_FUNCTION_EZU(1015,soname,fnname) ( SizeT pad ) \
+   int VG_REPLACE_FUNCTION_EZU(10150,soname,fnname) ( SizeT pad ); \
+   int VG_REPLACE_FUNCTION_EZU(10150,soname,fnname) ( SizeT pad ) \
    { \
       /* 0 denotes that malloc_trim() either wasn't able \
          to do anything, or was not implemented */ \
@@ -676,9 +676,9 @@
 
 #define POSIX_MEMALIGN(soname, fnname) \
    \
-   int VG_REPLACE_FUNCTION_EZU(1016,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(10160,soname,fnname) \
           ( void **memptr, SizeT alignment, SizeT size ); \
-   int VG_REPLACE_FUNCTION_EZU(1016,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(10160,soname,fnname) \
           ( void **memptr, SizeT alignment, SizeT size ) \
    { \
       void *mem; \
@@ -689,7 +689,7 @@
           || (alignment & (alignment - 1)) != 0) \
          return VKI_EINVAL; \
       \
-      mem = VG_REPLACE_FUNCTION_EZU(1011,VG_Z_LIBC_SONAME,memalign) \
+      mem = VG_REPLACE_FUNCTION_EZU(10110,VG_Z_LIBC_SONAME,memalign) \
                (alignment, size); \
       \
       if (mem != NULL) { \
@@ -707,8 +707,8 @@
 
 #define MALLOC_USABLE_SIZE(soname, fnname) \
    \
-   SizeT VG_REPLACE_FUNCTION_EZU(1017,soname,fnname) ( void* p ); \
-   SizeT VG_REPLACE_FUNCTION_EZU(1017,soname,fnname) ( void* p ) \
+   SizeT VG_REPLACE_FUNCTION_EZU(10170,soname,fnname) ( void* p ); \
+   SizeT VG_REPLACE_FUNCTION_EZU(10170,soname,fnname) ( void* p ) \
    {  \
       SizeT pszB; \
       \
@@ -740,8 +740,8 @@
 
 #define PANIC(soname, fnname) \
    \
-   void VG_REPLACE_FUNCTION_EZU(1018,soname,fnname) ( void ); \
-   void VG_REPLACE_FUNCTION_EZU(1018,soname,fnname) ( void )  \
+   void VG_REPLACE_FUNCTION_EZU(10180,soname,fnname) ( void ); \
+   void VG_REPLACE_FUNCTION_EZU(10180,soname,fnname) ( void )  \
    { \
       panic(#fnname); \
    }
@@ -752,8 +752,8 @@
 
 #define MALLOC_STATS(soname, fnname) \
    \
-   void VG_REPLACE_FUNCTION_EZU(1019,soname,fnname) ( void ); \
-   void VG_REPLACE_FUNCTION_EZU(1019,soname,fnname) ( void )  \
+   void VG_REPLACE_FUNCTION_EZU(10190,soname,fnname) ( void ); \
+   void VG_REPLACE_FUNCTION_EZU(10190,soname,fnname) ( void )  \
    { \
       /* Valgrind's malloc_stats implementation does nothing. */ \
    } 
@@ -768,8 +768,8 @@
 // doesn't know that the call to mallinfo fills in mi.
 #define MALLINFO(soname, fnname) \
    \
-   struct vg_mallinfo VG_REPLACE_FUNCTION_EZU(1020,soname,fnname) ( void ); \
-   struct vg_mallinfo VG_REPLACE_FUNCTION_EZU(1020,soname,fnname) ( void ) \
+   struct vg_mallinfo VG_REPLACE_FUNCTION_EZU(10200,soname,fnname) ( void ); \
+   struct vg_mallinfo VG_REPLACE_FUNCTION_EZU(10200,soname,fnname) ( void ) \
    { \
       static struct vg_mallinfo mi; \
       if (!init_done) init(); \
@@ -787,11 +787,11 @@
     NULL, // reserved
     NULL, // reserved
     NULL, // GrP fixme malloc_size
-    (void*)VG_REPLACE_FUNCTION_EZU(1002,VG_Z_LIBC_SONAME,malloc_zone_malloc), 
-    (void*)VG_REPLACE_FUNCTION_EZU(1006,VG_Z_LIBC_SONAME,malloc_zone_calloc), 
-    (void*)VG_REPLACE_FUNCTION_EZU(1013,VG_Z_LIBC_SONAME,malloc_zone_valloc), 
-    (void*)VG_REPLACE_FUNCTION_EZU(1004,VG_Z_LIBC_SONAME,malloc_zone_free), 
-    (void*)VG_REPLACE_FUNCTION_EZU(1008,VG_Z_LIBC_SONAME,malloc_zone_realloc), 
+    (void*)VG_REPLACE_FUNCTION_EZU(10020,VG_Z_LIBC_SONAME,malloc_zone_malloc), 
+    (void*)VG_REPLACE_FUNCTION_EZU(10060,VG_Z_LIBC_SONAME,malloc_zone_calloc), 
+    (void*)VG_REPLACE_FUNCTION_EZU(10130,VG_Z_LIBC_SONAME,malloc_zone_valloc), 
+    (void*)VG_REPLACE_FUNCTION_EZU(10040,VG_Z_LIBC_SONAME,malloc_zone_free), 
+    (void*)VG_REPLACE_FUNCTION_EZU(10080,VG_Z_LIBC_SONAME,malloc_zone_realloc), 
     NULL, // GrP fixme destroy
     "ValgrindMallocZone", 
     NULL, // batch_malloc
@@ -806,8 +806,8 @@
 
 #define DEFAULT_ZONE(soname, fnname) \
    \
-   void *VG_REPLACE_FUNCTION_EZU(1021,soname,fnname) ( void ); \
-   void *VG_REPLACE_FUNCTION_EZU(1021,soname,fnname) ( void )  \
+   void *VG_REPLACE_FUNCTION_EZU(10210,soname,fnname) ( void ); \
+   void *VG_REPLACE_FUNCTION_EZU(10210,soname,fnname) ( void )  \
    { \
       return &vg_default_zone; \
    }
@@ -820,8 +820,8 @@
 // GrP fixme bypass libc's use of zone->introspect->check
 #define ZONE_CHECK(soname, fnname) \
    \
-   int VG_REPLACE_FUNCTION_EZU(1022,soname,fnname)(void* zone); \
-   int VG_REPLACE_FUNCTION_EZU(1022,soname,fnname)(void* zone)  \
+   int VG_REPLACE_FUNCTION_EZU(10220,soname,fnname)(void* zone); \
+   int VG_REPLACE_FUNCTION_EZU(10220,soname,fnname)(void* zone)  \
    { \
       return 1; \
    }
diff --git a/coregrind/pub_core_demangle.h b/coregrind/pub_core_demangle.h
index fc62134..39bb4ad 100644
--- a/coregrind/pub_core_demangle.h
+++ b/coregrind/pub_core_demangle.h
@@ -60,7 +60,8 @@
                              /*OUT*/HChar* so, Int soLen,
                              /*OUT*/HChar* fn, Int fnLen,
                              /*OUT*/Bool* isWrap,
-                             /*OUT*/Int*  eclassTag );
+                             /*OUT*/Int*  eclassTag,
+                             /*OUT*/Int*  eclassPrio );
 
 #endif   // __PUB_CORE_DEMANGLE_H
 
diff --git a/include/pub_tool_redir.h b/include/pub_tool_redir.h
index 15894e3..4ca8358 100644
--- a/include/pub_tool_redir.h
+++ b/include/pub_tool_redir.h
@@ -51,24 +51,31 @@
    sure you use the VG_REPLACE_FN_ macros and not the VG_WRAP_FN_
    macros.
 
-   Finally there is the concept of behavioural equivalence tags.  A
-   tag is a 4-digit decimal number (0001 to 9999) encoded in the name.
-   If two replacement functions have the same tag then the redirect
-   mechanism will assume that they have identical behaviour.  If, when
-   processing redirections at library load time, the set of available
-   specifications yields more than one replacement or wrapper function
-   for a given address, the system will try to resolve the situation
-   by examining the tags on the replacements/wrappers.  In particular,
-   if all of them have the same tag, then they are all claiming to
-   behave identically, so any of them may be chosen to be the actual
-   redirection target.  Of course if not all of them have the same tag
-   then the redirection is ambiguous and the system will have to stop.
+   Finally there is the concept of prioritised behavioural equivalence
+   tags.  A tag is a 5-digit decimal number (00000 to 99999) encoded
+   in the name.  The top 4 digits are the equivalence class number,
+   and the last digit is a priority.
 
-   The tag is mandatory and must comprise 4 decimal digits.  The tag
-   0000 is special and means "does not have behaviour identical to any
+   When processing redirections at library load time, if the set of
+   available specifications yields more than one replacement or
+   wrapper function for a given address, the system will try to
+   resolve the situation by examining the tags on the
+   replacements/wrappers.
+
+   If two replacement/wrapper functions have the same tag and
+   priority, then the redirection machinery will assume they have
+   identical behaviour and can choose between them arbitrarily.  If
+   they have the same tag but different priorities, then the one with
+   higher priority will be chosen.  If neither case holds, then the
+   redirection is ambiguous and the system will ignore one of them
+   arbitrarily, but print a warning when running at -v or above.
+
+   The tag is mandatory and must comprise 5 decimal digits.  The tag
+   00000 is special and means "does not have behaviour identical to any
    other replacement/wrapper function".  Hence if you wish to write a
    wrap/replacement function that is not subject to the above
-   resolution rules, use 0000 for the tag.
+   resolution rules, use 00000 for the tag.  Tags 00001 through 00009
+   may not be used for any purpose.
 
 
    Replacement
@@ -83,12 +90,12 @@
 
    zEncodedSoname should be a Z-encoded soname (see below for
    Z-encoding details) and fnname should be an unencoded fn name.  A
-   default-safe equivalence tag of 0000 is assumed (see comments
+   default-safe equivalence tag of 00000 is assumed (see comments
    above).  The resulting name is
 
-      _vgr0000ZU_zEncodedSoname_fnname
+      _vgr00000ZU_zEncodedSoname_fnname
 
-   The "_vgr0000ZU_" is a prefix that gets discarded upon decoding.
+   The "_vgr00000ZU_" is a prefix that gets discarded upon decoding.
    It identifies this function as a replacement and specifies its
    equivalence tag.
 
@@ -104,7 +111,7 @@
    Z-encoded.  This can sometimes be necessary.  In this case the
    resulting function name is
 
-      _vgr0000ZZ_zEncodedSoname_zEncodedFnname
+      _vgr00000ZZ_zEncodedSoname_zEncodedFnname
 
    When it sees this either such name, the core's symbol-table reading
    machinery and redirection machinery first Z-decode the soname and 
@@ -138,12 +145,12 @@
 
    To write function names which explicitly state the equivalence class
    tag, use
-     VG_REPLACE_FUNCTION_EZU(4-digit-tag,zEncodedSoname,fnname)
+     VG_REPLACE_FUNCTION_EZU(5-digit-tag,zEncodedSoname,fnname)
    or
-     VG_REPLACE_FUNCTION_EZZ(4-digit-tag,zEncodedSoname,zEncodedFnname)
+     VG_REPLACE_FUNCTION_EZZ(5-digit-tag,zEncodedSoname,zEncodedFnname)
 
-   As per comments above, the tag must be a 4 digit decimal number,
-   padded with leading zeroes, in the range 0001 to 9999 inclusive.
+   As per comments above, the tag must be a 5 digit decimal number,
+   padded with leading zeroes, in the range 00010 to 99999 inclusive.
 
 
    Wrapping
@@ -206,16 +213,16 @@
 
 /* Convenience macros defined in terms of the above 4. */
 #define VG_REPLACE_FUNCTION_ZU(_soname,_fnname) \
-   VG_CONCAT6(_vgr,0000,ZU_,_soname,_,_fnname)
+   VG_CONCAT6(_vgr,00000,ZU_,_soname,_,_fnname)
 
 #define VG_REPLACE_FUNCTION_ZZ(_soname,_fnname) \
-   VG_CONCAT6(_vgr,0000,ZZ_,_soname,_,_fnname)
+   VG_CONCAT6(_vgr,00000,ZZ_,_soname,_,_fnname)
 
 #define VG_WRAP_FUNCTION_ZU(_soname,_fnname) \
-   VG_CONCAT6(_vgw,0000,ZU_,_soname,_,_fnname)
+   VG_CONCAT6(_vgw,00000,ZU_,_soname,_,_fnname)
 
 #define VG_WRAP_FUNCTION_ZZ(_soname,_fnname) \
-   VG_CONCAT6(_vgw,0000,ZZ_,_soname,_,_fnname)
+   VG_CONCAT6(_vgw,00000,ZZ_,_soname,_,_fnname)
 
 
 /* --------- Some handy Z-encoded names. --------- */
diff --git a/include/valgrind.h b/include/valgrind.h
index cac4490..0603aee 100644
--- a/include/valgrind.h
+++ b/include/valgrind.h
@@ -689,10 +689,10 @@
 #define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd
 
 #define I_WRAP_SONAME_FNNAME_ZU(soname,fnname)                    \
-   VG_CONCAT4(_vgw0000ZU_,soname,_,fnname)
+   VG_CONCAT4(_vgw00000ZU_,soname,_,fnname)
 
 #define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname)                    \
-   VG_CONCAT4(_vgw0000ZZ_,soname,_,fnname)
+   VG_CONCAT4(_vgw00000ZZ_,soname,_,fnname)
 
 /* Use this macro from within a wrapper function to collect the
    context (address and possibly other info) of the original function.
diff --git a/memcheck/mc_replace_strmem.c b/memcheck/mc_replace_strmem.c
index 1570de3..42f8ece 100644
--- a/memcheck/mc_replace_strmem.c
+++ b/memcheck/mc_replace_strmem.c
@@ -53,43 +53,46 @@
    THEY RUN ON THE SIMD CPU!
    ------------------------------------------------------------------ */
 
-/* Assignment of behavioural equivalence class tags: 2NNN is intended
+/* Assignment of behavioural equivalence class tags: 2NNNP is intended
    to be reserved for Memcheck.  Current usage:
 
-   2001 STRRCHR
-   2002 STRCHR
-   2003 STRCAT
-   2004 STRNCAT
-   2005 STRLCAT
-   2006 STRNLEN
-   2007 STRLEN
-   2008 STRCPY
-   2009 STRNCPY
-   2010 STRLCPY
-   2011 STRNCMP
-   2012 STRCASECMP
-   2013 STRNCASECMP
-   2014 STRCASECMP_L
-   2015 STRNCASECMP_L
-   2016 STRCMP
-   2017 MEMCHR
-   2018 MEMMOVE
-   2019 MEMCMP
-   2020 STPCPY
-   2021 MEMSET
-   2022 MEMCPY
-   2023 BCOPY
-   2024 GLIBC25___MEMMOVE_CHK
-   2025 GLIBC232_STRCHRNUL
-   2026 GLIBC232_RAWMEMCHR
-   2027 GLIBC25___STRCPY_CHK
-   2028 GLIBC25___STPCPY_CHK
-   2029 GLIBC25_MEMPCPY
-   2030 GLIBC26___MEMCPY_CHK
-   2031 STRSTR
-   2032 STRPBRK
-   2033 STRCSPN
-   2034 STRSPN
+   20010 STRRCHR
+   20020 STRCHR
+   20030 STRCAT
+   20040 STRNCAT
+   20050 STRLCAT
+   20060 STRNLEN
+   20070 STRLEN
+   20080 STRCPY
+   20090 STRNCPY
+   20100 STRLCPY
+   20110 STRNCMP
+   20120 STRCASECMP
+   20130 STRNCASECMP
+   20140 STRCASECMP_L
+   20150 STRNCASECMP_L
+   20160 STRCMP
+   20170 MEMCHR
+
+   20180 MEMCPY    if there's a conflict between memcpy and
+   20181 MEMMOVE   memmove, prefer memmove
+
+   20190 MEMCMP
+   20200 STPCPY
+   20210 MEMSET
+   2022P unused (was previously MEMMOVE)
+   20230 BCOPY
+   20240 GLIBC25___MEMMOVE_CHK
+   20250 GLIBC232_STRCHRNUL
+   20260 GLIBC232_RAWMEMCHR
+   20270 GLIBC25___STRCPY_CHK
+   20280 GLIBC25___STPCPY_CHK
+   20290 GLIBC25_MEMPCPY
+   20300 GLIBC26___MEMCPY_CHK
+   20310 STRSTR
+   20320 STRPBRK
+   20330 STRCSPN
+   20340 STRSPN
 */
 
 
@@ -152,8 +155,8 @@
 
 
 #define STRRCHR(soname, fnname) \
-   char* VG_REPLACE_FUNCTION_EZU(2001,soname,fnname)( const char* s, int c ); \
-   char* VG_REPLACE_FUNCTION_EZU(2001,soname,fnname)( const char* s, int c ) \
+   char* VG_REPLACE_FUNCTION_EZU(20010,soname,fnname)( const char* s, int c ); \
+   char* VG_REPLACE_FUNCTION_EZU(20010,soname,fnname)( const char* s, int c ) \
    { \
       UChar  ch   = (UChar)((UInt)c); \
       UChar* p    = (UChar*)s; \
@@ -178,8 +181,8 @@
    
 
 #define STRCHR(soname, fnname) \
-   char* VG_REPLACE_FUNCTION_EZU(2002,soname,fnname) ( const char* s, int c ); \
-   char* VG_REPLACE_FUNCTION_EZU(2002,soname,fnname) ( const char* s, int c ) \
+   char* VG_REPLACE_FUNCTION_EZU(20020,soname,fnname) ( const char* s, int c ); \
+   char* VG_REPLACE_FUNCTION_EZU(20020,soname,fnname) ( const char* s, int c ) \
    { \
       UChar  ch = (UChar)((UInt)c); \
       UChar* p  = (UChar*)s; \
@@ -206,9 +209,9 @@
 
 
 #define STRCAT(soname, fnname) \
-   char* VG_REPLACE_FUNCTION_EZU(2003,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20030,soname,fnname) \
             ( char* dst, const char* src ); \
-   char* VG_REPLACE_FUNCTION_EZU(2003,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20030,soname,fnname) \
             ( char* dst, const char* src ) \
    { \
       const Char* src_orig = src; \
@@ -234,9 +237,9 @@
 #endif
 
 #define STRNCAT(soname, fnname) \
-   char* VG_REPLACE_FUNCTION_EZU(2004,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20040,soname,fnname) \
             ( char* dst, const char* src, SizeT n ); \
-   char* VG_REPLACE_FUNCTION_EZU(2004,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20040,soname,fnname) \
             ( char* dst, const char* src, SizeT n ) \
    { \
       const Char* src_orig = src; \
@@ -270,9 +273,9 @@
    Truncation occurred if retval >= n. 
 */
 #define STRLCAT(soname, fnname) \
-   SizeT VG_REPLACE_FUNCTION_EZU(2005,soname,fnname) \
+   SizeT VG_REPLACE_FUNCTION_EZU(20050,soname,fnname) \
         ( char* dst, const char* src, SizeT n ); \
-   SizeT VG_REPLACE_FUNCTION_EZU(2005,soname,fnname) \
+   SizeT VG_REPLACE_FUNCTION_EZU(20050,soname,fnname) \
         ( char* dst, const char* src, SizeT n ) \
    { \
       const Char* src_orig = src; \
@@ -307,9 +310,9 @@
 
 
 #define STRNLEN(soname, fnname) \
-   SizeT VG_REPLACE_FUNCTION_EZU(2006,soname,fnname) \
+   SizeT VG_REPLACE_FUNCTION_EZU(20060,soname,fnname) \
             ( const char* str, SizeT n ); \
-   SizeT VG_REPLACE_FUNCTION_EZU(2006,soname,fnname) \
+   SizeT VG_REPLACE_FUNCTION_EZU(20060,soname,fnname) \
             ( const char* str, SizeT n ) \
    { \
       SizeT i = 0; \
@@ -328,9 +331,9 @@
 // confusing if you aren't expecting it.  Other small functions in this file
 // may also be inline by gcc.
 #define STRLEN(soname, fnname) \
-   SizeT VG_REPLACE_FUNCTION_EZU(2007,soname,fnname) \
+   SizeT VG_REPLACE_FUNCTION_EZU(20070,soname,fnname) \
       ( const char* str ); \
-   SizeT VG_REPLACE_FUNCTION_EZU(2007,soname,fnname) \
+   SizeT VG_REPLACE_FUNCTION_EZU(20070,soname,fnname) \
       ( const char* str )  \
    { \
       SizeT i = 0; \
@@ -345,9 +348,9 @@
 
 
 #define STRCPY(soname, fnname) \
-   char* VG_REPLACE_FUNCTION_EZU(2008,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20080,soname,fnname) \
       ( char* dst, const char* src ); \
-   char* VG_REPLACE_FUNCTION_EZU(2008,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20080,soname,fnname) \
       ( char* dst, const char* src ) \
    { \
       const Char* src_orig = src; \
@@ -376,9 +379,9 @@
 
 
 #define STRNCPY(soname, fnname) \
-   char* VG_REPLACE_FUNCTION_EZU(2009,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20090,soname,fnname) \
             ( char* dst, const char* src, SizeT n ); \
-   char* VG_REPLACE_FUNCTION_EZU(2009,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20090,soname,fnname) \
             ( char* dst, const char* src, SizeT n ) \
    { \
       const Char* src_orig = src; \
@@ -406,9 +409,9 @@
 /* Copy up to n-1 bytes from src to dst. Then nul-terminate dst if n > 0. 
    Returns strlen(src). Does not zero-fill the remainder of dst. */
 #define STRLCPY(soname, fnname) \
-   SizeT VG_REPLACE_FUNCTION_EZU(2010,soname,fnname) \
+   SizeT VG_REPLACE_FUNCTION_EZU(20100,soname,fnname) \
        ( char* dst, const char* src, SizeT n ); \
-   SizeT VG_REPLACE_FUNCTION_EZU(2010,soname,fnname) \
+   SizeT VG_REPLACE_FUNCTION_EZU(20100,soname,fnname) \
        ( char* dst, const char* src, SizeT n ) \
    { \
       const char* src_orig = src; \
@@ -435,9 +438,9 @@
 
 
 #define STRNCMP(soname, fnname) \
-   int VG_REPLACE_FUNCTION_EZU(2011,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(20110,soname,fnname) \
           ( const char* s1, const char* s2, SizeT nmax ); \
-   int VG_REPLACE_FUNCTION_EZU(2011,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(20110,soname,fnname) \
           ( const char* s1, const char* s2, SizeT nmax ) \
    { \
       SizeT n = 0; \
@@ -463,9 +466,9 @@
 
 
 #define STRCASECMP(soname, fnname) \
-   int VG_REPLACE_FUNCTION_EZU(2012,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(20120,soname,fnname) \
           ( const char* s1, const char* s2 ); \
-   int VG_REPLACE_FUNCTION_EZU(2012,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(20120,soname,fnname) \
           ( const char* s1, const char* s2 ) \
    { \
       extern int tolower(int); \
@@ -492,9 +495,9 @@
 
 
 #define STRNCASECMP(soname, fnname) \
-   int VG_REPLACE_FUNCTION_EZU(2013,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(20130,soname,fnname) \
           ( const char* s1, const char* s2, SizeT nmax ); \
-   int VG_REPLACE_FUNCTION_EZU(2013,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(20130,soname,fnname) \
           ( const char* s1, const char* s2, SizeT nmax ) \
    { \
       extern int tolower(int); \
@@ -525,9 +528,9 @@
 
 
 #define STRCASECMP_L(soname, fnname) \
-   int VG_REPLACE_FUNCTION_EZU(2014,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(20140,soname,fnname) \
           ( const char* s1, const char* s2, void* locale ); \
-   int VG_REPLACE_FUNCTION_EZU(2014,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(20140,soname,fnname) \
           ( const char* s1, const char* s2, void* locale ) \
    { \
       extern int tolower_l(int, void*) __attribute__((weak));    \
@@ -553,9 +556,9 @@
 
 
 #define STRNCASECMP_L(soname, fnname) \
-   int VG_REPLACE_FUNCTION_EZU(2015,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(20150,soname,fnname) \
           ( const char* s1, const char* s2, SizeT nmax, void* locale ); \
-   int VG_REPLACE_FUNCTION_EZU(2015,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(20150,soname,fnname) \
           ( const char* s1, const char* s2, SizeT nmax, void* locale ) \
    { \
       extern int tolower_l(int, void*) __attribute__((weak));    \
@@ -584,9 +587,9 @@
 
 
 #define STRCMP(soname, fnname) \
-   int VG_REPLACE_FUNCTION_EZU(2016,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(20160,soname,fnname) \
           ( const char* s1, const char* s2 ); \
-   int VG_REPLACE_FUNCTION_EZU(2016,soname,fnname) \
+   int VG_REPLACE_FUNCTION_EZU(20160,soname,fnname) \
           ( const char* s1, const char* s2 ) \
    { \
       register unsigned char c1; \
@@ -612,9 +615,9 @@
 
 
 #define MEMCHR(soname, fnname) \
-   void* VG_REPLACE_FUNCTION_EZU(2017,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20170,soname,fnname) \
             (const void *s, int c, SizeT n); \
-   void* VG_REPLACE_FUNCTION_EZU(2017,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20170,soname,fnname) \
             (const void *s, int c, SizeT n) \
    { \
       SizeT i; \
@@ -705,16 +708,17 @@
    }
 
 #define MEMMOVE(soname, fnname)  \
-   MEMMOVE_OR_MEMCPY(2018, soname, fnname, 0)
+   MEMMOVE_OR_MEMCPY(20181, soname, fnname, 0)
 
 #define MEMCPY(soname, fnname) \
-   MEMMOVE_OR_MEMCPY(2022, soname, fnname, 1)
+   MEMMOVE_OR_MEMCPY(20180, soname, fnname, 1)
 
 #if defined(VGO_linux)
 /* For older memcpy we have to use memmove-like semantics and skip the
    overlap check; sigh; see #275284. */
 MEMMOVE(VG_Z_LIBC_SONAME, memcpyZAGLIBCZu2Zd2Zd5) /* memcpy@GLIBC_2.2.5 */
 MEMCPY(VG_Z_LIBC_SONAME,  memcpyZAZAGLIBCZu2Zd14) /* memcpy@@GLIBC_2.14 */
+MEMCPY(VG_Z_LIBC_SONAME,  memcpy) /* fallback case */
 MEMCPY(VG_Z_LD_SO_1,      memcpy) /* ld.so.1 */
 MEMCPY(VG_Z_LD64_SO_1,    memcpy) /* ld64.so.1 */
 #elif defined(VGO_darwin)
@@ -733,9 +737,9 @@
 
 
 #define MEMCMP(soname, fnname) \
-   int VG_REPLACE_FUNCTION_EZU(2019,soname,fnname)       \
+   int VG_REPLACE_FUNCTION_EZU(20190,soname,fnname)       \
           ( const void *s1V, const void *s2V, SizeT n ); \
-   int VG_REPLACE_FUNCTION_EZU(2019,soname,fnname)       \
+   int VG_REPLACE_FUNCTION_EZU(20190,soname,fnname)       \
           ( const void *s1V, const void *s2V, SizeT n )  \
    { \
       int res; \
@@ -770,9 +774,9 @@
 /* Copy SRC to DEST, returning the address of the terminating '\0' in
    DEST. (minor variant of strcpy) */
 #define STPCPY(soname, fnname) \
-   char* VG_REPLACE_FUNCTION_EZU(2020,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20200,soname,fnname) \
             ( char* dst, const char* src ); \
-   char* VG_REPLACE_FUNCTION_EZU(2020,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20200,soname,fnname) \
             ( char* dst, const char* src ) \
    { \
       const Char* src_orig = src; \
@@ -803,9 +807,9 @@
 
 
 #define MEMSET(soname, fnname) \
-   void* VG_REPLACE_FUNCTION_EZU(2021,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20210,soname,fnname) \
             (void *s, Int c, SizeT n); \
-   void* VG_REPLACE_FUNCTION_EZU(2021,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20210,soname,fnname) \
             (void *s, Int c, SizeT n) \
    { \
       Addr a  = (Addr)s;   \
@@ -827,7 +831,7 @@
 #endif
 
 
-/* memmove -- use the MEMMOVE defn which also serves for memcpy. */
+/* memmove -- use the MEMMOVE defn above. */
 MEMMOVE(VG_Z_LIBC_SONAME, memmove)
 #if defined(VGO_darwin)
 MEMMOVE(VG_Z_DYLD,        memmove)
@@ -835,9 +839,9 @@
 
 
 #define BCOPY(soname, fnname) \
-   void VG_REPLACE_FUNCTION_EZU(2023,soname,fnname) \
+   void VG_REPLACE_FUNCTION_EZU(20230,soname,fnname) \
             (const void *srcV, void *dstV, SizeT n); \
-   void VG_REPLACE_FUNCTION_EZU(2023,soname,fnname) \
+   void VG_REPLACE_FUNCTION_EZU(20230,soname,fnname) \
             (const void *srcV, void *dstV, SizeT n) \
    { \
       SizeT i; \
@@ -863,9 +867,9 @@
 /* glibc 2.5 variant of memmove which checks the dest is big enough.
    There is no specific part of glibc that this is copied from. */
 #define GLIBC25___MEMMOVE_CHK(soname, fnname) \
-   void* VG_REPLACE_FUNCTION_EZU(2024,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20240,soname,fnname) \
             (void *dstV, const void *srcV, SizeT n, SizeT destlen); \
-   void* VG_REPLACE_FUNCTION_EZU(2024,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20240,soname,fnname) \
             (void *dstV, const void *srcV, SizeT n, SizeT destlen) \
    { \
       SizeT i; \
@@ -897,9 +901,9 @@
 
 /* Find the first occurrence of C in S or the final NUL byte.  */
 #define GLIBC232_STRCHRNUL(soname, fnname) \
-   char* VG_REPLACE_FUNCTION_EZU(2025,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20250,soname,fnname) \
             (const char* s, int c_in); \
-   char* VG_REPLACE_FUNCTION_EZU(2025,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20250,soname,fnname) \
             (const char* s, int c_in) \
    { \
       unsigned char  c        = (unsigned char) c_in; \
@@ -916,9 +920,9 @@
 
 /* Find the first occurrence of C in S.  */
 #define GLIBC232_RAWMEMCHR(soname, fnname) \
-   char* VG_REPLACE_FUNCTION_EZU(2026,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20260,soname,fnname) \
             (const char* s, int c_in); \
-   char* VG_REPLACE_FUNCTION_EZU(2026,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20260,soname,fnname) \
             (const char* s, int c_in) \
    { \
       unsigned char  c        = (unsigned char) c_in; \
@@ -937,9 +941,9 @@
 /* glibc variant of strcpy that checks the dest is big enough.
    Copied from glibc-2.5/debug/test-strcpy_chk.c. */
 #define GLIBC25___STRCPY_CHK(soname,fnname) \
-   char* VG_REPLACE_FUNCTION_EZU(2027,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20270,soname,fnname) \
             (char* dst, const char* src, SizeT len); \
-   char* VG_REPLACE_FUNCTION_EZU(2027,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20270,soname,fnname) \
             (char* dst, const char* src, SizeT len) \
    { \
       char* ret = dst; \
@@ -964,9 +968,9 @@
 /* glibc variant of stpcpy that checks the dest is big enough.
    Copied from glibc-2.5/debug/test-stpcpy_chk.c. */
 #define GLIBC25___STPCPY_CHK(soname,fnname) \
-   char* VG_REPLACE_FUNCTION_EZU(2028,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20280,soname,fnname) \
             (char* dst, const char* src, SizeT len); \
-   char* VG_REPLACE_FUNCTION_EZU(2028,soname,fnname) \
+   char* VG_REPLACE_FUNCTION_EZU(20280,soname,fnname) \
             (char* dst, const char* src, SizeT len) \
    { \
       if (! len) \
@@ -989,9 +993,9 @@
 
 /* mempcpy */
 #define GLIBC25_MEMPCPY(soname, fnname) \
-   void* VG_REPLACE_FUNCTION_EZU(2029,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20290,soname,fnname) \
             ( void *dst, const void *src, SizeT len ); \
-   void* VG_REPLACE_FUNCTION_EZU(2029,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20290,soname,fnname) \
             ( void *dst, const void *src, SizeT len ) \
    { \
       register char *d; \
@@ -1027,9 +1031,9 @@
 
 
 #define GLIBC26___MEMCPY_CHK(soname, fnname) \
-   void* VG_REPLACE_FUNCTION_EZU(2030,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20300,soname,fnname) \
             (void* dst, const void* src, SizeT len, SizeT dstlen ); \
-   void* VG_REPLACE_FUNCTION_EZU(2030,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20300,soname,fnname) \
             (void* dst, const void* src, SizeT len, SizeT dstlen ) \
    { \
       register char *d; \
@@ -1070,9 +1074,9 @@
 
 
 #define STRSTR(soname, fnname) \
-   void* VG_REPLACE_FUNCTION_EZU(2031,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20310,soname,fnname) \
          (void* haystack, void* needle); \
-   void* VG_REPLACE_FUNCTION_EZU(2031,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20310,soname,fnname) \
          (void* haystack, void* needle) \
    { \
       UChar* h = (UChar*)haystack; \
@@ -1112,9 +1116,9 @@
 
 
 #define STRPBRK(soname, fnname) \
-   void* VG_REPLACE_FUNCTION_EZU(2032,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20320,soname,fnname) \
          (void* sV, void* acceptV); \
-   void* VG_REPLACE_FUNCTION_EZU(2032,soname,fnname) \
+   void* VG_REPLACE_FUNCTION_EZU(20320,soname,fnname) \
          (void* sV, void* acceptV) \
    { \
       UChar* s = (UChar*)sV; \
@@ -1149,9 +1153,9 @@
 
 
 #define STRCSPN(soname, fnname) \
-   SizeT VG_REPLACE_FUNCTION_EZU(2033,soname,fnname) \
+   SizeT VG_REPLACE_FUNCTION_EZU(20330,soname,fnname) \
          (void* sV, void* rejectV); \
-   SizeT VG_REPLACE_FUNCTION_EZU(2033,soname,fnname) \
+   SizeT VG_REPLACE_FUNCTION_EZU(20330,soname,fnname) \
          (void* sV, void* rejectV) \
    { \
       UChar* s = (UChar*)sV; \
@@ -1187,9 +1191,9 @@
 
 
 #define STRSPN(soname, fnname) \
-   SizeT VG_REPLACE_FUNCTION_EZU(2034,soname,fnname) \
+   SizeT VG_REPLACE_FUNCTION_EZU(20340,soname,fnname) \
          (void* sV, void* acceptV); \
-   SizeT VG_REPLACE_FUNCTION_EZU(2034,soname,fnname) \
+   SizeT VG_REPLACE_FUNCTION_EZU(20340,soname,fnname) \
          (void* sV, void* acceptV) \
    { \
       UChar* s = (UChar*)sV; \