From: Julian Seward Date: Tue, 14 Mar 2006 00:56:29 +0000 (+0000) Subject: Minor futzing (fontification, etc) of the function-wrappers documentation. X-Git-Tag: svn/VALGRIND_3_2_0~187 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e97332eaee348c95d26a4b80cd77c5c52b01b0aa;p=thirdparty%2Fvalgrind.git Minor futzing (fontification, etc) of the function-wrappers documentation. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@5764 --- diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml index 9b500e36e0..df72e4bc11 100644 --- a/docs/xml/manual-core.xml +++ b/docs/xml/manual-core.xml @@ -417,7 +417,7 @@ distribution, provides some good examples. - Recall that valgrind-2.0.X is a modular system, in which + Recall that Valgrind is a modular system, in which different instrumentation tools can observe your program whilst it is running. Since different tools detect different kinds of errors, it is necessary to say which tool(s) the suppression is meaningful @@ -1586,56 +1586,68 @@ here. - -A wrapper is a function of identical type, but with a special name -which identifies it as the wrapper for foo. Wrappers need to include -supporting macros from valgrind.h. Here is a simple wrapper which -prints the arguments and return value: +A wrapper is a function of identical type, but with a special name +which identifies it as the wrapper for foo. +Wrappers need to include +supporting macros from valgrind.h. +Here is a simple wrapper which prints the arguments and return value: + - #include "valgrind.h" - int I_WRAP_SONAME_FNNAME_ZU(NONE,foo)( int x, int y ) - { - int result; - OrigFn fn; - VALGRIND_GET_ORIG_FN(fn); - printf("foo's wrapper: args %d %d\n", x, y); - CALL_FN_W_WW(result, fn, x,y); - printf("foo's wrapper: result %d\n", result); - return result; - } +#include +#include "valgrind.h" +int I_WRAP_SONAME_FNNAME_ZU(NONE,foo)( int x, int y ) +{ + int result; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + printf("foo's wrapper: args %d %d\n", x, y); + CALL_FN_W_WW(result, fn, x,y); + printf("foo's wrapper: result %d\n", result); + return result; +} ]]> To become active, the wrapper merely needs to be present in a text section somewhere in the same process' address space as the function it wraps, and for its ELF symbol name to be visible to Valgrind. In -practice, this means either compiling to a .o and linking it in, or -compiling to a .so and LD_PRELOADing it in. The latter is more +practice, this means either compiling to a +.o and linking it in, or +compiling to a .so and +LD_PRELOADing it in. The latter is more convenient in that it doesn't require relinking. All wrappers have approximately the above form. There are three crucial macros: -I_WRAP_SONAME_FNNAME_ZU: this generates the real name of the wrapper. +I_WRAP_SONAME_FNNAME_ZU: +this generates the real name of the wrapper. This is an encoded name which Valgrind notices when reading symbol table information. What it says is: I am the wrapper for any function -named "foo" which is found in an ELF shared object with an empty -("NONE") soname field. The specification mechanism is powerful in -that wildcards are allowed for both sonames and function names. More -details below. - -VALGRIND_GET_ORIG_FN: once in the the wrapper, the first priority is +named foo which is found in +an ELF shared object with an empty +("NONE") soname field. The specification +mechanism is powerful in +that wildcards are allowed for both sonames and function names. +The fine details are discussed below. + +VALGRIND_GET_ORIG_FN: +once in the the wrapper, the first priority is to get hold of the address of the original (and any other supporting -information needed). This is stored in a value of opaque type OrigFn. -The information is acquired using VALGRIND_GET_ORIG_FN. It is crucial +information needed). This is stored in a value of opaque +type OrigFn. +The information is acquired using +VALGRIND_GET_ORIG_FN. It is crucial to make this macro call before calling any other wrapped function in the same thread. -CALL_FN_W_WW: eventually we will want to call the function being +CALL_FN_W_WW: eventually we will +want to call the function being wrapped. Calling it directly does not work, since that just gets us back to the wrapper and tends to kill the program in short order by -stack overflow. Instead, the result lvalue, OrigFn and arguments are -handed to one of a family of macros of the form CALL_FN_*. These +stack overflow. Instead, the result lvalue, +OrigFn and arguments are +handed to one of a family of macros of the form +CALL_FN_*. These cause Valgrind to call the original and avoid recursion back to the wrapper. @@ -1659,10 +1671,13 @@ functions, where typically we are not completely sure of either the function name or the soname, or alternatively we want to wrap a whole bunch of functions at once. -For example, pthread_create() in GNU libpthread's is usually a -versioned symbol - one whose name ends in, eg, "@GLIBC_2.3". Hence we +For example, pthread_create +in GNU libpthread is usually a +versioned symbol - one whose name ends in, eg, +@GLIBC_2.3. Hence we are not sure what its real name is. We also want to cover any soname -of the form "libpthread.so*". So the header of the wrapper will be +of the form libpthread.so*. +So the header of the wrapper will be ZZ Z ]]> -Hence libpthreadZdsoZd0 is an encoding of the soname "libpthread.so.0" -and "pthreadZucreateZAZa" is an encoding of the function name -"pthread_create@*". +Hence libpthreadZdsoZd0 is an +encoding of the soname libpthread.so.0 +and pthreadZucreateZAZa is an encoding +of the function name pthread_create@*. + -The macro I_WRAP_SONAME_FNNAME_ZZ constructs a wrapper name in which +The macro I_WRAP_SONAME_FNNAME_ZZ +constructs a wrapper name in which both the soname (first component) and function name (second component) are Z-encoded. Encoding the function name can be tiresome and is -often unnecessary, so a second macro, I_WRAP_SONAME_FNNAME_ZU, can be -used instead. The _ZU variant is also useful for writing wrappers for +often unnecessary, so a second macro, +I_WRAP_SONAME_FNNAME_ZU, can be +used instead. The _ZU variant is +also useful for writing wrappers for C++ functions, in which the function name is usually already mangled using some other convention in which Z plays an important role; having to encode a second time quickly becomes confusing. Since the function name field may contain wildcards, it can be -anything, including just "*". The same is true for the soname. +anything, including just *. +The same is true for the soname. However, some ELF objects - specifically, main executables - do not have sonames. Any object lacking a soname is treated as if its soname -was "NONE", which is why the original example above had a name -I_WRAP_SONAME_FNNAME_ZU(NONE,foo). +was NONE, which is why the original +example above had a name +I_WRAP_SONAME_FNNAME_ZU(NONE,foo). @@ -1716,20 +1738,27 @@ appear and disappear (are dlopen'd and dlclose'd) on the fly. Valgrind tries to maintain sensible behaviour in such situations. For example, suppose a process has dlopened (an ELF object with -soname) object1.so, which contains function1(). It starts to use -function1() immediately. - -After a while it dlopens wrappers.so, which contains a wrapper -for function1 in (soname) object1.so. All subsequent calls to -function1() are rerouted to the wrapper. - -If wrappers.so is later dlclose'd, calls to function1() are -naturally rerouted back to the original. - -Alternatively, if object1.so is dlclose'd but wrappers.so remains, -then the wrapper exported by wrapper.so becomes inactive, since there +soname) object1.so, which contains +function1. It starts to use +function1 immediately. + +After a while it dlopens wrappers.so, +which contains a wrapper +for function1 in (soname) +object1.so. All subsequent calls to +function1 are rerouted to the wrapper. + +If wrappers.so is +later dlclose'd, calls to function1 are +naturally routed back to the original. + +Alternatively, if object1.so +is dlclose'd but wrappers.so remains, +then the wrapper exported by wrapper.so +becomes inactive, since there is no way to get to it - there is no original to call any more. However, -Valgrind remembers that the wrapper is still present. If object1.so +Valgrind remembers that the wrapper is still present. If +object1.so is eventually dlopen'd again, the wrapper will become active again. In short, valgrind inspects all code loading/unloading events to @@ -1746,49 +1775,65 @@ will honour only the first one. Debugging Figuring out what's going on given the dynamic nature of wrapping -can be difficult. The --trace-redir=yes flag makes this possible +can be difficult. The +--trace-redir=yes flag makes +this possible by showing the complete state of the redirection subsystem after -every mmap/munmap event affecting code (text). +every +mmap/munmap +event affecting code (text). There are two central concepts: -- A "redirection specification" is a binding of + + + A "redirection specification" is a binding of a (soname pattern, fnname pattern) pair to a code address. These bindings are created by writing functions with names - made with the I_WRAP_SONAME_FNNAME_{ZZ,_ZU} macros. + made with the + I_WRAP_SONAME_FNNAME_{ZZ,_ZU} + macros. -- An "active redirection" is code-address to code-address binding - currently in effect. + An "active redirection" is code-address to + code-address binding currently in effect. + + The state of the wrapping-and-redirection subsystem comprises a set of specifications and a set of active bindings. The specifications are -acquired/discarded by watching all mmap/munmap events on code (text) +acquired/discarded by watching all +mmap/munmap +events on code (text) sections. The active binding set is (conceptually) recomputed from the specifications, and all known symbol names, following any change to the specification set. ---trace-redir=yes shows the contents of both sets following any such -event. +--trace-redir=yes shows the contents +of both sets following any such event. --v prints a line of text each time an active specification is -used for the first time. +-v prints a line of text each +time an active specification is used for the first time. Hence for maximum debugging effectiveness you will need to use both flags. One final comment. The function-wrapping facility is closely tied to Valgrind's ability to replace (redirect) specified -functions, for example to redirect calls to malloc() to its +functions, for example to redirect calls to +malloc to its own implementation. Indeed, a replacement function can be regarded as a wrapper function which does not call the original. However, to make the implementation more robust, the two kinds of interception (wrapping vs replacement) are treated differently. ---trace-redir=yes shows specifications and bindings for both +--trace-redir=yes shows +specifications and bindings for both replacement and wrapper functions. To differentiate the -two, replacement bindings are printed using "R->" whereas -wraps are printed using "W->". +two, replacement bindings are printed using +R-> whereas +wraps are printed using W->. + @@ -1797,18 +1842,22 @@ wraps are printed using "W->". Limitations - control flow For the most part, the function wrapping implementation is robust. -The only real caveat is that is is crucial, in a wrapper, get hold of -the OrigFn information using VALGRIND_GET_ORIG_FN before calling any -other wrapped function. Once you have the OrigFn, arbitrary +The only important caveat is: in a wrapper, get hold of +the OrigFn information using +VALGRIND_GET_ORIG_FN before calling any +other wrapped function. Once you have the +OrigFn, arbitrary intercalling, recursion between, and longjumping out of wrappers should work correctly. There is never any interaction between wrapped -functions and merely replaced functions (eg malloc), so you can call -malloc etc safely from within wrappers. +functions and merely replaced functions +(eg malloc), so you can call +malloc etc safely from within wrappers. + The above comments are true for {x86,amd64,ppc32}-linux. On ppc64-linux function wrapping is more fragile due to the (arguably poorly designed) ppc64-linux ABI. This mandates the use of a shadow -stack which tracks entries/exits of both wrapper and replacment +stack which tracks entries/exits of both wrapper and replacement functions. This gives two limitations: firstly, longjumping out of wrappers will rapidly lead to disaster, since the shadow stack will not get correctly cleared. Secondly, since the shadow stack has @@ -1829,31 +1878,33 @@ cooperation. Limitations - original function signatures As shown in the above example, to call the original you must use a -macro of the form CALL_FN_*. For technical reasons it is impossible +macro of the form CALL_FN_*. +For technical reasons it is impossible to create a single macro to deal with all argument types and numbers, so a family of macros covering the most common cases is supplied. In -what follows, 'W' denotes a machine-word-typed value (a pointer or an -C 'long'), and 'v' denotes C's "void" type. The currently available -macros are: +what follows, 'W' denotes a machine-word-typed value (a pointer or a +C long), +and 'v' denotes C's void type. +The currently available macros are: The set of supported types can be expanded as needed. It is @@ -1878,12 +1929,13 @@ process. Examples -In the source tree, memcheck/tests/wrap[1-8].c provide a series of +In the source tree, +memcheck/tests/wrap[1-8].c provide a series of examples, ranging from very simple to quite advanced. -auxprogs/libmpiwrap.c is an example of wrapping a big, complex API -(the MPI-2 interface). This file defines almost 300 different -wrappers. +auxprogs/libmpiwrap.c is an example +of wrapping a big, complex API (the MPI-2 interface). This file defines +almost 300 different wrappers.