Add more infrastructure to be used for fixing #275284 (Valgrind
memcpy/memmove redirection stopped working in glibc 2.14/x86_64), but
don't fix the problem yet.  Should be no end-user visible change.

* in m_redir.c, when processing redirection specifications, consider
  all the names associated with an address, not just the primary name.

* add plumbing to support the notion of "behavioural equivalence class
  tags" of redirect/wrap functions.  These can be used by m_redir to
  resolve some situations in which the available set of redirect
  specifications causes some address to get redirected to two
  different functions.  (Framework is in place, but such resolution is
  not implemented yet.)



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11984 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/m_demangle/demangle.c b/coregrind/m_demangle/demangle.c
index f98a1f2..ccc5926 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)) {
+                                 z_demangled, N_ZBUF, NULL, NULL )) {
          orig = z_demangled;
       }
    }
@@ -146,7 +146,8 @@
 Bool VG_(maybe_Z_demangle) ( const HChar* sym, 
                              /*OUT*/HChar* so, Int soLen,
                              /*OUT*/HChar* fn, Int fnLen,
-                             /*OUT*/Bool* isWrap )
+                             /*OUT*/Bool* isWrap,
+                             /*OUT*/Int*  eclassTag )
 {
 #  define EMITSO(ch)                           \
       do {                                     \
@@ -181,32 +182,50 @@
            &&  sym[1] == 'v'
            &&  sym[2] == 'g'
            && (sym[3] == 'r' || sym[3] == 'w' || sym[3] == 'n')
-           &&  sym[4] == 'Z'
-           && (sym[5] == 'Z' || sym[5] == 'U')
-           &&  sym[6] == '_';
+           &&  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;
+   }
    if (!valid)
       return False;
 
-   fn_is_encoded = sym[5] == 'Z';
+   fn_is_encoded = sym[9] == 'Z';
 
    if (isWrap)
       *isWrap = sym[3] == 'w';
 
+   if (eclassTag) {
+      *eclassTag =    1000 * ((Int)sym[4] - '0')
+                   +  100 * ((Int)sym[5] - '0')
+                   +  10 * ((Int)sym[6] - '0')
+                   +  1 * ((Int)sym[7] - '0');
+      vg_assert(*eclassTag >= 0 && *eclassTag <= 9999);
+   }
+
    /* Now check the soname prefix isn't "VG_Z_", as described in
       pub_tool_redir.h. */
    is_VG_Z_prefixed =
-      sym[ 7] == 'V' &&
-      sym[ 8] == 'G' &&
-      sym[ 9] == '_' &&
-      sym[10] == 'Z' &&
-      sym[11] == '_';
+      sym[11] == 'V' &&
+      sym[12] == 'G' &&
+      sym[13] == '_' &&
+      sym[14] == 'Z' &&
+      sym[15] == '_';
    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 = 7;
+   i = 11;
    while (True) {
 
       if (sym[i] == '_')
diff --git a/coregrind/m_redir.c b/coregrind/m_redir.c
index e5f72d2..7208d39 100644
--- a/coregrind/m_redir.c
+++ b/coregrind/m_redir.c
@@ -77,14 +77,18 @@
    table).
 
    Redirect specifications are really symbols with "funny" prefixes
-   (_vgrZU_ and _vgrZZ_).  These names tell m_redir that the
+   (_vgrNNNNZU_ and _vgrNNNNZZ_).  These names tell m_redir that the
    associated code should replace the standard entry point for some
    set of functions.  The set of functions is specified by a (soname
    pattern, function name pattern) pair which is encoded in the symbol
    name following the prefix.  The names use a Z-encoding scheme so
    that they may contain punctuation characters and wildcards (*).
    The encoding scheme is described in pub_tool_redir.h and is decoded
-   by VG_(maybe_Z_demangle).
+   by VG_(maybe_Z_demangle).  The NNNN are behavioural equivalence
+   class tags, and are used to by code in this module to resolve
+   situations where one address appears to be redirected to more than
+   one replacement/wrapper.  This is also described in
+   pub_tool_redir.h.
 
    When a shared object is unloaded, this module learns of it via a
    call to VG_(redir_notify_delete_DebugInfo).  It then removes from
@@ -226,6 +230,9 @@
       HChar* from_fnpatt;  /* from fnname pattern  */
       Addr   to_addr;      /* where redirecting to */
       Bool   isWrap;       /* wrap or replacement? */
+      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. */
       const HChar** mandatory; /* non-NULL ==> abort V and print the
                                   strings if from_sopatt is loaded but
                                   from_fnpatt cannot be found */
@@ -270,6 +277,7 @@
       Addr     to_addr;     /* where redirecting to */
       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 */
       Bool     isWrap;      /* wrap or replacement? */
       Bool     isIFunc;     /* indirect function? */
    }
