Skip to content

Commit

Permalink
i#4128: Add cleancall reg read/write flags
Browse files Browse the repository at this point in the history
Adds new dr_cleancall_save_t flags which are required for proper
interaction between clean calls and drreg:
DR_CLEANCALL_READS_APP_CONTEXT must be set for dr_get_mcontext() to
obtain the proper values, and #DR_CLEANCALL_WRITES_APP_CONTEXT must be
set to ensure that dr_set_mcontext() is persistent.

Adds a clean call insertion event to enable drreg to know about clean
calls at the time they are inserted.  dr_insert_clean_call_ex()
invokes the callback and passes the flags to drreg, who then treats
the clean call as an app instruction.

For annotations, for now we leave drreg looking for the annotation
label (possible future changes #5160 or #5161 would eliminate this
special case).

dr_insert_{cbr,ubr,mbr,call}_instrumentation() always set both labels.

drwrap always sets both labels for pre and post callbacks.

Updates uses throughout our tests and samples to use the new flags as
appropriate.

Adds a new dedicated test client.drwrap-drreg-test which tests both a
drwrap call and a direct clean call.

Fixes a missing drwrap cache invalidation on module unload that the
new test uncovers.

Fixes #4128
  • Loading branch information
derekbruening committed Oct 14, 2021
1 parent 64b15ab commit 6e65d8a
Show file tree
Hide file tree
Showing 28 changed files with 897 additions and 220 deletions.
6 changes: 6 additions & 0 deletions api/docs/release.dox
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,12 @@ Further non-compatibility-affecting changes include:
This is currently an experimental option.
- Added \ref page_drcallstack Extension for walking application callstacks, with
an initial Linux-only implementation.
- Added new #dr_cleancall_save_t flags which are required for proper interaction
between clean calls and drreg: #DR_CLEANCALL_READS_APP_CONTEXT must be set for
dr_get_mcontext() to obtain the proper values, and #DR_CLEANCALL_WRITES_APP_CONTEXT
must be set to ensure that dr_set_mcontext() is persistent.
- Added a new event dr_register_clean_call_insertion_event(), meant for use by
register management libraries.

**************************************************
<hr>
Expand Down
18 changes: 9 additions & 9 deletions api/samples/cbr.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* **********************************************************
* Copyright (c) 2014-2020 Google, Inc. All rights reserved.
* Copyright (c) 2014-2021 Google, Inc. All rights reserved.
* Copyright (c) 2008 VMware, Inc. All rights reserved.
* **********************************************************/

Expand Down Expand Up @@ -354,10 +354,10 @@ event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst
/* Callout for the not-taken case. Insert after
* the cbr (i.e., 3rd argument is NULL).
*/
dr_insert_clean_call(drcontext, bb, NULL, (void *)at_not_taken,
false /* don't save fp state */,
2 /* 2 args for at_not_taken */, OPND_CREATE_INTPTR(src),
OPND_CREATE_INTPTR(fall));
dr_insert_clean_call_ex(drcontext, bb, NULL, (void *)at_not_taken,
DR_CLEANCALL_READS_APP_CONTEXT,
2 /* 2 args for at_not_taken */,
OPND_CREATE_INTPTR(src), OPND_CREATE_INTPTR(fall));
}

/* After the callout, jump to the original fallthrough
Expand All @@ -380,10 +380,10 @@ event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst

if (insert_taken) {
/* Callout for the taken case */
dr_insert_clean_call(drcontext, bb, NULL, (void *)at_taken,
false /* don't save fp state */,
2 /* 2 args for at_taken */, OPND_CREATE_INTPTR(src),
OPND_CREATE_INTPTR(targ));
dr_insert_clean_call_ex(drcontext, bb, NULL, (void *)at_taken,
DR_CLEANCALL_READS_APP_CONTEXT,
2 /* 2 args for at_taken */, OPND_CREATE_INTPTR(src),
OPND_CREATE_INTPTR(targ));
}

