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 )