DynamoRIO
|
Event callback registration routines. More...
#include "dr_config.h"
Data Structures | |
struct | _dr_fault_fragment_info_t |
struct | _dr_restore_state_info_t |
struct | _dr_kernel_xfer_info_t |
struct | _dr_exception_t |
struct | _dr_siginfo_t |
Typedefs | |
typedef struct _dr_fault_fragment_info_t | dr_fault_fragment_info_t |
typedef struct _dr_restore_state_info_t | dr_restore_state_info_t |
typedef struct _dr_kernel_xfer_info_t | dr_kernel_xfer_info_t |
typedef struct _dr_exception_t | dr_exception_t |
typedef struct _dr_siginfo_t | dr_siginfo_t |
Enumerations | |
enum | dr_emit_flags_t { DR_EMIT_DEFAULT = 0, DR_EMIT_STORE_TRANSLATIONS = 0x01, DR_EMIT_PERSISTABLE = 0x02, DR_EMIT_MUST_END_TRACE = 0x04, DR_EMIT_GO_NATIVE = 0x08 } |
enum | dr_custom_trace_action_t |
enum | dr_exit_flags_t { DR_EXIT_MULTI_THREAD = 0x01, DR_EXIT_SKIP_THREAD_EXIT = 0x02 } |
enum | dr_kernel_xfer_type_t { DR_XFER_SIGNAL_DELIVERY, DR_XFER_SIGNAL_RETURN, DR_XFER_APC_DISPATCHER, DR_XFER_EXCEPTION_DISPATCHER, DR_XFER_RAISE_DISPATCHER, DR_XFER_CALLBACK_DISPATCHER, DR_XFER_CALLBACK_RETURN, DR_XFER_CONTINUE, DR_XFER_SET_CONTEXT_THREAD, DR_XFER_CLIENT_REDIRECT, DR_XFER_RSEQ_ABORT } |
enum | dr_signal_action_t { DR_SIGNAL_DELIVER, DR_SIGNAL_SUPPRESS, DR_SIGNAL_BYPASS, DR_SIGNAL_REDIRECT } |
Functions | |
DR_API void | dr_register_exit_event (void(*func)(void)) |
DR_API bool | dr_unregister_exit_event (void(*func)(void)) |
DR_API bool | dr_register_post_attach_event (void(*func)(void)) |
DR_API bool | dr_unregister_post_attach_event (void(*func)(void)) |
DR_API void | dr_register_pre_detach_event (void(*func)(void)) |
DR_API bool | dr_unregister_pre_detach_event (void(*func)(void)) |
DR_API void | dr_register_bb_event (dr_emit_flags_t(*func)(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating)) |
DR_API bool | dr_unregister_bb_event (dr_emit_flags_t(*func)(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating)) |
DR_API void | dr_register_trace_event (dr_emit_flags_t(*func)(void *drcontext, void *tag, instrlist_t *trace, bool translating)) |
DR_API bool | dr_unregister_trace_event (dr_emit_flags_t(*func)(void *drcontext, void *tag, instrlist_t *trace, bool translating)) |
DR_API void | dr_register_end_trace_event (dr_custom_trace_action_t(*func)(void *drcontext, void *tag, void *next_tag)) |
DR_API bool | dr_unregister_end_trace_event (dr_custom_trace_action_t(*func)(void *drcontext, void *tag, void *next_tag)) |
DR_API void | dr_register_delete_event (void(*func)(void *drcontext, void *tag)) |
DR_API bool | dr_unregister_delete_event (void(*func)(void *drcontext, void *tag)) |
DR_API void | dr_register_restore_state_event (void(*func)(void *drcontext, void *tag, dr_mcontext_t *mcontext, bool restore_memory, bool app_code_consistent)) |
DR_API bool | dr_unregister_restore_state_event (void(*func)(void *drcontext, void *tag, dr_mcontext_t *mcontext, bool restore_memory, bool app_code_consistent)) |
DR_API void | dr_register_restore_state_ex_event (bool(*func)(void *drcontext, bool restore_memory, dr_restore_state_info_t *info)) |
DR_API bool | dr_unregister_restore_state_ex_event (bool(*func)(void *drcontext, bool restore_memory, dr_restore_state_info_t *info)) |
DR_API void | dr_register_thread_init_event (void(*func)(void *drcontext)) |
DR_API bool | dr_unregister_thread_init_event (void(*func)(void *drcontext)) |
DR_API void | dr_register_thread_exit_event (void(*func)(void *drcontext)) |
DR_API bool | dr_unregister_thread_exit_event (void(*func)(void *drcontext)) |
DR_API void | dr_set_process_exit_behavior (dr_exit_flags_t flags) |
DR_API void | dr_allow_unsafe_static_behavior (void) |
DR_API void | dr_register_fork_init_event (void(*func)(void *drcontext)) |
DR_API bool | dr_unregister_fork_init_event (void(*func)(void *drcontext)) |
DR_API void | dr_register_module_load_event (void(*func)(void *drcontext, const module_data_t *info, bool loaded)) |
DR_API bool | dr_unregister_module_load_event (void(*func)(void *drcontext, const module_data_t *info, bool loaded)) |
DR_API void | dr_register_module_unload_event (void(*func)(void *drcontext, const module_data_t *info)) |
DR_API bool | dr_unregister_module_unload_event (void(*func)(void *drcontext, const module_data_t *info)) |
DR_API void | dr_register_kernel_xfer_event (void(*func)(void *drcontext, const dr_kernel_xfer_info_t *info)) |
DR_API bool | dr_unregister_kernel_xfer_event (void(*func)(void *drcontext, const dr_kernel_xfer_info_t *info)) |
DR_API void | dr_register_exception_event (bool(*func)(void *drcontext, dr_exception_t *excpt)) |
DR_API bool | dr_unregister_exception_event (bool(*func)(void *drcontext, dr_exception_t *excpt)) |
DR_API void | dr_register_filter_syscall_event (bool(*func)(void *drcontext, int sysnum)) |
DR_API bool | dr_unregister_filter_syscall_event (bool(*func)(void *drcontext, int sysnum)) |
DR_API void | dr_register_pre_syscall_event (bool(*func)(void *drcontext, int sysnum)) |
DR_API bool | dr_unregister_pre_syscall_event (bool(*func)(void *drcontext, int sysnum)) |
DR_API void | dr_register_post_syscall_event (void(*func)(void *drcontext, int sysnum)) |
DR_API bool | dr_unregister_post_syscall_event (void(*func)(void *drcontext, int sysnum)) |
DR_API void | dr_register_signal_event (dr_signal_action_t(*func)(void *drcontext, dr_siginfo_t *siginfo)) |
DR_API bool | dr_unregister_signal_event (dr_signal_action_t(*func)(void *drcontext, dr_siginfo_t *siginfo)) |
DR_API void | dr_register_low_on_memory_event (void(*func)()) |
DR_API bool | dr_unregister_low_on_memory_event (void(*func)()) |
DR_API void | dr_register_clean_call_insertion_event (void(*func)(void *drcontext, instrlist_t *ilist, instr_t *where, dr_cleancall_save_t call_flags)) |
DR_API bool | dr_unregister_clean_call_insertion_event (void(*func)(void *drcontext, instrlist_t *ilist, instr_t *where, dr_cleancall_save_t call_flags)) |
DR_API void | dr_register_nudge_event (void(*func)(void *drcontext, uint64 argument), client_id_t id) |
DR_API bool | dr_unregister_nudge_event (void(*func)(void *drcontext, uint64 argument), client_id_t id) |
DR_API bool | dr_nudge_client (client_id_t id, uint64 argument) |
DR_API dr_config_status_t | dr_nudge_client_ex (process_id_t process_id, client_id_t client_id, uint64 argument, uint timeout_ms) |
DR_API bool | dr_is_nudge_thread (void *drcontext) |
DR_API app_pc | dr_persist_start (void *perscxt) |
DR_API size_t | dr_persist_size (void *perscxt) |
DR_API bool | dr_fragment_persistable (void *drcontext, void *perscxt, void *tag) |
DR_API bool | dr_register_persist_ro (size_t(*func_size)(void *drcontext, void *perscxt, size_t file_offs, void **user_data DR_PARAM_OUT), bool(*func_persist)(void *drcontext, void *perscxt, file_t fd, void *user_data), bool(*func_resurrect)(void *drcontext, void *perscxt, byte **map DR_PARAM_OUT)) |
DR_API bool | dr_unregister_persist_ro (size_t(*func_size)(void *drcontext, void *perscxt, size_t file_offs, void **user_data DR_PARAM_OUT), bool(*func_persist)(void *drcontext, void *perscxt, file_t fd, void *user_data), bool(*func_resurrect)(void *drcontext, void *perscxt, byte **map DR_PARAM_OUT)) |
DR_API bool | dr_register_persist_rx (size_t(*func_size)(void *drcontext, void *perscxt, size_t file_offs, void **user_data DR_PARAM_OUT), bool(*func_persist)(void *drcontext, void *perscxt, file_t fd, void *user_data), bool(*func_resurrect)(void *drcontext, void *perscxt, byte **map DR_PARAM_OUT)) |
DR_API bool | dr_unregister_persist_rx (size_t(*func_size)(void *drcontext, void *perscxt, size_t file_offs, void **user_data DR_PARAM_OUT), bool(*func_persist)(void *drcontext, void *perscxt, file_t fd, void *user_data), bool(*func_resurrect)(void *drcontext, void *perscxt, byte **map DR_PARAM_OUT)) |
DR_API bool | dr_register_persist_rw (size_t(*func_size)(void *drcontext, void *perscxt, size_t file_offs, void **user_data DR_PARAM_OUT), bool(*func_persist)(void *drcontext, void *perscxt, file_t fd, void *user_data), bool(*func_resurrect)(void *drcontext, void *perscxt, byte **map DR_PARAM_OUT)) |
DR_API bool | dr_unregister_persist_rw (size_t(*func_size)(void *drcontext, void *perscxt, size_t file_offs, void **user_data DR_PARAM_OUT), bool(*func_persist)(void *drcontext, void *perscxt, file_t fd, void *user_data), bool(*func_resurrect)(void *drcontext, void *perscxt, byte **map DR_PARAM_OUT)) |
DR_API bool | dr_register_persist_patch (bool(*func_patch)(void *drcontext, void *perscxt, byte *bb_start, size_t bb_size, void *user_data)) |
DR_API bool | dr_unregister_persist_patch (bool(*func_patch)(void *drcontext, void *perscxt, byte *bb_start, size_t bb_size, void *user_data)) |
DR_API bool | dr_is_detaching (void) |
Detailed Description
Event callback registration routines.
Typedef Documentation
◆ dr_exception_t
typedef struct _dr_exception_t dr_exception_t |
Data structure passed with an exception event. Contains the machine context and the Win32 exception record.
◆ dr_fault_fragment_info_t
typedef struct _dr_fault_fragment_info_t dr_fault_fragment_info_t |
Data structure passed within dr_exception_t, dr_siginfo_t, and dr_restore_state_info_t. Contains information about the code fragment inside the code cache at the exception/signal/translation interruption point.
◆ dr_kernel_xfer_info_t
typedef struct _dr_kernel_xfer_info_t dr_kernel_xfer_info_t |
Data structure passed for dr_register_kernel_xfer_event().
◆ dr_restore_state_info_t
typedef struct _dr_restore_state_info_t dr_restore_state_info_t |
Data structure passed to a restore_state_ex event handler (see dr_register_restore_state_ex_event()). Contains the machine context at the translation point and other translation information.
◆ dr_siginfo_t
typedef struct _dr_siginfo_t dr_siginfo_t |
Data structure passed with a signal event. Contains the machine context at the signal interruption point and other signal information.
Enumeration Type Documentation
◆ dr_custom_trace_action_t
DR will call the end trace event if it is registered prior to adding each basic block to a trace being generated. The return value of the event callback should be from the dr_custom_trace_action_t enum.
- Note
- DR treats CUSTOM_TRACE_CONTINUE as an advisement only. Certain fragments are not suitable to be included in a trace and if DR runs into one it will end the trace regardless of what the client returns through the event callback.
◆ dr_emit_flags_t
enum dr_emit_flags_t |
Flags controlling the behavior of basic blocks and traces when emitted into the code cache. These flags are bitmasks that can be combined by or-ing together. For multiple clients, the flags returned by each client are or-ed together.
Enumerator | |
---|---|
DR_EMIT_DEFAULT | Emit as normal. |
DR_EMIT_STORE_TRANSLATIONS | Store translation information at emit time rather than calling the basic block or trace event later to recreate the information. Note that even if a standalone basic block has stored translations, if when it is added to a trace it does not request storage (and the trace callback also does not request storage) then the basic block callback may still be called to translate for the trace.
|
DR_EMIT_PERSISTABLE | Only valid when applied to a basic block. Indicates that the block is eligible for persisting to a persistent code cache file on disk. By default, no blocks are eligible, as tools must take care in order to properly support persistence. Note that the block is not guaranteed to be persisted if it contains complex features that prevent DR from easily persisting it. |
DR_EMIT_MUST_END_TRACE | Only valid when applied to a basic block. Indicates that the block must terminate a trace. Normally this should be set when an abnormal exit is used from the block that is incompatible with trace building's attempt to inline the continuation from the block to its successor. Note that invoking dr_redirect_execution() from a clean call called from a block aborts trace building and thus this flag need not be set for that scenario. |
DR_EMIT_GO_NATIVE | Requests that DR relinquish control of the current thread and let it run natively until the client indicates that DR should take over again. While native, on Windows, currently only the thread init event (dr_register_thread_init_event()) will be raised, and nothing on Linux: no events will occur in the native thread. On Windows, DR tries to monitor any actions a native thread might take that affect correct execution from the code cache, but running natively carries risks. Consider this feature experimental, particularly on Linux. |
◆ dr_exit_flags_t
enum dr_exit_flags_t |
Flags controlling thread behavior at process exit time in release build. See dr_set_process_exit_behavior() for further details.
Enumerator | |
---|---|
DR_EXIT_MULTI_THREAD | Do not guarantee that the process exit event is executed single-threaded. This is equivalent to specifying the |
DR_EXIT_SKIP_THREAD_EXIT | Do not invoke thread exit event callbacks at process exit time. Thread exit event callbacks will still be invoked at other times. This is equivalent to setting the |
◆ dr_kernel_xfer_type_t
Identifies the type of kernel transfer for dr_register_kernel_xfer_event().
◆ dr_signal_action_t
enum dr_signal_action_t |
Return value of client signal event callback, determining how DR will proceed with the signal.
Enumerator | |
---|---|
DR_SIGNAL_DELIVER | Deliver signal to the application as normal. |
DR_SIGNAL_SUPPRESS | Suppress signal as though it never happened. |
DR_SIGNAL_BYPASS | Deliver signal according to the default SIG_DFL action, as would happen if the application had no handler. |
DR_SIGNAL_REDIRECT | Do not deliver the signal. Instead, redirect control to the application state specified in dr_siginfo_t.mcontext. Clients may modify dr_siginfo_t.mcontext.pc to redirect control to a desired pc, but beware that it is important to set the app state (like the register values) as expected by the transfer point. |
Function Documentation
◆ dr_allow_unsafe_static_behavior()
DR_API void dr_allow_unsafe_static_behavior | ( | void | ) |
The DR_DISALLOW_UNSAFE_STATIC declaration requests that DR perform sanity checks to ensure that client libraries will also operate safely when linked statically into an application. This overrides that request, facilitating having runtime options that are not supported in a static context.
◆ dr_fragment_persistable()
DR_API bool dr_fragment_persistable | ( | void * | drcontext, |
void * | perscxt, | ||
void * | tag | ||
) |
Takes in the perscxt
opaque parameter passed to various persistence events and returns whether the fragment identified by tag
is being persisted. This routine can be called outside of a persistence event, in which case the perscxt
parameter should be NULL.
◆ dr_is_detaching()
DR_API bool dr_is_detaching | ( | void | ) |
Query whether detach is in progress. This is useful for clients that want to avoid the cost of resetting their global state on exit if there is no detaching and thus no chance of re-attaching.
◆ dr_is_nudge_thread()
DR_API bool dr_is_nudge_thread | ( | void * | drcontext | ) |
On Windows, nudges are implemented via remotely injected threads. This routine returns whether or not the thread indicated by drcontext
is such a nudge thread.
- Note
- Windows only.
◆ dr_nudge_client()
DR_API bool dr_nudge_client | ( | client_id_t | id, |
uint64 | argument | ||
) |
Triggers an asynchronous nudge event in the current process. The callback function registered with dr_register_nudge_event() will be called with the supplied argument
(in a new non-application thread on Windows).
- Note
- On Linux, the nudge will not be delivered until this thread exits the code cache. Thus, if this routine is called from a clean call, dr_redirect_execution() should be used to ensure cache exit.
◆ dr_nudge_client_ex()
DR_API dr_config_status_t dr_nudge_client_ex | ( | process_id_t | process_id, |
client_id_t | client_id, | ||
uint64 | argument, | ||
uint | timeout_ms | ||
) |
Triggers an asynchronous nudge event in a target process. The callback function registered with dr_register_nudge_event() for the specified client in the specified process will be called with the supplied argument
(in a new non-application thread on Windows).
- Note
- On Linux, if
pid
is the current process, the nudge will not be delivered until this thread exits the code cache. Thus, if this routine is called from a clean call andpid
is the current process, dr_redirect_execution() should be used to ensure cache exit.
- Parameters
-
[in] process_id The system id of the process to nudge (see dr_get_process_id()) [in] client_id The unique client ID provided at client registration. [in] argument An argument passed to the client's nudge handler. [in] timeout_ms Windows only. The number of milliseconds to wait for each nudge to complete before continuing. If INFINITE is supplied then the wait is unbounded. If 0 is supplied the no wait is performed. If a non-0 wait times out DR_NUDGE_TIMEOUT will be returned.
- Returns
- A dr_config_status_t code indicating the result of the nudge.
◆ dr_persist_size()
DR_API size_t dr_persist_size | ( | void * | perscxt | ) |
Takes in the perscxt
opaque parameter passed to various persistence events and returns the size of the code region being persisted.
◆ dr_persist_start()
DR_API app_pc dr_persist_start | ( | void * | perscxt | ) |
Takes in the perscxt
opaque parameter passed to various persistence events and returns the beginning address of the code region being persisted.
◆ dr_register_bb_event()
DR_API void dr_register_bb_event | ( | dr_emit_flags_t(*)(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) | func | ) |
Registers a callback function for the basic block event. DR calls func
before inserting a new basic block into the code cache. When adding a basic block to a new trace, DR calls func
again with for_trace
set to true, giving the client the opportunity to keep its same instrumentation in the trace, or to change it. The original basic block's instrumentation is unchanged by whatever action is taken in the for_trace
call.
DR constructs dynamic basic blocks, which are distinct from a compiler's classic basic blocks. DR does not know all entry points ahead of time, and will end up duplicating the tail of a basic block if a later entry point is discovered that targets the middle of a block created earlier, or if a later entry point targets straight-line code that falls through into code already present in a block.
DR may call func
again if it needs to translate from code cache addresses back to application addresses, which happens on faulting instructions as well as in certain situations involving suspended threads or forcibly relocated threads. The translating
parameter distinguishes the two types of calls and is further explained below.
drcontext
is a pointer to the input program's machine context. Clients should not inspect or modify the context; it is provided as an opaque pointer (i.e.,void *
) to be passed to API routines that require access to this internal data. drcontext is specific to the current thread, but in normal configurations the basic block being created is thread-shared: thus, when allocating data structures with the same lifetime as the basic block, usually global heap (dr_global_alloc()) is a better choice than heap tied to the thread that happened to first create the basic block (dr_thread_alloc()). Thread-private heap is fine for temporary structures such as instr_t and instrlist_t.tag
is a unique identifier for the basic block fragment. Use dr_fragment_app_pc() to translate it to an application address.bb
is a pointer to the list of instructions that comprise the basic block. Clients can examine, manipulate, or completely replace the instructions in the list.translating
indicates whether this callback is for basic block creation (false) or is for address translation (true). This is further explained below.
The callback function should return a dr_emit_flags_t flag.
The user is free to inspect and modify the block before it executes, but must adhere to the following restrictions:
- If there is more than one application branch, only the last can be conditional.
- An application conditional branch must be the final instruction in the block.
- An application direct call must be the final instruction in the block unless it is inserted by DR for elision and the subsequent instructions are the callee.
- There can only be one indirect branch (call, jump, or return) in a basic block, and it must be the final instruction in the block.
- There can only be one far branch (call, jump, or return) in a basic block, and it must be the final instruction in the block.
- The AArch64 instruction ISB must be the final instruction in the block.
- The exit control-flow of a block ending in a system call or int instruction cannot be changed, nor can instructions be inserted after the system call or int instruction itself, unless the system call or int instruction is removed entirely.
- The number of an interrupt cannot be changed. (Note that the parameter to a system call, normally kept in the eax register, can be freely changed in a basic block: but not in a trace.)
- A system call or interrupt instruction can only be added if it satisfies the above constraints: i.e., if it is the final instruction in the block and the only system call or interrupt.
- Any AArch64 OP_isb instruction must be the last instruction in its block.
- All IT blocks must be legal. For example, application instructions inside an IT block cannot be removed or added to without also updating the OP_it instruction itself. Clients can use the combination of dr_remove_it_instrs() and dr_insert_it_instrs() to more easily manage IT blocks while maintaining the simplicity of examining individual instructions in isolation.
- The block's application source code (as indicated by the translation targets, set by instr_set_translation()) must remain within the original bounds of the block (the one exception to this is that a jump can translate to its target). Otherwise, DR's cache consistency algorithms cannot guarantee to properly invalidate the block if the source application code is modified. To send control to other application code regions, truncate the block and use a direct jump to target the desired address, which will then materialize in the subsequent block, rather than embedding the desired instructions in this block.
- There is a limit on the size of a basic block in the code cache. DR performs its own modifications, especially on memory writes for cache consistency of self-modifying (or false sharing) code regions. If an assert fires in debug build indicating a limit was reached, either truncate blocks or use the -max_bb_instrs runtime option to ask DR to make them smaller.
To support transparent fault handling, DR must translate a fault in the code cache into a fault at the corresponding application address. DR must also be able to translate when a suspended thread is examined by the application or by DR itself for internal synchronization purposes. If the client is only adding observational instrumentation (i.e., meta instructions: see instr_set_meta()) (which should not fault) and is not modifying, reordering, or removing application instructions, these details can be ignored. In that case the client should return DR_EMIT_DEFAULT and set up its basic block callback to be deterministic and idempotent. If the client is performing modifications, then in order for DR to properly translate a code cache address the client must use instr_set_translation() in the basic block creation callback to set the corresponding application address (the address that should be presented to the application as the faulting address, or the address that should be restarted after a suspend) for each modified instruction and each added application instruction (see instr_set_app()).
There are two methods for using the translated addresses:
- Return DR_EMIT_STORE_TRANSLATIONS from the basic block creation callback. DR will then store the translation addresses and use the stored information on a fault. The basic block callback for
tag
will not be called withtranslating
set to true. Note that unless DR_EMIT_STORE_TRANSLATIONS is also returned forfor_trace
calls (or DR_EMIT_STORE_TRANSLATIONS is returned in the trace callback), each constituent block comprising the trace will need to be re-created with bothfor_trace
andtranslating
set to true. Storing translations uses additional memory that can be significant: up to 20% in some cases, as it prevents DR from using its simple data structures and forces it to fall back to its complex, corner-case design. This is why DR does not store all translations by default. - Return DR_EMIT_DEFAULT from the basic block creation callback. DR will then call the callback again during fault translation with
translating
set to true. All modifications tobb
that were performed on the creation callback must be repeated on the translating callback. This option is only possible when basic block modifications are deterministic and idempotent, but it saves memory. Naturally, global state changes triggered by block creation should be wrapped in checks fortranslating
being false. Even in this case, instr_set_translation() should be called for application instructions even whentranslating
is false, as DR may decide to store the translations at creation time for reasons of its own.
Furthermore, if the client's modifications change any part of the machine state besides the program counter, the client should use dr_register_restore_state_event() or dr_register_restore_state_ex_event() to restore the registers and application memory to their original application values.
For meta instructions that do not reference application memory (i.e., they should not fault), leave the translation field as NULL. A NULL value instructs DR to use the subsequent application instruction's translation as the application address, and to fail when translating the full state. Since the full state will only be needed when relocating a thread (as stated, there will not be a fault here), failure indicates that this is not a valid relocation point, and DR's thread synchronization scheme will use another spot. If the translation field is set to a non-NULL value, the client should be willing to also restore the rest of the machine state at that point (restore spilled registers, etc.) via dr_register_restore_state_event() or dr_register_restore_state_ex_event(). This is necessary for meta instructions that reference application memory. DR takes care of such potentially-faulting instructions added by its own API routines (dr_insert_clean_call() arguments that reference application data, dr_insert_mbr_instrumentation()'s read of application indirect branch data, etc.)
- Note
- In order to present a more straightforward code stream to clients, this release of DR disables several internal optimizations. As a result, some applications may see a performance degradation. Applications making heavy use of system calls are the most likely to be affected. Future releases may allow clients some control over performance versus visibility. The -opt_speed option can regain some of this performance at the cost of more complex basic blocks that cross control transfers.
- If multiple clients are present, the instruction list for a basic block passed to earlier-registered clients will contain the instrumentation and modifications put in place by later-registered clients.
- Basic blocks can be deleted due to hitting capacity limits or cache consistency events (when the source application code of a basic block is modified). In that case, the client will see a new basic block callback if the block is then executed again after deletion. The deletion event (dr_register_delete_event()) will be raised at deletion time.
- If the -thread_private runtime option is specified, clients should expect to see duplicate tags for separate threads, albeit with different dcrcontext values. Additionally, DR employs a cache-sizing algorithm for thread private operation that proactively deletes fragments. Even with thread-shared caches enabled, however, certain situations cause DR to emit thread-private basic blocks (e.g., self-modifying code). In this case, clients should be prepared to see duplicate tags without an intermediate deletion.
-
A client can change the control flow of the application by changing the control transfer instruction at end of the basic block. If a basic block is ended with a non-control transfer instruction, an application jump instruction can be inserted. If a basic block is ended with a conditional branch,
instrlist_set_fall_through_target
can be used to change the fall-through target. If a basic block is ended with a call instruction,instrlist_set_return_target
can be used to change the return target of the call.
◆ dr_register_clean_call_insertion_event()
DR_API void dr_register_clean_call_insertion_event | ( | void(*)(void *drcontext, instrlist_t *ilist, instr_t *where, dr_cleancall_save_t call_flags) | func | ) |
Registers a callback function that is invoked whenever a clean call is inserted in instrumentation, such as by dr_insert_clean_call(), dr_insert_clean_call_ex(), or dr_insert_clean_call_ex_varg(). 'where' is a label with note value DR_NOTE_CLEAN_CALL_END; the clean call sequence will be inserted prior to the label after all clean call callbacks are finished (so the clean call sequence is not yet visible at the time this callback is invoked).
◆ dr_register_delete_event()
DR_API void dr_register_delete_event | ( | void(*)(void *drcontext, void *tag) | func | ) |
Registers a callback function for the fragment deletion event. DR calls func
whenever it removes a fragment from the code cache. Due to DR's high-performance non-precise flushing, a fragment can be made inaccessible but not actually freed for some time. A new fragment can thus be created before the deletion event for the old fragment is raised. We recommended using a counter to ignore subsequent deletion events when using per-fragment data structures and duplicate fragments are seen.
- Note
- drcontext may be NULL when thread-shared fragments are being deleted during process exit. For this reason, thread-private heap should not be used for data structures intended to be freed at thread-shared fragment deletion.
◆ dr_register_end_trace_event()
DR_API void dr_register_end_trace_event | ( | dr_custom_trace_action_t(*)(void *drcontext, void *tag, void *next_tag) | func | ) |
Registers a callback function for the end-trace event. DR calls func
before extending a trace with a new basic block. The func
should return one of the dr_custom_trace_action_t enum values.
◆ dr_register_exception_event()
DR_API void dr_register_exception_event | ( | bool(*)(void *drcontext, dr_exception_t *excpt) | func | ) |
Registers a callback function for the exception event. DR calls func
whenever the application throws an exception. If func
returns true, the exception is delivered to the application's handler along with any changes made to excpt->mcontext
. If func
returns false, the faulting instruction in the code cache is re-executed using excpt->raw_mcontext
, including any changes made to that structure. Clients are expected to use excpt->raw_mcontext
when using faults as a mechanism to push rare cases out of an instrumentation fastpath that need to examine instrumentation instructions rather than the translated application state and should normally not examine it for application instruction faults. Certain registers may not contain proper application values in excpt->raw_mcontext
for exceptions in application instructions. Clients are cautioned against relying on any details of code cache layout or register usage beyond instrumentation inserted by the client itself when examining excpt->raw_mcontext
.
If multiple callbacks are registered, the first one that returns false will short-circuit event delivery to later callbacks.
DR raises this event for exceptions outside the code cache that could come from code generated by a client. For such exceptions, mcontext is not translated and is identical to raw_mcontext.
To skip the passing of the exception to the application's exception handlers and to send control elsewhere instead, a client can call dr_redirect_execution() from func
.
- Note
excpt->fault_fragment_info
data is provided withexcpt->raw_mcontext
. It is valid only ifexcpt->fault_fragment_info.cache_start_pc
is notNULL
. It provides clients information about the code fragment being executed at the exception interruption point. Clients are cautioned against relying on any details of code cache layout or register usage beyond instrumentation inserted by the client itself.- Only valid on Windows.
- The function is not called for RaiseException.
◆ dr_register_exit_event()
DR_API void dr_register_exit_event | ( | void(*)(void) | func | ) |
Registers a callback function for the process exit event. DR calls func
when the process exits. By default, the process exit event will be executed with only a single live thread. dr_set_process_exit_behavior() can provide superior exit performance for clients that have flexible exit event requirements.
On Linux, SYS_execve does NOT result in an exit event, but it WILL result in the client library being reloaded and its dr_client_main() routine being called.
◆ dr_register_filter_syscall_event()
DR_API void dr_register_filter_syscall_event | ( | bool(*)(void *drcontext, int sysnum) | func | ) |
Registers a callback function for the syscall filter event. DR calls func
to decide whether to invoke the syscall events for each system call site encountered with a statically-determinable system call number. If func
returns true, the pre-syscall (dr_register_pre_syscall_event()) and post-syscall (dr_register_post_syscall_event()) events will be invoked. Otherwise, the events may or may not occur, depending on whether DR itself needs to intercept them and whether the system call number is statically determinable. System call number determination can depend on whether the -opt_speed option is enabled. If a system call number is not determinable, the filter event will not be called, but the pre and post events will be called.
Intercepting every system call can be detrimental to performance for certain types of applications. Filtering provides for greater performance by letting uninteresting system calls execute without interception overhead.
◆ dr_register_fork_init_event()
DR_API void dr_register_fork_init_event | ( | void(*)(void *drcontext) | func | ) |
Registers a callback function for the fork event. DR calls func
whenever the application forks a new process.
- Note
- Valid on Linux only.
◆ dr_register_kernel_xfer_event()
DR_API void dr_register_kernel_xfer_event | ( | void(*)(void *drcontext, const dr_kernel_xfer_info_t *info) | func | ) |
Registers a callback function for the kernel transfer event. DR calls func
whenever the kernel is about to directly transfer control without an explicit user-mode control transfer instruction. This includes the following scenarios, which are distinguished by type:
- On UNIX, a signal is about to be delivered to an application handler. This event differs from a dr_register_signal_event() callback in that the latter is called regardless of whether the application has a handler, and it does not provide the target context of any handler.
- On UNIX, a signal return system call is about to be invoked.
- On Windows, the asynchronous procedure call dispatcher is about to be invoked.
- On Windows, the callback dispatcher is about to be invoked.
- On Windows, the exception dispatcher is about to be invoked.
- On Windows, the NtContinue system call is about to be invoked.
- On Windows, the NtSetContextThread system call is about to be invoked.
- On Windows, the NtCallbackReturn system call is about to be invoked.
- On Windows, interrupt 0x2b is about to be invoked.
- The client requests redirection using dr_redirect_execution() or DR_SIGNAL_REDIRECT.
The prior context, if known, is provided in info->source_mcontext
; if unknown, info->source_mcontext
is NULL. Multimedia state is typically not provided in info->source_mcontext
, which is reflected in its flags
.
The target program counter and stack are provided in info->target_pc
and info->target_xsp
. Further target state can be examined by calling dr_get_mcontext() and modified by calling dr_set_mcontext(). Changes to the target state, including the pc, are supported for all cases except NtCallbackReturn and interrupt 0x2b. However, dr_get_mcontext() and dr_set_mcontext() are limited for the Windows system calls NtContinue and NtSetContextThread to the ContextFlags set by the application: dr_get_mcontext() will adjust the dr_mcontext_t.flags to reflect what's available, and dr_set_mcontext() will only set what's also set in ContextFlags. Given the disparity in how Ebp/Rbp is handled (in DR_MC_INTEGER but in CONTEXT_CONTROL), clients that care about that register are better off using system call events instead of kernel transfer events to take actions on these two system calls.
This is a convenience event: all of the above events can be detected using combinations of other events. This event is meant to be used to identify all changes in the program counter that do not arise from explicit control flow instructions.
◆ dr_register_low_on_memory_event()
DR_API void dr_register_low_on_memory_event | ( | void(*)() | func | ) |
Registers a callback function for the low on memory event. DR calls func
whenever virtual memory is tight and enables the client to help free space.
◆ dr_register_module_load_event()
DR_API void dr_register_module_load_event | ( | void(*)(void *drcontext, const module_data_t *info, bool loaded) | func | ) |
Registers a callback function for the module load event. DR calls func
whenever the application loads a module (typically a library but this term includes the executable). The loaded
parameter indicates whether the module is fully initialized by the loader or in the process of being loaded. This parameter is present only for backward compatibility: current versions of DR always pass true, and the client can assume that relocating, rebinding, and (on Linux) segment remapping have already occurred.
- Note
- The module_data_t
info
passed to the callback routine is valid only for the duration of the callback and should not be freed; a persistent copy can be made with dr_copy_module_data(). - Registration cannot be done during the basic block event: it should be done at initialization time.
◆ dr_register_module_unload_event()
DR_API void dr_register_module_unload_event | ( | void(*)(void *drcontext, const module_data_t *info) | func | ) |
Registers a callback function for the module unload event. DR calls func
whenever the application unloads a module.
- Note
- The module_data_t
*info
passed to the callback routine is valid only for the duration of the callback and should not be freed; a persistent copy can be made with dr_copy_module_data().
◆ dr_register_nudge_event()
DR_API void dr_register_nudge_event | ( | void(*)(void *drcontext, uint64 argument) | func, |
client_id_t | id | ||
) |
Registers a callback function for nudge events. External entities can nudge a process through the dr_nudge_process() or dr_nudge_pid() drconfig API routines on Windows or using the drnudgeunix
tool on Linux. A client in this process can use dr_nudge_client() to raise a nudge, while a client in another process can use dr_nudge_client_ex().
DR calls func
whenever the current process receives a nudge. On Windows, the nudge event is delivered in a new non-application thread. Callers must specify the target client by passing the client ID that was provided in dr_client_main().
◆ dr_register_persist_patch()
DR_API bool dr_register_persist_patch | ( | bool(*)(void *drcontext, void *perscxt, byte *bb_start, size_t bb_size, void *user_data) | func_patch | ) |
- Warning
- This patching interface is in flux and is subject to change in the next release. Consider it experimental in this release.
Registers a callback function for patching code prior to storing it in a persisted cache file. The length of each instruction cannot be changed, but displacements and offsets can be adjusted to make the code position-independent. A patch callback is only called once per persisted file, regardless of whether one or all of read-only, executable, or writable data has been added. Use the user_data
parameter to pass the file offset or other data from the other persistence events to this one.
- Parameters
-
[in] func_patch The function to call to perform any necessary patching of the to-be-persisted basic block code. The function should decode up to bb_size
bytes frombb_start
and look for call or jump displacements or rip-relative data references that need to be updated to use data in the persisted file. There is no padding between instructions, so a simple decode loop will find every instruction. Theperscxt
parameter can be passed to the routines dr_persist_start(), dr_persist_size(), and dr_fragment_persistable() to identify the region of code being persisted.
- Returns
- whether successful.
◆ dr_register_persist_ro()
DR_API bool dr_register_persist_ro | ( | size_t(*)(void *drcontext, void *perscxt, size_t file_offs, void **user_data DR_PARAM_OUT) | func_size, |
bool(*)(void *drcontext, void *perscxt, file_t fd, void *user_data) | func_persist, | ||
bool(*)(void *drcontext, void *perscxt, byte **map DR_PARAM_OUT) | func_resurrect | ||
) |
Registers callback functions for storing read-only data in each persisted cache file. When generating a new persisted cache file, DR first calls func_size
to obtain the size required for read-only data in each persisted cache file. DR subsequently calls func_persist
to write the actual data. DR ensures that no other thread will execute in between the calls to func_size
and func_persist
.
Upon loading a previously-written persisted cache file, DR calls func_resurrect
to validate and read back in data from the persisted file.
For each callback, the perscxt
parameter can be passed to the routines dr_persist_start(), dr_persist_size(), and dr_fragment_persistable() to identify the region of code being persisted.
- Parameters
-
[in] func_size The function to call to determine the size needed for persisted data. The file_offs
parameter indicates the offset from the start of the persisted file where this data will reside (which is needed to calculate patch displacements). The callback can store a void* value into the address specified byuser_data
. This value will be passed tofunc_persist
and if a patch callback is registered (see dr_register_persist_patch()) tofunc_patch
. The same value will be shared with persisted code callbacks (see dr_register_persist_rx()) and writable data callbacks (see dr_register_persist_rw()).[in] func_persist The function to call to write the actual data. Data to be persisted should be written to the file fd
via dr_write_file(). The data will be read-only when the persisted file is loaded back in for use. The return value of the function indicates success of the write. If the function returns false, the persisted cache file being generated will be abandoned under the assumption of a non-recoverable error.[in] func_resurrect The function to call to validate previously written data. The map
variable points to the mapped-in data that was written at persist time. The return value of the function indicates success of the resurrection. If the function returns false, the persisted cache file being loaded will be abandoned under the assumption of a non-recoverable error. Any validation that the persisted file is suitable for use should be performed by the function prior to any restoration work needed for the data. Themap
address should be updated to point to the end of the persisted data (i.e., on return it should equal its start value plus the size that was passed to dr_register_persist_ro_size()). DR will perform self-consistency checks, including whether the whole pcache is present and that a checksum of at least part of the file matches, prior to calling this callback. Thus, the client can assume that it is not truncated.
- Note
func_resurrect
may be called during persisted file generation if a persisted file already exists, in order to merge with that file.
- Returns
- whether successful.
◆ dr_register_persist_rw()
DR_API bool dr_register_persist_rw | ( | size_t(*)(void *drcontext, void *perscxt, size_t file_offs, void **user_data DR_PARAM_OUT) | func_size, |
bool(*)(void *drcontext, void *perscxt, file_t fd, void *user_data) | func_persist, | ||
bool(*)(void *drcontext, void *perscxt, byte **map DR_PARAM_OUT) | func_resurrect | ||
) |
Registers callback functions for storing writable data in each persisted cache file. When generating a new persisted cache file, DR first calls func_size
to obtain the size required for writable data in each persisted cache file. DR subsequently calls func_persist
to write the actual data. DR ensures that no other thread will execute in between the calls to func_size
and func_persist
.
Upon loading a previously-written persisted cache file, DR calls func_resurrect
to validate and read back in data from the persisted file.
For each callback, the perscxt
parameter can be passed to the routines dr_persist_start(), dr_persist_size(), and dr_fragment_persistable() to identify the region of code being persisted.
- Parameters
-
[in] func_size The function to call to determine the size needed for persisted data. The file_offs
parameter indicates the offset from the start of the persisted file where this data will reside (which is needed to calculate patch displacements). The callback can store a void* value into the address specified byuser_data
. This value will be passed tofunc_persist
and if a patch callback is registered (see dr_register_persist_patch()) tofunc_patch
. The same value will be shared with persisted code callbacks (see dr_register_persist_rx()) and read-only data callbacks (see dr_register_persist_ro()).[in] func_persist The function to call to write the actual data. Data to be persisted should be written to the file fd
via dr_write_file(). The data will be writable when the persisted file is loaded back in for use. The return value of the function indicates success of the write. If the function returns false, the persisted cache file being generated will be abandoned under the assumption of a non-recoverable error.[in] func_resurrect The function to call to validate previously written data. The map
variable points to the mapped-in data that was written at persist time. The return value of the function indicates success of the resurrection. If the function returns false, the persisted cache file being loaded will be abandoned under the assumption of a non-recoverable error. Any validation that the persisted file is suitable for use should be performed by the function prior to any restoration work needed for the data. Themap
address should be updated to point to the end of the persisted data (i.e., on return it should equal its start value plus the size that was passed to dr_register_persist_rw_size()). DR will perform self-consistency checks, including whether the whole pcache is present and that a checksum of at least part of the file matches, prior to calling this callback. Thus, the client can assume that it is not truncated.
- Note
func_resurrect
may be called during persisted file generation if a persisted file already exists, in order to merge with that file.
- Returns
- whether successful.
◆ dr_register_persist_rx()
DR_API bool dr_register_persist_rx | ( | size_t(*)(void *drcontext, void *perscxt, size_t file_offs, void **user_data DR_PARAM_OUT) | func_size, |
bool(*)(void *drcontext, void *perscxt, file_t fd, void *user_data) | func_persist, | ||
bool(*)(void *drcontext, void *perscxt, byte **map DR_PARAM_OUT) | func_resurrect | ||
) |
Registers callback functions for storing executable code (outside of normal code blocks) in each persisted cache file. When generating a new persisted cache file, DR first calls func_size
to obtain the size required for executable code in each persisted cache file. DR subsequently calls func_persist
to write the actual code. DR ensures that no other thread will execute in between the calls to func_size
and func_persist
.
Upon loading a previously-written persisted cache file, DR calls func_resurrect
to validate and read back in code from the persisted file.
For each callback, the perscxt
parameter can be passed to the routines dr_persist_start(), dr_persist_size(), and dr_fragment_persistable() to identify the region of code being persisted.
- Parameters
-
[in] func_size The function to call to determine the size needed for persisted code. The file_offs
parameter indicates the offset from the start of the persisted file where this code will reside (which is needed to calculate patch displacements). The callback can store a void* value into the address specified byuser_data
. This value will be passed tofunc_persist
and if a patch callback is registered (see dr_register_persist_patch()) tofunc_patch
. The same value will be shared with read-only data callbacks (see dr_register_persist_ro()) and writable data callbacks (see dr_register_persist_rw()).[in] func_persist The function to call to write the actual code. Code to be persisted should be written to the file fd
via dr_write_file(). The code will be read-only when the persisted file is loaded back in for use. The return value of the function indicates success of the write. If the function returns false, the persisted cache file being generated will be abandoned under the assumption of a non-recoverable error.[in] func_resurrect The function to call to validate previously written code. The map
variable points to the mapped-in code that was written at persist time. The return value of the function indicates success of the resurrection. If the function returns false, the persisted cache file being loaded will be abandoned under the assumption of a non-recoverable error. Any validation that the persisted file is suitable for use should be performed by the function prior to any restoration work needed for the code. Themap
address should be updated to point to the end of the persisted data (i.e., on return it should equal its start value plus the size that was passed to dr_register_persist_rx_size()). DR will perform self-consistency checks, including whether the whole pcache is present and that a checksum of at least part of the file matches, prior to calling this callback. Thus, the client can assume that it is not truncated.
- Note
func_resurrect
may be called during persisted file generation if a persisted file already exists, in order to merge with that file.
- Returns
- whether successful.
◆ dr_register_post_attach_event()
DR_API bool dr_register_post_attach_event | ( | void(*)(void) | func | ) |
Registers a function which is called after all other threads have been taken over during a process attach event, whether externally triggered or internally triggered (via dr_app_start() or related functions). If this process instance was not initiated by an attach or takeover methodology where multiple application threads may exist at the time of takeover (such as a process newly created on Linux), this registration function returns false.
The attach methodology operates in a staggered fashion, with each thread being taken over and executed under DR control in turn. If the application has many threads, threads taken over early in this process may execute substantial amounts of instrumented code before the threads taken over last start executing instrumented code. The purpose of this event is to provide a single control point where all threads are known to be under DR control and running instrumented code.
◆ dr_register_post_syscall_event()
DR_API void dr_register_post_syscall_event | ( | void(*)(void *drcontext, int sysnum) | func | ) |
Registers a callback function for the post-syscall event. DR calls func
whenever the application just finished invoking a system call, if any client asked for that system call number to be intercepted via the filter event (dr_register_filter_syscall_event()) or if DR itself needs to intercept the system call. Any client registering a pre- or post-syscall event should also register a filter event.
The result of the system call can be modified with dr_syscall_set_result() or dr_syscall_set_result_ex().
System calls that change control flow or terminate the current thread or process typically do not have a post-syscall event. These include SYS_exit, SYS_exit_group, SYS_execve, SYS_sigreturn, and SYS_rt_sigreturn on Linux, and NtTerminateThread, NtTerminateProcess (depending on the parameters), NtCallbackReturn, and NtContinue on Windows.
The application's machine state can be accessed and set with dr_get_mcontext() and dr_set_mcontext().
On MacOS, whether 32-bit or 64-bit, the system call number passed (sysnum
) has been normalized to a positive number with the top 8 bits set to 0x1 for a Mach system call, 0x3 for Machdep, and 0x0 for BSD (allowing the direct use of SYS_ constants). Access the raw eax register to view the unmodified number.
Additional system calls may be invoked by calling dr_syscall_invoke_another() prior to returning from the post-syscall event callback. The system call to be invoked should be specified with dr_syscall_set_sysnum(), and its parameters can be set with dr_syscall_set_param().
◆ dr_register_pre_detach_event()
DR_API void dr_register_pre_detach_event | ( | void(*)(void) | func | ) |
Registers a function which is called at the start of a full detach of DR from the current process, whether externally triggered or internally triggered (via dr_app_stop_and_cleanup() or related functions), as well as at the start of DR sending all threads native but not cleaning up its own state (through dr_app_stop()).
The detach methodology operates in a staggered fashion, with each thread being returned to native control in turn. If the application has many threads, threads detached late in this process may execute substantial amounts of instrumented code before the full detach process is complete, while threads detached early are running natively. The purpose of this event is to provide a single final control point where all threads are known to be under DR control and running instrumented code. The exit event (see dr_register_exit_event()) is not called until after all other threads have been detached.
◆ dr_register_pre_syscall_event()
DR_API void dr_register_pre_syscall_event | ( | bool(*)(void *drcontext, int sysnum) | func | ) |
Registers a callback function for the pre-syscall event. DR calls func
whenever the application is about to invoke a system call, if any client asked for that system call number to be intercepted via the filter event (dr_register_filter_syscall_event()). Any client registering a pre- or post-syscall event should also register a filter event.
The application parameters to the system call can be viewed with dr_syscall_get_param() and set with dr_syscall_set_param(). The system call number can also be changed with dr_syscall_set_sysnum().
The application's machine state can be accessed and set with dr_get_mcontext() and dr_set_mcontext(). Changing registers in this way overlaps with system call parameter changes on some platforms. On Linux, for SYS_clone, client changes to the ebp/rbp register will be ignored by the clone child.
On MacOS, whether 32-bit or 64-bit, the system call number passed (sysnum
) has been normalized to a positive number with the top 8 bits set to 0x1 for a Mach system call, 0x3 for Machdep, and 0x0 for BSD (allowing the direct use of SYS_ constants). Access the raw eax register to view the unmodified number.
If func
returns true, the application's system call is invoked normally; if func
returns false, the system call is skipped. If it is skipped, the return value can be set with dr_syscall_set_result() or dr_syscall_set_result_ex(). If the system call is skipped, there will not be a post-syscall event. If multiple callbacks are registered, the first one that returns false will short-circuit event delivery to later callbacks.
◆ dr_register_restore_state_event()
DR_API void dr_register_restore_state_event | ( | void(*)(void *drcontext, void *tag, dr_mcontext_t *mcontext, bool restore_memory, bool app_code_consistent) | func | ) |
Registers a callback function for the machine state restoration event. DR calls func
whenever it needs to translate a code cache machine context from the code cache to its corresponding original application context. DR needs to translate when instructions fault in the cache as well as when a suspended thread is examined or relocated for internal purposes.
If a client is only adding instrumentation (meta-code: see instr_is_meta()) that does not reference application memory, and is not reordering or removing application instructions, then it need not register for this event. If, however, a client is modifying application code or is adding code that can fault, the client must be capable of restoring the original context.
When DR needs to translate a code cache context, DR recreates the faulting instruction's containing fragment, storing translation information along the way, by calling the basic block and/or trace event callbacks with the translating
parameter set to true. DR uses the recreated code to identify the application instruction (mcontext.pc
) corresponding to the faulting code cache instruction. If the client asked to store translation information by returning DR_EMIT_STORE_TRANSLATIONS from the basic block or trace event callback, then this step of re-calling the event callback is skipped and the stored value is used as the application address (mcontext.pc
).
DR then calls the fault state restoration event to allow the client to restore the registers and application memory to their proper values as they would have appeared if the original application code had been executed up to the mcontext.pc
instruction. Memory should only be restored if the restore_memory
parameter is true; if it is false, DR may only be querying for the address (mcontext.pc
) or register state and may not relocate this thread.
DR will call this event for all translation attempts, even when the register state already contains application values, to allow clients to restore memory.
The app_code_consistent
parameter indicates whether the original application code containing the instruction being translated is guaranteed to still be in the same state it was when the code was placed in the code cache. This guarantee varies depending on the type of cache consistency being used by DR.
The client can update mcontext.pc
in this callback. The client should not change mcontext.flags:
it should remain DR_MC_ALL.
- Note
- The passed-in
drcontext
may correspond to a different thread than the thread executing the callback. Do not assume that the executing thread is the target thread.
◆ dr_register_restore_state_ex_event()
DR_API void dr_register_restore_state_ex_event | ( | bool(*)(void *drcontext, bool restore_memory, dr_restore_state_info_t *info) | func | ) |
Registers a callback function for the machine state restoration event with extended information.
This event is identical to that for dr_register_restore_state_event() with the following exceptions:
- Additional information is provided in the dr_restore_state_info_t structure, including the pre-translation context (containing the address inside the code cache of the translation point) and the starting address of the containing fragment in the code cache. Certain registers may not contain proper application values in
info->raw_mcontext
. Clients are cautioned against relying on any details of code cache layout or register usage beyond instrumentation inserted by the client itself when examininginfo->raw_mcontext
. - The callback function returns a boolean indicating the success of the translation. When DR is translating not for a fault but for thread relocation, the
restore_memory
parameter will be false. Such translation can target a meta-instruction that can fault (i.e., it has a non-NULL translation field). For that scenario, a client can choose not to translate. Such instructions do not always require full translation for faults, and allowing translation failure removes the requirement that a client must translate at all such instructions. Note, however, that returning false can cause performance degradation as DR must then resume the thread and attempt to re-suspend it at a safer spot. Clients must return true for translation points in application code in order to avoid catastropic failure to suspend, and should thus identify whether translation points are inside their own instrumentation before returning false. Translation for relocation will never occur in meta instructions, so clients only need to look for meta-may-fault instructions. Clients should never return false whenrestore_memory
is true. - If multiple callbacks are registered, the first one that returns false will short-circuit event delivery to later callbacks.
◆ dr_register_signal_event()
DR_API void dr_register_signal_event | ( | dr_signal_action_t(*)(void *drcontext, dr_siginfo_t *siginfo) | func | ) |
Requests that DR call the provided callback function func
whenever a signal is received by any application thread. The return value of func
determines whether DR delivers the signal to the application. To redirect execution return DR_SIGNAL_REDIRECT (do not call dr_redirect_execution() from a signal callback). The callback function will be called even if the application has no handler or has registered a SIG_IGN or SIG_DFL handler. If multiple callbacks are registered, the first one that returns other than DR_SIGNAL_DELIVER will short-circuit event delivery to later callbacks.
Modifications to the fields of siginfo->mcontext
will be propagated to the application if it has a handler for the signal, if DR_SIGNAL_DELIVER is returned.
The siginfo->raw_mcontext
data is only provided for non-delayable signals (e.g., SIGSEGV) that must be delivered immediately. Whether it is supplied is specified in siginfo->raw_mcontext_valid
. It is intended for clients using faults as a mechanism to push rare cases out of an instrumentation fastpath that need to examine instrumentation instructions rather than the translated application state. Certain registers may not contain proper application values in excpt->raw_mcontext
for exceptions in application instructions. Clients are cautioned against relying on any details of code cache layout or register usage beyond instrumentation inserted by the client itself. If DR_SIGNAL_SUPPRESS is returned, siginfo->mcontext
is ignored and siginfo->raw_mcontext
is used as the resumption context. The client's changes to siginfo->raw_mcontext
will take effect.
For a delayable signal, DR raises a signal event only when about to deliver the signal to the application. Thus, if the application has blocked a delayable signal, the corresponding signal event will not occur until the application unblocks the signal, even if such a signal is delivered by the kernel. For non-delayable signals, DR will raise a signal event on initial receipt of the signal, with the siginfo->blocked
field set. Such a blocked signal will have a second event raised when it is delivered to the application (if it is not suppressed by the client, and if there is not already a pending blocked signal, for non-real-time signals).
DR raises this event for faults outside the code cache that could come from code generated by a client. For such cases, mcontext is not translated and is identical to raw_mcontext.
DR will not raise a signal event for a SIGSEGV or SIGBUS raised by a client code fault rather than the application. Use dr_safe_read(), dr_safe_write(), or DR_TRY_EXCEPT() to prevent such faults.
- Note
siginfo->fault_fragment_info
data is provided withsiginfo->raw_mcontext
. It is valid only ifsiginfo->fault_fragment_info.cache_start_pc
is notNULL
. It provides clients information about the code fragment being executed at the signal interruption point. Clients are cautioned against relying on any details of code cache layout or register usage beyond instrumentation inserted by the client itself.- Only valid on Linux.
- DR always requests SA_SIGINFO for all signals.
- This version of DR does not intercept the signals SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, or SIGTTOU. Future versions should add support for these signals.
- If the client uses signals for its own communication it should set a flag to distinguish its own uses of signals from the application's use. Races where the two are re-ordered should not be problematic.
◆ dr_register_thread_exit_event()
DR_API void dr_register_thread_exit_event | ( | void(*)(void *drcontext) | func | ) |
Registers a callback function for the thread exit event. DR calls func
whenever an application thread exits. The passed-in drcontext should be used instead of calling dr_get_current_drcontext(), as the thread exit event may be invoked from other threads, and using dr_get_current_drcontext() can result in failure to clean up the right resources, and at process exit time it may return NULL.
On Linux, SYS_execve may or may not result in a thread exit event. If the client registers its thread exit callback as a pre-SYS_execve callback as well, it must ensure that the callback acts as noop if called for the second time.
On Linux, the thread exit event may be invoked twice for the same thread if that thread is alive during a process fork, but doesn't call the fork itself. The first time the event callback is executed from the fork child immediately after the fork, the second time it is executed during the regular thread exit. Clients may want to avoid touching resources shared between processes, like files, from the post-fork execution of the callback. The post-fork version of the callback can be recognized by dr_get_process_id() returning a different value than dr_get_process_id_from_drcontext().
See dr_set_process_exit_behavior() for options controlling performance and whether thread exit events are invoked at process exit time in release build.
◆ dr_register_thread_init_event()
DR_API void dr_register_thread_init_event | ( | void(*)(void *drcontext) | func | ) |
Registers a callback function for the thread initialization event. DR calls func
whenever the application creates a new thread.
◆ dr_register_trace_event()
DR_API void dr_register_trace_event | ( | dr_emit_flags_t(*)(void *drcontext, void *tag, instrlist_t *trace, bool translating) | func | ) |
Registers a callback function for the trace event. DR calls func
before inserting a new trace into the code cache. DR may call func
again if it needs to translate from code cache addresses back to application addresses, which happens on faulting instructions as well as in certain situations involving suspended threads or forcibly relocated threads. The translating
parameter distinguishes the two types of calls and behaves identically to the same parameter in the basic block callback: see dr_register_bb_event() for further details.
Traces are not built if the -disable_traces runtime option is specified.
drcontext
is a pointer to the input program's machine context. Clients should not inspect or modify the context; it is provided as an opaque pointer (i.e.,void *
) to be passed to API routines that require access to this internal data.tag
is a unique identifier for the trace fragment.trace
is a pointer to the list of instructions that comprise the trace.translating
indicates whether this callback is for trace creation (false) or is for fault address recreation (true). This is further explained below.
The callback function should return a dr_emit_flags_t flag.
The user is free to inspect and modify the non-control-flow instructions in the trace before it executes, with certain restrictions that include those for basic blocks (see dr_register_bb_event()). Additional restrictions unique to traces also apply:
- The sequence of blocks composing the trace cannot be changed once the trace is created. Instead, modify the component blocks by changing the block continuation addresses in the basic block callbacks (called with
for_trace
set to true) as the trace is being built. - The (application) control flow instruction (if any) terminating each component block cannot be changed.
- Application control flow instructions cannot be added.
- The parameter to a system call, normally kept in the eax register, cannot be changed.
- A system call or interrupt instruction cannot be added.
- If both a floating-point state save instruction (fnstenv, fnsave, fxsave, xsave, or xsaveopt) and a prior regular floating-point instruction are present, the regular instruction cannot be removed.
If hitting a size limit due to extensive instrumentation, reduce the -max_trace_bbs option to start with a smaller trace.
The basic block restrictions on modifying application source code apply to traces as well. If the user wishes to change which basic blocks comprise the trace, either the dr_register_end_trace_event() should be used or the for_trace
basic block callbacks should modify their continuation addresses via direct jumps.
All of the comments for dr_register_bb_event() regarding transparent fault handling and state translation apply to the trace callback as well. Please read those comments carefully.
- Note
- As each basic block is added to a new trace, the basic block callback (see dr_register_bb_event()) is called with its
for_trace
parameter set to true. In order to preserve basic block instrumentation inside of traces, a client need only act identically with respect to thefor_trace
parameter; it can ignore the trace event if its goal is to place instrumentation on all code. - Certain control flow modifications applied to a basic block can prevent it from becoming part of a trace: e.g., adding additional application control transfers.
- If multiple clients are present, the instruction list for a trace passed to earlier-registered clients will contain the instrumentation and modifications put in place by later-registered clients; similarly for each constituent basic block.
- Traces can be deleted due to hitting capacity limits or cache consistency events (when the source application code of a trace is modified). In that case, the client will see a new trace callback if a new trace containing that code is created again after deletion. The deletion event (dr_register_delete_event()) will be raised at deletion time.
◆ dr_set_process_exit_behavior()
DR_API void dr_set_process_exit_behavior | ( | dr_exit_flags_t | flags | ) |
Specifies how process exit should be handled with respect to thread exit events and thread synchronization in release build. In debug build, and in release build by default, all threads are always synchronized at exit time, resulting in a single-threaded process exit event, and all thread exit event callbacks are always called. This routine can provide more performant exits in release build by avoiding the synchronization if the client is willing to skip thread exit events at process exit and is willing to execute its process exit event with multiple live threads.
◆ dr_unregister_bb_event()
DR_API bool dr_unregister_bb_event | ( | dr_emit_flags_t(*)(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) | func | ) |
Unregister a callback function for the basic block event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
- Note
- We do not recommend unregistering for the basic block event unless it aways returned DR_EMIT_STORE_TRANSLATIONS (including when
for_trace
is true, or if the client has a trace creation callback that returns DR_EMIT_STORE_TRANSLATIONS). Unregistering can prevent proper state translation on a later fault or other translation event for this basic block or for a trace that includes this basic block. Instead of unregistering, turn the event callback into a nop.
◆ dr_unregister_clean_call_insertion_event()
DR_API bool dr_unregister_clean_call_insertion_event | ( | void(*)(void *drcontext, instrlist_t *ilist, instr_t *where, dr_cleancall_save_t call_flags) | func | ) |
Unregisters a callback function that was registered with dr_register_call_insertion_event().
◆ dr_unregister_delete_event()
DR_API bool dr_unregister_delete_event | ( | void(*)(void *drcontext, void *tag) | func | ) |
Unregister a callback function for the fragment deletion event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_end_trace_event()
DR_API bool dr_unregister_end_trace_event | ( | dr_custom_trace_action_t(*)(void *drcontext, void *tag, void *next_tag) | func | ) |
Unregister a callback function for the end-trace event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_exception_event()
DR_API bool dr_unregister_exception_event | ( | bool(*)(void *drcontext, dr_exception_t *excpt) | func | ) |
Unregister a callback function for the exception event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_exit_event()
DR_API bool dr_unregister_exit_event | ( | void(*)(void) | func | ) |
Unregister a callback function for the process exit event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_filter_syscall_event()
DR_API bool dr_unregister_filter_syscall_event | ( | bool(*)(void *drcontext, int sysnum) | func | ) |
Unregister a callback function for the syscall filter event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_fork_init_event()
DR_API bool dr_unregister_fork_init_event | ( | void(*)(void *drcontext) | func | ) |
Unregister a callback function for the fork event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_kernel_xfer_event()
DR_API bool dr_unregister_kernel_xfer_event | ( | void(*)(void *drcontext, const dr_kernel_xfer_info_t *info) | func | ) |
Unregister a callback function for the kernel transfer event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_low_on_memory_event()
DR_API bool dr_unregister_low_on_memory_event | ( | void(*)() | func | ) |
Unregister a callback function for low on memory events.
- Returns
- true if unregistration is successful and false if it is not (e.g., the function was not registered).
◆ dr_unregister_module_load_event()
DR_API bool dr_unregister_module_load_event | ( | void(*)(void *drcontext, const module_data_t *info, bool loaded) | func | ) |
Unregister a callback for the module load event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
- Note
- Unregistering for this event is not supported during the basic block event.
◆ dr_unregister_module_unload_event()
DR_API bool dr_unregister_module_unload_event | ( | void(*)(void *drcontext, const module_data_t *info) | func | ) |
Unregister a callback function for the module unload event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_nudge_event()
DR_API bool dr_unregister_nudge_event | ( | void(*)(void *drcontext, uint64 argument) | func, |
client_id_t | id | ||
) |
Unregister a callback function for the nudge event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_persist_patch()
DR_API bool dr_unregister_persist_patch | ( | bool(*)(void *drcontext, void *perscxt, byte *bb_start, size_t bb_size, void *user_data) | func_patch | ) |
Unregister a callback function for patching persisted code.
- Returns
- true if unregistration is successful and false if it is not (e.g., the function was not registered).
◆ dr_unregister_persist_ro()
DR_API bool dr_unregister_persist_ro | ( | size_t(*)(void *drcontext, void *perscxt, size_t file_offs, void **user_data DR_PARAM_OUT) | func_size, |
bool(*)(void *drcontext, void *perscxt, file_t fd, void *user_data) | func_persist, | ||
bool(*)(void *drcontext, void *perscxt, byte **map DR_PARAM_OUT) | func_resurrect | ||
) |
Unregister callback functions for storing read-only data in a persisted cache file.
- Returns
- true if unregistration is successful and false if it is not (e.g., one of the functions was not registered).
◆ dr_unregister_persist_rw()
DR_API bool dr_unregister_persist_rw | ( | size_t(*)(void *drcontext, void *perscxt, size_t file_offs, void **user_data DR_PARAM_OUT) | func_size, |
bool(*)(void *drcontext, void *perscxt, file_t fd, void *user_data) | func_persist, | ||
bool(*)(void *drcontext, void *perscxt, byte **map DR_PARAM_OUT) | func_resurrect | ||
) |
Unregister callback functions for storing writable data in a persisted cache file.
- Returns
- true if unregistration is successful and false if it is not (e.g., one of the functions was not registered).
◆ dr_unregister_persist_rx()
DR_API bool dr_unregister_persist_rx | ( | size_t(*)(void *drcontext, void *perscxt, size_t file_offs, void **user_data DR_PARAM_OUT) | func_size, |
bool(*)(void *drcontext, void *perscxt, file_t fd, void *user_data) | func_persist, | ||
bool(*)(void *drcontext, void *perscxt, byte **map DR_PARAM_OUT) | func_resurrect | ||
) |
Unregister callback functions for storing executable code in a persisted cache file.
- Returns
- true if unregistration is successful and false if it is not (e.g., one of the functions was not registered).
◆ dr_unregister_post_attach_event()
DR_API bool dr_unregister_post_attach_event | ( | void(*)(void) | func | ) |
Unregister a callback function for the post-attach event (see dr_register_post_attach_event()).
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_post_syscall_event()
DR_API bool dr_unregister_post_syscall_event | ( | void(*)(void *drcontext, int sysnum) | func | ) |
Unregister a callback function for the post-syscall event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_pre_detach_event()
DR_API bool dr_unregister_pre_detach_event | ( | void(*)(void) | func | ) |
Unregister a callback function for the post-attach event (see dr_register_pre_detach_event()).
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_pre_syscall_event()
DR_API bool dr_unregister_pre_syscall_event | ( | bool(*)(void *drcontext, int sysnum) | func | ) |
Unregister a callback function for the pre-syscall event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_restore_state_event()
DR_API bool dr_unregister_restore_state_event | ( | void(*)(void *drcontext, void *tag, dr_mcontext_t *mcontext, bool restore_memory, bool app_code_consistent) | func | ) |
Unregister a callback function for the machine state restoration event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_restore_state_ex_event()
DR_API bool dr_unregister_restore_state_ex_event | ( | bool(*)(void *drcontext, bool restore_memory, dr_restore_state_info_t *info) | func | ) |
Unregister a callback function for the machine state restoration event with extended ifnormation.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_signal_event()
DR_API bool dr_unregister_signal_event | ( | dr_signal_action_t(*)(void *drcontext, dr_siginfo_t *siginfo) | func | ) |
Unregister a callback function for the signal event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_thread_exit_event()
DR_API bool dr_unregister_thread_exit_event | ( | void(*)(void *drcontext) | func | ) |
Unregister a callback function for the thread exit event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_thread_init_event()
DR_API bool dr_unregister_thread_init_event | ( | void(*)(void *drcontext) | func | ) |
Unregister a callback function for the thread initialization event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
◆ dr_unregister_trace_event()
DR_API bool dr_unregister_trace_event | ( | dr_emit_flags_t(*)(void *drcontext, void *tag, instrlist_t *trace, bool translating) | func | ) |
Unregister a callback function for the trace event.
- Returns
- true if unregistration is successful and false if it is not (e.g.,
func
was not registered).
- Note
- We do not recommend unregistering for the trace event unless it always returned DR_EMIT_STORE_TRANSLATIONS, as doing so can prevent proper state translation on a later fault or other translation event. Instead of unregistering, turn the event callback into a nop.