/* After the callout, jump to the original target
Expand Down
38 changes: 7 additions & 31 deletions clients/drcachesim/tracer/tracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1708,34 +1708,9 @@ event_delay_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t
}
# endif

/* hit_instr_count_threshold does not always return. Restore scratch registers and
* aflags.
*/
# ifdef X86_64
/* FIXME i#4711: Need to restore for x86 the arithmetic flags and (if used) the
* scratch register before the call to hit_instr_count_threshold. However, this
* fix seems to cause instability. So, we're leaving x86 as technically broken to
* keep our tests green until the source of instability is found.
*/
# ifndef DISABLED_FOR_BUG_4711
drreg_statelessly_restore_app_value(drcontext, bb, DR_REG_NULL, instr, instr, NULL,
NULL);
if (scratch != DR_REG_NULL) {
drreg_statelessly_restore_app_value(drcontext, bb, scratch, instr, instr, NULL,
NULL);
}
# endif
# elif defined(AARCH64)
drreg_statelessly_restore_app_value(drcontext, bb, scratch1, instr, instr, NULL,
NULL);
if (scratch2 != DR_REG_NULL) {
drreg_statelessly_restore_app_value(drcontext, bb, scratch2, instr, instr, NULL,
NULL);
}
# endif
dr_insert_clean_call(drcontext, bb, instr, (void *)hit_instr_count_threshold,
false /*fpstate */, 1,
OPND_CREATE_INTPTR((ptr_uint_t)instr_get_app_pc(instr)));
dr_insert_clean_call_ex(drcontext, bb, instr, (void *)hit_instr_count_threshold,
DR_CLEANCALL_READS_APP_CONTEXT, 1,
OPND_CREATE_INTPTR((ptr_uint_t)instr_get_app_pc(instr)));
MINSERT(bb, instr, skip_call);

# ifdef X86_64
Expand All @@ -1759,9 +1734,10 @@ event_delay_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t
* inlining of check_instr_count_threshold is not implemented for i386. For now we pay
* the cost of a clean call every time for 32-bit architectures.
*/
dr_insert_clean_call(drcontext, bb, instr, (void *)check_instr_count_threshold,
false /*fpstate */, 2, OPND_CREATE_INT32(num_instrs),
OPND_CREATE_INTPTR((ptr_uint_t)instr_get_app_pc(instr)));
dr_insert_clean_call_ex(drcontext, bb, instr, (void *)check_instr_count_threshold,
DR_CLEANCALL_READS_APP_CONTEXT, 2,
OPND_CREATE_INT32(num_instrs),
OPND_CREATE_INTPTR((ptr_uint_t)instr_get_app_pc(instr)));
#endif
return DR_EMIT_DEFAULT;
}
Expand Down
9 changes: 6 additions & 3 deletions clients/drcpusim/drcpusim.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* ******************************************************************************
* Copyright (c) 2015-2017 Google, Inc. All rights reserved.
* Copyright (c) 2015-2021 Google, Inc. All rights reserved.
* ******************************************************************************/

