Macros | |
#define | DRMGR_PRIORITY_NAME_DRWRAP "drwrap" |
#define | DRWRAP_REPLACE_NATIVE_DATA_SLOT SPILL_SLOT_2 |
#define | DRWRAP_REPLACE_NATIVE_SP_SLOT SPILL_SLOT_3 |
Enumerations | |
enum | { DRMGR_PRIORITY_APP2APP_DRWRAP = -500, DRMGR_PRIORITY_INSERT_DRWRAP = 500, DRMGR_PRIORITY_FAULT_DRWRAP = 500 } |
enum | drwrap_wrap_flags_t { DRWRAP_FLAGS_NONE = 0x00, DRWRAP_UNWIND_ON_EXCEPTION = 0x01 } |
enum | drwrap_callconv_t { DRWRAP_CALLCONV_AMD64 = 0x01000000, DRWRAP_CALLCONV_MICROSOFT_X64 = 0x02000000, DRWRAP_CALLCONV_ARM = 0x03000000, DRWRAP_CALLCONV_CDECL = 0x04000000, DRWRAP_CALLCONV_STDCALL = DRWRAP_CALLCONV_CDECL, DRWRAP_CALLCONV_FASTCALL = 0x05000000, DRWRAP_CALLCONV_THISCALL = 0x06000000, DRWRAP_CALLCONV_AARCH64 = 0x07000000, DRWRAP_CALLCONV_DEFAULT = DRWRAP_CALLCONV_AARCH64, DRWRAP_CALLCONV_VARARG = DRWRAP_CALLCONV_DEFAULT } |
enum | drwrap_global_flags_t { DRWRAP_SAFE_READ_RETADDR = 0x01, DRWRAP_SAFE_READ_ARGS = 0x02, DRWRAP_NO_FRILLS = 0x04, DRWRAP_FAST_CLEANCALLS = 0x08 } |
Functions | |
DR_EXPORT bool | drwrap_init (void) |
DR_EXPORT void | drwrap_exit (void) |
DR_EXPORT bool | drwrap_replace (app_pc original, app_pc replacement, bool override) |
DR_EXPORT bool | drwrap_replace_native (app_pc original, app_pc replacement, bool at_entry, uint stack_adjust, void *user_data, bool override) |
DR_EXPORT bool | drwrap_is_replaced (app_pc func) |
DR_EXPORT bool | drwrap_is_replaced_native (app_pc func) |
DR_EXPORT void | drwrap_replace_native_fini (void *drcontext) |
DR_EXPORT bool | drwrap_wrap (app_pc func, void(*pre_func_cb)(void *wrapcxt, OUT void **user_data), void(*post_func_cb)(void *wrapcxt, void *user_data)) |
DR_EXPORT bool | drwrap_wrap_ex (app_pc func, void(*pre_func_cb)(void *wrapcxt, INOUT void **user_data), void(*post_func_cb)(void *wrapcxt, void *user_data), void *user_data, uint flags) |
DR_EXPORT bool | drwrap_unwrap (app_pc func, void(*pre_func_cb)(void *wrapcxt, OUT void **user_data), void(*post_func_cb)(void *wrapcxt, void *user_data)) |
DR_EXPORT app_pc | drwrap_get_drcontext (void *wrapcxt) |
DR_EXPORT app_pc | drwrap_get_func (void *wrapcxt) |
DR_EXPORT dr_mcontext_t * | drwrap_get_mcontext (void *wrapcxt) |
DR_EXPORT dr_mcontext_t * | drwrap_get_mcontext_ex (void *wrapcxt, dr_mcontext_flags_t flags) |
DR_EXPORT bool | drwrap_set_mcontext (void *wrapcxt) |
DR_EXPORT app_pc | drwrap_get_retaddr (void *wrapcxt) |
DR_EXPORT void * | drwrap_get_arg (void *wrapcxt, int arg) |
DR_EXPORT bool | drwrap_set_arg (void *wrapcxt, int arg, void *val) |
DR_EXPORT void * | drwrap_get_retval (void *wrapcxt) |
DR_EXPORT bool | drwrap_set_retval (void *wrapcxt, void *val) |
DR_EXPORT bool | drwrap_skip_call (void *wrapcxt, void *retval, size_t stdcall_args_size) |
DR_EXPORT drext_status_t | drwrap_redirect_execution (void *wrapcxt) |
DR_EXPORT bool | drwrap_is_redirect_requested (void *wrapcxt) |
DR_EXPORT bool | drwrap_register_post_call_notify (void(*cb)(app_pc pc)) |
DR_EXPORT bool | drwrap_unregister_post_call_notify (void(*cb)(app_pc pc)) |
DR_EXPORT bool | drwrap_mark_as_post_call (app_pc pc) |
DR_EXPORT bool | drwrap_set_global_flags (drwrap_global_flags_t flags) |
DR_EXPORT bool | drwrap_is_wrapped (app_pc func, void(*pre_func_cb)(void *wrapcxt, OUT void **user_data), void(*post_func_cb)(void *wrapcxt, void *user_data)) |
DR_EXPORT bool | drwrap_is_post_wrap (app_pc pc) |
#define DRMGR_PRIORITY_NAME_DRWRAP "drwrap" |
Name of drmgr instrumentation pass priorities for app2app, insert, and exception on Windows.
#define DRWRAP_REPLACE_NATIVE_DATA_SLOT SPILL_SLOT_2 |
Spill slot used to store user_data parameter for drwrap_replace_native()
#define DRWRAP_REPLACE_NATIVE_SP_SLOT SPILL_SLOT_3 |
Spill slot used to store application stack address (or DR_REG_LR for ARM) for drwrap_replace_native().
anonymous enum |
Priorities of drmgr instrumentation passes used by drwrap. Users of drwrap can use the name DRMGR_PRIORITY_NAME_DRWRAP in the drmgr_priority_t.before field or can use these numeric priorities in the drmgr_priority_t.priority field to ensure proper instrumentation pass ordering.
Enumerator | |
---|---|
DRMGR_PRIORITY_APP2APP_DRWRAP | Priority of drwrap_replace() |
DRMGR_PRIORITY_INSERT_DRWRAP | Priority of drwrap_wrap() |
DRMGR_PRIORITY_FAULT_DRWRAP | Priority of fault handling event |
enum drwrap_callconv_t |
Values to specify the calling convention of the wrapped function. Pass one of these values to drwrap_wrap_ex() in the flags parameter using bitwise OR, e.g.: DRWRAP_UNWIND_ON_EXCEPTION | DRWRAP_CALLCONV_DEFAULT (see drwrap_wrap_flags_t).
Values for the flags parameter to drwrap_set_global_flags()
Enumerator | |
---|---|
DRWRAP_SAFE_READ_RETADDR | By default the return address is read directly. A more conservative and safe approach would use a safe read to avoid crashing when the stack is unsafe to access. This flag will cause the return address to be read safely. If any call to drwrap_set_global_flags() sets this flag, no later call can remove it. |
DRWRAP_SAFE_READ_ARGS | By default function arguments stored in memory are read and written directly. A more conservative and safe approach would use a safe read or write to avoid crashing when the stack is unsafe to access. This flag will cause all arguments in memory to be read and written safely. If any call to drwrap_set_global_flags() sets this flag, no later call can remove it. |
DRWRAP_NO_FRILLS | If this flag is set, then a leaner wrapping mechanism is used with lower overhead. However, several features are not supported with this flag:
|
DRWRAP_FAST_CLEANCALLS | If this flag is set, then a leaner clean call is used to invoke wrap pre callbacks. This clean call assumes that all wrap requests are for function entrance points and that standard ABI calling conventions are used for those functions. This means that caller-saved registers may not be saved and thus will have invalid values in drwrap_get_mcontext(). When using this setting and skipping a function via drwrap_skip_call() (or calling dr_redirect_execution() directly), setting xmm registers (in particular those used as return values) will work correctly (of course, be sure to retrieve the existing xmm values via drwrap_get_mcontext() or drwrap_get_mcontext_ex(DR_MC_ALL) first). Only set this flag if you are certain that all uses of wrapping in your client and all libraries it uses can abide the above restrictions. Once set, this flag cannot be unset. |
enum drwrap_wrap_flags_t |
Values for the flags parameter to drwrap_wrap_ex(), which may also be combined with at most one value from drwrap_callconv_t (using bitwise OR).
Enumerator | |
---|---|
DRWRAP_FLAGS_NONE | Provided for convenience when calling drwrap_wrap_ex() with no flags. |
DRWRAP_UNWIND_ON_EXCEPTION | When a Windows exception occurs, all post-call callbacks for all live wrapped functions on the wrap stack for which this flag is set are called. If this flag is not set (the default), each post-call callback will still be called if drwrap's heuristics later detect that that particular callback has been bypassed, but those heuristics are not guaranteed. |
DR_EXPORT void drwrap_exit | ( | void | ) |
Cleans up the drwrap extension.
DR_EXPORT void* drwrap_get_arg | ( | void * | wrapcxt, |
int | arg | ||
) |
Returns the value of the arg-th
argument (0-based) to the wrapped function represented by wrapcxt
. Uses the calling convention set by drwrap_wrap_ex(), or for drwrap_wrap() assumes the regular C calling convention. May only be called from a drwrap_wrap
pre-function callback. To access argument values in a post-function callback, store them in the user_data
parameter passed between the pre and post functions.
This routine may de-reference application memory directly, so the caller should wrap in DR_TRY_EXCEPT if crashes must be avoided.
DR_EXPORT app_pc drwrap_get_drcontext | ( | void * | wrapcxt | ) |
Returns the DynamoRIO context. This routine can be faster than dr_get_current_drcontext() but should return the same result.
DR_EXPORT app_pc drwrap_get_func | ( | void * | wrapcxt | ) |
Returns the address of the wrapped function represented by wrapcxt
.
DR_EXPORT dr_mcontext_t* drwrap_get_mcontext | ( | void * | wrapcxt | ) |
Returns the machine context of the wrapped function represented by wrapcxt
corresponding to the application state at the time of the pre-function or post-function wrap callback. In order for any changes to the returned context to take effect, drwrap_set_mcontext() must be called.
DR_EXPORT dr_mcontext_t* drwrap_get_mcontext_ex | ( | void * | wrapcxt, |
dr_mcontext_flags_t | flags | ||
) |
Identical to drwrap_get_mcontext() but only fills in the state indicated by flags
.
DR_EXPORT app_pc drwrap_get_retaddr | ( | void * | wrapcxt | ) |
Returns the return address of the wrapped function represented by wrapcxt
.
This routine may de-reference application memory directly, so the caller should wrap in DR_TRY_EXCEPT if crashes must be avoided.
DR_EXPORT void* drwrap_get_retval | ( | void * | wrapcxt | ) |
Returns the return value of the wrapped function represented by wrapcxt
. Assumes a pointer-sized return value. May only be called from a drwrap_wrap
post-function callback.
DR_EXPORT bool drwrap_init | ( | void | ) |
Initializes the drwrap extension. Must be called prior to any of the other routines. Can be called multiple times (by separate components, normally) but each call must be paired with a corresponding call to drwrap_exit().
DR_EXPORT bool drwrap_is_post_wrap | ( | app_pc | pc | ) |
pc
is currently considered a post-wrap point, for any wrap request. DR_EXPORT bool drwrap_is_redirect_requested | ( | void * | wrapcxt | ) |
May only be called from a drwrap_wrap
post-function callback. This function queries the drwrap state to determine whether a prior post-function callback has requested redirection to another pc
(in which case the dr_mcontext_t in the wrapcxt
may no longer be changed).
DR_EXPORT bool drwrap_is_replaced | ( | app_pc | func | ) |
func
is currently replaced via drwrap_replace() DR_EXPORT bool drwrap_is_replaced_native | ( | app_pc | func | ) |
func
is currently replaced via drwrap_replace_native() DR_EXPORT bool drwrap_is_wrapped | ( | app_pc | func, |
void(*)(void *wrapcxt, OUT void **user_data) | pre_func_cb, | ||
void(*)(void *wrapcxt, void *user_data) | post_func_cb | ||
) |
func
is currently wrapped with pre_func_cb
and post_func_cb
. DR_EXPORT bool drwrap_mark_as_post_call | ( | app_pc | pc | ) |
Records the address pc
as a post-call address for instrumentation for post-call function wrapping purposes.
pc
is legitimate, as that code will be stored for consistency purposes and the post-call entry will be invalidated if it changes. This means that when using this routine for the performance purposes described in the drwrap_register_post_call_notify() documentation, the tool should wait for a newly loaded module to be relocated before calling this routine. A good approach is to wait for the first execution of code from the new module.DR_EXPORT drext_status_t drwrap_redirect_execution | ( | void * | wrapcxt | ) |
May only be called from a drwrap_wrap() post-function callback. Redirects execution to the pc
specified in the dr_mcontext_t of the wrapcxt
after executing all remaining post-function callbacks. Automatically calls drwrap_set_mcontext
to make the redirection to pc
effective; calls to drwrap_set_mcontext() from subsequent post-function callbacks will be denied to prevent clobbering the redirection mcontext. Redirecting execution from nested invocations of a recursive function is not supported.
DR_EXPORT bool drwrap_register_post_call_notify | ( | void(*)(app_pc pc) | cb | ) |
Registers a callback cb
to be called every time a new post-call address is encountered. The intended use is for tools that want faster start-up time by avoiding flushes for inserting wrap instrumentation at post-call sites. A tool can use this callback to record all of the post-call addresses to disk, and use drwrap_mark_as_post_call() during module load of the next execution. It is up to the tool to verify that the module has not changed since its addresses were recorded.
DR_EXPORT bool drwrap_replace | ( | app_pc | original, |
app_pc | replacement, | ||
bool | override | ||
) |
Replaces the application function that starts at the address original
with the code at the address replacement
.
Only one replacement is supported per target address. If a replacement already exists for original
, this function fails unless override
is true, in which case it replaces the prior replacement. To remove a replacement, pass NULL for replacement
and true for override
. When removing or replacing a prior replacement, existing replaced code in the code cache will be flushed lazily: i.e., there may be some execution in other threads after this call is made.
Only the first target replacement address in a basic block will be honored. All code after that address is removed.
When replacing a function, it is up to the user to ensure that the replacement mirrors the calling convention and other semantics of the original function. The replacement code will be executed as application code, NOT as client code.
DR_EXPORT bool drwrap_replace_native | ( | app_pc | original, |
app_pc | replacement, | ||
bool | at_entry, | ||
uint | stack_adjust, | ||
void * | user_data, | ||
bool | override | ||
) |
Replaces the application function that starts at the address original
with the natively-executed (i.e., as the client) code at the address replacement
. The replacement should either be the function entry point or a call site for the function, indicated by the at_entry
parameter. For a call site, only that particular call will be replaced, rather than every call to replacement
.
The replacement function must call drwrap_replace_native_fini() prior to returning. If it fails to do so, control will be lost and subsequent application code will not be under DynamoRIO control. The fini routine sets up a continuation function that is used rather than a direct return. This continuation strategy enables the replacement function to use application locks (if they are marked with dr_mark_safe_to_suspend()) safely, as there is no code cache return point.
The replacement function should use the same calling convention as the original with respect to argument access. In order to match the calling convention return for conventions in which the callee cleans up arguments on the stack, use the stack_adjust
parameter to request a return that adjusts the stack. This return will be executed as a regular basic block and thus a stack-tracking client will not observe any missing stack adjustments. The stack_adjust
parameter must be a multiple of sizeof(void*).
If user_data
!= NULL, it is stored in a scratch slot for access by replacement
by calling dr_read_saved_reg() and passing DRWRAP_REPLACE_NATIVE_DATA_SLOT.
Only one replacement is supported per target address. If a replacement already exists for original
, this function fails unless override
is true, in which case it replaces the prior replacement. To remove a replacement, pass NULL for replacement
and true for override
. When removing or replacing a prior replacement, existing replaced code in the code cache will be flushed lazily: i.e., there may be some execution in other threads after this call is made.
Non-native replacements take precedence over native. I.e., if a drwrap_replace() replacement exists for original
, then a native replacement request for original
will never take effect.
Only the first target replacement address in a basic block will be honored. All code after that address is removed.
When replacing a function, it is up to the user to ensure that the replacement mirrors the calling convention and other semantics of the original function.
The replacement code will be executed as client code, NOT as application code. However, it will use the application stack and other machine state. Usually it is good practice to call dr_switch_to_app_state() inside the replacement code, and then dr_switch_to_dr_state() before returning, in particular on Windows. To additionally use a clean DR stack, consider using dr_call_on_clean_stack() from the initial replacement layer (which allows the outer layer to handle stdcall, which dr_call_on_clean_stack does not support).
The replacement code is not allowed to invoke dr_flush_region() or dr_delete_fragment() as it has no dr_mcontext_t with which to invoke dr_redirect_execution(): it must return to the call-out point in the code cache. If the replacement code does not return to its return address, DR will lose control of the application and not continue executing it properly.
[in] | original | The address of either the application function entry point (in which case at_entry must be true) or of a call site (the actual call or tailcall/inter-library jump) (in which case at_entry must be false). |
[in] | replacement | The function entry to use instead. |
[in] | at_entry | Indicates whether original is the function entry point or a call site. |
[in] | stack_adjust | The stack adjustment performed at return for the calling convention used by original . On ARM, this must be zero. |
[in] | user_data | Data made available when replacement is executed. |
[in] | override | Whether to replace any existing replacement for original . |
ret
instruction appearing in the code stream with an application address that is different from an execution without a native replacement. The return address will be identical, however, assuming original
does not replace its own return address.DR_EXPORT void drwrap_replace_native_fini | ( | void * | drcontext | ) |
The replacement function passed to drwrap_replace_native() must call this function prior to returning. If this function is not called, DynamoRIO will lose control of the application.
DR_EXPORT bool drwrap_set_arg | ( | void * | wrapcxt, |
int | arg, | ||
void * | val | ||
) |
Sets the the arg-th
argument (0-based) to the wrapped function represented by wrapcxt
to val
. Uses the calling convention set by drwrap_wrap_ex(), or for drwrap_wrap() assumes the regular C calling convention. May only be called from a drwrap_wrap
pre-function callback. To access argument values in a post-function callback, store them in the user_data
parameter passed between the pre and post functions.
This routine may write to application memory directly, so the caller should wrap in DR_TRY_EXCEPT if crashes must be avoided.
DR_EXPORT bool drwrap_set_global_flags | ( | drwrap_global_flags_t | flags | ) |
Sets flags that affect the global behavior of the drwrap module. This can be called at any time and it will affect future behavior.
DR_EXPORT bool drwrap_set_mcontext | ( | void * | wrapcxt | ) |
Propagates any changes made to the dr_mcontext_t pointed by drwrap_get_mcontext() back to the application.
DR_EXPORT bool drwrap_set_retval | ( | void * | wrapcxt, |
void * | val | ||
) |
Sets the return value of the wrapped function represented by wrapcxt
to val
. Assumes a pointer-sized return value. May only be called from a drwrap_wrap
post-function callback.
DR_EXPORT bool drwrap_skip_call | ( | void * | wrapcxt, |
void * | retval, | ||
size_t | stdcall_args_size | ||
) |
May only be called from a drwrap_wrap
pre-function callback. Skips execution of the original function and returns to the function's caller with a return value of retval
. The post-function callback will not be invoked; nor will any pre-function callbacks (if multiple were registered) that have not yet been called. If the original function uses the stdcall
calling convention, the total size of its arguments must be supplied. The return value is set regardless of whether the original function officially returns a value or not. Further state changes may be made with drwrap_get_mcontext() and drwrap_set_mcontext() prior to calling this function.
DR_EXPORT bool drwrap_unregister_post_call_notify | ( | void(*)(app_pc pc) | cb | ) |
Unregisters a callback registered with drwrap_register_post_call_notify().
DR_EXPORT bool drwrap_unwrap | ( | app_pc | func, |
void(*)(void *wrapcxt, OUT void **user_data) | pre_func_cb, | ||
void(*)(void *wrapcxt, void *user_data) | post_func_cb | ||
) |
Removes a previously-requested wrap for the function func
and the callback pair pre_func_cb
and post_func_cb
. This must be the same pair that was passed to dr_wrap
.
This routine can be called from pre_func_cb
or post_func_cb
.
DR_EXPORT bool drwrap_wrap | ( | app_pc | func, |
void(*)(void *wrapcxt, OUT void **user_data) | pre_func_cb, | ||
void(*)(void *wrapcxt, void *user_data) | post_func_cb | ||
) |
Wraps the application function that starts at the address original
by calling pre_func_cb
prior to every invocation of original
and calling post_func_cb
after every invocation of original
. One of the callbacks can be NULL, but not both. Uses the default calling convention for the platform (see DRWRAP_CALLCONV_DEFAULT in drwrap_callconv_t).
Wrap requests should normally be made up front during process initialization or module load (see dr_register_module_load_event()). If a wrap request is made after the target code may have already been executed by the application, the caller should flush the target code from the cache using the desired flush method after issuing the wrap request.
Multiple wrap requests are allowed for one original
function (unless DRWRAP_NO_FRILLS is set). Their callbacks are called sequentially in the reverse order of registration.
The pre_func_cb
can examine (drwrap_get_arg()) and set (drwrap_set_arg()) the arguments to original
and can skip the call to original
(drwrap_skip_call()). The post_func_cb
can examine (drwrap_get_retval()) and set (drwrap_set_retval()) original's
return value. The opaque pointer wrapcxt
passed to each callback should be passed to these routines.
When an abnormal stack unwind, such as longjmp or a Windows exception, occurs, drwrap does its best to detect it. All post-calls that would be missed will still be invoked, but with wrapcxt
set to NULL. Since there is no post-call environment, it does not make sense to query the return value or arguments. The call is invoked to allow for cleanup of state allocated in pre_func_cb
. However, detection of a stack unwind is not guaranteed. When wrapping a series of functions that do not themselves contain exception handlers, pass the DRWRAP_UNWIND_ON_EXCEPTION flag to drwrap_wrap_ex() to ensure that all post-call callbacks will be called on an exception.
DR_EXPORT bool drwrap_wrap_ex | ( | app_pc | func, |
void(*)(void *wrapcxt, INOUT void **user_data) | pre_func_cb, | ||
void(*)(void *wrapcxt, void *user_data) | post_func_cb, | ||
void * | user_data, | ||
uint | flags | ||
) |
Identical to drwrap_wrap() except for two additional parameters: user_data
, which is passed as the initial value of *user_data to pre_func_cb
, and flags
, which are the bitwise combination of the drwrap_wrap_flags_t and at most one drwrap_callconv_t.
Specify the calling convention by combining one of the DRWRAP_CALLCONV_* values (of drwrap_callconv_t) with the flags. It is not allowed to specify multiple calling conventions. If the specified calling convention is incorrect for func
, the wrap will succeed, but calls to drwrap_set_arg() and drwrap_get_arg() for func
will either access the wrong argument value, or will access a register or stack slot that does not contain any argument value. If no calling convention is specified, defaults to DRWRAP_CALLCONV_DEFAULT.