@@ -314,6 +322,45 @@
         TopSpec* parent_sym 
      );
 
+
+/* Copy all the names from a given symbol into an AR_DINFO allocated,
+   NULL terminated array, for easy iteration.  Caller must pass also
+   the address of a 2-entry array which can be used in the common case
+   to avoid dynamic allocation. */
+static UChar** alloc_symname_array ( UChar* pri_name, UChar** sec_names,
+                                     UChar** twoslots )
+{
+   /* Special-case the common case: only one name.  We expect the
+      caller to supply a stack-allocated 2-entry array for this. */
+   if (sec_names == NULL) {
+      twoslots[0] = pri_name;
+      twoslots[1] = NULL;
+      return twoslots;
+   }
+   /* Else must use dynamic allocation.  Figure out size .. */
+   Word    n_req = 1;
+   UChar** pp    = sec_names;
+   while (*pp) { n_req++; pp++; }
+   /* .. allocate and copy in. */
+   UChar** arr = dinfo_zalloc( "redir.asa.1", (n_req+1) * sizeof(UChar*) );
+   Word    i   = 0;
+   arr[i++] = pri_name;
+   pp = sec_names;
+   while (*pp) { arr[i++] = *pp; pp++; }
+   tl_assert(i == n_req);
+   tl_assert(arr[n_req] == NULL);
+   return arr;
+}
+
+
+/* Free the array allocated by alloc_symname_array, if any. */
+static void free_symname_array ( UChar** names, UChar** twoslots )
+{
+   if (names != twoslots)
+      dinfo_free(names);
+}
+
+
 /* Notify m_redir of the arrival of a new DebugInfo.  This is fairly
    complex, but the net effect is to (1) add a new entry to the
    topspecs list, and (2) figure out what new binding are now active,
@@ -321,106 +368,126 @@
 
 #define N_DEMANGLED 256
 
-void VG_(redir_notify_new_DebugInfo)( DebugInfo* newsi )
+void VG_(redir_notify_new_DebugInfo)( DebugInfo* newdi )
 {
    Bool         ok, isWrap;
-   Int          i, nsyms;
+   Int          i, nsyms, becTag;
    Spec*        specList;
    Spec*        spec;
    TopSpec*     ts;
    TopSpec*     newts;
    UChar*       sym_name_pri;
+   UChar**      sym_names_sec;
    Addr         sym_addr, sym_toc;
    HChar        demangled_sopatt[N_DEMANGLED];
    HChar        demangled_fnpatt[N_DEMANGLED];
    Bool         check_ppcTOCs = False;
    Bool         isText;
-   const UChar* newsi_soname;
+   const UChar* newdi_soname;
 
 #  if defined(VG_PLAT_USES_PPCTOC)
    check_ppcTOCs = True;
 #  endif
 
-   vg_assert(newsi);
-   newsi_soname = VG_(DebugInfo_get_soname)(newsi);
-   vg_assert(newsi_soname != NULL);
+   vg_assert(newdi);
+   newdi_soname = VG_(DebugInfo_get_soname)(newdi);
+   vg_assert(newdi_soname != NULL);
 
    /* stay sane: we don't already have this. */
    for (ts = topSpecs; ts; ts = ts->next)
-      vg_assert(ts->seginfo != newsi);
+      vg_assert(ts->seginfo != newdi);
 
    /* scan this DebugInfo's symbol table, pulling out and demangling
       any specs found */
 
    specList = NULL; /* the spec list we're building up */
 
