#include "pub_core_debuginfo.h"
#include "pub_core_redir.h"
#include "pub_core_scheduler.h"
+#include "pub_core_seqmatch.h" // For VG_(string_match)
#include "pub_core_signals.h"
#include "pub_core_stacks.h" // For VG_(register_stack)
#include "pub_core_syswrap.h"
" --kernel-variant=variant1,variant2,... known variants: bproc [none]\n"
" handle non-standard kernel variants\n"
" --show-emwarns=no|yes show warnings about emulation limits? [no]\n"
+" --require-text-symbol=:sonamepattern:symbolpattern abort run if the\n"
+" stated shared object doesn't have the stated\n"
+" text symbol. Patterns can contain ? and *.\n"
"\n";
Char* usage2 =
else if VG_STR_CLO(arg, "--suppressions", tmp_str) {
if (VG_(clo_n_suppressions) >= VG_CLO_MAX_SFILES) {
- VG_(message)(Vg_UserMsg, "Too many suppression files specified.\n");
+ VG_(message)(Vg_UserMsg,
+ "Too many suppression files specified.\n");
VG_(message)(Vg_UserMsg,
"Increase VG_CLO_MAX_SFILES and recompile.\n");
VG_(err_bad_option)(arg);
VG_(clo_n_suppressions)++;
}
+ else if VG_STR_CLO(arg, "--require-text-symbol", tmp_str) {
+ if (VG_(clo_n_req_tsyms) >= VG_CLO_MAX_REQ_TSYMS) {
+ VG_(message)(Vg_UserMsg,
+ "Too many --require-text-symbol= specifications.\n");
+ VG_(message)(Vg_UserMsg,
+ "Increase VG_CLO_MAX_REQ_TSYMS and recompile.\n");
+ VG_(err_bad_option)(arg);
+ }
+ /* String needs to be of the form C?*C?*, where C is any
+ character, but is the same both times. Having it in this
+ form facilitates finding the boundary between the sopatt
+ and the fnpatt just by looking for the second occurrence
+ of C, without hardwiring any assumption about what C
+ is. */
+ Char patt[7];
+ Bool ok = True;
+ ok = tmp_str && VG_(strlen)(tmp_str) > 0;
+ if (ok) {
+ patt[0] = patt[3] = tmp_str[0];
+ patt[1] = patt[4] = '?';
+ patt[2] = patt[5] = '*';
+ patt[6] = 0;
+ ok = VG_(string_match)(patt, tmp_str);
+ }
+ if (!ok) {
+ VG_(message)(Vg_UserMsg,
+ "Invalid --require-text-symbol= specification.\n");
+ VG_(err_bad_option)(arg);
+ }
+ VG_(clo_req_tsyms)[VG_(clo_n_req_tsyms)] = tmp_str;
+ VG_(clo_n_req_tsyms)++;
+ }
+
/* "stuvwxyz" --> stuvwxyz (binary) */
else if VG_STR_CLO(arg, "--trace-flags", tmp_str) {
Int j;
Char* VG_(clo_sim_hints) = NULL;
Bool VG_(clo_sym_offsets) = False;
Bool VG_(clo_read_var_info) = False;
+Int VG_(clo_n_req_tsyms) = 0;
+HChar* VG_(clo_req_tsyms)[VG_CLO_MAX_REQ_TSYMS];
+HChar* VG_(clo_require_text_symbol) = NULL;
Bool VG_(clo_run_libc_freeres) = True;
Bool VG_(clo_track_fds) = False;
Bool VG_(clo_show_below_main)= False;
platform-specific.
The module is notified of redirection state changes by m_debuginfo.
- That calls VG_(redir_notify_new_SegInfo) when a new SegInfo (shared
- object symbol table, basically) appears. Appearance of new symbols
- can cause new (active) redirections to appear for two reasons: the
- symbols in the new table may match existing redirection
- specifications (see comments below), and because the symbols in the
- new table may themselves supply new redirect specifications which
- match existing symbols (or ones in the new table).
+ That calls VG_(redir_notify_new_DebugInfo) when a new DebugInfo
+ (shared object symbol table, basically) appears. Appearance of new
+ symbols can cause new (active) redirections to appear for two
+ reasons: the symbols in the new table may match existing
+ redirection specifications (see comments below), and because the
+ symbols in the new table may themselves supply new redirect
+ specifications which match existing symbols (or ones in the new
+ table).
Redirect specifications are really symbols with "funny" prefixes
(_vgrZU_ and _vgrZZ_). These names tell m_redir that the
by VG_(maybe_Z_demangle).
When a shared object is unloaded, this module learns of it via a
- call to VG_(redir_notify_delete_SegInfo). It then removes from its
- tables all active redirections in any way associated with that
+ call to VG_(redir_notify_delete_DebugInfo). It then removes from
+ its tables all active redirections in any way associated with that
object, and tidies up the translation caches accordingly.
That takes care of tracking the redirection state. When a
===========================================================
Incremental implementation:
- When a new SegInfo appears:
+ When a new DebugInfo appears:
- it may be the source of new specs
- it may be the source of new matches for existing specs
Therefore:
- - (new Specs x existing SegInfos): scan all symbols in the new
- SegInfo to find new specs. Each of these needs to be compared
- against all symbols in all the existing SegInfos to generate
+ - (new Specs x existing DebugInfos): scan all symbols in the new
+ DebugInfo to find new specs. Each of these needs to be compared
+ against all symbols in all the existing DebugInfos to generate
new actives.
- - (existing Specs x new SegInfo): scan all symbols in the SegInfo,
- trying to match them to any existing specs, also generating
- new actives.
+ - (existing Specs x new DebugInfo): scan all symbols in the
+ DebugInfo, trying to match them to any existing specs, also
+ generating new actives.
- - (new Specs x new SegInfo): scan all symbols in the new SegInfo,
- trying to match them against the new specs, to generate new
- actives.
+ - (new Specs x new DebugInfo): scan all symbols in the new
+ DebugInfo, trying to match them against the new specs, to
+ generate new actives.
- Finally, add new new specs to the current set of specs.
else add (s,d) to Actives
and discard (s,1) and (d,1) (maybe overly conservative)
- When a SegInfo disappears:
+ When a DebugInfo disappears:
- delete all specs acquired from the seginfo
- delete all actives derived from the just-deleted specs
- if each active (s,d) deleted, discard (s,1) and (d,1)
}
Spec;
-/* Top-level data structure. It contains a pointer to a SegInfo and
- also a list of the specs harvested from that SegInfo. Note that
+/* Top-level data structure. It contains a pointer to a DebugInfo and
+ also a list of the specs harvested from that DebugInfo. Note that
seginfo is allowed to be NULL, meaning that the specs are
pre-loaded ones at startup and are not associated with any
particular seginfo. */
TopSpec;
/* This is the top level list of redirections. m_debuginfo maintains
- a list of SegInfos, and the idea here is to maintain a list with
+ a list of DebugInfos, and the idea here is to maintain a list with
the same number of elements (in fact, with one more element, so as
to record abovementioned preloaded specifications.) */
static TopSpec* topSpecs = NULL;
static void handle_maybe_load_notifier( const UChar* soname,
HChar* symbol, Addr addr );
+static void handle_require_text_symbols ( DebugInfo* );
/*------------------------------------------------------------*/
/*--- NOTIFICATIONS ---*/
}
}
- /* Ok. Now specList holds the list of specs from the DebugInfo.
+ /* Ok. Now specList holds the list of specs from the DebugInfo.
Build a new TopSpec, but don't add it to topSpecs yet. */
newts = dinfo_zalloc("redir.rnnD.4", sizeof(TopSpec));
vg_assert(newts);
if (VG_(clo_trace_redir))
show_redir_state("after VG_(redir_notify_new_DebugInfo)");
+
+ /* 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);
}
#undef N_DEMANGLED
return False;
}
+
/*------------------------------------------------------------*/
/*--- NOTIFY-ON-LOAD FUNCTIONS ---*/
/*------------------------------------------------------------*/
}
+/*------------------------------------------------------------*/
+/*--- REQUIRE-TEXT-SYMBOL HANDLING ---*/
+/*------------------------------------------------------------*/
+
+/* In short: check that the currently-being-loaded object has text
+ symbols that satisfy any --require-text-symbol= specifications that
+ apply to it, and abort the run with an error message if not.
+*/
+static void handle_require_text_symbols ( DebugInfo* di )
+{
+ /* First thing to do is figure out which, if any,
+ --require-text-symbol specification strings apply to this
+ object. Most likely none do, since it is not expected to
+ frequently be used. Work through the list of specs and
+ accumulate in fnpatts[] the fn patterns that pertain to this
+ object. */
+ HChar* fnpatts[VG_CLO_MAX_REQ_TSYMS];
+ Int fnpatts_used = 0;
+ Int i, j;
+ const HChar* di_soname = VG_(DebugInfo_get_soname)(di);
+ vg_assert(di_soname); // must be present
+
+ VG_(memset)(&fnpatts, 0, sizeof(fnpatts));
+
+ vg_assert(VG_(clo_n_req_tsyms) >= 0);
+ vg_assert(VG_(clo_n_req_tsyms) <= VG_CLO_MAX_REQ_TSYMS);
+ for (i = 0; i < VG_(clo_n_req_tsyms); i++) {
+ HChar* spec = VG_(clo_req_tsyms)[i];
+ vg_assert(spec && VG_(strlen)(spec) >= 4);
+ // clone the spec, so we can stick a zero at the end of the sopatt
+ spec = VG_(strdup)("m_redir.hrts.1", spec);
+ HChar sep = spec[0];
+ HChar* sopatt = &spec[1];
+ HChar* fnpatt = VG_(strchr)(sopatt, sep);
+ // the initial check at clo processing in time in m_main
+ // should ensure this.
+ vg_assert(fnpatt && *fnpatt == sep);
+ *fnpatt = 0;
+ fnpatt++;
+ if (VG_(string_match)(sopatt, di_soname))
+ fnpatts[fnpatts_used++]
+ = VG_(strdup)("m_redir.hrts.2", fnpatt);
+ VG_(free)(spec);
+ }
+
+ if (fnpatts_used == 0)
+ return; /* no applicable spec strings */
+
+ /* So finally, fnpatts[0 .. fnpatts_used - 1] contains the set of
+ (patterns for) text symbol names that must be found in this
+ object, in order to continue. That is, we must find at least
+ one text symbol name that matches each pattern, else we must
+ abort the run. */
+
+ if (0) VG_(printf)("for %s\n", di_soname);
+ for (i = 0; i < fnpatts_used; i++)
+ if (0) VG_(printf)(" fnpatt: %s\n", fnpatts[i]);
+
+ /* For each spec, look through the syms to find one that matches.
+ This isn't terribly efficient but it happens rarely, so no big
+ deal. */
+ for (i = 0; i < fnpatts_used; i++) {
+ Bool found = False;
+ HChar* fnpatt = fnpatts[i];
+ Int nsyms = VG_(DebugInfo_syms_howmany)(di);
+ for (j = 0; j < nsyms; j++) {
+ Bool isText = False;
+ HChar* sym_name = NULL;
+ VG_(DebugInfo_syms_getidx)( di, j, NULL, NULL,
+ NULL, &sym_name, &isText, NULL );
+ /* ignore data symbols */
+ if (0) VG_(printf)("QQQ %s\n", sym_name);
+ vg_assert(sym_name);
+ if (!isText)
+ continue;
+ if (VG_(string_match)(fnpatt, sym_name)) {
+ found = True;
+ break;
+ }
+ }
+
+ if (!found) {
+ HChar* v = "valgrind: ";
+ VG_(printf)("\n");
+ VG_(printf)(
+ "%sFatal error at when loading library with soname\n", v);
+ VG_(printf)(
+ "%s %s\n", v, di_soname);
+ VG_(printf)(
+ "%sCannot find any text symbol with a name "
+ "that matches the pattern\n", v);
+ VG_(printf)("%s %s\n", v, fnpatt);
+ VG_(printf)("%sas required by a --require-text-symbol= "
+ "specification.\n", v);
+ VG_(printf)("\n");
+ VG_(printf)(
+ "%sCannot continue -- exiting now.\n", v);
+ VG_(printf)("\n");
+ VG_(exit)(1);
+ }
+ }
+
+ /* All required specs were found. Just free memory and return. */
+ for (i = 0; i < fnpatts_used; i++)
+ VG_(free)(fnpatts[i]);
+}
+
+
/*------------------------------------------------------------*/
/*--- SANITY/DEBUG ---*/
/*------------------------------------------------------------*/
/* The max number of suppression files. */
#define VG_CLO_MAX_SFILES 100
+/* The max number of --require-text-symbol= specification strings. */
+#define VG_CLO_MAX_REQ_TSYMS 100
+
/* Should we stop collecting errors if too many appear? default: YES */
extern Bool VG_(clo_error_limit);
/* Alternative exit code to hand to parent if errors were found.
/* The file descriptor to read for input. default: 0 == stdin */
extern Int VG_(clo_input_fd);
+
/* The number of suppression files specified. */
extern Int VG_(clo_n_suppressions);
/* The names of the suppression files. */
/* Read DWARF3 variable info even if tool doesn't ask for it? */
extern Bool VG_(clo_read_var_info);
+/* An array of strings harvested from --require-text-symbol=
+ flags.
+
+ Each string specifies a pair: a soname pattern and a text symbol
+ name pattern, separated by a colon. The patterns can be written
+ using the normal "?" and "*" wildcards. For example:
+ ":*libc.so*:foo?bar".
+
+ These flags take effect when reading debuginfo from objects. If an
+ object is loaded and the object's soname matches the soname
+ component of one of the specified pairs, then Valgrind will examine
+ all the text symbol names in the object. If none of them match the
+ symbol name component of that same specification, then the run is
+ aborted, with an error message.
+
+ The purpose of this is to support reliable usage of marked-up
+ libraries. For example, suppose we have a version of GCC's
+ libgomp.so which has been marked up with annotations to support
+ Helgrind. It is only too easy and confusing to load the 'wrong'
+ libgomp.so into the application. So the idea is: add a text symbol
+ in the marked-up library (eg), "annotated_for_helgrind_3_6", and
+ then give the flag
+
+ --require-text-symbol=:*libgomp*so*:annotated_for_helgrind_3_6
+
+ so that when libgomp.so is loaded, we scan the symbol table, and if
+ the symbol isn't present the run is aborted, rather than continuing
+ silently with the un-marked-up library. Note that you should put
+ the entire flag in quotes to stop shells messing up the * and ?
+ wildcards. */
+extern Int VG_(clo_n_req_tsyms);
+extern HChar* VG_(clo_req_tsyms)[VG_CLO_MAX_REQ_TSYMS];
+
/* Track open file descriptors? */
extern Bool VG_(clo_track_fds);
//--------------------------------------------------------------------
// PURPOSE: This module deals with:
+//
// - code replacement: intercepting calls to client functions, and
// pointing them to a different piece of code.
+//
// - loading notification: telling the core where certain client-space
// functions are when they get loaded.
+//
// - function wrapping: add calls to code before and after client
// functions execute, for inspection and/or modification.
+//
+// - checking of --require-text-symbol= specifications: when a new
+// object is loaded, its symbol table is examined, and if a symbol
+// (as required by the specifications) is not found then the run
+// is aborted. See comment by VG_(clo_n_req_tsyms) in
+// pub_core_options.h for background. This doesn't have anything
+// to do with function intercepting or wrapping, but it does have
+// to do with examining all symbols at object load time, so this
+// module seems like a logical place to put it.
+//
//--------------------------------------------------------------------
#include "pub_tool_redir.h"
</listitem>
</varlistentry>
+ <varlistentry id="opt.require-text-symbol"
+ xreflabel="--require-text-symbol">
+ <term>
+ <option><![CDATA[--require-text-symbol=:sonamepatt:fnnamepatt]]></option>
+ </term>
+ <listitem>
+ <para>When a shared object whose soname
+ matches <varname>sonamepatt</varname> is loaded into the
+ process, examine all the text symbols it exports. If none of
+ those match <varname>fnnamepatt</varname>, print an error
+ message and abandon the run. This makes it possible to ensure
+ that the run does not continue unless a given shared object
+ contains a particular function name.
+ </para>
+ <para>
+ Both <varname>sonamepatt</varname> and
+ <varname>fnnamepatt</varname> can be written using the usual
+ <varname>?</varname> and <varname>*</varname> wildcards. For
+ example: <varname>":*libc.so*:foo?bar"</varname>. You may use
+ characters other than a colon to separate the two patterns. It
+ is only important that the first character and the separator
+ character are the same. For example, the above example could
+ also be written <varname>"Q*libc.so*Qfoo?bar"</varname>.
+ Multiple <varname> --require-text-symbol</varname> flags are
+ allowed, in which case shared objects that are loaded into
+ the process will be checked against all of them.
+ </para>
+ <para>
+ The purpose of this is to support reliable usage of marked-up
+ libraries. For example, suppose we have a version of GCC's
+ <varname>libgomp.so</varname> which has been marked up with
+ annotations to support Helgrind. It is only too easy and
+ confusing to load the wrong, un-annotated
+ <varname>libgomp.so</varname> into the application. So the idea
+ is: add a text symbol in the marked-up library, for
+ example <varname>annotated_for_helgrind_3_6</varname>, and then
+ give the flag
+ <varname>--require-text-symbol=:*libgomp*so*:annotated_for_helgrind_3_6</varname>
+ so that when <varname>libgomp.so</varname> is loaded, Valgrind
+ scans its symbol table, and if the symbol isn't present the run
+ is aborted, rather than continuing silently with the
+ un-marked-up library. Note that you should put the entire flag
+ in quotes to stop shells expanding up the <varname>*</varname>
+ and <varname>?</varname> wildcards.
+ </para>
+ </listitem>
+ </varlistentry>
+
+
</variablelist>
<!-- end of xi:include in the manpage -->
rcrl.stderr.exp rcrl.stdout.exp rcrl.vgtest \
readline1.stderr.exp readline1.stdout.exp \
readline1.vgtest \
+ require-text-symbol-1.vgtest \
+ require-text-symbol-1.stderr.exp
+ require-text-symbol-2.vgtest \
+ require-text-symbol-2.stderr.exp-libcso6 \
res_search.stderr.exp res_search.stdout.exp res_search.vgtest \
resolv.stderr.exp resolv.stdout.exp resolv.vgtest \
rlimit_nofile.stderr.exp rlimit_nofile.stdout.exp rlimit_nofile.vgtest \
pth_atfork1 pth_blockedsig pth_cancel1 pth_cancel2 pth_cvsimple \
pth_empty pth_exit pth_exit2 pth_mutexspeed pth_once pth_rwlock \
pth_stackalign \
- rcrl readline1 res_search resolv \
+ rcrl readline1 \
+ require-text-symbol \
+ res_search resolv \
rlimit_nofile selfrun sem semlimit sha1_test \
shortpush shorts stackgrowth sigstackgrowth \
syscall-restart1 syscall-restart2 \
--kernel-variant=variant1,variant2,... known variants: bproc [none]
handle non-standard kernel variants
--show-emwarns=no|yes show warnings about emulation limits? [no]
+ --require-text-symbol=:sonamepattern:symbolpattern abort run if the
+ stated shared object doesn't have the stated
+ text symbol. Patterns can contain ? and *.
user options for Nulgrind:
(none)
--kernel-variant=variant1,variant2,... known variants: bproc [none]
handle non-standard kernel variants
--show-emwarns=no|yes show warnings about emulation limits? [no]
+ --require-text-symbol=:sonamepattern:symbolpattern abort run if the
+ stated shared object doesn't have the stated
+ text symbol. Patterns can contain ? and *.
user options for Nulgrind:
(none)
--- /dev/null
+prog: require-text-symbol
+vgopts: -q "--require-text-symbol=:*libc.so*:strl?n"
--- /dev/null
+
+valgrind: Fatal error at when loading library with soname
+valgrind: libc.so.6
+valgrind: Cannot find any text symbol with a name that matches the pattern
+valgrind: doesntexist
+valgrind: as required by a --require-text-symbol= specification.
+
+valgrind: Cannot continue -- exiting now.
+
--- /dev/null
+prog: require-text-symbol
+vgopts: -q "--require-text-symbol=:*libc.so*:doesntexist"
--- /dev/null
+
+/* Doesn't do anything. The point of this is to test for the presence
+ of a couple of symbols in libc.so. See the .vgtest files. */
+
+int main ( void )
+{
+ return 0;
+}