DynamoRIO
|
Data Structures | |
struct | _drwrap_stats_t |
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 |
Typedefs | |
typedef ptr_int_t | atomic_int_t |
typedef struct _drwrap_stats_t | drwrap_stats_t |
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, DRWRAP_NO_DYNAMIC_RETADDRS = 0x02, DRWRAP_REPLACE_RETADDR = 0x04 } |
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_RISCV_LP64 = 0x08000000, DRWRAP_CALLCONV_DEFAULT = DRWRAP_CALLCONV_AARCH64, DRWRAP_CALLCONV_VARARG = DRWRAP_CALLCONV_DEFAULT, DRWRAP_CALLCONV_MASK = 0xff000000 } |
enum | drwrap_global_flags_t { DRWRAP_SAFE_READ_RETADDR = 0x01, DRWRAP_SAFE_READ_ARGS = 0x02, DRWRAP_NO_FRILLS = 0x04, DRWRAP_FAST_CLEANCALLS = 0x08, DRWRAP_INVERT_CONTROL = 0x10 } |
Functions | |
DR_EXPORT bool | drwrap_init (void) |
DR_EXPORT void | drwrap_exit (void) |
DR_EXPORT dr_emit_flags_t | drwrap_invoke_insert (void *drcontext, void *tag, instrlist_t *bb, instr_t *inst, instr_t *where, bool for_trace, bool translating, void *user_data) |
DR_EXPORT dr_emit_flags_t | drwrap_invoke_insert_cleanup_only (void *drcontext, void *tag, instrlist_t *bb, instr_t *inst, instr_t *where, bool for_trace, bool translating, void *user_data) |
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, DR_PARAM_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, DR_PARAM_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, DR_PARAM_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, DR_PARAM_OUT void **user_data), void(*post_func_cb)(void *wrapcxt, void *user_data)) |
DR_EXPORT bool | drwrap_is_post_wrap (app_pc pc) |
DR_EXPORT bool | drwrap_get_stats (DR_PARAM_OUT drwrap_stats_t *stats) |
DR_EXPORT void | drwrap_get_retaddr_if_sentinel (void *drcontext, DR_PARAM_INOUT app_pc *possibly_sentinel) |
Detailed Description
Macro Definition Documentation
◆ DRMGR_PRIORITY_NAME_DRWRAP
#define DRMGR_PRIORITY_NAME_DRWRAP "drwrap" |
Name of drmgr instrumentation pass priorities for app2app, insert, and exception on Windows.
◆ DRWRAP_REPLACE_NATIVE_DATA_SLOT
#define DRWRAP_REPLACE_NATIVE_DATA_SLOT SPILL_SLOT_2 |
Spill slot used to store user_data parameter for drwrap_replace_native()
◆ DRWRAP_REPLACE_NATIVE_SP_SLOT
#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().
Typedef Documentation
◆ atomic_int_t
typedef ptr_int_t atomic_int_t |
An integer sized for support by dr_atomic_addX_return_sum().
◆ drwrap_stats_t
typedef struct _drwrap_stats_t drwrap_stats_t |
Contains statistics retrievable by drwrap_get_stats().
Enumeration Type Documentation
◆ anonymous enum
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 |
◆ drwrap_callconv_t
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).
◆ drwrap_global_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. |
DRWRAP_INVERT_CONTROL | This flag must only be set before calling drwrap_init(). If set, drwrap will not register for the drmgr insertion event. The user must instead explicitly invoke drwrap_invoke_insert() and drwrap_invoke_insert_cleanup_only() from its own handler for the insertion event. This "inverted control" mode is provided for better compatibility with drbbdup where the user wishes to only perform wrapping in a subset of the drbbdup cases. For cases where wrapping should occur, drwrap_invoke_insert() should be called; for cases where no wrapping should occcur, drwrap_invoke_insert_cleanup_only() should be called (required for cleanup of drwrap state). Only wrapping is supported this way: drwrap_replace() and drwrap_replace_native() are not supported when this flag is set. As this is a global change in how drwrap operates, be careful that its use does not conflict with drwrap uses in any libraries or joint clients: multiple calls to drwrap_invoke_insert() or drwrap_invoke_insert_cleanup_only() are not supported, and one use of inversion will not combine well with another use that requires drwrap_replace(). |
◆ drwrap_wrap_flags_t
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. |
DRWRAP_NO_DYNAMIC_RETADDRS | If this flag is set, then post-call callbacks are only invoked from return sites that can be identified statically. Static identification happens in two ways: from observing a CALL instruction, and from drwrap_mark_as_post_call(). Dynamically observing return addresses from inside callees incurs overhead due to synchronization costs, with further overhead to replace existing code with instrumented code. When this flag is set, some post-call callbacks may be missed. |
DRWRAP_REPLACE_RETADDR | If this flag is set, then post-call points are identified by changing the application return address upon entering the callee. This is more efficient than the default method, which requires shared storage and locks and flushing. However, this does violate transparency, and may cause some applications to fail. In particular, detaching on AArchXX requires scanning the stack to find where the return address was stored, which could conceivably replace an integer or non-pointer value that happens to match the sentinel used. Also, the transparency violation may be exposed to the client's dr_register_kernel_xfer_event() callback if it inspects the mcontext PC on the stack; drwrap_get_retaddr_if_sentinel() may be used to mitigate such cases. Use DRWRAP_REPLACE_RETADDR at your own risk. Currently is not supported for RISC-V. |
Function Documentation
◆ drwrap_exit()
DR_EXPORT void drwrap_exit | ( | void | ) |
Cleans up the drwrap extension.
◆ drwrap_get_arg()
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.
◆ drwrap_get_drcontext()
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.
◆ drwrap_get_func()
DR_EXPORT app_pc drwrap_get_func | ( | void * | wrapcxt | ) |
Returns the address of the wrapped function represented by wrapcxt
.
◆ drwrap_get_mcontext()
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. The pc field is set appropriately. In order for any changes to the returned context to take effect, drwrap_set_mcontext() must be called.
- Note
- if the DRWRAP_FAST_CLEANCALLS flag is set, caller-saved register values in the fields controlled by DR_MC_MULTIMEDIA will not contain valid values.
◆ drwrap_get_mcontext_ex()
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
.
◆ drwrap_get_retaddr()
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.
◆ drwrap_get_retaddr_if_sentinel()
DR_EXPORT void drwrap_get_retaddr_if_sentinel | ( | void * | drcontext, |
DR_PARAM_INOUT app_pc * | possibly_sentinel | ||
) |
If the provided app_pc (possibly_sentinel
) is indeed the return address sentinel used to implement DRWRAP_REPLACE_RETADDR, this routine replaces it with the actual return address of the inner-most nested wrapped function. Otherwise, it is a no-op. This allows mitigation of a transparency violation under the DRWRAP_REPLACE_RETADDR strategy where the actual app return address on the stack is replaced with a return address sentinel.
◆ drwrap_get_retval()
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.
◆ drwrap_get_stats()
DR_EXPORT bool drwrap_get_stats | ( | DR_PARAM_OUT drwrap_stats_t * | stats | ) |
Retrieves various statistics exported by DR as global, process-wide values. The API is not thread-safe. The caller is expected to pass a pointer to a valid, initialized dr_stats_t value, with the size field set (see dr_stats_t). Returns false if stats are not enabled.
◆ drwrap_init()
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().
Some drwrap behavior (such as DRWRAP_INVERT_CONTROL) must be set by calling drwrap_set_global_flags() before calling this routine.
- Returns
- whether successful.
◆ drwrap_invoke_insert()
DR_EXPORT dr_emit_flags_t drwrap_invoke_insert | ( | void * | drcontext, |
void * | tag, | ||
instrlist_t * | bb, | ||
instr_t * | inst, | ||
instr_t * | where, | ||
bool | for_trace, | ||
bool | translating, | ||
void * | user_data | ||
) |
When drwrap_global_flags_t DRWRAP_INVERT_CONTROL is set, the user must call this function from a drmgr insertion event handler (typically registered with drmgr_register_bb_instrumentation_event() or if using drbbdup with the instrument_instr_ex field in drbbdup_options_t). This function will insert instrumentation for function wrapping pre and post callbacks. It is up to the user to control the ordering, since the priority DRMGR_PRIORITY_INSERT_DRWRAP will not apply. The separate "where" handles cases such as with drbbdup's final app instruction (which cannot be duplicated into each case) or with emulation where the instruction "inst" to monitor is distinct from the location "where" to insert instrumentation. The user_data
parameter is ignored.
◆ drwrap_invoke_insert_cleanup_only()
DR_EXPORT dr_emit_flags_t drwrap_invoke_insert_cleanup_only | ( | void * | drcontext, |
void * | tag, | ||
instrlist_t * | bb, | ||
instr_t * | inst, | ||
instr_t * | where, | ||
bool | for_trace, | ||
bool | translating, | ||
void * | user_data | ||
) |
When drwrap_global_flags_t DRWRAP_INVERT_CONTROL is set and the user is using multiple types of instrumentation such as with drbbdup, the user must call this function for each instrumentation case where function wrapping should not be enabled. (If the instrumentation type changes to don't-wrap and then changes back to wrap all in the middle of a wrapped function, the full post-wrap callback (plus cleanup) would then naturally be called.) This will insert required cleanup for instrumentation cases changing in the middle of a wrapped function. It will not invoke any wrapped function callbacks. It is up to the user to control the ordering, since the priority DRMGR_PRIORITY_INSERT_DRWRAP will not apply. The separate "where" handles cases such as with drbbdup's final app instruction (which cannot be duplicated into each case) or with emulation where the instruction "inst" to monitor is distinct from the location "where" to insert instrumentation. The user_data
parameter is ignored.
◆ drwrap_is_post_wrap()
DR_EXPORT bool drwrap_is_post_wrap | ( | app_pc | pc | ) |
- Returns
- whether
pc
is currently considered a post-wrap point, for any wrap request.
◆ drwrap_is_redirect_requested()
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).
- Returns
- true if a prior post-function callback has requested a redirect
◆ drwrap_is_replaced()
DR_EXPORT bool drwrap_is_replaced | ( | app_pc | func | ) |
- Returns
- whether
func
is currently replaced via drwrap_replace()
◆ drwrap_is_replaced_native()
DR_EXPORT bool drwrap_is_replaced_native | ( | app_pc | func | ) |
- Returns
- whether
func
is currently replaced via drwrap_replace_native()
◆ drwrap_is_wrapped()
DR_EXPORT bool drwrap_is_wrapped | ( | app_pc | func, |
void(*)(void *wrapcxt, DR_PARAM_OUT void **user_data) | pre_func_cb, | ||
void(*)(void *wrapcxt, void *user_data) | post_func_cb | ||
) |
- Returns
- whether
func
is currently wrapped withpre_func_cb
andpost_func_cb
.
◆ drwrap_mark_as_post_call()
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.
- Note
- Only call this when the code leading up to
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.
- Returns
- whether successful.
◆ drwrap_redirect_execution()
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.
- Note
- It is the client's responsibility to adjust the register state and/or memory to accommodate the redirection target; otherwise the application may behave in unexpected ways. If the client intends to repeat execution of the wrapped function, the stack pointer must be adjusted accordingly during the post-function callback so that the correct return address is in the conventional location before execution enters the wrapped function. This is necessary because the pre-function callback occurs at the beginning of the wrapped function (i.e., after the call instruction has executed), while the post-function callback occurs after the return instruction has executed (as if inserted following the call instruction).
- Returns
- DREXT_SUCCESS if the redirect request is accepted; DREXT_ERROR_STATE_INCOMPATIBLE if this function was called outside of a post-function callback, or DREXT_ERROR if the redirect could not be fulfilled for any other reason.
◆ drwrap_register_post_call_notify()
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.
- Returns
- whether successful.
◆ drwrap_replace()
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.
- Note
- The priority of the app2app pass used here is DRMGR_PRIORITY_APP2APP_DRWRAP and its name is DRMGR_PRIORITY_NAME_DRWRAP.
- Not supported if DRWRAP_INVERT_CONTROL is set.
- Returns
- whether successful.
◆ drwrap_replace_native()
DR_EXPORT bool drwrap_replace_native | ( | app_pc | original, |
app_pc | replacement, | ||
bool | at_entry, | ||
uint | stack_adjust, | ||
void * | user_data, | ||
bool | override | ||
) |
- Warning
- This interface is in flux and is subject to change in the next release. Consider it experimental in this release.
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.
- Parameters
-
[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 caseat_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
.
- Note
- The mechanism used for a native replacement results in a
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, assumingoriginal
does not replace its own return address. - The application stack address at which its return address is stored is available by calling dr_read_saved_reg() and passing DRWRAP_REPLACE_NATIVE_SP_SLOT.
- The priority of the app2app pass used here is DRMGR_PRIORITY_APP2APP_DRWRAP and its name is DRMGR_PRIORITY_NAME_DRWRAP.
- Far calls are not supported.
- Not supported if DRWRAP_INVERT_CONTROL is set.
- Returns
- whether successful.
◆ drwrap_replace_native_fini()
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.
◆ drwrap_set_arg()
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.
- Returns
- whether successful.
◆ drwrap_set_global_flags()
DR_EXPORT bool drwrap_set_global_flags | ( | drwrap_global_flags_t | flags | ) |
Sets flags that affect the global behavior of the drwrap module. This adds the passed-in flags
to the current set of flags; it does not remove flags. This can be called at any time and it will affect future behavior.
- Returns
- whether the flags were changed.
◆ drwrap_set_mcontext()
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.
- Note
- if the DRWRAP_FAST_CLEANCALLS flag is set, caller-saved register values in the fields controlled by DR_MC_MULTIMEDIA will not contain valid values, but this should be fine because their values were scratch according to the ABI at the wrap point..
◆ drwrap_set_retval()
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.
- Returns
- whether successful.
◆ drwrap_skip_call()
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.
- Note
- It is up to the client to ensure that the application behaves as desired when the original function is skipped.
- Returns
- whether successful.
◆ drwrap_unregister_post_call_notify()
DR_EXPORT bool drwrap_unregister_post_call_notify | ( | void(*)(app_pc pc) | cb | ) |
Unregisters a callback registered with drwrap_register_post_call_notify().
- Returns
- whether successful.
◆ drwrap_unwrap()
DR_EXPORT bool drwrap_unwrap | ( | app_pc | func, |
void(*)(void *wrapcxt, DR_PARAM_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
.
- Returns
- whether successful.
◆ drwrap_wrap()
DR_EXPORT bool drwrap_wrap | ( | app_pc | func, |
void(*)(void *wrapcxt, DR_PARAM_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) and passes both DR_CLEANCALL_READS_APP_CONTEXT and DR_CLEANCALL_WRITES_APP_CONTEXT to the insertion of the calls.
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.
This routine may call dr_unlink_flush_region(), which means that it cannot be called while any locks are held that could block a thread processing a registered event callback or cache callout.
- Note
- The priority of the app2app pass used here is DRMGR_PRIORITY_INSERT_DRWRAP and its name is DRMGR_PRIORITY_NAME_DRWRAP.
- Returns
- whether successful.
◆ drwrap_wrap_ex()
DR_EXPORT bool drwrap_wrap_ex | ( | app_pc | func, |
void(*)(void *wrapcxt, DR_PARAM_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.
This routine may call dr_unlink_flush_region(), which means that it cannot be called while any locks are held that could block a thread processing a registered event callback or cache callout.