-   nsyms = VG_(DebugInfo_syms_howmany)( newsi );
+   nsyms = VG_(DebugInfo_syms_howmany)( newdi );
    for (i = 0; i < nsyms; i++) {
-      VG_(DebugInfo_syms_getidx)( newsi, i, &sym_addr, &sym_toc,
-                                  NULL, &sym_name_pri, NULL, &isText, NULL );
-      ok = VG_(maybe_Z_demangle)( sym_name_pri, demangled_sopatt, N_DEMANGLED,
-                                  demangled_fnpatt, N_DEMANGLED, &isWrap );
-      /* ignore data symbols */
-      if (!isText)
-         continue;
-      if (!ok) {
-         /* It's not a full-scale redirect, but perhaps it is a load-notify
-            fn?  Let the load-notify department see it. */
-         handle_maybe_load_notifier( newsi_soname, sym_name_pri, sym_addr );
-         continue; 
+      VG_(DebugInfo_syms_getidx)( newdi, i, &sym_addr, &sym_toc,
+                                  NULL, &sym_name_pri, &sym_names_sec,
+                                  &isText, NULL );
+      /* Set up to conveniently iterate over all names for this symbol. */
+      UChar*  twoslots[2];
+      UChar** names_init = alloc_symname_array(sym_name_pri, sym_names_sec,
+                                               &twoslots[0]);
+      UChar** names;
+      for (names = names_init; *names; names++) {
+         ok = VG_(maybe_Z_demangle)( *names,
+                                     demangled_sopatt, N_DEMANGLED,
+                                     demangled_fnpatt, N_DEMANGLED,
+                                     &isWrap, &becTag );
+         /* ignore data symbols */
+         if (!isText)
+            continue;
+         if (!ok) {
+            /* It's not a full-scale redirect, but perhaps it is a load-notify
+               fn?  Let the load-notify department see it. */
+            handle_maybe_load_notifier( newdi_soname, *names, sym_addr );
+            continue; 
+         }
+         if (check_ppcTOCs && sym_toc == 0) {
+            /* This platform uses toc pointers, but none could be found
+               for this symbol, so we can't safely redirect/wrap to it.
+               Just skip it; we'll make a second pass over the symbols in
+               the following loop, and complain at that point. */
+            continue;
+         }
+         spec = dinfo_zalloc("redir.rnnD.1", sizeof(Spec));
+         vg_assert(spec);
+         spec->from_sopatt = dinfo_strdup("redir.rnnD.2", demangled_sopatt);
+         spec->from_fnpatt = dinfo_strdup("redir.rnnD.3", demangled_fnpatt);
+         vg_assert(spec->from_sopatt);
+         vg_assert(spec->from_fnpatt);
+         spec->to_addr = sym_addr;
+         spec->isWrap = isWrap;
+         spec->becTag = becTag;
+         /* check we're not adding manifestly stupid destinations */
+         vg_assert(is_plausible_guest_addr(sym_addr));
+         spec->next = specList;
+         spec->mark = False; /* not significant */
+         spec->done = False; /* not significant */
+         specList = spec;
       }
-      if (check_ppcTOCs && sym_toc == 0) {
-         /* This platform uses toc pointers, but none could be found
-            for this symbol, so we can't safely redirect/wrap to it.
-            Just skip it; we'll make a second pass over the symbols in
-            the following loop, and complain at that point. */
-         continue;
-      }
-      spec = dinfo_zalloc("redir.rnnD.1", sizeof(Spec));
-      vg_assert(spec);
-      spec->from_sopatt = dinfo_strdup("redir.rnnD.2", demangled_sopatt);
-      spec->from_fnpatt = dinfo_strdup("redir.rnnD.3", demangled_fnpatt);
-      vg_assert(spec->from_sopatt);
-      vg_assert(spec->from_fnpatt);
-      spec->to_addr = sym_addr;
-      spec->isWrap = isWrap;
-      /* check we're not adding manifestly stupid destinations */
-      vg_assert(is_plausible_guest_addr(sym_addr));
-      spec->next = specList;
-      spec->mark = False; /* not significant */
-      spec->done = False; /* not significant */
-      specList = spec;
+      free_symname_array(names_init, &twoslots[0]);
    }
 
    if (check_ppcTOCs) {
       for (i = 0; i < nsyms; i++) {
-         VG_(DebugInfo_syms_getidx)( newsi, i, &sym_addr, &sym_toc,
-                                     NULL, &sym_name_pri, NULL,
+         VG_(DebugInfo_syms_getidx)( newdi, i, &sym_addr, &sym_toc,
+                                     NULL, &sym_name_pri, &sym_names_sec,
                                      &isText, NULL );
-         ok = isText
-              && VG_(maybe_Z_demangle)( 
-                    sym_name_pri, demangled_sopatt, N_DEMANGLED,
-                    demangled_fnpatt, N_DEMANGLED, &isWrap );
-         if (!ok)
-            /* not a redirect.  Ignore. */
-            continue;
-         if (sym_toc != 0)
-            /* has a valid toc pointer.  Ignore. */
-            continue;
+         UChar*  twoslots[2];
+         UChar** names_init = alloc_symname_array(sym_name_pri, sym_names_sec,
+                                                  &twoslots[0]);
+         UChar** names;
+         for (names = names_init; *names; names++) {
+            ok = isText
+                 && VG_(maybe_Z_demangle)( 
+                       *names, demangled_sopatt, N_DEMANGLED,
+                       demangled_fnpatt, N_DEMANGLED, &isWrap, NULL );
+            if (!ok)
+               /* not a redirect.  Ignore. */
+               continue;
+            if (sym_toc != 0)
+               /* has a valid toc pointer.  Ignore. */
+               continue;
 
-         for (spec = specList; spec; spec = spec->next) 
-            if (0 == VG_(strcmp)(spec->from_sopatt, demangled_sopatt)
-                && 0 == VG_(strcmp)(spec->from_fnpatt, demangled_fnpatt))
-               break;
-         if (spec)
-            /* a redirect to some other copy of that symbol, which
-               does have a TOC value, already exists */
-            continue;
+            for (spec = specList; spec; spec = spec->next) 
+               if (0 == VG_(strcmp)(spec->from_sopatt, demangled_sopatt)
+                   && 0 == VG_(strcmp)(spec->from_fnpatt, demangled_fnpatt))
+                  break;
+            if (spec)
+               /* a redirect to some other copy of that symbol, which
+                  does have a TOC value, already exists */
+               continue;
 
-         /* Complain */
-         VG_(message)(Vg_DebugMsg,
-                      "WARNING: no TOC ptr for redir/wrap to %s %s\n",
-                      demangled_sopatt, demangled_fnpatt);
+            /* Complain */
+            VG_(message)(Vg_DebugMsg,
+                         "WARNING: no TOC ptr for redir/wrap to %s %s\n",
+                         demangled_sopatt, demangled_fnpatt);
+         }
+         free_symname_array(names_init, &twoslots[0]);
       }
    }
 
@@ -429,7 +496,7 @@
    newts = dinfo_zalloc("redir.rnnD.4", sizeof(TopSpec));
    vg_assert(newts);
    newts->next    = NULL; /* not significant */
-   newts->seginfo = newsi;
+   newts->seginfo = newdi;
    newts->specs   = specList;
    newts->mark    = False; /* not significant */
 
@@ -439,10 +506,10 @@
       (1) actives formed by matching the new specs in specList against
           all symbols currently listed in topSpecs
 
-      (2) actives formed by matching the new symbols in newsi against
+      (2) actives formed by matching the new symbols in newdi against
           all specs currently listed in topSpecs
 
-      (3) actives formed by matching the new symbols in newsi against
+      (3) actives formed by matching the new symbols in newdi against
           the new specs in specList
 
       This is necessary in order to maintain the invariant that
@@ -460,12 +527,12 @@
    /* Case (2) */
    for (ts = topSpecs; ts; ts = ts->next) {
       generate_and_add_actives( ts->specs, ts, 
-                                newsi,     newts );
+                                newdi,     newts );
    }
 
    /* Case (3) */
    generate_and_add_actives( specList, newts, 
-                             newsi,    newts );
+                             newdi,    newts );
 
    /* Finally, add the new TopSpec. */
    newts->next = topSpecs;
