New flag --require-text-symbol=:sopatt:fnpatt, to be used to check

that specified shared objects contain specified symbols.  Along with a
couple of regtests that unfortunately will fail on MacOSX.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11125
This commit is contained in:
Julian Seward 2010-05-09 22:30:43 +00:00
parent ef5b3e48b6
commit 2e6b7655d1
14 changed files with 315 additions and 26 deletions

View File

@ -55,6 +55,7 @@
#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"
@ -168,6 +169,9 @@ static void usage_NORETURN ( Bool debug_help )
" --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 =
@ -538,7 +542,8 @@ void main_process_cmd_line_options ( /*OUT*/Bool* logging_to_fd,
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);
@ -547,6 +552,39 @@ void main_process_cmd_line_options ( /*OUT*/Bool* logging_to_fd,
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;

View File

@ -84,6 +84,9 @@ Int VG_(clo_backtrace_size) = 12;
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;

View File

@ -67,13 +67,14 @@
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
@ -86,8 +87,8 @@
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
@ -168,23 +169,23 @@
===========================================================
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.
@ -195,7 +196,7 @@
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)
@ -234,8 +235,8 @@ typedef
}
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. */
@ -249,7 +250,7 @@ typedef
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;
@ -298,6 +299,7 @@ static void show_active ( HChar* left, Active* act );
static void handle_maybe_load_notifier( const UChar* soname,
HChar* symbol, Addr addr );
static void handle_require_text_symbols ( DebugInfo* );
/*------------------------------------------------------------*/
/*--- NOTIFICATIONS ---*/
@ -422,7 +424,7 @@ void VG_(redir_notify_new_DebugInfo)( DebugInfo* newsi )
}
}
/* 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);
@ -471,6 +473,11 @@ void VG_(redir_notify_new_DebugInfo)( DebugInfo* newsi )
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
@ -1123,6 +1130,7 @@ static Bool is_aix5_glink_idiom ( Addr sym_addr )
return False;
}
/*------------------------------------------------------------*/
/*--- NOTIFY-ON-LOAD FUNCTIONS ---*/
/*------------------------------------------------------------*/
@ -1160,6 +1168,114 @@ void handle_maybe_load_notifier( const UChar* soname,
}
/*------------------------------------------------------------*/
/*--- 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 ---*/
/*------------------------------------------------------------*/

View File

@ -42,6 +42,9 @@
/* 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.
@ -82,6 +85,7 @@ extern Bool VG_(clo_time_stamp);
/* 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. */
@ -125,6 +129,39 @@ extern Bool VG_(clo_sym_offsets);
/* 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);

View File

@ -33,12 +33,25 @@
//--------------------------------------------------------------------
// 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"

View File

@ -1501,6 +1501,55 @@ need to use these.</para>
</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 -->

View File

@ -113,6 +113,10 @@ EXTRA_DIST = \
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 \
@ -166,7 +170,9 @@ check_PROGRAMS = \
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 \

View File

@ -57,6 +57,9 @@ usage: valgrind [options] prog-and-args
--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)

View File

@ -57,6 +57,9 @@ usage: valgrind [options] prog-and-args
--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)

View File

@ -0,0 +1,2 @@
prog: require-text-symbol
vgopts: -q "--require-text-symbol=:*libc.so*:strl?n"

View File

@ -0,0 +1,9 @@
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.

View File

@ -0,0 +1,2 @@
prog: require-text-symbol
vgopts: -q "--require-text-symbol=:*libc.so*:doesntexist"

View File

@ -0,0 +1,8 @@
/* 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;
}