DynamoRIO exports a rich Application Programming Interface (API) to the user for building a DynamoRIO client. A DynamoRIO client is a library that is coupled with DynamoRIO in order to jointly operate on an input program binary:
To interact with the client, DynamoRIO provides specific events that a client can intercept. Event interception functions, if supplied by a user client, are called by DynamoRIO at appropriate times.
DynamoRIO can alternatively be used as a third-party disassembly library (see IA-32/AMD64 Disassembly Library).
drconfig.exe
, drrun.exe
, and drinject.exe
. The corresponding libraries (whose APIs are exposed by the tools) are drconfiglib.dll
and drinjectlib.dll
with header files dr_config.h
and dr_inject.h
. On Linux, the tools are the drconfig
, drrun
, and drinject
scripts.When using DynamoRIO as a third-party disassembly library (see IA-32/AMD64 Disassembly Library), no deployment is needed, as DynamoRIO does not control a target application when used as a regular library.
drrun.exe
tool supports the first, simpler model, while the drconfig.exe
and drinject.exe
tools support the second, more powerful model. The drconfig.exe
tool, or the corresponding the drconfiglib.dll
library, can also be used to nudge running processes.
Configuration information is stored in files in the current user's profile directory, which is obtained from the environment variable $USERPROFILE
. Thus, configurations are persistent across reboots and are private to each user. DynamoRIO also supports global configurations, which are stored in the "config" subdirectory of the directory specified by the DYNAMORIO_HOME
registry value in the registry key \HKLM\SOFTWARE\DynamoRIO\DynamoRIO
(or for 32-bit on 64-bit Windows (WOW64) \HKLM\SOFTWARE\Wow6432Node\DynamoRIO\DynamoRIO
). Setting that DYNAMORIO_HOME
value and creating the directory it points to must be done manually. The provided tools support reading and writing both local and global configuration files, and automatically creating the local directory. DynamoRIO gives local files precedence when both exist. Note that applications that do not have a $USEPROFILE environment variable can only be executed using global configurations.
Configurations are per-process, with the basename of the process used for identification (e.g., calc.exe
). One-time configuration also uses the process id to specify that the configuration is for that process instance only.
As an example, assume you have unpacked the DynamoRIO distribution and your current directory is its base directory. Run calc.exe
with the bbsize sample client using the following configure-and-run command:
bin32/drrun.exe -client samples/bin32/bbsize.dll 0 "" calc
To use system-wide injection, allowing for an application to be run under DynamoRIO regardless of how it is invoked, configure the application first (-syswide_on requires administrative privileges):
bin32/drconfig.exe -reg calc.exe -syswide_on -client samples/bin32/bbsize.dll 0 ""
The next time calc.exe
is started by the current user, it will run under DynamoRIO with the bbsize client.
To unregister calc.exe
, issue the following command:
bin32/drconfig.exe -unreg calc.exe
Invoke any of the drconfig.exe
, drrun.exe
, or drinject.exe
tools with no arguments to see the full list of options available.
By default on Windows DynamoRIO only follows into children that are configured (via drconfig.exe
). To follow all children, use the -follow_children runtime option.
To nudge all instances of calc.exe
running under DynamoRIO with argument "5", use:
bin32/drconfig.exe -nudge calc.exe 0 5
calc.exe
processes running under DynamoRIO. The third argument, 0, is an ID supplied at registration which uniquely identifies the target client (see dr_deploy.h for details). Note that nudging 64-bit applications is not yet supported on Windows.
To view 32-bit or WOW64 processes running under DynamoRIO the drview.exe
tool can be used. The bin64 version will display both 32-bit and 64-bit processes and will indicate which are 32-bit. The bin32 version will display 64-bit processes but is unable to determine whether DynamoRIO is present.
\HKLM\SOFTWARE\Microsoft\Windows\Windows NT\CurrentVersion\AppInit_DLLs
key (for 32-bit on 64-bit Windows (WOW64), \HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs
) for -syswide_on to inject into new processes without having to directly launch them drrun.exe
or drinject.exe
. For injection to work, the registered process must statically link to user32.dll (only a few small non-graphical windows applications don't link user32.dll). If a target application does not link to user32.dll, DynamoRIO can still inject if the process is launched with drinject.exe
or if the parent process (usually cmd.exe or explorer.exe for user launched processes) is running under DynamoRIO. The drinject.exe tool uses the configuration information set by drconfig.exe
for the target application.
drconfig.exe
and drrun.exe
in these scenarios, be sure that the cmd shell being used was started with elevated permissions.There are two methods for invoking an application under DynamoRIO:
drrun
drconfig
and launch via drinject
As an example of the simpler method, the following command runs ls
under DynamoRIO with the bbsize sample client:
% bin32/drrun -client samples/bin32/libbbsize.so 0 "" ls
drrun
with no options to get a list of the options and environment variable shortcuts it supports. To disable following across child execve calls, use the -no_follow_children runtime option.
Use the scripts in bin32/
for 32-bit applications and the scripts in bin64/
for 64-bit applications.
The two-step method allows for greater control over child processes. The drconfig
script writes a configuration file for a given application name. DynamoRIO reads its options from the configuration file at runtime. Once each process name is configured, the drinject
script can be used to invoke the parent process. The drrun
script can also be used but it creates a temporary configuration file that will override settings requested via drconfig
. The configuration file for each application is stored in $HOME/.dynamorio/<appname>.config32
(or a config64
suffix for 64-bit). DynamoRIO also supports global configuration files in /etc/dynamorio/<appname>.config32
when a local configuration file is not found. drconfig
does not support directly writing a global config file but such files can be copied from or modeled on local files.
If a target application executes an execve
that discards the $HOME
environment variable, the resulting process will not run under DynamoRIO control. Use global configuration files to handle this situation.
To nudge a process with pid targetpid
running under DynamoRIO and pass argument "5" to the nudge callback, use the nudgeunix
tool:
bin32/nudgeunix -pid targetpid -client 0 5
Client registration requires users to specify the priority of each client. DynamoRIO calls each client's dr_init() routine sequentially according to this priority. Clients with a numerically lower priority value are called first and therefore given the first opportunity to register callbacks (the client with priority 0 is called first). Since DynamoRIO delivers event callbacks sequentially, client priority and the order of event registration is important. For a given event, the first registered callback is called last. This scheme gives precedence to the first registered callback since that callback is given the final opportunity to modify the instruction stream or influence DynamoRIO's operation.
Typically, a client will register for the desired events at initialization in its dr_init() routine. DynamoRIO then calls the registered functions at the appropriate times. Each event has a specific registration routine (e.g., dr_register_thread_init_event()) and an associated unregistration routine. The header file dr_events.h contains the declarations for all registration and unregistration routines.
Note that clients are allowed to register multiple callbacks for the same event. DynamoRIO also supports mutiple clients, each of which can register for the same event. In this case, DynamoRIO sequences event callbacks in reverse order of when they were registered. In other words, the first registered callback receives event notification last. This scheme gives priority to a callback registered earlier, since it can override or modify the actions of clients registered later. Note that DynamoRIO calls each client's dr_init() routine according to the client's priority (see Multiple Clients and dr_register_client() in the deployment API).
Systems registering multiple callbacks for a single event should be aware that client modifications are visible in subsequent callbacks. DynamoRIO makes no attempt to mitigate interference among callback functions. It is the responsibility of a client to ensure compatibility among its callback functions and the callback functions of other clients.
Clients can also unregister a callback using the appropriate unregister routine (see dr_events.h). While unusual, it is possible for one callback routine to unregister another. In this case, DynamoRIO still calls routines that were registered before the event. Unregistration takes effect before the next event.
On Linux, an exec (SYS_execve) does NOT result in an exit event, but it WILL result in the client library being reloaded and its dr_init() routine being called again. The system call events can be used for notification of SYS_execve.
DynamoRIO's API provides:
See dr_tools.h and dr_proc.h for specifics of each routine.
Another class of utilities provided by DynamoRIO are structures and routines for decoding, encoding, and manipulating IA-32 and AMD64 instructions. These are described in Instruction Representation.
In addition, on Windows,DynamoRIO provides a number of utility functions that it fowards to a core Windows system library that we believe to be safe for clients to use:
In general, these routines match their standard C library counterparts. However, be warned that some of these may be more limited. In particular, _vsnprintf and _snprintf do not support floating-point values. DynamoRIO provides its own dr_snprintf() that does support floating-point values, but does not support printing wide characters. When printing floating-point values be sure to save the application's floating point state so as to avoid corrupting it.
#include "dr_api.h"
The client's target operating system and architecture must be specified by setting pre-processor defines before including the DynamoRIO header files. The appropriate library must then be linked with. The define choices are:
WINDOWS
or LINUX
X86_32
or X86_64
For transparency reasons (see Client Transparency), clients should be self-contained and should not share libraries with the application. Thus, when a client is linked the linker options for no default libraries, no startup files, and no entry point should be used. Not specifying those options will result in an unsafe client library with the potential to deadlock or crash.
Additionally, 64-bit clients must set a preferred base address in the lower 2GB.
The DynamoRIO release supplies CMake configuration files to facilitate building clients with the proper compiler and linker flags. CMake is a cross-platform build system that generates Makefiles or other development system project files. A DynamoRIOConfig.cmake
configuration file, along with supporting files, is distributed in the cmake/
directory.
In its CMakeLists.txt
file, a client should first invoke a find_package(DynamoRIO)
command. This can optionally take a version parameter. This adds DynamoRIO as an imported target. If found, the client should then invoke the configure_DynamoRIO_client()
function in order to configure build settings. Here is an example:
add_library(myclient SHARED myclient.c) find_package(DynamoRIO) if (NOT DynamoRIO_FOUND) message(FATAL_ERROR "DynamoRIO package required to build") endif(NOT DynamoRIO_FOUND) configure_DynamoRIO_client(myclient)
The samples/CMakeLists.txt
file in the release package serves as another example. The top of DynamoRIOConfig.cmake
contains detailed instructions as well.
When configuring, the DynamoRIO_DIR
CMake variable can be passed in to identify the directory that contains the DynamoRIOConfig.cmake
file. For example:
mkdir ../build cd ../build cmake -DDynamoRIO_DIR=$DYNAMORIO_HOME/cmake ../myclient make
The compiler needs to be configured prior to invoking cmake. If using gcc with a non-default target platform, the CFLAGS
and CXXFLAGS
environment variables should be set prior to invoking cmake. For example, to configure a 32-bit client when gcc's default is 64-bit:
mkdir ../build cd ../build CFLAGS=-m32 cmake -DDynamoRIO_DIR=$DYNAMORIO_HOME/cmake ../myclient make
Note that CXXFLAGS
should be set instead for a C++ client, and both should be set when building both types of clients from the same configuration (e.g., samples/CMakeLists.txt
).
If a client is not using CMake, the appropriate compiler and linker flags can be gleaned from DynamoRIOConfig.cmake
. One method is to invoke CMake to generate a Makefile and then build with VERBOSE=1
. We also summarize here the key flags required for 32-bit clients for gcc:
gcc -fPIC -shared -nostartfiles -nodefaultlibs -lgcc -DLINUX -DX86_32 -I$DYNAMORIO_HOME/include my-client.c
And for cl:
cl my-client.c /I$DYNAMORIO_HOME/include /GS- /DWINDOWS /DX86_32 /link /NODEFAULTLIB /NOENTRY /libpath:$DYNAMORIO_HOME/bin dynamorio.lib /dll /out:my-client.dll
For a 64-bit client with cl:
cl my-client.c /I$DYNAMORIO_HOME/include /GS- /DWINDOWS /DX86_64 /link /NODEFAULTLIB /NOENTRY /libpath:$DYNAMORIO_HOME/bin dynamorio.lib /dll /out:my-client.dll /base:0x72000000 /fixed
For 64-bit Linux clients, setting the preferred base takes several steps. Refer to DynamoRIOConfig.cmake
for details.
Current Extensions provide symbol access and container data structures. Each Extension has its own documentation and has its functions and data structures documented separately from the main API. See the full list of Extensions here.
Currently we provide a private loader for Windows and we plan to supply a private Linux loader in the near future. With private loading, the client uses a separate copy of each library from any copy used by the application. Even with this separation, if these libraries use global resources there can still be conflicts. Our Windows private loader redirects heap allocation in the main process heap to instead use DynamoRIO's internal heap. The loader also attempts to isolate other global resource usage and global callbacks. In this release it should be considered a beta feature. Please file reports on any transparency problems observed when using the private loader.
On Linux, where we do not yet have a private loader, ld provides the -wrap option, which allows us to override the C library's memory heap allocation routines with our own. For convenience, DynamoRIO exports __wrap_malloc(), __wrap_realloc(), and __wrap_free() for this purpose. These routines behave like their C library counterparts, but operate on DynamoRIO's global memory pool. Use the -Xlinker flag with gcc to replace the libc routines with DynamoRIO's _wrap routines, e.g.,
gcc -Xlinker -wrap=malloc -Xlinker -wrap=realloc -Xlinker -wrap=free ...
On Linux, this is most easily accomplished by specifying the path to the static version of the library on the gcc command line. gcc's -print-file-name option is useful for discovering this path, e.g.,
g++ -print-file-name=libstdc++.a
A full gcc command line for building a C++ client might look something like this:
g++ -o my-client.so -I<header dir> \ -fPIC -shared -nodefaultlibs \ -Xlinker -wrap=malloc -Xlinker -wrap=realloc -Xlinker -wrap=free \ `g++ -print-file-name=libstdc++.a` \ `g++ -print-file-name=libgcc.a` \ `g++ -print-file-name=libgcc_eh.a` \ my-client.cpp
See also the stl_test.cpp sample and CMake build files provided with this documentation. The provided CMake build files will set these flags automatically when the DynamoRIO_CXX
CMake variable is set (see Building a Client).
On Windows, when using the Microsoft Visual C++ compiler, simply use the /MT
compiler flag. The client will still use the kernel32.dll
library but our private loader will load a separate copy of that library and redirect heap allocation automatically. Our private loader does not yet support locating SxS libraries, so using /MD
will most likely not work unless using an older version of the compiler.
Be aware that we have successfully built and tested several small C++ clients using the gcc toolchain, but have not performed extensive testing. Our clients successfully compile and run linked with the g++ libraries included in Red Hat Enterprise Linux 4, but on later distributions the pre-built libraries may not work and you may need to build static versions from the gcc sources with the appropriate flags (including -fPIC).
We do not recommend that a client or its libraries invoke their own system calls as this bypasses DynamoRIO's monitoring of changes to the process address space and changes to threads or control flow. Such system calls will also not work properly on Linux when using sysenter on some systems. If you see an assert to that effect in debug build on Linux, try the -sysenter_is_int80 option.
drconfig.exe
, drrun.exe
, or dr_register_process() on Windows and via the drconfig
and drrun
scripts on Linux. See Deployment.
drconfig.exe
. When -follow_children
is specified DynamoRIO injects into all child processes.
-no_follow_children
is specified DynamoRIO only injects across an execve if a configuration file exists (typically created by drconfig:
see Deployment) for the new application name.
Options controlling notifications from DynamoRIO:
Options aiding in debugging:
Options available only in the debug build of DynamoRIO:
drconfig.exe
configuration for the target application. On Linux the drconfig
and drrun
scripts allow for setting the logging directory. There is one main log file per directory named app.0.TID.html, where TID is the thread identifier of the initial thread. There is also a log file per thread, named log.N.TID.html, where N is the thread's creation ordinal and TID is its thread identifier. The loglevel may be changed during program execution, but if it began at 0 then it cannot be raised later. The -logmask parameter can be used to control which DynamoRIO modules output data to the log files. dr_log() allows the client to write to the above logfiles.
For bug reports, use the Issue Tracker. Please include a detailed description of the problem (is it an application crash? a DynamoRIO crash? a hang? a debug build assert?) and how to reproduce it.
drrun
script by setting the LD_LIBRARY_PATH and LD_PRELOAD environment variables from within the debugger.int3
it can find its way into the code cache and cause errors as the debugger will not realize that the resulting trap is from a breakpoint. Use read watchpoints on the code in question instead.