@@ -477,7 +544,7 @@
    /* Really finally (quite unrelated to all the above) check the
       names in the module against any --require-text-symbol=
       specifications we might have. */
-   handle_require_text_symbols(newsi);
+   handle_require_text_symbols(newdi);
 }
 
 #undef N_DEMANGLED
@@ -502,7 +569,8 @@
 
     if (VG_(clo_trace_redir)) {
        VG_(message)( Vg_DebugMsg,
-                     "Adding redirect for indirect function 0x%llx from 0x%llx -> 0x%llx\n",
+                     "Adding redirect for indirect function "
+                     "0x%llx from 0x%llx -> 0x%llx\n",
                      (ULong)old_from, (ULong)new_from, (ULong)new.to_addr );
     }
 }
@@ -522,12 +590,13 @@
         TopSpec* parent_sym 
      )
 {
-   Spec*  sp;
-   Bool   anyMark, isText, isIFunc;
-   Active act;
-   Int    nsyms, i;
-   Addr   sym_addr;
-   UChar* sym_name_pri;
+   Spec*   sp;
+   Bool    anyMark, isText, isIFunc;
+   Active  act;
+   Int     nsyms, i;
+   Addr    sym_addr;
+   UChar*  sym_name_pri;
+   UChar** sym_names_sec;
 
    /* First figure out which of the specs match the seginfo's soname.
       Also clear the 'done' bits, so that after the main loop below
@@ -548,28 +617,38 @@
       of trashing the caches less. */
    nsyms = VG_(DebugInfo_syms_howmany)( di );
    for (i = 0; i < nsyms; i++) {
-      VG_(DebugInfo_syms_getidx)( di, i, &sym_addr, NULL, NULL,
-                                  &sym_name_pri, NULL, &isText, &isIFunc );
+      VG_(DebugInfo_syms_getidx)( di, i, &sym_addr, NULL,
+                                  NULL, &sym_name_pri, &sym_names_sec,
+                                  &isText, &isIFunc );
+      UChar*  twoslots[2];
+      UChar** names_init = alloc_symname_array(sym_name_pri, sym_names_sec,
+                                               &twoslots[0]);
+      UChar** names;
+      for (names = names_init; *names; names++) {
 
-      /* ignore data symbols */
-      if (!isText)
-         continue;
+         /* ignore data symbols */
+         if (!isText)
+            continue;
 
-      for (sp = specs; sp; sp = sp->next) {
-         if (!sp->mark)
-            continue; /* soname doesn't match */
-         if (VG_(string_match)( sp->from_fnpatt, sym_name_pri )) {
-            /* got a new binding.  Add to collection. */
-            act.from_addr   = sym_addr;
-            act.to_addr     = sp->to_addr;
-            act.parent_spec = parent_spec;
-            act.parent_sym  = parent_sym;
-            act.isWrap      = sp->isWrap;
-            act.isIFunc     = isIFunc;
-            sp->done = True;
-            maybe_add_active( act );
-         }
-      } /* for (sp = specs; sp; sp = sp->next) */
+         for (sp = specs; sp; sp = sp->next) {
+            if (!sp->mark)
+               continue; /* soname doesn't match */
+            if (VG_(string_match)( sp->from_fnpatt, *names )) {
+               /* got a new binding.  Add to collection. */
+               act.from_addr   = sym_addr;
+               act.to_addr     = sp->to_addr;
+               act.parent_spec = parent_spec;
+               act.parent_sym  = parent_sym;
+               act.becTag      = sp->becTag;
+               act.isWrap      = sp->isWrap;
+               act.isIFunc     = isIFunc;
+               sp->done = True;
+               maybe_add_active( act );
+            }
+         } /* for (sp = specs; sp; sp = sp->next) */
+
+      } /* iterating over names[] */
+      free_symname_array(names_init, &twoslots[0]);
    } /* for (i = 0; i < nsyms; i++)  */
 
    /* Now, finally, look for Specs which were marked to be done, but
@@ -830,6 +909,7 @@
    act.to_addr     = to;
    act.parent_spec = NULL;
    act.parent_sym  = NULL;
+   act.becTag      = 0; /* "not equivalent to any other fn" */
    act.isWrap      = False;
    act.isIFunc     = False;
    maybe_add_active( act );
@@ -1211,24 +1291,34 @@
       This isn't terribly efficient but it happens rarely, so no big
       deal. */
    for (i = 0; i < fnpatts_used; i++) {
-      Bool   found = False;
+      Bool   found  = False;
       HChar* fnpatt = fnpatts[i];
-      Int    nsyms = VG_(DebugInfo_syms_howmany)(di);
+      Int    nsyms  = VG_(DebugInfo_syms_howmany)(di);
       for (j = 0; j < nsyms; j++) {
-         Bool   isText       = False;
-         UChar* sym_name_pri = NULL;
+         Bool    isText        = False;
+         UChar*  sym_name_pri  = NULL;
+         UChar** sym_names_sec = NULL;
          VG_(DebugInfo_syms_getidx)( di, j, NULL, NULL,
-                                     NULL, &sym_name_pri, NULL,
+                                     NULL, &sym_name_pri, &sym_names_sec,
                                      &isText, NULL );
-         /* ignore data symbols */
-         if (0) VG_(printf)("QQQ %s\n", sym_name_pri);
-         vg_assert(sym_name_pri);
-         if (!isText)
-            continue;
-         if (VG_(string_match)(fnpatt, sym_name_pri)) {
-            found = True;
-            break;
+         UChar*  twoslots[2];
+         UChar** names_init = alloc_symname_array(sym_name_pri, sym_names_sec,
+                                                  &twoslots[0]);
+         UChar** names;
+         for (names = names_init; *names; names++) {
+            /* ignore data symbols */
+            if (0) VG_(printf)("QQQ %s\n", *names);
+            vg_assert(sym_name_pri);
+            if (!isText)
+               continue;
+            if (VG_(string_match)(fnpatt, *names)) {
+               found = True;
+               break;
+            }
          }
+         free_symname_array(names_init, &twoslots[0]);
+         if (found)
+            break;
       }
 
       if (!found) {
@@ -1265,10 +1355,11 @@
 static void show_spec ( HChar* left, Spec* spec )
 {
    VG_(message)( Vg_DebugMsg, 
-                 "%s%25s %30s %s-> 0x%08llx\n",
+                 "%s%25s %30s %s-> (%04d) 0x%08llx\n",
                  left,
                  spec->from_sopatt, spec->from_fnpatt,
                  spec->isWrap ? "W" : "R",
+                 spec->becTag,
                  (ULong)spec->to_addr );
 }
 
@@ -1283,11 +1374,11 @@
    ok = VG_(get_fnname_w_offset)(act->to_addr, name2, 64);
    if (!ok) VG_(strcpy)(name2, "???");
 
-   VG_(message)(Vg_DebugMsg, "%s0x%08llx (%20s) %s-> 0x%08llx %s\n", 
+   VG_(message)(Vg_DebugMsg, "%s0x%08llx (%20s) %s-> (%04d) 0x%08llx %s\n", 
                              left, 
                              (ULong)act->from_addr, name1,
                              act->isWrap ? "W" : "R",
-                             (ULong)act->to_addr, name2 );
+                             act->becTag, (ULong)act->to_addr, name2 );
 }
 
 static void show_redir_state ( HChar* who )
diff --git a/coregrind/pub_core_demangle.h b/coregrind/pub_core_demangle.h
index 71cc4c3..fc62134 100644
--- a/coregrind/pub_core_demangle.h
+++ b/coregrind/pub_core_demangle.h
@@ -59,7 +59,8 @@
 Bool VG_(maybe_Z_demangle) ( const HChar* sym, 
                              /*OUT*/HChar* so, Int soLen,
                              /*OUT*/HChar* fn, Int fnLen,
-                             /*OUT*/Bool* isWrap );
+                             /*OUT*/Bool* isWrap,
+                             /*OUT*/Int*  eclassTag );
 
 #endif   // __PUB_CORE_DEMANGLE_H
 
diff --git a/include/pub_tool_redir.h b/include/pub_tool_redir.h
index 7360de1..15894e3 100644
--- a/include/pub_tool_redir.h
+++ b/include/pub_tool_redir.h
@@ -51,6 +51,26 @@
    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.
+
+   The tag is mandatory and must comprise 4 decimal digits.  The tag
+   0000 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.
+
+
    Replacement
    ~~~~~~~~~~~
    To write a replacement function, do this:
@@ -61,12 +81,16 @@
          ... body ...
       }
 
-   zEncodedSoname should be a Z-encoded soname (see below for Z-encoding
-   details) and fnname should be an unencoded fn name.  The resulting name is
+   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
+   above).  The resulting name is
 