/*
Expand Down Expand Up @@ -756,8 +756,11 @@ event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst
dr_save_reg(drcontext, bb, instr, DR_REG_XCX, SPILL_SLOT_2);
// XXX: technically drmgr doesn't want us inserting instrs *after* the
// app instr but this is the simplest way to go.
dr_insert_clean_call(drcontext, bb, instr_get_next(instr), (void *)fake_cpuid,
false, 0);
dr_insert_clean_call_ex(
drcontext, bb, instr_get_next(instr), (void *)fake_cpuid,
static_cast<dr_cleancall_save_t>(DR_CLEANCALL_READS_APP_CONTEXT |
DR_CLEANCALL_WRITES_APP_CONTEXT),
0);
}
#endif
return DR_EMIT_DEFAULT;
Expand Down
6 changes: 6 additions & 0 deletions core/annotations.c
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,9 @@ instrument_annotation(dcontext_t *dcontext, IN OUT app_pc *start_pc,
* The placeholder is "ok to mangle" because it (partially)
* implements the app's annotation. The placeholder will be
* removed post-client during mangling.
* We only support writing the return value and no other registers
* (otherwise we'd need drmgr to pass DR_CLEANCALL_WRITES_APP_CONTEXT
* to the cleancall callback).
*/
instr_t *return_placeholder =
INSTR_XL8(INSTR_CREATE_mov_st(dcontext, opnd_create_reg(REG_XAX),
Expand Down Expand Up @@ -476,6 +479,9 @@ instrument_valgrind_annotation(dcontext_t *dcontext, instrlist_t *bb, instr_t *x
/* Append `mov $0x0,%edx` so that clients and tools recognize that %xdx will be
* written here. The placeholder is "ok to mangle" because it (partially) implements
* the app's annotation. The placeholder will be removed post-client during mangling.
* We only support writing the return value and no other registers (otherwise
* we'd need drmgr to pass DR_CLEANCALL_WRITES_APP_CONTEXT to the cleancall
* callback).
*/
return_placeholder = INSTR_XL8(
INSTR_CREATE_mov_st(dcontext, opnd_create_reg(REG_XDX), OPND_CREATE_INT32(0)),
Expand Down
12 changes: 8 additions & 4 deletions core/arch/x86/mangle.c
Original file line number Diff line number Diff line change
Expand Up @@ -3058,10 +3058,14 @@ mangle_annotation_helper(dcontext_t *dcontext, instr_t *label, instrlist_t *ilis
dcontext, (ptr_int_t)pc, dr_reg_spill_slot_opnd(dcontext, SPILL_SLOT_2),
ilist, label, NULL, NULL);
}
dr_insert_clean_call_ex_varg(dcontext, ilist, label,
receiver->instrumentation.callback,
receiver->save_fpstate ? DR_CLEANCALL_SAVE_FLOAT : 0,
handler->num_args, args);
dr_insert_clean_call_ex_varg(
dcontext, ilist, label, receiver->instrumentation.callback,
(receiver->save_fpstate ? DR_CLEANCALL_SAVE_FLOAT : 0) |
/* Setting a return value is already handled with an inserted app
* insruction, so we do not see the WRITE_CONTEXT flag.
*/
DR_CLEANCALL_READS_APP_CONTEXT,
handler->num_args, args);
if (handler->num_args != 0) {
HEAP_ARRAY_FREE(dcontext, args, opnd_t, handler->num_args, ACCT_CLEANCALL,
UNPROTECTED);
Expand Down
8 changes: 4 additions & 4 deletions core/ir/arm/table_a32_pred.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* **********************************************************
* Copyright (c) 2014-2018 Google, Inc. All rights reserved.
* Copyright (c) 2014-2021 Google, Inc. All rights reserved.
* **********************************************************/

/*
Expand Down Expand Up @@ -714,9 +714,9 @@ const instr_info_t A32_ext_opc4[][16] = {
{OP_strd, 0x010000f0, "strd", MNRq, xx, RBEw, RB2w, xx, pred, x, top4[3][0x0f]},/*PUW=100*/
}, { /* 1 */
{EXT_BIT9, 0x01200000, "(ext bit9 2)", xx, xx, xx, xx, xx, no, x, 2},
{OP_bx, 0x01200010, "bx", xx, xx, RDw, xx, xx, pred, x, END_LIST},
{OP_bxj, 0x01200020, "bxj", xx, xx, RDw, xx, xx, pred, x, END_LIST},
{OP_blx_ind, 0x01200030, "blx", LRw, xx, RDw, xx, xx, pred, x, END_LIST},
{OP_bx, 0x012fff10, "bx", xx, xx, RDw, xx, xx, pred, x, END_LIST},
{OP_bxj, 0x012fff20, "bxj", xx, xx, RDw, xx, xx, pred, x, END_LIST},
{OP_blx_ind, 0x012fff30, "blx", LRw, xx, RDw, xx, xx, pred, x, END_LIST},
{EXT_BIT9, 0x01200040, "(ext bit9 3)", xx, xx, xx, xx, xx, no, x, 3},
{OP_qsub, 0x01200050, "qsub", RBw, xx, RDw, RAw, xx, pred, x, END_LIST},
{INVALID, 0x01200060, "(bad)", xx, xx, xx, xx, xx, no, x, NA},
Expand Down
22 changes: 22 additions & 0 deletions core/lib/dr_events.h
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,28 @@ DR_API
bool
dr_unregister_low_on_memory_event(void (*func)());

DR_API
/**
* 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(). The callback is also invoked at points where
* a clean call will be inserted after instrumentation, such as for annotations.
*/
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
/**
* Unregisters a callback function that was registered with
* dr_register_call_insertion_event().
*/
bool
dr_unregister_clean_call_insertion_event(void (*func)(void *drcontext, instrlist_t *ilist,
instr_t *where,
dr_cleancall_save_t call_flags));

/****************************************************************************
* SECURITY SUPPORT
*/
Expand Down
71 changes: 24 additions & 47 deletions core/lib/dr_ir_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,9 +369,14 @@ DR_API
* and will be copied into the proper location for that argument
* slot as specified by the calling convention.
*
* Stores the application state information on the DR stack, where it can
* be accessed from \c callee using dr_get_mcontext() and modified using
* dr_set_mcontext().
* Stores the application state information on the DR stack, where it can be
* accessed from \c callee using dr_get_mcontext() and modified using
* dr_set_mcontext(). However, if register reservation code is in use (e.g.,
* via the drreg extension library: \ref page_drreg), dr_insert_clean_call_ex()
* must be called with its flags argument including
* #DR_CLEANCALL_READS_APP_CONTEXT (for dr_get_mcontext() use) and/or
* #DR_CLEANCALL_WRITES_APP_CONTEXT (for dr_set_mcontext() use) to ensure proper
* interaction with register reservations.
*
* On x86, if \p save_fpstate is true, preserves the x87 floating-point and
* MMX state on the
Expand Down Expand Up @@ -434,48 +439,6 @@ void
dr_insert_clean_call(void *drcontext, instrlist_t *ilist, instr_t *where, void *callee,
bool save_fpstate, uint num_args, ...);

/**
* Flags to request non-default preservation of state in a clean call
* as well as other call options.
*/
typedef enum {
/**
* Save legacy floating-point state (x86-specific; not saved by default).
* The last floating-point instruction address (FIP) in the saved state is
* left in an untranslated state (i.e., it may point into the code cache).
* This flag is orthogonal to the saving of SIMD registers and related flags below.
*/
DR_CLEANCALL_SAVE_FLOAT = 0x0001,
/**
* Skip saving the flags and skip clearing the flags (including
* DF) for client execution. Note that this can cause problems
* if dr_redirect_execution() is called from a clean call,
* as an uninitialized flags value can cause subtle errors.
*/
DR_CLEANCALL_NOSAVE_FLAGS = 0x0002,
/** Skip saving any XMM or YMM registers (saved by default). */
DR_CLEANCALL_NOSAVE_XMM = 0x0004,
/** Skip saving any XMM or YMM registers that are never used as parameters. */
DR_CLEANCALL_NOSAVE_XMM_NONPARAM = 0x0008,
/** Skip saving any XMM or YMM registers that are never used as return values. */
DR_CLEANCALL_NOSAVE_XMM_NONRET = 0x0010,
/**
* Requests that an indirect call be used to ensure reachability, both for
* reaching the callee and for any out-of-line helper routine calls.
* Only honored for 64-bit mode, where r11 will be used for the indirection.
*/
DR_CLEANCALL_INDIRECT = 0x0020,
/* internal use only: maps to META_CALL_RETURNS_TO_NATIVE in insert_meta_call_vargs */
DR_CLEANCALL_RETURNS_TO_NATIVE = 0x0040,
/**
* Requests that out-of-line state save and restore routines be used even
* when a subset of the state does not need to be preserved for this callee.
* Also disables inlining.
* This helps guarantee that the inserted code remains small.
*/
DR_CLEANCALL_ALWAYS_OUT_OF_LINE = 0x0080,
} dr_cleancall_save_t;

DR_API
/**
* Identical to dr_insert_clean_call() except it takes in \p
Expand Down Expand Up @@ -667,6 +630,7 @@ DR_API
* two arguments:
* -# address of call instruction (caller)
* -# target address of call (callee)
* \note Sets #DR_CLEANCALL_READS_APP_CONTEXT and #DR_CLEANCALL_WRITES_APP_CONTEXT.
*/
void
dr_insert_call_instrumentation(void *drcontext, instrlist_t *ilist, instr_t *instr,
Expand All @@ -682,6 +646,7 @@ DR_API
* \note Only the address portion of a far indirect branch is considered.
* \note \p scratch_slot must be <= dr_max_opnd_accessible_spill_slot(). \p
* scratch_slot is used internally to this routine and will be clobbered.
* \note Sets #DR_CLEANCALL_READS_APP_CONTEXT and #DR_CLEANCALL_WRITES_APP_CONTEXT.
*/
/* If we re-enable -opt_speed (or -indcall2direct directly) we should add back:
* \note This routine is not supported when the -opt_speed option is specified.
Expand All @@ -698,6 +663,7 @@ DR_API
* -# address of branch instruction
* -# target address of branch
* -# 0 if the branch is not taken, 1 if it is taken
* \note Sets #DR_CLEANCALL_READS_APP_CONTEXT and #DR_CLEANCALL_WRITES_APP_CONTEXT.
*/
void
dr_insert_cbr_instrumentation(void *drcontext, instrlist_t *ilist, instr_t *instr,
Expand All @@ -714,6 +680,7 @@ DR_API
* -# 0 if the branch is not taken, 1 if it is taken
* -# user defined operand (e.g., TLS slot, immed value, register, etc.)
* \note The user defined operand cannot use register ebx!
* \note Sets #DR_CLEANCALL_READS_APP_CONTEXT and #DR_CLEANCALL_WRITES_APP_CONTEXT.
*/
void
dr_insert_cbr_instrumentation_ex(void *drcontext, instrlist_t *ilist, instr_t *instr,
Expand All @@ -730,6 +697,7 @@ DR_API
*
* \warning Basic block eliding is controlled by -max_elide_jmp. If that
* option is set to non-zero, ubrs may never be seen.
* \note Sets #DR_CLEANCALL_READS_APP_CONTEXT and #DR_CLEANCALL_WRITES_APP_CONTEXT.
*/
void
dr_insert_ubr_instrumentation(void *drcontext, instrlist_t *ilist, instr_t *instr,
Expand Down Expand Up @@ -790,7 +758,12 @@ DR_API
* by the \p flags field of \p context into \p context.
*
* This routine may only be called from:
* - A clean call invoked by dr_insert_clean_call() or dr_prepare_for_call()
* - A clean call invoked by dr_insert_clean_call() or dr_prepare_for_call(). If
* register reservation code is in use (e.g., via the drreg extension library \ref
* page_drreg), dr_insert_clean_call_ex() must be used with its flags argument
* including #DR_CLEANCALL_READS_APP_CONTEXT to ensure proper interaction with
* register reservations.
* - A pre- or post-syscall event (dr_register_pre_syscall_event(),
* dr_register_post_syscall_event())
* - Basic block or trace creation events (dr_register_bb_event(),
Expand Down Expand Up @@ -854,7 +827,11 @@ DR_API
* flags field of \p context to the values in \p context.
*
* This routine may only be called from:
* - A clean call invoked by dr_insert_clean_call() or dr_prepare_for_call()
* - A clean call invoked by dr_insert_clean_call() or dr_prepare_for_call(). If
* register reservation code is in use (e.g., via the drreg extension library \ref
* page_drreg), dr_insert_clean_call_ex() must be used with its flags argument
* including #DR_CLEANCALL_WRITES_APP_CONTEXT to ensure proper interaction
* with register reservations.
* - A pre- or post-syscall event (dr_register_pre_syscall_event(),
* dr_register_post_syscall_event())
* dr_register_thread_exit_event())
Expand Down
Loading

0 comments on commit 6e65d8a

Please sign in to comment.