From: Julian Seward Date: Sun, 9 May 2010 22:30:43 +0000 (+0000) Subject: New flag --require-text-symbol=:sopatt:fnpatt, to be used to check X-Git-Tag: svn/VALGRIND_3_6_0~299 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2e6b7655d1aac70e024b0600bb9344f1fd84a01c;p=thirdparty%2Fvalgrind.git 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 --- diff --git a/coregrind/m_main.c b/coregrind/m_main.c index b8569ed060..5c69df6b8e 100644 --- a/coregrind/m_main.c +++ b/coregrind/m_main.c @@ -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; diff --git a/coregrind/m_options.c b/coregrind/m_options.c index 8162a48fee..d6aea74f09 100644 --- a/coregrind/m_options.c +++ b/coregrind/m_options.c @@ -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; diff --git a/coregrind/m_redir.c b/coregrind/m_redir.c index b1ab3c9e75..124507ae53 100644 --- a/coregrind/m_redir.c +++ b/coregrind/m_redir.c @@ -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 ---*/ /*------------------------------------------------------------*/ diff --git a/coregrind/pub_core_options.h b/coregrind/pub_core_options.h index fa358eb223..716a124c06 100644 --- a/coregrind/pub_core_options.h +++ b/coregrind/pub_core_options.h @@ -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); diff --git a/coregrind/pub_core_redir.h b/coregrind/pub_core_redir.h index 817699aca4..8bd12f7f3d 100644 --- a/coregrind/pub_core_redir.h +++ b/coregrind/pub_core_redir.h @@ -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" diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml index 24ab6aeea3..724dac99e5 100644 --- a/docs/xml/manual-core.xml +++ b/docs/xml/manual-core.xml @@ -1501,6 +1501,55 @@ need to use these. + + + + + + When a shared object whose soname + matches sonamepatt is loaded into the + process, examine all the text symbols it exports. If none of + those match fnnamepatt, 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. + + + Both sonamepatt and + fnnamepatt can be written using the usual + ? and * wildcards. For + example: ":*libc.so*:foo?bar". 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 "Q*libc.so*Qfoo?bar". + Multiple --require-text-symbol flags are + allowed, in which case shared objects that are loaded into + the process will be checked against all of them. + + + 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, un-annotated + libgomp.so into the application. So the idea + is: add a text symbol in the marked-up library, for + example 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, 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 * + and ? wildcards. + + + + + diff --git a/none/tests/Makefile.am b/none/tests/Makefile.am index ff28e2a1fa..a299436fca 100644 --- a/none/tests/Makefile.am +++ b/none/tests/Makefile.am @@ -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 \ diff --git a/none/tests/cmdline1.stdout.exp b/none/tests/cmdline1.stdout.exp index 19dfa1f90a..c06b53fd32 100644 --- a/none/tests/cmdline1.stdout.exp +++ b/none/tests/cmdline1.stdout.exp @@ -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) diff --git a/none/tests/cmdline2.stdout.exp b/none/tests/cmdline2.stdout.exp index ba700bd37c..60e0328f28 100644 --- a/none/tests/cmdline2.stdout.exp +++ b/none/tests/cmdline2.stdout.exp @@ -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) diff --git a/none/tests/require-text-symbol-1.stderr.exp b/none/tests/require-text-symbol-1.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/none/tests/require-text-symbol-1.vgtest b/none/tests/require-text-symbol-1.vgtest new file mode 100644 index 0000000000..fca0092c78 --- /dev/null +++ b/none/tests/require-text-symbol-1.vgtest @@ -0,0 +1,2 @@ +prog: require-text-symbol +vgopts: -q "--require-text-symbol=:*libc.so*:strl?n" diff --git a/none/tests/require-text-symbol-2.stderr.exp-libcso6 b/none/tests/require-text-symbol-2.stderr.exp-libcso6 new file mode 100644 index 0000000000..0d66f2d5f2 --- /dev/null +++ b/none/tests/require-text-symbol-2.stderr.exp-libcso6 @@ -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. + diff --git a/none/tests/require-text-symbol-2.vgtest b/none/tests/require-text-symbol-2.vgtest new file mode 100644 index 0000000000..48b553b439 --- /dev/null +++ b/none/tests/require-text-symbol-2.vgtest @@ -0,0 +1,2 @@ +prog: require-text-symbol +vgopts: -q "--require-text-symbol=:*libc.so*:doesntexist" diff --git a/none/tests/require-text-symbol.c b/none/tests/require-text-symbol.c new file mode 100644 index 0000000000..15b827cf77 --- /dev/null +++ b/none/tests/require-text-symbol.c @@ -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; +}