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
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 */
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? */
}
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,
#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;
- }
- 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;
+ 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;
}
- 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;
-
- 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);
+ 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;
+
+ /* 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]);
}
}
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 */
(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
/* 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;
/* 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
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 );
}
}
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
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
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 );
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) {
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 );
}
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 )
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:
... 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
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
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
VG_WRAP_FUNCTION_ZU
VG_WRAP_FUNCTION_ZZ
+ VG_WRAP_FUNCTION_EZU
+ VG_WRAP_FUNCTION_EZZ
instead.
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
+
+/* 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)
-#define VG_WRAP_FUNCTION_ZU(soname,fnname) VG_CONCAT4(_vgwZU_,soname,_,fnname)
-#define VG_WRAP_FUNCTION_ZZ(soname,fnname) VG_CONCAT4(_vgwZZ_,soname,_,fnname)
/* --------- Some handy Z-encoded names. --------- */