-      _vgrZU_zEncodedSoname_fnname
+      _vgr0000ZU_zEncodedSoname_fnname
 
-   The "_vgrZU_" is a prefix that gets discarded upon decoding.
+   The "_vgr0000ZU_" is a prefix that gets discarded upon decoding.
+   It identifies this function as a replacement and specifies its
+   equivalence tag.
 
    It is also possible to write
 
@@ -80,7 +104,7 @@
    Z-encoded.  This can sometimes be necessary.  In this case the
    resulting function name is
 
-      _vgrZZ_zEncodedSoname_zEncodedFnname
+      _vgr0000ZZ_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 
@@ -112,6 +136,16 @@
    underscores, since the intercept-handlers in m_redir.c detect the
    end of the soname by looking for the first trailing underscore.
 
+   To write function names which explicitly state the equivalence class
+   tag, use
+     VG_REPLACE_FUNCTION_EZU(4-digit-tag,zEncodedSoname,fnname)
+   or
+     VG_REPLACE_FUNCTION_EZZ(4-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.
+
+
    Wrapping
    ~~~~~~~~
    This is identical to replacement, except that you should use the
@@ -119,6 +153,8 @@
 
       VG_WRAP_FUNCTION_ZU
       VG_WRAP_FUNCTION_ZZ
+      VG_WRAP_FUNCTION_EZU
+      VG_WRAP_FUNCTION_EZZ
 
    instead.
 
@@ -153,11 +189,34 @@
    args are fully macro-expanded before pasting them together. */
 #define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd
 
-#define VG_REPLACE_FUNCTION_ZU(soname,fnname) VG_CONCAT4(_vgrZU_,soname,_,fnname)
-#define VG_REPLACE_FUNCTION_ZZ(soname,fnname) VG_CONCAT4(_vgrZZ_,soname,_,fnname)
+#define VG_CONCAT6(_aa,_bb,_cc,_dd,_ee,_ff) _aa##_bb##_cc##_dd##_ee##_ff
 
-#define VG_WRAP_FUNCTION_ZU(soname,fnname) VG_CONCAT4(_vgwZU_,soname,_,fnname)
-#define VG_WRAP_FUNCTION_ZZ(soname,fnname) VG_CONCAT4(_vgwZZ_,soname,_,fnname)
+/* The 4 basic macros. */
+#define VG_REPLACE_FUNCTION_EZU(_eclasstag,_soname,_fnname) \
+   VG_CONCAT6(_vgr,_eclasstag,ZU_,_soname,_,_fnname)
+
+#define VG_REPLACE_FUNCTION_EZZ(_eclasstag,_soname,_fnname) \
+   VG_CONCAT6(_vgr,_eclasstag,ZZ_,_soname,_,_fnname)
+
+#define VG_WRAP_FUNCTION_EZU(_eclasstag,_soname,_fnname) \
+   VG_CONCAT6(_vgw,_eclasstag,ZU_,_soname,_,_fnname)
+
+#define VG_WRAP_FUNCTION_EZZ(_eclasstag,_soname,_fnname) \
+   VG_CONCAT6(_vgw,_eclasstag,ZZ_,_soname,_,_fnname)
+
+/* Convenience macros defined in terms of the above 4. */
+#define VG_REPLACE_FUNCTION_ZU(_soname,_fnname) \
+   VG_CONCAT6(_vgr,0000,ZU_,_soname,_,_fnname)
+
+#define VG_REPLACE_FUNCTION_ZZ(_soname,_fnname) \
+   VG_CONCAT6(_vgr,0000,ZZ_,_soname,_,_fnname)
+
+#define VG_WRAP_FUNCTION_ZU(_soname,_fnname) \
+   VG_CONCAT6(_vgw,0000,ZU_,_soname,_,_fnname)
+
+#define VG_WRAP_FUNCTION_ZZ(_soname,_fnname) \
+   VG_CONCAT6(_vgw,0000,ZZ_,_soname,_,_fnname)
+
 
 /* --------- Some handy Z-encoded names. --------- */
 
diff --git a/include/valgrind.h b/include/valgrind.h
index fc572b6..cac4490 100644
--- a/include/valgrind.h
+++ b/include/valgrind.h
@@ -679,17 +679,20 @@
 */
 
 /* Use these to write the name of your wrapper.  NOTE: duplicates
-   VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. */
+   VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h.  NOTE also: inserts
+   the default behaviour equivalance class tag "0000" into the name.
+   See pub_tool_redir.h for details -- normally you don't need to
+   think about this, though. */
 
 /* Use an extra level of macroisation so as to ensure the soname/fnname
    args are fully macro-expanded before pasting them together. */
 #define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd
 
 #define I_WRAP_SONAME_FNNAME_ZU(soname,fnname)                    \
-   VG_CONCAT4(_vgwZU_,soname,_,fnname)
+   VG_CONCAT4(_vgw0000ZU_,soname,_,fnname)
 
 #define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname)                    \
-   VG_CONCAT4(_vgwZZ_,soname,_,fnname)
+   VG_CONCAT4(_vgw0000ZZ_,soname,_,fnname)
 
 /* Use this macro from within a wrapper function to collect the
    context (address and possibly other info) of the original function.