DynamoRIO
Function Wrapping and Replacing

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_tdrwrap_get_mcontext (void *wrapcxt)
 
DR_EXPORT dr_mcontext_tdrwrap_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

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

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).

Enumerator
DRWRAP_CALLCONV_AMD64 

The AMD64 ABI calling convention.

DRWRAP_CALLCONV_MICROSOFT_X64 

The Microsoft x64 calling convention.

DRWRAP_CALLCONV_ARM 

The ARM calling convention.

DRWRAP_CALLCONV_CDECL 

The IA-32 cdecl calling convention.

DRWRAP_CALLCONV_STDCALL 

The Microsoft IA-32 stdcall calling convention.

DRWRAP_CALLCONV_FASTCALL 

The IA-32 fastcall calling convention.

DRWRAP_CALLCONV_THISCALL 

The Microsoft IA-32 thiscall calling convention.

DRWRAP_CALLCONV_AARCH64 

The ARM AArch64 calling convention.

DRWRAP_CALLCONV_RISCV_LP64 

The RISC-V LP64 calling convention.

DRWRAP_CALLCONV_DEFAULT 

Default calling convention for the platform.

DRWRAP_CALLCONV_VARARG 

The platform-specific calling convention for a vararg function.

DRWRAP_CALLCONV_MASK 

Mask for isolating the calling convention from other flags.

◆ 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:

  • Only one wrap request per address is allowed. A second request will fail, even if an earlier request was unwrapped, unless the same pre and post callback functions are used.
  • Wrapping should occur prior to any execution (e.g., at startup or module load time). A new wrap request that occurs between the pre and post wrap points may have its post callback called even though its pre callback was never called.
  • Unwrapping should only happen on module unload. It is not supported between a pre and post callback. 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_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

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.

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 with pre_func_cb and post_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]originalThe 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]replacementThe function entry to use instead.
[in]at_entryIndicates whether original is the function entry point or a call site.
[in]stack_adjustThe stack adjustment performed at return for the calling convention used by original. On ARM, this must be zero.
[in]user_dataData made available when replacement is executed.
[in]overrideWhether 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, assuming original 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.