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_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 )