DynamoRIO
|
Macros | |
#define | DRMGR_PRIORITY_NAME_DRX_FAULT "drx_fault" |
#define | DRX_FILE_SKIP_OPEN 0x8000 |
#define | DRMGR_PRIORITY_NAME_DRX_BUF_INIT "drx_buf.init" |
#define | DRMGR_PRIORITY_NAME_DRX_BUF_EXIT "drx_buf.exit" |
Typedefs | |
typedef void(* | drx_buf_full_cb_t) (void *drcontext, void *buf_base, size_t size) |
typedef struct _drx_buf_t | drx_buf_t |
Enumerations | |
enum | { DRMGR_PRIORITY_FAULT_DRX = -7500 } |
enum | { DRX_COUNTER_64BIT = 0x01, DRX_COUNTER_REL_ACQ = 0x02, DRX_COUNTER_LOCK = 0x10 } |
enum | { DRX_BUF_FAST_CIRCULAR_BUFSZ = (1 << 16) } |
enum | { DRMGR_PRIORITY_THREAD_INIT_DRX_BUF = -7500, DRMGR_PRIORITY_THREAD_EXIT_DRX_BUF = -7500 } |
Functions | |
DR_EXPORT bool | drx_init (void) |
DR_EXPORT void | drx_exit (void) |
DR_EXPORT ptr_uint_t | drx_reserve_note_range (size_t size) |
DR_EXPORT bool | drx_aflags_are_dead (instr_t *where) |
DR_EXPORT bool | drx_insert_counter_update (void *drcontext, instrlist_t *ilist, instr_t *where, dr_spill_slot_t slot, IF_AARCHXX_OR_RISCV64_(dr_spill_slot_t slot2) void *addr, int value, uint flags) |
DR_EXPORT bool | drx_register_soft_kills (bool(*event_cb)(process_id_t pid, int exit_code)) |
DR_EXPORT size_t | drx_instrlist_size (instrlist_t *ilist) |
DR_EXPORT size_t | drx_instrlist_app_size (instrlist_t *ilist) |
DR_EXPORT file_t | drx_open_unique_file (const char *dir, const char *prefix, const char *suffix, uint extra_flags, char *result DR_PARAM_OUT, size_t result_len) |
DR_EXPORT file_t | drx_open_unique_appid_file (const char *dir, ptr_int_t id, const char *prefix, const char *suffix, uint extra_flags, char *result DR_PARAM_OUT, size_t result_len) |
DR_EXPORT bool | drx_open_unique_appid_dir (const char *dir, ptr_int_t id, const char *prefix, const char *suffix, char *result DR_PARAM_OUT, size_t result_len) |
DR_EXPORT drx_buf_t * | drx_buf_create_circular_buffer (size_t buf_size) |
DR_EXPORT drx_buf_t * | drx_buf_create_trace_buffer (size_t buffer_size, drx_buf_full_cb_t full_cb) |
DR_EXPORT bool | drx_buf_free (drx_buf_t *buf) |
DR_EXPORT void | drx_buf_insert_load_buf_ptr (void *drcontext, drx_buf_t *buf, instrlist_t *ilist, instr_t *where, reg_id_t buf_ptr) |
DR_EXPORT void | drx_buf_insert_update_buf_ptr (void *drcontext, drx_buf_t *buf, instrlist_t *ilist, instr_t *where, reg_id_t buf_ptr, reg_id_t scratch, ushort stride) |
DR_EXPORT bool | drx_buf_insert_buf_store (void *drcontext, drx_buf_t *buf, instrlist_t *ilist, instr_t *where, reg_id_t buf_ptr, reg_id_t scratch, opnd_t opnd, opnd_size_t opsz, short offset) |
DR_EXPORT void * | drx_buf_get_buffer_ptr (void *drcontext, drx_buf_t *buf) |
DR_EXPORT void | drx_buf_set_buffer_ptr (void *drcontext, drx_buf_t *buf, void *new_ptr) |
DR_EXPORT void * | drx_buf_get_buffer_base (void *drcontext, drx_buf_t *buf) |
DR_EXPORT size_t | drx_buf_get_buffer_size (void *drcontext, drx_buf_t *buf) |
DR_EXPORT bool | drx_tail_pad_block (void *drcontext, instrlist_t *ilist) |
DR_EXPORT void | drx_buf_insert_buf_memcpy (void *drcontext, drx_buf_t *buf, instrlist_t *ilist, instr_t *where, reg_id_t dst, reg_id_t src, ushort len) |
DR_EXPORT bool | drx_expand_scatter_gather (void *drcontext, instrlist_t *bb, DR_PARAM_OUT bool *expanded) |
Detailed Description
Macro Definition Documentation
◆ DRMGR_PRIORITY_NAME_DRX_BUF_EXIT
#define DRMGR_PRIORITY_NAME_DRX_BUF_EXIT "drx_buf.exit" |
Name of drx_buf thread exit priority for buffer cleanup and full_cb callback.
◆ DRMGR_PRIORITY_NAME_DRX_BUF_INIT
#define DRMGR_PRIORITY_NAME_DRX_BUF_INIT "drx_buf.init" |
Name of drx_buf thread init priority for buffer initialization.
◆ DRMGR_PRIORITY_NAME_DRX_FAULT
#define DRMGR_PRIORITY_NAME_DRX_FAULT "drx_fault" |
Name of drx fault handling event.
◆ DRX_FILE_SKIP_OPEN
#define DRX_FILE_SKIP_OPEN 0x8000 |
Flag for use with drx_open_unique_file() or drx_open_unique_appid_file() in extra_flags
to skip the file open and get the path string only.
- Note
- This flag value must not conflict with any DR_FILE_* flag value used by dr_open_file().
Typedef Documentation
◆ drx_buf_full_cb_t
typedef void(* drx_buf_full_cb_t) (void *drcontext, void *buf_base, size_t size) |
Callback for drx_buf_init_trace_buffer()
, called when the buffer has been filled. The valid buffer data is contained within the interval [buf_base..buf_base+size).
◆ drx_buf_t
typedef struct _drx_buf_t drx_buf_t |
Opaque handle which represents a buffer for use by the drx_buf framework.
Enumeration Type Documentation
◆ anonymous enum
◆ anonymous enum
anonymous enum |
◆ anonymous enum
anonymous enum |
Enumerator | |
---|---|
DRX_BUF_FAST_CIRCULAR_BUFSZ | Buffer size to be specified in drx_buf_create_circular_buffer() in order to make use of the fast circular buffer optimization. |
◆ anonymous enum
anonymous enum |
Priorities of drmgr instrumentation passes used by drx_buf. Users of drx_buf can use the names DRMGR_PRIORITY_NAME_DRX_BUF_INIT and DRMGR_PRIORITY_NAME_DRX_BUF_EXIT 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_THREAD_INIT_DRX_BUF | Priority of drx_buf thread init event |
DRMGR_PRIORITY_THREAD_EXIT_DRX_BUF | Priority of drx_buf thread exit event |
Function Documentation
◆ drx_aflags_are_dead()
DR_EXPORT bool drx_aflags_are_dead | ( | instr_t * | where | ) |
Analyze if arithmetic flags are dead after (including) instruction where
.
- Note
- May be called without calling drx_init().
◆ drx_buf_create_circular_buffer()
DR_EXPORT drx_buf_t* drx_buf_create_circular_buffer | ( | size_t | buf_size | ) |
Initializes the drx_buf extension with a circular buffer which wraps around when full.
- Note
- All buffer sizes are supported. However, a buffer size of DRX_BUF_FAST_CIRCULAR_BUFSZ bytes is specially optimized for performance. This buffer is referred to explicitly in the documentation as the "fast circular buffer".
- Returns
- NULL if unsuccessful, a valid opaque struct pointer if successful.
◆ drx_buf_create_trace_buffer()
DR_EXPORT drx_buf_t* drx_buf_create_trace_buffer | ( | size_t | buffer_size, |
drx_buf_full_cb_t | full_cb | ||
) |
Initializes the drx_buf extension with a buffer; full_cb
is called when the buffer becomes full.
- Returns
- NULL if unsuccessful, a valid opaque struct pointer if successful.
◆ drx_buf_free()
DR_EXPORT bool drx_buf_free | ( | drx_buf_t * | buf | ) |
Cleans up the buffer associated with buf
.
- Returns
- whether successful.
◆ drx_buf_get_buffer_base()
DR_EXPORT void* drx_buf_get_buffer_base | ( | void * | drcontext, |
drx_buf_t * | buf | ||
) |
Retrieves a pointer to the base of the buffer.
◆ drx_buf_get_buffer_ptr()
DR_EXPORT void* drx_buf_get_buffer_ptr | ( | void * | drcontext, |
drx_buf_t * | buf | ||
) |
Retrieves a pointer to the top of the buffer, that is, returns the same value as would an invocation of drx_buf_insert_load_buf_ptr().
◆ drx_buf_get_buffer_size()
DR_EXPORT size_t drx_buf_get_buffer_size | ( | void * | drcontext, |
drx_buf_t * | buf | ||
) |
Retrieves the capacity of the buffer.
◆ drx_buf_insert_buf_memcpy()
DR_EXPORT void drx_buf_insert_buf_memcpy | ( | void * | drcontext, |
drx_buf_t * | buf, | ||
instrlist_t * | ilist, | ||
instr_t * | where, | ||
reg_id_t | dst, | ||
reg_id_t | src, | ||
ushort | len | ||
) |
Constructs a memcpy-like operation that is compatible with drx_buf.
- Note
- drx_buf_insert_buf_memcpy() will increment the buffer pointer internally.
◆ drx_buf_insert_buf_store()
DR_EXPORT bool drx_buf_insert_buf_store | ( | void * | drcontext, |
drx_buf_t * | buf, | ||
instrlist_t * | ilist, | ||
instr_t * | where, | ||
reg_id_t | buf_ptr, | ||
reg_id_t | scratch, | ||
opnd_t | opnd, | ||
opnd_size_t | opsz, | ||
short | offset | ||
) |
Inserts instructions to store opsz
bytes of opnd
at offset
bytes from buf_ptr
. opnd
must be a register or an immediate opnd of some appropriate size.
- Returns
- whether successful.
- Note
opsz
must be eitherOPSZ_1
,OPSZ_2
,OPSZ_4
orOPSZ_8
.-
scratch
is only necessary on ARM when storing an immediate operand. -
This method simply wraps a store that also sets an app translation. Make sure that
where
has a translation set.
◆ drx_buf_insert_load_buf_ptr()
DR_EXPORT void drx_buf_insert_load_buf_ptr | ( | void * | drcontext, |
drx_buf_t * | buf, | ||
instrlist_t * | ilist, | ||
instr_t * | where, | ||
reg_id_t | buf_ptr | ||
) |
Inserts instructions to load the address of the TLS buffer at where
into buf_ptr
.
◆ drx_buf_insert_update_buf_ptr()
DR_EXPORT void drx_buf_insert_update_buf_ptr | ( | void * | drcontext, |
drx_buf_t * | buf, | ||
instrlist_t * | ilist, | ||
instr_t * | where, | ||
reg_id_t | buf_ptr, | ||
reg_id_t | scratch, | ||
ushort | stride | ||
) |
Inserts instructions to increment the buffer pointer by stride
to accommodate the writes that occurred since the last time the base pointer was loaded.
- Note
scratch
is only necessary on ARM, in the case of the fast circular buffer. On x86 scratch is completely unused.
◆ drx_buf_set_buffer_ptr()
DR_EXPORT void drx_buf_set_buffer_ptr | ( | void * | drcontext, |
drx_buf_t * | buf, | ||
void * | new_ptr | ||
) |
Allows one to set the buffer pointer so that subsequent invocations of drx_buf_insert_load_buf_ptr() will use this new value instead.
◆ drx_exit()
DR_EXPORT void drx_exit | ( | void | ) |
Cleans up the drx extension.
◆ drx_expand_scatter_gather()
DR_EXPORT bool drx_expand_scatter_gather | ( | void * | drcontext, |
instrlist_t * | bb, | ||
DR_PARAM_OUT bool * | expanded | ||
) |
Expands AVX2 gather and AVX-512 gather and scatter instructions to a sequence of equivalent scalar load and stores, mask register bit tests, and mask register bit updates.
Clients applying this expansion are encouraged to use emulation-aware instrumentation via drmgr_orig_app_instr_for_fetch() and drmgr_orig_app_instr_for_operands() in order to observe the original opcode with the expanded memory operands.
- Warning
- The added multi-instruction sequence contains several control-transfer instructions and is not straight-line code, which can complicate subsequent analysis routines.
The client must use the drmgr
Extension to order its instrumentation in order to use this function. This function must be called from the application-to-application ("app2app") stage (see drmgr_register_bb_app2app_event()).
This transformation is deterministic, so the caller can return DR_EMIT_DEFAULT from its event.
The *dq, *qd, *qq, *dpd, *qps, and *qpd opcodes are not yet supported in 32-bit mode. In this case, the function will return false and no expansion will occur.
- Parameters
-
[in] drcontext The opaque context. [in] bb Instruction list passed to the app2app event. [out] expanded Whether any expansion occurred.
- Returns
- whether successful.
◆ drx_init()
DR_EXPORT bool drx_init | ( | void | ) |
Initializes the drx extension. Must be called prior to any drx routine that does not explicitly state otherwise. Can be called multiple times (by separate components, normally) but each call must be paired with a corresponding call to drx_exit().
- Returns
- whether successful.
◆ drx_insert_counter_update()
DR_EXPORT bool drx_insert_counter_update | ( | void * | drcontext, |
instrlist_t * | ilist, | ||
instr_t * | where, | ||
dr_spill_slot_t | slot, | ||
IF_AARCHXX_OR_RISCV64_(dr_spill_slot_t slot2) void * | addr, | ||
int | value, | ||
uint | flags | ||
) |
Inserts into ilist
prior to where
meta-instruction(s) to add the constant value
to the counter located at addr
. Different DRX_COUNTER_* options can be specified by flags
.
When used with drmgr, this routine uses the drreg extension. It must be called from drmgr's insertion phase. The drreg extension will be used to spill the arithmetic flags and any scratch registers needed. It is up to the caller to ensure that enough spill slots are available, through drreg's initialization. The slot and slot2 parameters must be set to SPILL_SLOT_MAX+1.
When used without drmgr, the spill slot slot
is used for storing arithmetic flags or a scratch register if necessary. The spill slot slot2
is used only on ARM for spilling a second scratch register.
- Returns
- whether successful.
- Note
- The counter update is racy (i.e., not synchronized among threads) unless DRX_COUNTER_LOCK is specified in
flags
. When DRX_COUNTER_LOCK is set, the instrumentation may fail if a 64-bit counter is updated in a 32-bit application or the counter crosses cache lines. Currently, DRX_COUNTER_LOCK is not yet supported on AArchXX. For AArchXX, if DRX_COUNTER_REL_ACQ is specified inflags
, release-acquire semantics are enforced for the counter update. The DRX_COUNTER_REL_ACQ flag can be used in conjunction with DRX_COUNTER_64BIT. -
To update multiple counters at the same place, multiple drx_insert_counter_update() invocations should be made in a row with the same
where
instruction and no other instructions should be inserted in between. In that case,drx
will try to merge the instrumentation for better performance.
◆ drx_instrlist_app_size()
DR_EXPORT size_t drx_instrlist_app_size | ( | instrlist_t * | ilist | ) |
Returns the number of application instructions (excluding meta-instructions) inside a basic block ilist
.
The function iterates over the ilist in order to obtain the count. The result is not cached. Therefore, avoid using this function during the insert stage of the instrumentation process.
◆ drx_instrlist_size()
DR_EXPORT size_t drx_instrlist_size | ( | instrlist_t * | ilist | ) |
Returns the number of instructions (including meta-instructions) inside a basic block ilist
.
The function iterates over the ilist in order to obtain the count. The result is not cached. Therefore, avoid using this function during the insert stage of the instrumentation process.
◆ drx_open_unique_appid_dir()
DR_EXPORT bool drx_open_unique_appid_dir | ( | const char * | dir, |
ptr_int_t | id, | ||
const char * | prefix, | ||
const char * | suffix, | ||
char *result | DR_PARAM_OUT, | ||
size_t | result_len | ||
) |
Creates a new directory with a name constructed from "dir/prefix.appname.id.xxxx.suffix", where xxxx is a 4-digit number incremented until a unique name is found that does not collide with any existing file. The appname string comes from dr_get_application_name(). The id portion of the string is from id
, which is meant to be either the process id or the thread id.
Returns whether successful. On success, optionally returns the resulting path in result
.
- Note
- May be called without calling drx_init().
◆ drx_open_unique_appid_file()
DR_EXPORT file_t drx_open_unique_appid_file | ( | const char * | dir, |
ptr_int_t | id, | ||
const char * | prefix, | ||
const char * | suffix, | ||
uint | extra_flags, | ||
char *result | DR_PARAM_OUT, | ||
size_t | result_len | ||
) |
Opens a new file with a name constructed from "dir/prefix.appname.id.xxxx.suffix", where xxxx is a 4-digit number incremented until a unique name is found that does not collide with any existing file. The appname string comes from dr_get_application_name(). The id portion of the string is from id
, which is meant to be either the process id or the thread id.
Passes extra_flags
through to the dr_open_file() call if extra_flags
is not DRX_FILE_SKIP_OPEN.
On success, returns the file handle and optionally the resulting path in result
. On failure, returns INVALID_FILE.
Skips dr_open_file() if extra_flags
is DRX_FILE_SKIP_OPEN. Returns INVALID_FILE and optionally the resulting path in result
. Unique name is not guaranteed and xxxx is set randomly.
- Note
- May be called without calling drx_init().
◆ drx_open_unique_file()
DR_EXPORT file_t drx_open_unique_file | ( | const char * | dir, |
const char * | prefix, | ||
const char * | suffix, | ||
uint | extra_flags, | ||
char *result | DR_PARAM_OUT, | ||
size_t | result_len | ||
) |
Opens a new file with a name constructed from "dir/prefix.xxxx.suffix", where xxxx is a 4-digit number incremented until a unique name is found that does not collide with any existing file.
Passes extra_flags
through to the dr_open_file() call if extra_flags
is not DRX_FILE_SKIP_OPEN.
On success, returns the file handle and optionally the resulting path in result
. On failure, returns INVALID_FILE.
Skips dr_open_file() if extra_flags
is DRX_FILE_SKIP_OPEN. Returns INVALID_FILE and optionally the resulting path in result
. Unique name is not guaranteed and xxxx is set randomly.
- Note
- May be called without calling drx_init().
◆ drx_register_soft_kills()
DR_EXPORT bool drx_register_soft_kills | ( | bool(*)(process_id_t pid, int exit_code) | event_cb | ) |
Registers for the "soft kills" event, which helps to execute process exit events when a process is terminated by another process.
The callback's return value indicates whether to skip the termination action by the application: i.e., true indicates to skip it (the usual case) and false indicates to continue with the application action. In some cases, individually continuing requires emulation when the original application action involved multiple processes.
When there are multiple registered callbacks, if any callback returns true, the application action is skipped.
In normal usage, upon receiving this callback the client will send a nudge (see dr_nudge_client_ex()) to the targeted process. The nudge handler then performs any shutdown actions, such as instrumentation result output. The handler then terminates the target process from within, allowing the callback in the targeting process to skip the termination action. Passing the exit code to the nudge handler is recommended to preserve the intended application exit code.
The nudge handler should support being invoked multiple times (typically by having only the first one take effect) as in some cases a parent process will terminate child processes in multiple ways.
This event must be registered for during process initialization, in order to properly track per-thread information. Un-registering is not supported: soft kills cannot be in effect for only part of the process lifetime.
Soft kills can be risky. If the targeted process is not under DynamoRIO control, the nudge might terminate it, but in a different manner than would have occurred. If the nudge fails for some reason but the targeter's termination is still skipped, the child process might be left alive, causing the application to behave incorrectly.
The implementation of this event uses drmgr's CLS (drmgr_register_cls_field()), which conflicts with dr_get_tls_field(). A client using this event must use drmgr_register_tls_field() instead of dr_get_tls_field().
- Returns
- whether successful.
◆ drx_reserve_note_range()
DR_EXPORT ptr_uint_t drx_reserve_note_range | ( | size_t | size | ) |
Reserves size
values in the namespace for use in the note
field of instructions. The reserved range starts at the return value and is contiguous. Returns DRX_NOTE_NONE on failure. Un-reserving is not supported.
◆ drx_tail_pad_block()
DR_EXPORT bool drx_tail_pad_block | ( | void * | drcontext, |
instrlist_t * | ilist | ||
) |
Pads a basic block with a label at the end for routines which rely on inserting instrumentation after every instruction. Note that users of this routine must act on the previous instruction in basic block events before skipping non-app instructions because the label is not marked as an app instruction.
- Note
- the padding label is not introduced if the basic block is already branch terminated.
- Returns
- whether padding was introduced.