diff --git a/api/docs/API.doxy b/api/docs/API.doxy
index 88afa15dbd3..cc85c688a40 100644
--- a/api/docs/API.doxy
+++ b/api/docs/API.doxy
@@ -1,5 +1,5 @@
# **********************************************************
-# Copyright (c) 2012-2015 Google, Inc. All rights reserved.
+# Copyright (c) 2012-2017 Google, Inc. All rights reserved.
# Copyright (c) 2007-2010 VMware, Inc. All rights reserved.
# **********************************************************/
@@ -83,7 +83,7 @@ GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
-# choices: linux, VMsafe, NYI_kernel_mediated_transfer_events
+# choices: linux, VMsafe
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
diff --git a/api/docs/intro.dox b/api/docs/intro.dox
index 829c6978a24..04f168d5f54 100644
--- a/api/docs/intro.dox
+++ b/api/docs/intro.dox
@@ -823,15 +823,13 @@ set of event callbacks. These events include the following:
(dr_register_fork_init_event())
- Application library load and unload
(dr_register_module_load_event(), dr_register_module_unload_event())
-\ifnot NYI_kernel_mediated_transfer_events
- Application fault or exception (signal on Linux)
(dr_register_exception_event(), dr_register_signal_event())
-\else
- - Kernel-mediated control transfers:
- - Application fault or exception
- - Application APC (Asynchronous Procedure Call) or callback (Windows)
- - Application signal (Linux)
-\endif
+ - Kernel-mediated control transfers (dr_register_kernel_xfer_event()):
+ - Application APC (Asynchronous Procedure Call), callback, or exception
+ dispatcher execution (Windows)
+ - Application signal delivery (Linux)
+ - System call that changes the context
- System call interception: pre-system call, post-system call, and system
call filtering by number
(dr_register_pre_syscall_event(), dr_register_post_syscall_event(),
diff --git a/api/docs/release.dox b/api/docs/release.dox
index abafb35c98d..25e19830de9 100644
--- a/api/docs/release.dox
+++ b/api/docs/release.dox
@@ -145,6 +145,10 @@ following minor compatibility changes:
such as a static initialiser, will still need to be rewritten.
DynamoRIO_PAGE_SIZE_COMPATIBILITY will be set automatically if the
client targets version 6.2 or earlier.
+ - Removed DRMGR_PRIORITY_INSERT_CLS_ENTRY, DRMGR_PRIORITY_INSERT_CLS_EXIT,
+ DRMGR_PRIORITY_NAME_CLS_ENTRY, and DRMGR_PRIORITY_NAME_CLS_EXIT, as
+ the new kernel xfer event (drmgr_register_kernel_xfer_event()) removes the
+ need for them.
Further non-compatibility-affecting changes include:
@@ -200,6 +204,9 @@ Further non-compatibility-affecting changes include:
identifier #DR_WINDOWS_VERSION_10_1703 to distinguish this major update.
- Added support for Windows 10 1709. We provide an artificial version
identifier #DR_WINDOWS_VERSION_10_1709 to distinguish this major update.
+ - Added an event for kernel-mediated control flow via
+ dr_register_kernel_xfer_event() with corresponding routines
+ drmgr_register_kernel_xfer_event() and drmgr_register_kernel_xfer_event_ex().
**************************************************
diff --git a/core/arch/arch.c b/core/arch/arch.c
index bfffcfcb1d6..5c1eca7c529 100644
--- a/core/arch/arch.c
+++ b/core/arch/arch.c
@@ -3256,6 +3256,7 @@ dr_mcontext_to_priv_mcontext(priv_mcontext_t *dst, dr_mcontext_t *src)
dst->xsp = save_xsp;
}
if (TEST(DR_MC_CONTROL, src->flags)) {
+ /* XXX i#2710: mc->lr should be under DR_MC_CONTROL */
dst->xsp = src->xsp;
dst->xflags = src->xflags;
dst->pc = src->pc;
diff --git a/core/globals.h b/core/globals.h
index b63654fbeb7..9360f6ef948 100644
--- a/core/globals.h
+++ b/core/globals.h
@@ -422,7 +422,9 @@ typedef struct _client_data_t {
/* flags for dr_get_mcontext (i#117/PR 395156) */
bool mcontext_in_dcontext;
bool suspended;
+ /* 2 other ways to point at a context for dr_{g,s}et_mcontext() */
priv_mcontext_t *cur_mc;
+ os_cxt_ptr_t os_cxt;
} client_data_t;
#else
# define IS_CLIENT_THREAD(dcontext) false
diff --git a/core/lib/globals_shared.h b/core/lib/globals_shared.h
index 28779ff3e69..a22da6a1476 100644
--- a/core/lib/globals_shared.h
+++ b/core/lib/globals_shared.h
@@ -1874,19 +1874,26 @@ typedef union _dr_simd_t {
/** Values for the flags field of dr_mcontext_t */
typedef enum {
/**
- * Selects the xdi, xsi, xbp, xbx, xdx, xcx, xax, and r8-r15 fields (i.e.,
+ * On x86, selects the xdi, xsi, xbp, xbx, xdx, xcx, xax, and r8-r15 fields (i.e.,
* all of the general-purpose registers excluding xsp, xip, and xflags).
+ * On ARM, selects r0-r12 and r14.
+ * On AArch64, selects r0-r30.
*/
DR_MC_INTEGER = 0x01,
+#ifdef AVOID_API_EXPORT
+/* XXX i#2710: The link register should be under DR_MC_CONTROL */
+#endif
/**
- * Selects the xsp, xflags, and xip fields.
- * \note: The xip field is only honored as an input for
+ * On x86, selects the xsp, xflags, and xip fields.
+ * On ARM, selects the sp, pc, and cpsr fields.
+ * On AArch64, selects the sp, pc, and nzcv fields.
+ * \note: The xip/pc field is only honored as an input for
* dr_redirect_execution(), and as an output for system call
* events.
*/
DR_MC_CONTROL = 0x02,
/**
- * Selects the ymm (and xmm) fields. This flag is ignored unless
+ * Selects the simd fields. This flag is ignored unless
* dr_mcontext_xmm_fields_valid() returns true. If
* dr_mcontext_xmm_fields_valid() returns false, the application values of
* the multimedia registers remain in the registers themselves.
diff --git a/core/lib/instrument.c b/core/lib/instrument.c
index ffe94fafb35..3810d98364f 100644
--- a/core/lib/instrument.c
+++ b/core/lib/instrument.c
@@ -214,6 +214,7 @@ static callback_list_t module_unload_callbacks = {0,};
static callback_list_t filter_syscall_callbacks = {0,};
static callback_list_t pre_syscall_callbacks = {0,};
static callback_list_t post_syscall_callbacks = {0,};
+static callback_list_t kernel_xfer_callbacks = {0,};
#ifdef WINDOWS
static callback_list_t exception_callbacks = {0,};
#else
@@ -747,6 +748,7 @@ void free_all_callback_lists()
free_callback_list(&filter_syscall_callbacks);
free_callback_list(&pre_syscall_callbacks);
free_callback_list(&post_syscall_callbacks);
+ free_callback_list(&kernel_xfer_callbacks);
#ifdef WINDOWS
free_callback_list(&exception_callbacks);
#else
@@ -1130,6 +1132,20 @@ dr_unregister_post_syscall_event(void (*func)(void *drcontext, int sysnum))
return remove_callback(&post_syscall_callbacks, (void (*)(void))func, true);
}
+void
+dr_register_kernel_xfer_event(void (*func)(void *drcontext,
+ const dr_kernel_xfer_info_t *info))
+{
+ add_callback(&kernel_xfer_callbacks, (void (*)(void))func, true);
+}
+
+bool
+dr_unregister_kernel_xfer_event(void (*func)(void *drcontext,
+ const dr_kernel_xfer_info_t *info))
+{
+ return remove_callback(&kernel_xfer_callbacks, (void (*)(void))func, true);
+}
+
#ifdef PROGRAM_SHEPHERDING
void
dr_register_security_event(void (*func)(void *drcontext, void *source_tag,
@@ -2044,12 +2060,61 @@ instrument_invoke_another_syscall(dcontext_t *dcontext)
return dcontext->client_data->invoke_another_syscall;
}
+bool
+instrument_kernel_xfer(dcontext_t *dcontext, dr_kernel_xfer_type_t type,
+ os_cxt_ptr_t source_os_cxt, dr_mcontext_t *source_dmc,
+ priv_mcontext_t *source_mc,
+ app_pc target_pc, reg_t target_xsp,
+ os_cxt_ptr_t target_os_cxt, priv_mcontext_t *target_mc,
+ int sig)
+{
+ if (kernel_xfer_callbacks.num == 0) {
+ return false;
+ }
+ dr_kernel_xfer_info_t info;
+ info.type = type;
+ info.source_mcontext = NULL;
+ info.target_pc = target_pc;
+ info.target_xsp = target_xsp;
+ info.sig = sig;
+ dr_mcontext_t dr_mcontext;
+ dr_mcontext.size = sizeof(dr_mcontext);
+ dr_mcontext.flags = DR_MC_CONTROL | DR_MC_INTEGER;
+
+ if (source_dmc != NULL)
+ info.source_mcontext = source_dmc;
+ else if (source_mc != NULL) {
+ if (priv_mcontext_to_dr_mcontext(&dr_mcontext, source_mc))
+ info.source_mcontext = &dr_mcontext;
+ } else if (!is_os_cxt_ptr_null(source_os_cxt)) {
+ if (os_context_to_mcontext(&dr_mcontext, NULL, source_os_cxt))
+ info.source_mcontext = &dr_mcontext;
+ }
+ /* Our compromise to reduce context copying is to provide the PC and XSP inline,
+ * and only get more if the user calls dr_get_mcontext(), which we support again
+ * without any copying if not used by taking in a raw os_context_t.
+ */
+ dcontext->client_data->os_cxt = target_os_cxt;
+ dcontext->client_data->cur_mc = target_mc;
+ call_all(kernel_xfer_callbacks, int (*)(void *, const dr_kernel_xfer_info_t *),
+ (void *)dcontext, &info);
+ set_os_cxt_ptr_null(&dcontext->client_data->os_cxt);
+ dcontext->client_data->cur_mc = NULL;
+ return true;
+}
+
#ifdef WINDOWS
/* Notify user of exceptions. Note: not called for RaiseException */
bool
instrument_exception(dcontext_t *dcontext, dr_exception_t *exception)
{
bool res = true;
+ /* Ensure that dr_get_mcontext() called from instrument_kernel_xfer() from
+ * dr_redirect_execution() will get the source context.
+ * cur_mc will later be clobbered by instrument_kernel_xfer() which is ok:
+ * the redirect ends the callback calling.
+ */
+ dcontext->client_data->cur_mc = dr_mcontext_as_priv_mcontext(exception->mcontext);
/* We short-circuit if any client wants to "own" the fault and not pass on.
* This does violate the "priority order" of events where the last one is
* supposed to have final say b/c it won't even see the event: but only one
@@ -2058,6 +2123,7 @@ instrument_exception(dcontext_t *dcontext, dr_exception_t *exception)
call_all_ret(res, = res &&, , exception_callbacks,
bool (*)(void *, dr_exception_t *),
(void *)dcontext, exception);
+ dcontext->client_data->cur_mc = NULL;
return res;
}
#else
@@ -6360,6 +6426,10 @@ dr_get_mcontext_priv(dcontext_t *dcontext, dr_mcontext_t *dmc, priv_mcontext_t *
return true;
}
+ if (!is_os_cxt_ptr_null(dcontext->client_data->os_cxt)) {
+ return os_context_to_mcontext(dmc, mc, dcontext->client_data->os_cxt);
+ }
+
if (dcontext->client_data->suspended) {
/* A thread suspended by dr_suspend_all_other_threads() has its
* context translated lazily here.
@@ -6467,6 +6537,16 @@ dr_set_mcontext(void *drcontext, dr_mcontext_t *context)
return false;
return true;
}
+ if (dcontext->client_data->cur_mc != NULL) {
+ return dr_mcontext_to_priv_mcontext(dcontext->client_data->cur_mc, context);
+ }
+ if (!is_os_cxt_ptr_null(dcontext->client_data->os_cxt)) {
+ /* It would be nice to fail for #DR_XFER_CALLBACK_RETURN but we'd need to
+ * store yet more state to do so. The pc will be ignored, and xsi
+ * changes will likely cause crashes.
+ */
+ return mcontext_to_os_context(dcontext->client_data->os_cxt, context, NULL);
+ }
/* copy the machine context to the dstack area created with
* dr_prepare_for_call(). note that xmm0-5 copied there
@@ -6527,6 +6607,23 @@ dr_redirect_execution(dr_mcontext_t *mcontext)
dcontext->next_tag = canonicalize_pc_target(dcontext, mcontext->pc);
dcontext->whereami = WHERE_FCACHE;
set_last_exit(dcontext, (linkstub_t *)get_client_linkstub());
+#ifdef CLIENT_INTERFACE
+ if (kernel_xfer_callbacks.num > 0) {
+ /* This can only be called from a clean call or an exception event.
+ * For both of those we can get the current mcontext via dr_get_mcontext()
+ * (the latter b/c we explicitly store to cur_mc just for this use case).
+ */
+ dr_mcontext_t src_dmc;
+ src_dmc.size = sizeof(src_dmc);
+ src_dmc.flags = DR_MC_CONTROL | DR_MC_INTEGER;
+ dr_get_mcontext(dcontext, &src_dmc);
+ if (instrument_kernel_xfer(dcontext, DR_XFER_CLIENT_REDIRECT,
+ osc_empty, &src_dmc, NULL,
+ dcontext->next_tag, mcontext->xsp, osc_empty,
+ dr_mcontext_as_priv_mcontext(mcontext), 0))
+ dcontext->next_tag = canonicalize_pc_target(dcontext, mcontext->pc);
+ }
+#endif
transfer_to_dispatch(dcontext, dr_mcontext_as_priv_mcontext(mcontext),
true/*full_DR_state*/);
/* on success we won't get here */
diff --git a/core/lib/instrument.h b/core/lib/instrument.h
index 0e14fc62f4f..58817a4a1dd 100644
--- a/core/lib/instrument.h
+++ b/core/lib/instrument.h
@@ -1,5 +1,5 @@
/* **********************************************************
- * Copyright (c) 2010-2016 Google, Inc. All rights reserved.
+ * Copyright (c) 2010-2017 Google, Inc. All rights reserved.
* Copyright (c) 2002-2010 VMware, Inc. All rights reserved.
* **********************************************************/
@@ -102,6 +102,15 @@ bool instrument_filter_syscall(dcontext_t *dcontext, int sysnum);
bool instrument_pre_syscall(dcontext_t *dcontext, int sysnum);
void instrument_post_syscall(dcontext_t *dcontext, int sysnum);
bool instrument_invoke_another_syscall(dcontext_t *dcontext);
+/* returns whether a client event was called which might have changed the context */
+bool instrument_kernel_xfer(dcontext_t *dcontext, dr_kernel_xfer_type_t type,
+ /* only one of these 3 should be non-NULL */
+ os_cxt_ptr_t source_os_cxt, dr_mcontext_t *source_dmc,
+ priv_mcontext_t *source_mc,
+ app_pc target_pc, reg_t target_xsp,
+ /* only one of these 2 should be non-NULL */
+ os_cxt_ptr_t target_os_cxt, priv_mcontext_t *target_mc,
+ int sig);
void instrument_nudge(dcontext_t *dcontext, client_id_t id, uint64 arg);
/* post instrument_event() cleanup */
diff --git a/core/lib/instrument_api.h b/core/lib/instrument_api.h
index fca628e77b5..8d1f8704c1d 100644
--- a/core/lib/instrument_api.h
+++ b/core/lib/instrument_api.h
@@ -918,6 +918,104 @@ bool
dr_unregister_module_unload_event(void (*func)(void *drcontext,
const module_data_t *info));
+/* DR_API EXPORT BEGIN */
+/** Identifies the type of kernel transfer for dr_register_kernel_xfer_event(). */
+typedef enum {
+ DR_XFER_SIGNAL_DELIVERY, /**< Signal delivery to application handler. */
+ DR_XFER_SIGNAL_RETURN, /**< Signal return system call. */
+ DR_XFER_APC_DISPATCHER, /**< Asynchronous procedure call dispatcher. */
+ DR_XFER_EXCEPTION_DISPATCHER, /**< Exception dispatcher. */
+ DR_XFER_RAISE_DISPATCHER, /**< Raised exception dispatcher. */
+ DR_XFER_CALLBACK_DISPATCHER, /**< Callback dispatcher. */
+ DR_XFER_CALLBACK_RETURN, /**< A return from a callback by syscall or interrupt. */
+ DR_XFER_CONTINUE, /**< NtContinue system call. */
+ DR_XFER_SET_CONTEXT_THREAD, /**< NtSetContextThread system call. */
+ DR_XFER_CLIENT_REDIRECT, /**< dr_redirect_execution() or #DR_SIGNAL_REDIRECT. */
+} dr_kernel_xfer_type_t;
+
+/** Data structure passed for dr_register_kernel_xfer_event(). */
+typedef struct _dr_kernel_xfer_info_t {
+ /** The type of event. */
+ dr_kernel_xfer_type_t type;
+ /**
+ * The source machine context which is about to be changed. This may be NULL
+ * if it is unknown, which is the case for #DR_XFER_CALLBACK_DISPATCHER.
+ */
+ const dr_mcontext_t *source_mcontext;
+ /**
+ * The target program counter of the transfer. To obtain the full target state,
+ * call dr_get_mcontext(). (For efficiency purposes, only frequently needed
+ * state is included by default.)
+ */
+ app_pc target_pc;
+ /**
+ * The target stack pointer of the transfer. To obtain the full target state,
+ * call dr_get_mcontext(). (For efficiency purposes, only frequently needed
+ * state is included by default.)
+ */
+ reg_t target_xsp;
+ /** For #DR_XFER_SIGNAL_DELIVERY and #DR_XFER_SIGNAL_RETURN, the signal number. */
+ int sig;
+} dr_kernel_xfer_info_t;
+/* DR_API EXPORT END */
+
+DR_API
+/**
+ * Registers a callback function for the kernel transfer event. DR
+ * calls \p 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 \p 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 \p info->source_mcontext; if
+ * unknown, \p info->source_mcontext is NULL. Multimedia state is typically
+ * not provided in \p info->source_mcontext, which is reflected in its \p flags.
+ *
+ * The target program counter and stack are provided in \p info->target_pc and \p
+ * 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.
+ */
+void
+dr_register_kernel_xfer_event(void (*func)(void *drcontext,
+ const dr_kernel_xfer_info_t *info));
+
+DR_API
+/**
+ * Unregister a callback function for the kernel transfer event.
+ * \return true if unregistration is successful and false if it is not
+ * (e.g., \p func was not registered).
+ */
+bool
+dr_unregister_kernel_xfer_event(void (*func)(void *drcontext,
+ const dr_kernel_xfer_info_t *info));
+
/* DR_API EXPORT BEGIN */
#ifdef WINDOWS
/* DR_API EXPORT END */
@@ -930,7 +1028,7 @@ dr_unregister_module_unload_event(void (*func)(void *drcontext,
typedef struct _dr_exception_t {
/**
* Machine context at exception point. The client should not
- * change \p mcontext.flags: it should remain DR_MC_ALL.
+ * change \p mcontext->flags: it should remain DR_MC_ALL.
*/
dr_mcontext_t *mcontext;
EXCEPTION_RECORD *record; /**< Win32 exception record. */
@@ -5339,7 +5437,7 @@ DR_API
/**
* Returns true if the xmm fields in dr_mcontext_t are valid
* (i.e., whether the underlying processor supports SSE).
- * \note If DR_MC_MULTIMEDIA is not specified when calling dr_get_mcontext(),
+ * \note If #DR_MC_MULTIMEDIA is not specified when calling dr_get_mcontext(),
* the xmm fields will not be filled in regardless of the return value
* of this routine.
*/
@@ -5368,10 +5466,20 @@ DR_API
* dr_register_exit_event())
* - A thread init event (dr_register_thread_init_event()) for all but
* the initial thread.
- *
- * Even when DR_MC_CONTROL is specified, does NOT copy the pc field,
+ * - A kernel transfer event (dr_register_kernel_xfer_event()). Here the obtained
+ * context is the target context of the transfer, not the source (about to be
+ * changed) context. For Windows system call event types #DR_XFER_CONTINUE and
+ * #DR_XFER_SET_CONTEXT_THREAD, only the portions of the context selected by the
+ * application are available. The \p flags field of \p context is adjusted to
+ * reflect which fields were returned. 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.
+ *
+ * Even when #DR_MC_CONTROL is specified, does NOT copy the pc field,
* except for system call events, when it will point at the
- * post-syscall address.
+ * post-syscall address, and kernel transfer events, when it will point to the
+ * target pc.
*
* Returns false if called from the init event or the initial thread's
* init event; returns true otherwise (cannot distinguish whether the
@@ -5389,7 +5497,7 @@ DR_API
*
* \note NUM_SIMD_SLOTS in the dr_mcontext_t.xmm array are filled in,
* but only if dr_mcontext_xmm_fields_valid() returns true and
- * DR_MC_MULTIMEDIA is set in the flags field.
+ * #DR_MC_MULTIMEDIA is set in the flags field.
*
* \note The context is the context saved at the dr_insert_clean_call() or
* dr_prepare_for_call() points. It does not correct for any registers saved
@@ -5414,12 +5522,21 @@ DR_API
* - A pre- or post-syscall event (dr_register_pre_syscall_event(),
* dr_register_post_syscall_event())
* dr_register_thread_exit_event())
- * - Basic block or trace creation events (dr_register_bb_event(),
- * dr_register_trace_event()), but for basic block creation only when the
- * basic block callback parameters \p for_trace and \p translating are
- * false, and for trace creation only when \p translating is false.
- *
- * Ignores the pc field.
+ * - A kernel transfer event (dr_register_kernel_xfer_event()) other than
+ * #DR_XFER_CALLBACK_RETURN. Here the modified context is the target context of
+ * the transfer, not the source (about to be changed) context. For Windows system
+ * call event types #DR_XFER_CONTINUE and #DR_XFER_SET_CONTEXT_THREAD, only the
+ * portions of the context selected by the application can be changed. The \p
+ * flags field of \p context is adjusted to reflect which fields these are. 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. - Basic block or trace creation events
+ * (dr_register_bb_event(), dr_register_trace_event()), but for basic block
+ * creation only when the basic block callback parameters \p for_trace and \p
+ * translating are false, and for trace creation only when \p translating is false.
+ *
+ * Ignores the pc field, except for kernel transfer events.
*
* If the size field of \p context is invalid, this routine will
* return false. A dr_mcontext_t obtained from DR will have the size field set.
@@ -5453,7 +5570,7 @@ DR_API
* of fields is not suported.
*
* \note dr_get_mcontext() can be used to get the register state (except pc)
- * saved in dr_insert_clean_call() or dr_prepare_for_call()
+ * saved in dr_insert_clean_call() or dr_prepare_for_call().
*
* \note If floating point state was saved by dr_prepare_for_call() or
* dr_insert_clean_call() it is not restored (other than the valid xmm
@@ -5472,7 +5589,7 @@ DR_API
* \note This routine may only be called from a clean call from the cache. It can not be
* called from any registered event callback except the exception event
* (dr_register_exception_event()). From a signal event callback, use the
- * DR_SIGNAL_REDIRECT return value rather than calling this routine.
+ * #DR_SIGNAL_REDIRECT return value rather than calling this routine.
*
* \note For ARM, to redirect execution to a Thumb target (#DR_ISA_ARM_THUMB),
* set the least significant bit of the mcontext pc to 1. Reference
@@ -5527,11 +5644,13 @@ DR_API
/**
* Copies the machine state in \p src into \p dst. Sets the \p
* ContextFlags field of \p dst to reflect the \p flags field of \p
- * src.
+ * src. However, CONTEXT_CONTROL includes Ebp/Rbp, while that's under
+ * #DR_MC_INTEGER, so we recommend always setting both #DR_MC_INTEGER
+ * and #DR_MC_CONTROL when calling this routine.
*
* It is up to the caller to ensure that \p dst is allocated and
* initialized properly in order to contain multimedia processor
- * state, if DR_MC_MULTIMEDIA is set in the \p flags field of \p src.
+ * state, if #DR_MC_MULTIMEDIA is set in the \p flags field of \p src.
*
* The current segment register values are filled in under the assumption
* that this context is for the calling thread.
diff --git a/core/unix/os.c b/core/unix/os.c
index 4cbcef34af7..e9df9604d22 100644
--- a/core/unix/os.c
+++ b/core/unix/os.c
@@ -3603,7 +3603,7 @@ thread_get_mcontext(thread_record_t *tr, priv_mcontext_t *mc)
if (ostd->suspend_count == 0)
return false;
ASSERT(ostd->suspended_sigcxt != NULL);
- sigcontext_to_mcontext(mc, ostd->suspended_sigcxt);
+ sigcontext_to_mcontext(mc, ostd->suspended_sigcxt, DR_MC_ALL);
return true;
}
@@ -3619,7 +3619,33 @@ thread_set_mcontext(thread_record_t *tr, priv_mcontext_t *mc)
if (ostd->suspend_count == 0)
return false;
ASSERT(ostd->suspended_sigcxt != NULL);
- mcontext_to_sigcontext(ostd->suspended_sigcxt, mc);
+ mcontext_to_sigcontext(ostd->suspended_sigcxt, mc, DR_MC_ALL);
+ return true;
+}
+
+/* Only one of mc and dmc can be non-NULL. */
+bool
+os_context_to_mcontext(dr_mcontext_t *dmc, priv_mcontext_t *mc, os_cxt_ptr_t osc)
+{
+ if (dmc != NULL)
+ sigcontext_to_mcontext(dr_mcontext_as_priv_mcontext(dmc), &osc, dmc->flags);
+ else if (mc != NULL)
+ sigcontext_to_mcontext(mc, &osc, DR_MC_ALL);
+ else
+ return false;
+ return true;
+}
+
+/* Only one of mc and dmc can be non-NULL. */
+bool
+mcontext_to_os_context(os_cxt_ptr_t osc, dr_mcontext_t *dmc, priv_mcontext_t *mc)
+{
+ if (dmc != NULL)
+ mcontext_to_sigcontext(&osc, dr_mcontext_as_priv_mcontext(dmc), dmc->flags);
+ else if (mc != NULL)
+ mcontext_to_sigcontext(&osc, mc, DR_MC_ALL);
+ else
+ return false;
return true;
}
diff --git a/core/unix/os_exports.h b/core/unix/os_exports.h
index 5808d02327b..fabfa349387 100644
--- a/core/unix/os_exports.h
+++ b/core/unix/os_exports.h
@@ -365,6 +365,27 @@ typedef struct _sig_full_cxt_t {
void *fp_simd_state;
} sig_full_cxt_t;
+typedef sig_full_cxt_t os_cxt_ptr_t;
+
+extern os_cxt_ptr_t osc_empty;
+
+static inline bool
+is_os_cxt_ptr_null(os_cxt_ptr_t osc)
+{
+ return osc.sc == NULL;
+}
+
+static inline void
+set_os_cxt_ptr_null(os_cxt_ptr_t *osc)
+{
+ *osc = osc_empty;
+}
+
+/* Only one of mc and dmc can be non-NULL. */
+bool os_context_to_mcontext(dr_mcontext_t *dmc, priv_mcontext_t *mc, os_cxt_ptr_t osc);
+/* Only one of mc and dmc can be non-NULL. */
+bool mcontext_to_os_context(os_cxt_ptr_t osc, dr_mcontext_t *dmc, priv_mcontext_t *mc);
+
void *
#ifdef MACOS
create_clone_record(dcontext_t *dcontext, reg_t *app_xsp,
diff --git a/core/unix/os_private.h b/core/unix/os_private.h
index 754f4d6b203..84fa273e39e 100644
--- a/core/unix/os_private.h
+++ b/core/unix/os_private.h
@@ -302,10 +302,12 @@ signal_handle_close(dcontext_t *dcontext, file_t fd);
#endif
void
-sigcontext_to_mcontext(priv_mcontext_t *mc, sig_full_cxt_t *sc_full);
+sigcontext_to_mcontext(priv_mcontext_t *mc, sig_full_cxt_t *sc_full,
+ dr_mcontext_flags_t flags);
void
-mcontext_to_sigcontext(sig_full_cxt_t *sc_full, priv_mcontext_t *mc);
+mcontext_to_sigcontext(sig_full_cxt_t *sc_full, priv_mcontext_t *mc,
+ dr_mcontext_flags_t flags);
bool
set_default_signal_action(int sig);
diff --git a/core/unix/signal.c b/core/unix/signal.c
index 561837f2717..80e71bb7f5d 100644
--- a/core/unix/signal.c
+++ b/core/unix/signal.c
@@ -207,6 +207,8 @@ static kernel_sigset_t init_sigmask;
static bool removed_sig_handler;
#endif
+os_cxt_ptr_t osc_empty;
+
/**** function prototypes ***********************************************/
/* in x86.asm */
@@ -2213,59 +2215,74 @@ sig_full_initialize(sig_full_cxt_t *sc_full, kernel_ucontext_t *ucxt)
}
void
-sigcontext_to_mcontext(priv_mcontext_t *mc, sig_full_cxt_t *sc_full)
+sigcontext_to_mcontext(priv_mcontext_t *mc, sig_full_cxt_t *sc_full,
+ dr_mcontext_flags_t flags)
{
sigcontext_t *sc = sc_full->sc;
ASSERT(mc != NULL && sc != NULL);
#ifdef X86
- mc->xax = sc->SC_XAX;
- mc->xbx = sc->SC_XBX;
- mc->xcx = sc->SC_XCX;
- mc->xdx = sc->SC_XDX;
- mc->xsi = sc->SC_XSI;
- mc->xdi = sc->SC_XDI;
- mc->xbp = sc->SC_XBP;
- mc->xsp = sc->SC_XSP;
- mc->xflags = sc->SC_XFLAGS;
- mc->pc = (app_pc) sc->SC_XIP;
+ if (TEST(DR_MC_INTEGER, flags)) {
+ mc->xax = sc->SC_XAX;
+ mc->xbx = sc->SC_XBX;
+ mc->xcx = sc->SC_XCX;
+ mc->xdx = sc->SC_XDX;
+ mc->xsi = sc->SC_XSI;
+ mc->xdi = sc->SC_XDI;
+ mc->xbp = sc->SC_XBP;
# ifdef X64
- mc->r8 = sc->SC_FIELD(r8);
- mc->r9 = sc->SC_FIELD(r9);
- mc->r10 = sc->SC_FIELD(r10);
- mc->r11 = sc->SC_FIELD(r11);
- mc->r12 = sc->SC_FIELD(r12);
- mc->r13 = sc->SC_FIELD(r13);
- mc->r14 = sc->SC_FIELD(r14);
- mc->r15 = sc->SC_FIELD(r15);
+ mc->r8 = sc->SC_FIELD(r8);
+ mc->r9 = sc->SC_FIELD(r9);
+ mc->r10 = sc->SC_FIELD(r10);
+ mc->r11 = sc->SC_FIELD(r11);
+ mc->r12 = sc->SC_FIELD(r12);
+ mc->r13 = sc->SC_FIELD(r13);
+ mc->r14 = sc->SC_FIELD(r14);
+ mc->r15 = sc->SC_FIELD(r15);
# endif /* X64 */
+ }
+ if (TEST(DR_MC_CONTROL, flags)) {
+ mc->xsp = sc->SC_XSP;
+ mc->xflags = sc->SC_XFLAGS;
+ mc->pc = (app_pc) sc->SC_XIP;
+ }
#elif defined(AARCH64)
- memcpy(&mc->r0, &sc->SC_FIELD(regs[0]), sizeof(mc->r0) * 31);
- mc->sp = sc->SC_FIELD(sp);
- mc->pc = (void *)sc->SC_FIELD(pc);
- mc->nzcv = sc->SC_FIELD(pstate);
+ if (TEST(DR_MC_INTEGER, flags))
+ memcpy(&mc->r0, &sc->SC_FIELD(regs[0]), sizeof(mc->r0) * 31);
+ if (TEST(DR_MC_CONTROL, flags)) {
+ /* XXX i#2710: the link register should be under DR_MC_CONTROL */
+ mc->sp = sc->SC_FIELD(sp);
+ mc->pc = (void *)sc->SC_FIELD(pc);
+ mc->nzcv = sc->SC_FIELD(pstate);
+ }
#elif defined (ARM)
- mc->r0 = sc->SC_FIELD(arm_r0);
- mc->r1 = sc->SC_FIELD(arm_r1);
- mc->r2 = sc->SC_FIELD(arm_r2);
- mc->r3 = sc->SC_FIELD(arm_r3);
- mc->r4 = sc->SC_FIELD(arm_r4);
- mc->r5 = sc->SC_FIELD(arm_r5);
- mc->r6 = sc->SC_FIELD(arm_r6);
- mc->r7 = sc->SC_FIELD(arm_r7);
- mc->r8 = sc->SC_FIELD(arm_r8);
- mc->r9 = sc->SC_FIELD(arm_r9);
- mc->r10 = sc->SC_FIELD(arm_r10);
- mc->r11 = sc->SC_FIELD(arm_fp);
- mc->r12 = sc->SC_FIELD(arm_ip);
- mc->r13 = sc->SC_FIELD(arm_sp);
- mc->r14 = sc->SC_FIELD(arm_lr);
- mc->r15 = sc->SC_FIELD(arm_pc);
- mc->cpsr= sc->SC_FIELD(arm_cpsr);
+ if (TEST(DR_MC_INTEGER, flags)) {
+ mc->r0 = sc->SC_FIELD(arm_r0);
+ mc->r1 = sc->SC_FIELD(arm_r1);
+ mc->r2 = sc->SC_FIELD(arm_r2);
+ mc->r3 = sc->SC_FIELD(arm_r3);
+ mc->r4 = sc->SC_FIELD(arm_r4);
+ mc->r5 = sc->SC_FIELD(arm_r5);
+ mc->r6 = sc->SC_FIELD(arm_r6);
+ mc->r7 = sc->SC_FIELD(arm_r7);
+ mc->r8 = sc->SC_FIELD(arm_r8);
+ mc->r9 = sc->SC_FIELD(arm_r9);
+ mc->r10 = sc->SC_FIELD(arm_r10);
+ mc->r11 = sc->SC_FIELD(arm_fp);
+ mc->r12 = sc->SC_FIELD(arm_ip);
+ /* XXX i#2710: the link register should be under DR_MC_CONTROL */
+ mc->r14 = sc->SC_FIELD(arm_lr);
+ }
+ if (TEST(DR_MC_CONTROL, flags)) {
+ mc->r13 = sc->SC_FIELD(arm_sp);
+ mc->r15 = sc->SC_FIELD(arm_pc);
+ mc->cpsr= sc->SC_FIELD(arm_cpsr);
+ }
# ifdef X64
# error NYI on AArch64
# endif /* X64 */
#endif /* X86/ARM */
- sigcontext_to_mcontext_simd(mc, sc_full);
+ if (TEST(DR_MC_MULTIMEDIA, flags))
+ sigcontext_to_mcontext_simd(mc, sc_full);
}
/* Note that unlike mcontext_to_context(), this routine does not fill in
@@ -2284,59 +2301,75 @@ sigcontext_to_mcontext(priv_mcontext_t *mc, sig_full_cxt_t *sc_full)
* and tweaks that context, so cpsr should be there.
*/
void
-mcontext_to_sigcontext(sig_full_cxt_t *sc_full, priv_mcontext_t *mc)
+mcontext_to_sigcontext(sig_full_cxt_t *sc_full, priv_mcontext_t *mc,
+ dr_mcontext_flags_t flags)
{
sigcontext_t *sc = sc_full->sc;
ASSERT(mc != NULL && sc != NULL);
#ifdef X86
- sc->SC_XAX = mc->xax;
- sc->SC_XBX = mc->xbx;
- sc->SC_XCX = mc->xcx;
- sc->SC_XDX = mc->xdx;
- sc->SC_XSI = mc->xsi;
- sc->SC_XDI = mc->xdi;
- sc->SC_XBP = mc->xbp;
- sc->SC_XSP = mc->xsp;
- sc->SC_XFLAGS = mc->xflags;
- sc->SC_XIP = (ptr_uint_t) mc->pc;
+ if (TEST(DR_MC_INTEGER, flags)) {
+ sc->SC_XAX = mc->xax;
+ sc->SC_XBX = mc->xbx;
+ sc->SC_XCX = mc->xcx;
+ sc->SC_XDX = mc->xdx;
+ sc->SC_XSI = mc->xsi;
+ sc->SC_XDI = mc->xdi;
+ sc->SC_XBP = mc->xbp;
# ifdef X64
- sc->SC_FIELD(r8) = mc->r8;
- sc->SC_FIELD(r9) = mc->r9;
- sc->SC_FIELD(r10) = mc->r10;
- sc->SC_FIELD(r11) = mc->r11;
- sc->SC_FIELD(r12) = mc->r12;
- sc->SC_FIELD(r13) = mc->r13;
- sc->SC_FIELD(r14) = mc->r14;
- sc->SC_FIELD(r15) = mc->r15;
+ sc->SC_FIELD(r8) = mc->r8;
+ sc->SC_FIELD(r9) = mc->r9;
+ sc->SC_FIELD(r10) = mc->r10;
+ sc->SC_FIELD(r11) = mc->r11;
+ sc->SC_FIELD(r12) = mc->r12;
+ sc->SC_FIELD(r13) = mc->r13;
+ sc->SC_FIELD(r14) = mc->r14;
+ sc->SC_FIELD(r15) = mc->r15;
# endif /* X64 */
+ }
+ if (TEST(DR_MC_CONTROL, flags)) {
+ sc->SC_XSP = mc->xsp;
+ sc->SC_XFLAGS = mc->xflags;
+ sc->SC_XIP = (ptr_uint_t) mc->pc;
+ }
#elif defined(AARCH64)
- memcpy(&sc->SC_FIELD(regs[0]), &mc->r0, sizeof(mc->r0) * 31);
- sc->SC_FIELD(sp) = mc->sp;
- sc->SC_FIELD(pc) = (ptr_uint_t)mc->pc;
- sc->SC_FIELD(pstate) = mc->nzcv;
+ if (TEST(DR_MC_INTEGER, flags)) {
+ memcpy(&sc->SC_FIELD(regs[0]), &mc->r0, sizeof(mc->r0) * 31);
+ }
+ if (TEST(DR_MC_CONTROL, flags)) {
+ /* XXX i#2710: the link register should be under DR_MC_CONTROL */
+ sc->SC_FIELD(sp) = mc->sp;
+ sc->SC_FIELD(pc) = (ptr_uint_t)mc->pc;
+ sc->SC_FIELD(pstate) = mc->nzcv;
+ }
#elif defined(ARM)
- sc->SC_FIELD(arm_r0) = mc->r0;
- sc->SC_FIELD(arm_r1) = mc->r1;
- sc->SC_FIELD(arm_r2) = mc->r2;
- sc->SC_FIELD(arm_r3) = mc->r3;
- sc->SC_FIELD(arm_r4) = mc->r4;
- sc->SC_FIELD(arm_r5) = mc->r5;
- sc->SC_FIELD(arm_r6) = mc->r6;
- sc->SC_FIELD(arm_r7) = mc->r7;
- sc->SC_FIELD(arm_r8) = mc->r8;
- sc->SC_FIELD(arm_r9) = mc->r9;
- sc->SC_FIELD(arm_r10) = mc->r10;
- sc->SC_FIELD(arm_fp) = mc->r11;
- sc->SC_FIELD(arm_ip) = mc->r12;
- sc->SC_FIELD(arm_sp) = mc->r13;
- sc->SC_FIELD(arm_lr) = mc->r14;
- sc->SC_FIELD(arm_pc) = mc->r15;
- sc->SC_FIELD(arm_cpsr)= mc->cpsr;
+ if (TEST(DR_MC_INTEGER, flags)) {
+ sc->SC_FIELD(arm_r0) = mc->r0;
+ sc->SC_FIELD(arm_r1) = mc->r1;
+ sc->SC_FIELD(arm_r2) = mc->r2;
+ sc->SC_FIELD(arm_r3) = mc->r3;
+ sc->SC_FIELD(arm_r4) = mc->r4;
+ sc->SC_FIELD(arm_r5) = mc->r5;
+ sc->SC_FIELD(arm_r6) = mc->r6;
+ sc->SC_FIELD(arm_r7) = mc->r7;
+ sc->SC_FIELD(arm_r8) = mc->r8;
+ sc->SC_FIELD(arm_r9) = mc->r9;
+ sc->SC_FIELD(arm_r10) = mc->r10;
+ sc->SC_FIELD(arm_fp) = mc->r11;
+ sc->SC_FIELD(arm_ip) = mc->r12;
+ /* XXX i#2710: the link register should be under DR_MC_CONTROL */
+ sc->SC_FIELD(arm_lr) = mc->r14;
+ }
+ if (TEST(DR_MC_CONTROL, flags)) {
+ sc->SC_FIELD(arm_sp) = mc->r13;
+ sc->SC_FIELD(arm_pc) = mc->r15;
+ sc->SC_FIELD(arm_cpsr)= mc->cpsr;
+ }
# ifdef X64
# error NYI on AArch64
# endif /* X64 */
#endif /* X86/ARM */
- mcontext_to_sigcontext_simd(sc_full, mc);
+ if (TEST(DR_MC_MULTIMEDIA, flags))
+ mcontext_to_sigcontext_simd(sc_full, mc);
}
static void
@@ -2344,7 +2377,7 @@ ucontext_to_mcontext(priv_mcontext_t *mc, kernel_ucontext_t *uc)
{
sig_full_cxt_t sc_full;
sig_full_initialize(&sc_full, uc);
- sigcontext_to_mcontext(mc, &sc_full);
+ sigcontext_to_mcontext(mc, &sc_full, DR_MC_ALL);
}
static void
@@ -2352,7 +2385,7 @@ mcontext_to_ucontext(kernel_ucontext_t *uc, priv_mcontext_t *mc)
{
sig_full_cxt_t sc_full;
sig_full_initialize(&sc_full, uc);
- mcontext_to_sigcontext(&sc_full, mc);
+ mcontext_to_sigcontext(&sc_full, mc, DR_MC_ALL);
}
#ifdef AARCHXX
@@ -2565,7 +2598,7 @@ thread_set_self_mcontext(priv_mcontext_t *mc)
#if defined(LINUX) && defined(X86)
sc_full.sc->fpstate = NULL; /* for mcontext_to_sigcontext */
#endif
- mcontext_to_sigcontext(&sc_full, mc);
+ mcontext_to_sigcontext(&sc_full, mc, DR_MC_ALL);
thread_set_segment_registers(sc_full.sc);
/* sigreturn takes the mode from cpsr */
IF_ARM(set_pc_mode_in_cpsr(sc_full.sc,
@@ -3158,9 +3191,23 @@ copy_frame_to_pending(dcontext_t *dcontext, int sig, sigframe_rt_t *frame
/* transfer control from signal handler to fcache return routine */
static void
-transfer_from_sig_handler_to_fcache_return(dcontext_t *dcontext, sigcontext_t *sc,
- app_pc next_pc, linkstub_t *last_exit)
+transfer_from_sig_handler_to_fcache_return(dcontext_t *dcontext, kernel_ucontext_t *uc,
+ sigcontext_t *sc_interrupted, int sig,
+ app_pc next_pc,
+ linkstub_t *last_exit, bool is_kernel_xfer)
{
+ sigcontext_t *sc = SIGCXT_FROM_UCXT(uc);
+#ifdef CLIENT_INTERFACE
+ if (is_kernel_xfer) {
+ sig_full_cxt_t sc_interrupted_full = { sc_interrupted, NULL/*not provided*/ };
+ sig_full_cxt_t sc_full;
+ sig_full_initialize(&sc_full, uc);
+ sc->SC_XIP = (ptr_uint_t) next_pc;
+ if (instrument_kernel_xfer(dcontext, DR_XFER_SIGNAL_DELIVERY, sc_interrupted_full,
+ NULL, NULL, next_pc, sc->SC_XSP, sc_full, NULL, sig))
+ next_pc = canonicalize_pc_target(dcontext, (app_pc)sc->SC_XIP);
+ }
+#endif
dcontext->next_tag = canonicalize_pc_target(dcontext, next_pc);
IF_ARM(dr_set_isa_mode(dcontext, get_pc_mode_from_cpsr(sc), NULL));
@@ -3246,7 +3293,7 @@ send_signal_to_client(dcontext_t *dcontext, int sig, sigframe_rt_t *frame,
fragment_t wrapper;
si.raw_mcontext_valid = true;
sigcontext_to_mcontext(dr_mcontext_as_priv_mcontext(si.raw_mcontext),
- &raw_sc_full);
+ &raw_sc_full, si.raw_mcontext->flags);
/* i#207: fragment tag and fcache start pc on fault. */
/* FIXME: we should avoid the fragment_pclookup since it is expensive
* and since we already did the work of a lookup when translating
@@ -3288,7 +3335,8 @@ send_signal_to_client(dcontext_t *dcontext, int sig, sigframe_rt_t *frame,
CLIENT_ASSERT(si.raw_mcontext->flags == DR_MC_ALL,
"signal mcontext flags cannot be changed");
mcontext_to_sigcontext(&raw_sc_full,
- dr_mcontext_as_priv_mcontext(si.raw_mcontext));
+ dr_mcontext_as_priv_mcontext(si.raw_mcontext),
+ si.raw_mcontext->flags);
}
heap_free(dcontext, si.mcontext, sizeof(*si.mcontext) HEAPACCT(ACCT_OTHER));
heap_free(dcontext, si.raw_mcontext, sizeof(*si.raw_mcontext) HEAPACCT(ACCT_OTHER));
@@ -3299,7 +3347,7 @@ send_signal_to_client(dcontext_t *dcontext, int sig, sigframe_rt_t *frame,
static bool
handle_client_action_from_cache(dcontext_t *dcontext, int sig, dr_signal_action_t action,
sigframe_rt_t *our_frame, sigcontext_t *sc_orig,
- bool blocked)
+ sigcontext_t *sc_interrupted, bool blocked)
{
thread_sig_info_t *info = (thread_sig_info_t *) dcontext->signal_field;
kernel_ucontext_t *uc = get_ucontext_from_rt_frame(our_frame);
@@ -3310,11 +3358,11 @@ handle_client_action_from_cache(dcontext_t *dcontext, int sig, dr_signal_action_
if (action == DR_SIGNAL_REDIRECT) {
/* send_signal_to_client copied mcontext into our
* master_signal_handler frame, so we set up for fcache_return w/
- * the mcontext state and this as next_tag
+ * our frame's state
*/
- ucontext_to_mcontext(get_mcontext(dcontext), uc);
- transfer_from_sig_handler_to_fcache_return(dcontext, sc, (app_pc) sc->SC_XIP,
- (linkstub_t *) get_asynch_linkstub());
+ transfer_from_sig_handler_to_fcache_return
+ (dcontext, uc, sc_interrupted, sig,
+ (app_pc) sc->SC_XIP, (linkstub_t *) get_asynch_linkstub(), true);
if (is_building_trace(dcontext)) {
LOG(THREAD, LOG_ASYNCH, 3, "\tsquashing trace-in-progress\n");
trace_abort(dcontext);
@@ -4038,13 +4086,16 @@ record_pending_signal(dcontext_t *dcontext, int sig, kernel_ucontext_t *ucxt,
f = fragment_pclookup(dcontext, (cache_pc)sc->SC_XIP, &wrapper);
sc_orig = *sc;
translate_sigcontext(dcontext, ucxt, true/*shouldn't fail*/, f);
+ /* make a copy before send_signal_to_client() tweaks it */
+ sigcontext_t sc_interrupted = *sc;
action = send_signal_to_client(dcontext, sig, frame, &sc_orig,
access_address, true/*blocked*/, f);
/* For blocked signal early event we disallow BYPASS (xref i#182/PR 449996) */
CLIENT_ASSERT(action != DR_SIGNAL_BYPASS,
"cannot bypass a blocked signal event");
if (!handle_client_action_from_cache(dcontext, sig, action, frame,
- &sc_orig, true/*blocked*/)) {
+ &sc_orig, &sc_interrupted,
+ true/*blocked*/)) {
ostd->processing_signal--;
return;
}
@@ -4336,7 +4387,7 @@ compute_memory_target(dcontext_t *dcontext, cache_pc instr_cache_pc,
*/
static bool
check_for_modified_code(dcontext_t *dcontext, cache_pc instr_cache_pc,
- sigcontext_t *sc, byte *target, bool native_state)
+ kernel_ucontext_t *uc, byte *target, bool native_state)
{
/* special case: we expect a seg fault for executable regions
* that were writable and marked read-only by us.
@@ -4355,7 +4406,7 @@ check_for_modified_code(dcontext_t *dcontext, cache_pc instr_cache_pc,
app_pc next_pc, translated_pc = NULL;
fragment_t *f = NULL;
fragment_t wrapper;
- ASSERT((cache_pc)sc->SC_XIP == instr_cache_pc);
+ ASSERT((cache_pc)SIGCXT_FROM_UCXT(uc)->SC_XIP == instr_cache_pc);
if (!native_state) {
/* For safe recreation we need to either be couldbelinking or hold
* the initexit lock (to keep someone from flushing current
@@ -4389,8 +4440,9 @@ check_for_modified_code(dcontext_t *dcontext, cache_pc instr_cache_pc,
} else {
ASSERT(!native_state);
/* Do not resume execution in cache, go back to dispatch. */
- transfer_from_sig_handler_to_fcache_return(dcontext, sc, next_pc,
- (linkstub_t *) get_selfmod_linkstub());
+ transfer_from_sig_handler_to_fcache_return
+ (dcontext, uc, NULL, SIGSEGV, next_pc,
+ (linkstub_t *) get_selfmod_linkstub(), false);
/* now have master_signal_handler return */
return true;
}
@@ -4742,7 +4794,7 @@ master_signal_handler_C(byte *xsp)
*/
if (is_write && !is_couldbelinking(dcontext) &&
OWN_NO_LOCKS(dcontext) &&
- check_for_modified_code(dcontext, pc, sc, target, true/*native*/))
+ check_for_modified_code(dcontext, pc, ucxt, target, true/*native*/))
break;
abort_on_fault(dcontext, DUMPCORE_CLIENT_EXCEPTION, pc, target, sig, frame,
exception_label_client, (sig == SIGSEGV) ? "SEGV" : "BUS",
@@ -4783,12 +4835,14 @@ master_signal_handler_C(byte *xsp)
* own gencode. client_exception_event() won't return if client
* wants to re-execute faulting instr.
*/
+ sigcontext_t sc_interrupted = *get_sigcontext_from_rt_frame(frame);
dr_signal_action_t action =
send_signal_to_client(dcontext, sig, frame, sc,
target, false/*!blocked*/, NULL);
if (action != DR_SIGNAL_DELIVER && /* for delivery, continue below */
!handle_client_action_from_cache(dcontext, sig, action, frame,
- sc, false/*!blocked*/)) {
+ sc, &sc_interrupted,
+ false/*!blocked*/)) {
/* client handled fault */
break;
}
@@ -4847,7 +4901,7 @@ master_signal_handler_C(byte *xsp)
* that were writable and marked read-only by us.
*/
if (is_write &&
- check_for_modified_code(dcontext, pc, sc, target, false/*!native*/)) {
+ check_for_modified_code(dcontext, pc, ucxt, target, false/*!native*/)) {
/* it was our signal, so don't pass to app -- return now */
break;
}
@@ -4931,7 +4985,8 @@ execute_handler_from_cache(dcontext_t *dcontext, int sig, sigframe_rt_t *our_fra
{
thread_sig_info_t *info = (thread_sig_info_t *) dcontext->signal_field;
/* we want to modify the sc in DR's frame */
- sigcontext_t *sc = get_sigcontext_from_rt_frame(our_frame);
+ kernel_ucontext_t *uc = get_ucontext_from_rt_frame(our_frame);
+ sigcontext_t *sc = SIGCXT_FROM_UCXT(uc);
kernel_sigset_t blocked;
/* Need to get xsp now before get new dcontext.
* This is the translated xsp, so we avoid PR 306410 (cleancall arg fault
@@ -4942,11 +4997,12 @@ execute_handler_from_cache(dcontext_t *dcontext, int sig, sigframe_rt_t *our_fra
* interruption point */);
#ifdef CLIENT_INTERFACE
+ sigcontext_t sc_interrupted = *sc;
dr_signal_action_t action =
send_signal_to_client(dcontext, sig, our_frame, sc_orig, access_address,
false/*not blocked*/, f);
if (!handle_client_action_from_cache(dcontext, sig, action, our_frame, sc_orig,
- false/*!blocked*/))
+ &sc_interrupted, false/*!blocked*/))
return false;
#else
if (info->app_sigaction[sig] == NULL ||
@@ -4978,6 +5034,7 @@ execute_handler_from_cache(dcontext_t *dcontext, int sig, sigframe_rt_t *our_fra
/* copy frame to appropriate stack and convert to non-rt if necessary */
copy_frame_to_stack(dcontext, sig, our_frame, (void *)xsp, false/*!pending*/);
LOG(THREAD, LOG_ASYNCH, 3, "\tcopied frame from "PFX" to "PFX"\n", our_frame, xsp);
+ sigcontext_t *app_sc = get_sigcontext_from_app_frame(info, sig, (void *)xsp);
/* Because of difficulties determining when/if a signal handler
* returns, we do what the kernel does: abandon all of our current
@@ -5034,10 +5091,10 @@ execute_handler_from_cache(dcontext_t *dcontext, int sig, sigframe_rt_t *our_fra
* and go through dispatch & interp, without messing up DR stack.
*/
transfer_from_sig_handler_to_fcache_return
- (dcontext, sc,
+ (dcontext, uc, app_sc, sig,
/* Make sure handler is next thing we execute */
(app_pc) SIGACT_PRIMARY_HANDLER(info->app_sigaction[sig]),
- (linkstub_t *) get_asynch_linkstub());
+ (linkstub_t *) get_asynch_linkstub(), true);
if ((info->app_sigaction[sig]->flags & SA_ONESHOT) != 0) {
/* clear handler now -- can't delete memory since sigreturn,
@@ -5149,6 +5206,7 @@ execute_handler_from_dispatch(dcontext_t *dcontext, int sig)
}
#ifdef CLIENT_INTERFACE
+ sigcontext_t sc_interrupted = *sc;
action = send_signal_to_client(dcontext, sig, frame, NULL,
info->sigpending[sig]->access_address,
false/*not blocked*/, NULL);
@@ -5157,13 +5215,20 @@ execute_handler_from_dispatch(dcontext_t *dcontext, int sig)
*/
if (action == DR_SIGNAL_REDIRECT) {
/* send_signal_to_client copied mcontext into frame's sc */
- ucontext_to_mcontext(get_mcontext(dcontext), uc);
+ priv_mcontext_t *mcontext = get_mcontext(dcontext);
+ ucontext_to_mcontext(mcontext, uc);
dcontext->next_tag = canonicalize_pc_target(dcontext, (app_pc) sc->SC_XIP);
if (is_building_trace(dcontext)) {
LOG(THREAD, LOG_ASYNCH, 3, "\tsquashing trace-in-progress\n");
trace_abort(dcontext);
}
IF_ARM(dr_set_isa_mode(dcontext, get_pc_mode_from_cpsr(sc), NULL));
+ mcontext->pc = dcontext->next_tag;
+ sig_full_cxt_t sc_interrupted_full = { &sc_interrupted, NULL/*not provided*/ };
+ if (instrument_kernel_xfer(dcontext, DR_XFER_CLIENT_REDIRECT, sc_interrupted_full,
+ NULL, NULL, dcontext->next_tag, mcontext->xsp,
+ osc_empty, mcontext, sig))
+ dcontext->next_tag = canonicalize_pc_target(dcontext, mcontext->pc);
return true; /* don't try another signal */
}
else if (action == DR_SIGNAL_SUPPRESS ||
@@ -5251,6 +5316,14 @@ execute_handler_from_dispatch(dcontext_t *dcontext, int sig)
*/
info->app_sigaction[sig]->handler = (handler_t) SIG_DFL;
}
+#ifdef CLIENT_INTERFACE
+ mcontext->pc = dcontext->next_tag;
+ sig_full_cxt_t sc_full = { sc, NULL/*not provided*/ };
+ if (instrument_kernel_xfer(dcontext, DR_XFER_SIGNAL_DELIVERY, sc_full, NULL, NULL,
+ dcontext->next_tag, mcontext->xsp, osc_empty, mcontext,
+ sig))
+ dcontext->next_tag = canonicalize_pc_target(dcontext, mcontext->pc);
+#endif
LOG(THREAD, LOG_ASYNCH, 3, "\tset xsp to "PFX"\n", xsp);
return true;
@@ -5662,6 +5735,7 @@ handle_sigreturn(dcontext_t *dcontext, void *ucxt_param, int style)
{
thread_sig_info_t *info = (thread_sig_info_t *) dcontext->signal_field;
sigcontext_t *sc = NULL; /* initialize to satisfy Mac clang */
+ kernel_ucontext_t *ucxt = NULL;
int sig = 0;
app_pc next_pc;
/* xsp was put in mcontext prior to pre_system_call() */
@@ -5688,7 +5762,6 @@ handle_sigreturn(dcontext_t *dcontext, void *ucxt_param, int style)
* so app can get away with it)
*/
if (rt) {
- kernel_ucontext_t *ucxt;
#ifdef LINUX
sigframe_rt_t *frame = (sigframe_rt_t *) (xsp IF_X86(- sizeof(char*)));
/* use si_signo instead of sig, less likely to be clobbered by app */
@@ -5764,6 +5837,9 @@ handle_sigreturn(dcontext_t *dcontext, void *ucxt_param, int style)
memcpy(&prevset.sig[1], &frame->IF_X86_ELSE(extramask, uc.sigset_ex),
sizeof(prevset.sig[1]));
}
+# ifdef ARM
+ ucxt = &frame->uc; /* we leave ucxt NULL for x86: not needed there */
+# endif
# endif
set_blocked(dcontext, &prevset, true/*absolute*/);
}
@@ -5798,6 +5874,14 @@ handle_sigreturn(dcontext_t *dcontext, void *ucxt_param, int style)
ASSERT(!safe_is_in_fcache(dcontext, (app_pc) sc->SC_XIP, (byte *)sc->SC_XSP));
+#ifdef CLIENT_INTERFACE
+ sig_full_cxt_t sc_full = { sc, NULL/*not provided*/ };
+ get_mcontext(dcontext)->pc = dcontext->next_tag;
+ instrument_kernel_xfer(dcontext, DR_XFER_SIGNAL_RETURN, osc_empty, NULL,
+ get_mcontext(dcontext), (app_pc)sc->SC_XIP, sc->SC_XSP,
+ sc_full, NULL, sig);
+#endif
+
#ifdef DEBUG
if (stats->loglevel >= 3 && (stats->logmask & LOG_ASYNCH) != 0) {
LOG(THREAD, LOG_ASYNCH, 3, "returning-to sigcontext "PFX":\n", sc);
@@ -5827,7 +5911,7 @@ handle_sigreturn(dcontext_t *dcontext, void *ucxt_param, int style)
* complexities of kernel's non-linux-like sigreturn semantics
*/
sig_full_cxt_t sc_full = {sc, NULL}; /* non-ARM so NULL ok */
- sigcontext_to_mcontext(get_mcontext(dcontext), &sc_full);
+ sigcontext_to_mcontext(get_mcontext(dcontext), &sc_full, DR_MC_ALL);
#else
/* HACK to get eax put into mcontext AFTER do_syscall */
dcontext->next_tag = (app_pc) sc->IF_X86_ELSE(SC_XAX, SC_R0);
diff --git a/core/win32/callback.c b/core/win32/callback.c
index 19efe2efcb9..18f23364ad3 100644
--- a/core/win32/callback.c
+++ b/core/win32/callback.c
@@ -508,7 +508,6 @@ if (AFTER_INTERCEPT_TAKE_OVER || AFTER_INTERCEPT_TAKE_OVER_SINGLE_SHOT
else
push no_cleanup # state->start_pc
endif
- push $0 # we assume always want !save_dcontext as arg to asynch_take_over
push/mov xsp # app_state_at_intercept_t *
call asynch_take_over
# should never reach here
@@ -567,8 +566,7 @@ endif (!AFTER_INTERCEPT_TAKE_OVER)
=> handler signature, exported as typedef intercept_function_t:
void handler(app_state_at_intercept_t *args)
-if AFTER_INTERCEPT_TAKE_OVER, then asynch_take_over is called, with "false" for
-its save_dcontext parameter
+if AFTER_INTERCEPT_TAKE_OVER, then asynch_take_over is called.
handler must make sure all paths exiting handler routine clear the
initstack mutex once not using the initstack itself!
@@ -2854,6 +2852,29 @@ is_syscall_trampoline(byte *pc, byte **tgt)
return false;
}
+#ifdef CLIENT_INTERFACE
+static void
+instrument_dispatcher(dcontext_t *dcontext, dr_kernel_xfer_type_t type,
+ app_state_at_intercept_t *state, CONTEXT *interrupted_cxt)
+{
+ app_pc nohook_pc = dr_fragment_app_pc(state->start_pc);
+ state->mc.pc = nohook_pc;
+ DWORD orig_flags = 0;
+ if (interrupted_cxt != NULL) {
+ /* Avoid copying simd fields: this event does not provide them. */
+ orig_flags = interrupted_cxt->ContextFlags;
+ interrupted_cxt->ContextFlags &=
+ ~(CONTEXT_DR_STATE & ~(CONTEXT_INTEGER|CONTEXT_CONTROL));
+ }
+ if (instrument_kernel_xfer(dcontext, type, interrupted_cxt, NULL, NULL,
+ state->mc.pc, state->mc.xsp, NULL, &state->mc, 0) &&
+ state->mc.pc != nohook_pc)
+ state->start_pc = state->mc.pc;
+ if (interrupted_cxt != NULL)
+ interrupted_cxt->ContextFlags = orig_flags;
+}
+#endif
+
/****************************************************************************
*/
/* TRACK_NTDLL: try to find where kernel re-emerges into user mode when it
@@ -3115,6 +3136,11 @@ intercept_new_thread(CONTEXT *cxt)
dstack = (byte *) ALIGN_FORWARD(dstack, PAGE_SIZE);
}
#endif
+ /* FIXME i#2718: we want the app_state_at_intercept_t context, which is
+ * the actual code to be run by the thread *now*, and not this CONTEXT which
+ * is what will be run later! We should make sure nobody is relying on
+ * the current (broken) context first.
+ */
context_to_mcontext_new_thread(&mc, cxt);
if (dynamo_thread_init(dstack, &mc _IF_CLIENT_INTERFACE(is_client)) != -1) {
app_pc thunk_xip = (app_pc)cxt->CXT_XIP;
@@ -3368,6 +3394,14 @@ intercept_ldr_init(app_state_at_intercept_t *state)
if (!is_thread_initialized()) {
if (intercept_new_thread(cxt))
return AFTER_INTERCEPT_LET_GO;
+#ifdef CLIENT_INTERFACE
+ /* We treat this as a kernel xfer, partly b/c of i#2718 where our
+ * thread init mcontext is wrong.
+ * We pretend it's an APC.
+ */
+ instrument_dispatcher(get_thread_private_dcontext(),
+ DR_XFER_APC_DISPATCHER, state, cxt);
+#endif
} else {
/* ntdll!LdrInitializeThunk is only used for initializing new
* threads so we should never get here unless early injected
@@ -3716,6 +3750,9 @@ intercept_apc(app_state_at_intercept_t *state)
asynch_retakeover_if_native();
state->callee_arg = (void *) false /* use cur dcontext */;
+#ifdef CLIENT_INTERFACE
+ instrument_dispatcher(dcontext, DR_XFER_APC_DISPATCHER, state, cxt);
+#endif
asynch_take_over(state);
} else
STATS_INC(num_APCs_noasynch);
@@ -3812,6 +3849,13 @@ intercept_nt_continue(CONTEXT *cxt, int flag)
LOG(THREAD, LOG_ASYNCH, 3, "target context:\n");
DOLOG(3, LOG_ASYNCH, { dump_context_info(cxt, THREAD, true); });
+#ifdef CLIENT_INTERFACE
+ get_mcontext(dcontext)->pc = dcontext->next_tag;
+ instrument_kernel_xfer(dcontext, DR_XFER_CONTINUE,
+ NULL, NULL, get_mcontext(dcontext),
+ (app_pc)cxt->CXT_XIP, cxt->CXT_XSP, cxt, NULL, 0);
+#endif
+
/* Updates debug register values.
* FIXME should check dr7 upper bits, and maybe dr6
* We ignore the potential race condition.
@@ -4001,6 +4045,13 @@ intercept_nt_setcontext(dcontext_t *dcontext, CONTEXT *cxt)
trace_abort(dcontext);
}
+#ifdef CLIENT_INTERFACE
+ get_mcontext(dcontext)->pc = dcontext->next_tag;
+ instrument_kernel_xfer(dcontext, DR_XFER_SET_CONTEXT_THREAD,
+ NULL, NULL, get_mcontext(dcontext),
+ (app_pc)cxt->CXT_XIP, cxt->CXT_XSP, cxt, NULL, 0);
+#endif
+
/* Yes, we use the same x86.asm and x86_code.c procedures as
* NtContinue: nt_continue_dynamo_start and nt_continue_start_setup
*/
@@ -5952,6 +6003,9 @@ intercept_exception(app_state_at_intercept_t *state)
* We don't save the cur dcontext.
*/
state->callee_arg = (void *) false /* use cur dcontext */;
+#ifdef CLIENT_INTERFACE
+ instrument_dispatcher(dcontext, DR_XFER_EXCEPTION_DISPATCHER, state, cxt);
+#endif
asynch_take_over(state);
} else
STATS_INC(num_exceptions_noasynch);
@@ -6010,6 +6064,10 @@ intercept_raise_exception(app_state_at_intercept_t *state)
* We don't save the cur dcontext.
*/
state->callee_arg = (void *) false /* use cur dcontext */;
+#ifdef CLIENT_INTERFACE
+ instrument_dispatcher(get_thread_private_dcontext(),
+ DR_XFER_RAISE_DISPATCHER, state, NULL);
+#endif
asynch_take_over(state);
} else
STATS_INC(num_raise_exceptions_noasynch);
@@ -6370,6 +6428,9 @@ intercept_callback_start(app_state_at_intercept_t *state)
asynch_retakeover_if_native();
state->callee_arg = (void *)(ptr_uint_t) true /* save cur dcontext */;
+#ifdef CLIENT_INTERFACE
+ instrument_dispatcher(dcontext, DR_XFER_CALLBACK_DISPATCHER, state, NULL);
+#endif
asynch_take_over(state);
} else
STATS_INC(num_callbacks_noasynch);
@@ -6713,6 +6774,20 @@ callback_start_return(priv_mcontext_t *mc)
dump_mcontext(get_mcontext(cur_dcontext), cur_dcontext->logfile,
DUMP_NOT_XML);
});
+
+# ifdef CLIENT_INTERFACE
+ priv_mcontext_t *cur_mc = get_mcontext(cur_dcontext);
+ /* XXX: if the retaddr is in the xsi slot, how do we get back the orig xsi value?
+ * Presumably we can't: should we document this?
+ */
+ cur_mc->pc = POST_SYSCALL_PC(cur_dcontext);
+ get_mcontext(prev_dcontext)->pc = prev_dcontext->next_tag;
+ /* We don't support changing the target context for cbret. */
+ instrument_kernel_xfer(cur_dcontext, DR_XFER_CALLBACK_RETURN,
+ NULL, NULL, get_mcontext(prev_dcontext),
+ cur_mc->pc, cur_mc->xsp, NULL, cur_mc, 0);
+# endif
+
#else /* DCONTEXT_IN_EDI */
/* restore previous dcontext */
prev_dcontext = cur_dcontext->next_saved;
diff --git a/core/win32/os.c b/core/win32/os.c
index 14ff1564b58..e99530432a3 100644
--- a/core/win32/os.c
+++ b/core/win32/os.c
@@ -4980,7 +4980,6 @@ thread_set_self_mcontext(priv_mcontext_t *mc)
ASSERT_NOT_REACHED();
}
-#ifdef CLIENT_INTERFACE
DR_API
bool
dr_mcontext_to_context(CONTEXT *dst, dr_mcontext_t *src)
@@ -5004,6 +5003,9 @@ dr_mcontext_to_context(CONTEXT *dst, dr_mcontext_t *src)
mcontext_to_context(dst, dr_mcontext_as_priv_mcontext(src),
true/*cur segs, which we document*/);
+ /* XXX: CONTEXT_CONTROL includes xbp, while that's under DR_MC_INTEGER.
+ * We document this difference and recommend passing both to avoid problems.
+ */
if (!TEST(DR_MC_INTEGER, src->flags))
dst->ContextFlags &= ~(CONTEXT_INTEGER);
if (!TEST(DR_MC_CONTROL, src->flags))
@@ -5011,7 +5013,70 @@ dr_mcontext_to_context(CONTEXT *dst, dr_mcontext_t *src)
return true;
}
-#endif
+
+/* CONTEXT_CONTROL includes xbp, but it's under DR_MC_INTEGER: callers beware! */
+static dr_mcontext_flags_t
+match_mcontext_flags_to_context_flags(dr_mcontext_flags_t mc_flags, DWORD cxt_flags)
+{
+ if (TEST(DR_MC_INTEGER, mc_flags) && !TESTALL(CONTEXT_INTEGER, cxt_flags))
+ mc_flags &= ~DR_MC_INTEGER;
+ if (TEST(DR_MC_CONTROL, mc_flags) && !TESTALL(CONTEXT_CONTROL, cxt_flags))
+ mc_flags &= ~DR_MC_CONTROL;
+ if (TEST(DR_MC_MULTIMEDIA, mc_flags) &&
+ !TESTALL(CONTEXT_DR_STATE & ~(CONTEXT_INTEGER|CONTEXT_CONTROL), cxt_flags))
+ mc_flags &= ~DR_MC_MULTIMEDIA;
+ return mc_flags;
+}
+
+/* Only one of mc and dmc can be non-NULL. */
+bool
+os_context_to_mcontext(dr_mcontext_t *dmc, priv_mcontext_t *mc, os_cxt_ptr_t osc)
+{
+ if (dmc != NULL) {
+ /* We have to handle mismatches between dmc->flags and osc->ContextFlags. We
+ * come here on NtContinue where often only CONTROL|INTEGER|SEGMENTS are
+ * available. Our general strategy: keep context_to_mcontext() happy and fix
+ * up here. We assume it's ok to clobber parts of dmc not requested by its
+ * flags, and ok to temporarily write to osc, even though it may be app
+ * memory.
+ */
+ DWORD orig_flags = osc->ContextFlags;
+ if (!TESTALL(CONTEXT_DR_STATE_NO_YMM, osc->ContextFlags))
+ osc->ContextFlags = CONTEXT_DR_STATE_NO_YMM;
+ context_to_mcontext(dr_mcontext_as_priv_mcontext(dmc), osc);
+ osc->ContextFlags = orig_flags;
+ /* We document the xbp difference: clients who care are advised to use syscall
+ * events instead of the kernel xfer events that come through here.
+ */
+ dmc->flags = match_mcontext_flags_to_context_flags(dmc->flags, orig_flags);
+ } else if (mc != NULL) {
+ /* We don't support coming here with an incomplete CONTEXT: it doesn't
+ * happen in the code base currently.
+ */
+ ASSERT(TESTALL(CONTEXT_DR_STATE_NO_YMM, osc->ContextFlags));
+ context_to_mcontext(mc, osc);
+ } else
+ return false;
+ return true;
+}
+
+/* Only one of mc and dmc can be non-NULL. */
+bool
+mcontext_to_os_context(os_cxt_ptr_t osc, dr_mcontext_t *dmc, priv_mcontext_t *mc)
+{
+ if (dmc != NULL) {
+ /* We document the xbp difference: clients who care are advised to use syscall
+ * events instead of the kernel xfer events that come through here.
+ */
+ dmc->flags =
+ match_mcontext_flags_to_context_flags(dmc->flags, osc->ContextFlags);
+ dr_mcontext_to_context(osc, dmc);
+ } else if (mc != NULL)
+ mcontext_to_context(osc, mc, true/*cur segs*/);
+ else
+ return false;
+ return true;
+}
int
get_num_processors()
diff --git a/core/win32/os_exports.h b/core/win32/os_exports.h
index 9bb749e5fd8..afd88297881 100644
--- a/core/win32/os_exports.h
+++ b/core/win32/os_exports.h
@@ -173,6 +173,26 @@ int exception_frame_chain_depth(dcontext_t *dcontext);
#define CONTEXT_HEAP_SIZE(cxt) (sizeof(cxt) IF_X64(+8)/*heap is 8-aligned already*/)
#define CONTEXT_HEAP_SIZE_OPAQUE (CONTEXT_HEAP_SIZE(CONTEXT))
+typedef CONTEXT *os_cxt_ptr_t;
+#define osc_empty NULL
+
+static inline bool
+is_os_cxt_ptr_null(os_cxt_ptr_t osc)
+{
+ return osc == NULL;
+}
+
+static inline void
+set_os_cxt_ptr_null(os_cxt_ptr_t *osc)
+{
+ *osc = NULL;
+}
+
+/* Only one of mc and dmc can be non-NULL. */
+bool os_context_to_mcontext(dr_mcontext_t *dmc, priv_mcontext_t *mc, os_cxt_ptr_t osc);
+/* Only one of mc and dmc can be non-NULL. */
+bool mcontext_to_os_context(os_cxt_ptr_t osc, dr_mcontext_t *dmc, priv_mcontext_t *mc);
+
bool
thread_get_context(thread_record_t *tr, CONTEXT *context);
diff --git a/ext/drmgr/drmgr.c b/ext/drmgr/drmgr.c
index 97aa62870eb..150f7283a25 100644
--- a/ext/drmgr/drmgr.c
+++ b/ext/drmgr/drmgr.c
@@ -60,6 +60,8 @@
#undef dr_unregister_module_load_event
#undef dr_register_module_unload_event
#undef dr_unregister_module_unload_event
+#undef dr_register_kernel_xfer_event
+#undef dr_unregister_kernel_xfer_event
#undef dr_register_signal_event
#undef dr_unregister_signal_event
#undef dr_register_exception_event
@@ -121,6 +123,7 @@ typedef struct _generic_event_entry_t {
void (*postsys_cb)(void *, int);
void (*modload_cb)(void *, const module_data_t *, bool);
void (*modunload_cb)(void *, const module_data_t *);
+ void (*kernel_xfer_cb)(void *, const dr_kernel_xfer_info_t *);
#ifdef UNIX
dr_signal_action_t (*signal_cb)(void *, dr_siginfo_t *);
#endif
@@ -242,6 +245,9 @@ static void *modload_event_lock;
static cb_list_t cblist_modunload;
static void *modunload_event_lock;
+static cb_list_t cblist_kernel_xfer;
+static void *kernel_xfer_event_lock;
+
#ifdef UNIX
static cb_list_t cblist_signal;
static void *signal_event_lock;
@@ -256,12 +262,6 @@ static cb_list_t cblist_fault;
static void *fault_event_lock;
static bool registered_fault; /* for lazy registration */
-#ifdef WINDOWS
-static byte *addr_KiCallback;
-static int sysnum_NtCallbackReturn;
-# define CBRET_INTERRUPT_NUM 0x2b
-#endif
-
static void
drmgr_thread_init_event(void *drcontext);
@@ -293,6 +293,9 @@ drmgr_modload_event(void *drcontext, const module_data_t *info,
static void
drmgr_modunload_event(void *drcontext, const module_data_t *info);
+static void
+drmgr_kernel_xfer_event(void *drcontext, const dr_kernel_xfer_info_t *info);
+
#ifdef UNIX
static dr_signal_action_t
drmgr_signal_event(void *drcontext, dr_siginfo_t *siginfo);
@@ -307,14 +310,6 @@ static bool
drmgr_restore_state_event(void *drcontext, bool restore_memory,
dr_restore_state_info_t *info);
-static bool
-drmgr_cls_presys_event(void *drcontext, int sysnum);
-
-#ifdef WINDOWS
-static void
-drmgr_cls_exit(void);
-#endif
-
static void
our_thread_init_event(void *drcontext);
@@ -346,6 +341,7 @@ drmgr_init(void)
postsys_event_lock = dr_rwlock_create();
modload_event_lock = dr_rwlock_create();
modunload_event_lock = dr_rwlock_create();
+ kernel_xfer_event_lock = dr_rwlock_create();
#ifdef UNIX
signal_event_lock = dr_rwlock_create();
#endif
@@ -360,6 +356,7 @@ drmgr_init(void)
dr_register_post_syscall_event(drmgr_postsyscall_event);
dr_register_module_load_event(drmgr_modload_event);
dr_register_module_unload_event(drmgr_modunload_event);
+ dr_register_kernel_xfer_event(drmgr_kernel_xfer_event);
#ifdef UNIX
dr_register_signal_event(drmgr_signal_event);
#endif
@@ -400,6 +397,7 @@ drmgr_exit(void)
dr_unregister_post_syscall_event(drmgr_postsyscall_event);
dr_unregister_module_load_event(drmgr_modload_event);
dr_unregister_module_unload_event(drmgr_modunload_event);
+ dr_unregister_kernel_xfer_event(drmgr_kernel_xfer_event);
#ifdef UNIX
dr_unregister_signal_event(drmgr_signal_event);
#endif
@@ -413,9 +411,6 @@ drmgr_exit(void)
dr_unregister_restore_state_ex_event(drmgr_restore_state_event);
registered_fault = false;
}
-#ifdef WINDOWS
- drmgr_cls_exit();
-#endif
dr_rwlock_destroy(fault_event_lock);
#ifdef UNIX
@@ -424,6 +419,7 @@ drmgr_exit(void)
#ifdef WINDOWS
dr_rwlock_destroy(exception_event_lock);
#endif
+ dr_rwlock_destroy(kernel_xfer_event_lock);
dr_rwlock_destroy(modunload_event_lock);
dr_rwlock_destroy(modload_event_lock);
dr_rwlock_destroy(postsys_event_lock);
@@ -1212,6 +1208,7 @@ drmgr_event_init(void)
cblist_init(&cblist_postsys, sizeof(generic_event_entry_t));
cblist_init(&cblist_modload, sizeof(generic_event_entry_t));
cblist_init(&cblist_modunload, sizeof(generic_event_entry_t));
+ cblist_init(&cblist_kernel_xfer, sizeof(generic_event_entry_t));
#ifdef UNIX
cblist_init(&cblist_signal, sizeof(generic_event_entry_t));
#endif
@@ -1236,6 +1233,7 @@ drmgr_event_exit(void)
cblist_delete(&cblist_postsys);
cblist_delete(&cblist_modload);
cblist_delete(&cblist_modunload);
+ cblist_delete(&cblist_kernel_xfer);
#ifdef UNIX
cblist_delete(&cblist_signal);
#endif
@@ -1336,8 +1334,10 @@ drmgr_presyscall_event(void *drcontext, int sysnum)
execute = (*iter.cbs.generic[i].cb.presys_cb)(drcontext, sysnum) && execute;
}
- /* this must go last (the whole reason we're wrapping this) */
- execute = drmgr_cls_presys_event(drcontext, sysnum) && execute;
+ /* We used to track NtCallbackReturn for CLS (before DR provided the kernel xfer
+ * event) and had to handle it last here. Now we have nothing ourselves to
+ * do here.
+ */
cblist_delete_local(drcontext, &iter, BUFFER_SIZE_ELEMENTS(local));
@@ -1497,7 +1497,7 @@ drmgr_modunload_event(void *drcontext, const module_data_t *info)
DR_EXPORT
bool
drmgr_register_signal_event(dr_signal_action_t (*func)
- (void *drcontext, dr_siginfo_t *siginfo))
+ (void *drcontext, dr_siginfo_t *siginfo))
{
return drmgr_generic_event_add(&cblist_signal, signal_event_lock,
(void (*)(void)) func, NULL);
@@ -1865,10 +1865,6 @@ drmgr_insert_write_tls_field(void *drcontext, int idx,
* CLS
*/
-#ifdef WINDOWS
-static int cls_initialized; /* 0=not tried; >0=success; <0=failure */
-#endif
-
static bool
drmgr_cls_stack_push_event(void *drcontext, bool new_depth)
{
@@ -2004,62 +2000,13 @@ drmgr_cls_stack_exit(void *drcontext)
}
#ifdef WINDOWS
-static bool
-drmgr_event_filter_syscall(void *drcontext, int sysnum)
-{
- if (sysnum == sysnum_NtCallbackReturn)
- return true;
- else
- return false;
-}
-
-static bool
-drmgr_cls_presys_event(void *drcontext, int sysnum)
-{
- /* we wrap the pre-syscall event to ensure this goes last,
- * after all other presys events, so we have no references
- * to the cls data before we swap it
- */
- if (sysnum == sysnum_NtCallbackReturn)
- drmgr_cls_stack_pop();
- return true;
-}
-
-/* Goes first with high negative priority */
-static dr_emit_flags_t
-drmgr_event_insert_cb(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst,
- bool for_trace, bool translating, void *user_data)
-{
- if (instr_get_app_pc(inst) == addr_KiCallback) {
- dr_insert_clean_call(drcontext, bb, inst, (void *)drmgr_cls_stack_push,
- false, 0);
- }
- return DR_EMIT_DEFAULT;
-}
-
-/* Goes last with high positive priority */
-static dr_emit_flags_t
-drmgr_event_insert_cbret(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst,
- bool for_trace, bool translating, void *user_data)
-{
- if (instr_opcode_valid(inst) &&
- /* For -fast_client_decode we can have level 0 instrs so check
- * to ensure this is an single instr with valid opcode.
- */
- instr_get_opcode(inst) == OP_int &&
- opnd_get_immed_int(instr_get_src(inst, 0)) == CBRET_INTERRUPT_NUM) {
- dr_insert_clean_call(drcontext, bb, inst, (void *)drmgr_cls_stack_pop,
- false, 0);
- }
- return DR_EMIT_DEFAULT;
-}
-
/* Determines the syscall from its Nt* wrapper.
* Returns -1 on error.
* FIXME: does not handle somebody hooking the wrapper.
*/
/* XXX: exporting this so drwrap can use it but I might prefer to have
- * this in drutil or the upcoming drsys
+ * this in drutil or the upcoming drsys, especially since drmgr no longer
+ * uses this now that DR provides a kernel xfer event.
*/
DR_EXPORT
int
@@ -2093,82 +2040,78 @@ drmgr_decode_sysnum_from_wrapper(app_pc entry)
instr_free(drcontext, &instr);
return num;
}
+#endif /* WINDOWS */
-static bool
-drmgr_cls_init(void)
+static void
+drmgr_kernel_xfer_event(void *drcontext, const dr_kernel_xfer_info_t *info)
{
- /* For callback init we watch for KiUserCallbackDispatcher.
- * For callback exit we watch for NtCallbackReturn or int 0x2b.
+ /* We used to watch KiUserCallbackDispatcher, identify NtCallbackReturn's number,
+ * and watch int 0x2b ourselves, but now DR provides us with an event that
+ * operates at the last moment before the kernel action, making our lives much
+ * easier: we just have to order all other xfer events vs ours.
*/
- module_data_t *data;
- module_handle_t ntdll_lib;
- app_pc addr_cbret;
- /* We need to go very early to push the new CLS context */
- drmgr_priority_t pri_cb = {sizeof(pri_cb), DRMGR_PRIORITY_NAME_CLS_ENTRY,
- NULL, NULL, DRMGR_PRIORITY_INSERT_CLS_ENTRY};
- drmgr_priority_t pri_cbret = {sizeof(pri_cbret), DRMGR_PRIORITY_NAME_CLS_EXIT,
- NULL, NULL, DRMGR_PRIORITY_INSERT_CLS_EXIT};
-
- if (cls_initialized > 0)
- return true;
- else if (cls_initialized < 0)
- return false;
- cls_initialized = -1;
+ generic_event_entry_t local[EVENTS_STACK_SZ];
+ cb_list_t iter;
+ uint i;
+ dr_rwlock_read_lock(kernel_xfer_event_lock);
+ cblist_create_local(drcontext, &cblist_kernel_xfer, &iter, (byte *)local,
+ BUFFER_SIZE_ELEMENTS(local));
+ dr_rwlock_read_unlock(kernel_xfer_event_lock);
- data = dr_lookup_module_by_name("ntdll.dll");
- if (data == NULL) {
- /* fatal error: something is really wrong w/ underlying DR */
- return false;
+ if (info->type == DR_XFER_CALLBACK_DISPATCHER) {
+ /* We want to go first for callback entry so clients have a new context. */
+ drmgr_cls_stack_push();
}
- ntdll_lib = data->handle;
- dr_free_module_data(data);
- addr_KiCallback = (app_pc) dr_get_proc_address(ntdll_lib, "KiUserCallbackDispatcher");
- if (addr_KiCallback == NULL)
- return false; /* should not happen */
- /* the wrapper is not good enough for two reasons: one, we want to swap
- * contexts at the last possible moment, not prior to executing a few
- * instrs; second, we'll miss hand-rolled syscalls
- */
- addr_cbret = (app_pc) dr_get_proc_address(ntdll_lib, "NtCallbackReturn");
- if (addr_cbret == NULL)
- return false; /* should not happen */
- sysnum_NtCallbackReturn = drmgr_decode_sysnum_from_wrapper(addr_cbret);
- if (sysnum_NtCallbackReturn == -1)
- return false; /* should not happen */
-
- if (!drmgr_register_bb_instrumentation_event(NULL, drmgr_event_insert_cb, &pri_cb) ||
- !drmgr_register_bb_instrumentation_event(NULL, drmgr_event_insert_cbret,
- &pri_cbret))
- return false;
- dr_register_filter_syscall_event(drmgr_event_filter_syscall);
- cls_initialized = 1;
- return true;
+
+ for (i = 0; i < iter.num; i++) {
+ if (!iter.cbs.generic[i].pri.valid)
+ continue;
+ (*iter.cbs.generic[i].cb.kernel_xfer_cb)(drcontext, info);
+ }
+
+ if (info->type == DR_XFER_CALLBACK_RETURN) {
+ /* We want to go last for cbret to swap contexts at the last possible moment,
+ * to ensure there are no references to cls data before we swap it.
+ */
+ drmgr_cls_stack_pop();
+ }
+
+ cblist_delete_local(drcontext, &iter, BUFFER_SIZE_ELEMENTS(local));
}
-static void
-drmgr_cls_exit(void)
+DR_EXPORT
+bool
+drmgr_register_kernel_xfer_event(void (*func)(void *drcontext,
+ const dr_kernel_xfer_info_t *info))
{
- if (cls_initialized > 0)
- dr_unregister_filter_syscall_event(drmgr_event_filter_syscall);
+ return drmgr_generic_event_add(&cblist_kernel_xfer, kernel_xfer_event_lock,
+ (void (*)(void)) func, NULL);
}
-#else
-static bool
-drmgr_cls_presys_event(void *drcontext, int sysnum)
+DR_EXPORT
+bool
+drmgr_register_kernel_xfer_event_ex(void (*func)(void *drcontext,
+ const dr_kernel_xfer_info_t *info),
+ drmgr_priority_t *priority)
{
- return true;
+ return drmgr_generic_event_add(&cblist_kernel_xfer, kernel_xfer_event_lock,
+ (void (*)(void)) func, priority);
+}
+
+DR_EXPORT
+bool
+drmgr_unregister_kernel_xfer_event(void (*func)(void *drcontext,
+ const dr_kernel_xfer_info_t *info))
+{
+ return drmgr_generic_event_remove(&cblist_kernel_xfer, kernel_xfer_event_lock,
+ (void (*)(void)) func);
}
-#endif /* WINDOWS */
DR_EXPORT
int
drmgr_register_cls_field(void (*cb_init_func)(void *drcontext, bool new_depth),
void (*cb_exit_func)(void *drcontext, bool thread_exit))
{
-#ifdef WINDOWS
- if (!drmgr_cls_init())
- return -1;
-#endif
if (cb_init_func == NULL || cb_exit_func == NULL)
return -1;
if (!drmgr_generic_event_add(&cblist_cls_init, cls_event_lock,
diff --git a/ext/drmgr/drmgr.h b/ext/drmgr/drmgr.h
index a71a112b019..c61b68af355 100644
--- a/ext/drmgr/drmgr.h
+++ b/ext/drmgr/drmgr.h
@@ -79,6 +79,8 @@ extern "C" {
# define dr_unregister_module_load_event DO_NOT_USE_module_load_USE_drmgr_events_instead
# define dr_register_module_unload_event DO_NOT_USE_module_unload_USE_drmgr_instead
# define dr_unregister_module_unload_event DO_NOT_USE_module_unload_USE_drmgr_instead
+# define dr_register_kernel_xfer_event DO_NOT_USE_kernel_xfer_event_USE_drmgr_instead
+# define dr_unregister_kernel_xfer_event DO_NOT_USE_kernel_xfer_event_USE_drmgr_instead
# define dr_register_signal_event DO_NOT_USE_signal_event_USE_drmgr_instead
# define dr_unregister_signal_event DO_NOT_USE_signal_event_USE_drmgr_instead
# define dr_register_exception_event DO_NOT_USE_exception_event_USE_drmgr_instead
@@ -546,35 +548,6 @@ drmgr_insert_write_tls_field(void *drcontext, int idx,
* CLS
*/
-/**
- * Priority of drmgr instrumentation pass used to track CLS. Users
- * of drmgr can use the names #DRMGR_PRIORITY_NAME_CLS_ENTRY or
- * #DRMGR_PRIORITY_NAME_CLS_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. The #DRMGR_PRIORITY_INSERT_CLS_ENTRY
- * should go before any client event and the #DRMGR_PRIORITY_INSERT_CLS_EXIT
- * should go after any client event.
- */
-enum {
- DRMGR_PRIORITY_INSERT_CLS_ENTRY = -500, /**< Priority of CLS entry tracking */
- DRMGR_PRIORITY_INSERT_CLS_EXIT = 5000, /**< Priority of CLS exit tracking */
-};
-
-/** Name of drmgr insert pass priority for CLS entry tracking */
-#ifdef WINDOWS
-# define DRMGR_PRIORITY_NAME_CLS_ENTRY "drmgr_cls_entry"
-#else
-# define DRMGR_PRIORITY_NAME_CLS_ENTRY NULL
-#endif
-
-/** Name of drmgr insert pass priority for CLS exit tracking */
-#ifdef WINDOWS
-# define DRMGR_PRIORITY_NAME_CLS_EXIT "drmgr_cls_exit"
-#else
-# define DRMGR_PRIORITY_NAME_CLS_EXIT NULL
-#endif
-
DR_EXPORT
/**
* Reserves a callback-local storage (cls) slot. Thread-local storage
@@ -620,7 +593,9 @@ DR_EXPORT
* provide a stack of contexts on Linux, or to provide a stack of
* contexts for any other purpose such as layered wrapped functions.
* These push and pop functions are automatically called on Windows
- * callback entry and exit.
+ * callback entry and exit, with the push called in DR's kernel xfer
+ * event prior to any client callback for that event, and pop called in
+ * the same event but after any client callback.
*/
int
drmgr_register_cls_field(void (*cb_init_func)(void *drcontext, bool new_depth),
@@ -955,6 +930,39 @@ bool
drmgr_unregister_module_unload_event(void (*func)
(void *drcontext, const module_data_t *info));
+DR_EXPORT
+/**
+ * Registers a callback function for the kernel transfer event, which
+ * behaves just like DR's kernel transfer event dr_register_kernel_xfer_event().
+ * \return whether successful.
+ */
+bool
+drmgr_register_kernel_xfer_event(void (*func)(void *drcontext,
+ const dr_kernel_xfer_info_t *info));
+
+DR_EXPORT
+/**
+ * Registers a callback function for the kernel transfer event, which behaves just
+ * like DR's kernel transfer event dr_register_kernel_xfer_event(), except that it is
+ * ordered according to \p priority. A default priority of 0 is used for events
+ * registered via drmgr_register_module_unload_event().
+ * \return whether successful.
+ */
+bool
+drmgr_register_kernel_xfer_event_ex(void (*func)(void *drcontext,
+ const dr_kernel_xfer_info_t *info),
+ drmgr_priority_t *priority);
+
+DR_EXPORT
+/**
+ * Unregister a callback function for the kernel transfer event.
+ * \return true if unregistration is successful and false if it is not
+ * (e.g., \p func was not registered).
+ */
+bool
+drmgr_unregister_kernel_xfer_event(void (*func)(void *drcontext,
+ const dr_kernel_xfer_info_t *info));
+
#ifdef UNIX
DR_EXPORT
/**
@@ -964,7 +972,7 @@ DR_EXPORT
*/
bool
drmgr_register_signal_event(dr_signal_action_t (*func)
- (void *drcontext, dr_siginfo_t *siginfo));
+ (void *drcontext, dr_siginfo_t *siginfo));
DR_EXPORT
/**
diff --git a/suite/tests/CMakeLists.txt b/suite/tests/CMakeLists.txt
index 38ee6b8525e..3d6c4881c12 100644
--- a/suite/tests/CMakeLists.txt
+++ b/suite/tests/CMakeLists.txt
@@ -1948,6 +1948,8 @@ if (CLIENT_INTERFACE)
# i#1866: stress our private loader
tobuild_ci(client.loader client-interface/loader.c "" "" "")
target_link_libraries(client.loader.dll "shlwapi")
+
+ tobuild_ci(client.winxfer client-interface/winxfer.c "" "" "")
endif (UNIX)
if (X86) # FIXME i#1551, i#1569: port asm to ARM and AArch64
tobuild_ci(client.file_io client-interface/file_io.c
diff --git a/suite/tests/client-interface/drmgr-test.dll.c b/suite/tests/client-interface/drmgr-test.dll.c
index f6b7d5f8287..e3509d2a5e5 100755
--- a/suite/tests/client-interface/drmgr-test.dll.c
+++ b/suite/tests/client-interface/drmgr-test.dll.c
@@ -110,6 +110,8 @@ static dr_emit_flags_t event_bb4_insert2(void *drcontext, void *tag, instrlist_t
static dr_emit_flags_t one_time_bb_event(void *drcontext, void *tag, instrlist_t *bb,
bool for_trace, bool translating);
+static void event_kernel_xfer(void *drcontext, const dr_kernel_xfer_info_t *info);
+
DR_EXPORT void
dr_init(client_id_t id)
{
@@ -176,6 +178,13 @@ dr_init(client_id_t id)
ok = drmgr_register_bb_app2app_event(one_time_bb_event, NULL);
CHECK(ok, "drmgr app2app registration failed");
+
+ ok = drmgr_register_kernel_xfer_event(event_kernel_xfer);
+ CHECK(ok, "drmgr_register_kernel_xfer_event failed");
+ ok = drmgr_unregister_kernel_xfer_event(event_kernel_xfer);
+ CHECK(ok, "drmgr_unregister_kernel_xfer_event failed");
+ ok = drmgr_register_kernel_xfer_event_ex(event_kernel_xfer, &priority);
+ CHECK(ok, "drmgr_register_kernel_xfer_event_ex failed");
}
static void
@@ -206,6 +215,8 @@ event_exit(void)
if (!drmgr_unregister_cls_field(event_thread_context_init,
event_thread_context_exit, cls_idx))
CHECK(false, "drmgr unregistration failed");
+ if (!drmgr_unregister_kernel_xfer_event(event_kernel_xfer))
+ CHECK(false, "drmgr_unregister_kernel_xfer_event failed");
drmgr_exit();
dr_fprintf(STDERR, "all done\n");
@@ -542,3 +553,13 @@ one_time_bb_event(void *drcontext, void *tag, instrlist_t *bb,
return DR_EMIT_DEFAULT;
}
+
+/* test kernel xfer event callback */
+static void
+event_kernel_xfer(void *drcontext, const dr_kernel_xfer_info_t *info)
+{
+ /* We rely on other tests for the details here. Mostly we're just testing
+ * the register/unregister logic.
+ */
+ CHECK(drcontext == dr_get_current_drcontext(), "sanity check");
+}
diff --git a/suite/tests/client-interface/events.dll.c b/suite/tests/client-interface/events.dll.c
index a0ad06e6fff..f1b2adb2d56 100644
--- a/suite/tests/client-interface/events.dll.c
+++ b/suite/tests/client-interface/events.dll.c
@@ -77,6 +77,8 @@ enum event_seq {
EVENT_PRE_SYSCALL_2,
EVENT_POST_SYSCALL_1,
EVENT_POST_SYSCALL_2,
+ EVENT_KERNEL_XFER_1,
+ EVENT_KERNEL_XFER_2,
EVENT_MODULE_UNLOAD_1,
EVENT_MODULE_UNLOAD_2,
EVENT_THREAD_EXIT_1,
@@ -113,6 +115,8 @@ const char * const name[EVENT_last] = {
"pre syscall event 2",
"post syscall event 1",
"post syscall event 2",
+ "kernel xfer event 1",
+ "kernel xfer event 2",
"module unload event 1",
"module unload event 2",
"thread exit event 1",
@@ -151,20 +155,23 @@ inc_count_second(int second)
}
-static
-void check_result(void)
+static void
+check_result(void)
{
int i;
for (i = 0; i < EVENT_last; i++) {
if (counts[i] == 0)
continue;
- dr_fprintf(STDERR, "%s is called %d time(s)\n", name[i], counts[i]);
+ if (counts[i] == 1)
+ dr_fprintf(STDERR, "%s is called 1 time\n", name[i]);
+ else
+ dr_fprintf(STDERR, "%s is called >1 time\n", name[i]);
dr_flush_file(STDOUT);
}
}
-static
-void exit_event1(void)
+static void
+exit_event1(void)
{
dr_fprintf(STDERR, "exit event 1\n");
dr_flush_file(STDOUT);
@@ -175,8 +182,8 @@ void exit_event1(void)
dr_mutex_destroy(mutex);
}
-static
-void exit_event2(void)
+static void
+exit_event2(void)
{
dr_fprintf(STDERR, "exit event 2\n");
dr_flush_file(STDOUT);
@@ -185,31 +192,31 @@ void exit_event2(void)
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-void thread_init_event1(void *drcontext)
+static void
+thread_init_event1(void *drcontext)
{
inc_count_first(EVENT_THREAD_INIT_1, EVENT_THREAD_INIT_2);
if (!dr_unregister_thread_init_event(thread_init_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-void thread_init_event2(void *drcontext)
+static void
+thread_init_event2(void *drcontext)
{
inc_count_second(EVENT_THREAD_INIT_2);
if (!dr_unregister_thread_init_event(thread_init_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-void thread_exit_event1(void *drcontext)
+static void
+thread_exit_event1(void *drcontext)
{
inc_count_first(EVENT_THREAD_EXIT_1, EVENT_THREAD_EXIT_2);
if (!dr_unregister_thread_exit_event(thread_exit_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-void thread_exit_event2(void *drcontext)
+static void
+thread_exit_event2(void *drcontext)
{
inc_count_second(EVENT_THREAD_EXIT_2);
if (!dr_unregister_thread_exit_event(thread_exit_event2))
@@ -217,15 +224,15 @@ void thread_exit_event2(void *drcontext)
}
#ifdef UNIX
-static
-void fork_init_event1(void *drcontext)
+static void
+fork_init_event1(void *drcontext)
{
inc_count_first(EVENT_FORK_INIT_1, EVENT_FORK_INIT_2);
if (!dr_unregister_fork_init_event(fork_init_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-void fork_init_event2(void *drcontext)
+static void
+fork_init_event2(void *drcontext)
{
int i;
dr_mutex_lock(mutex);
@@ -296,24 +303,24 @@ dr_custom_trace_action_t end_trace_event2(void *dcontext, void *trace_tag, void
return CUSTOM_TRACE_DR_DECIDES;
}
-static
-void delete_event1(void *dcontext, void *tag)
+static void
+delete_event1(void *dcontext, void *tag)
{
inc_count_first(EVENT_DELETE_1, EVENT_DELETE_2);
if (!dr_unregister_delete_event(delete_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-void delete_event2(void *dcontext, void *tag)
+static void
+delete_event2(void *dcontext, void *tag)
{
inc_count_second(EVENT_DELETE_2);
if (!dr_unregister_delete_event(delete_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-void module_load_event_perm(void *drcontext, const module_data_t *info, bool loaded)
+static void
+module_load_event_perm(void *drcontext, const module_data_t *info, bool loaded)
{
/* Test i#138 */
if (info->full_path == NULL || info->full_path[0] == '\0')
@@ -328,40 +335,40 @@ void module_load_event_perm(void *drcontext, const module_data_t *info, bool loa
#endif
}
-static
-void module_load_event1(void *drcontext, const module_data_t *info, bool loaded)
+static void
+module_load_event1(void *drcontext, const module_data_t *info, bool loaded)
{
inc_count_first(EVENT_MODULE_LOAD_1, EVENT_MODULE_LOAD_2);
if (!dr_unregister_module_load_event(module_load_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-void module_load_event2(void *drcontext, const module_data_t *info, bool loaded)
+static void
+module_load_event2(void *drcontext, const module_data_t *info, bool loaded)
{
inc_count_second(EVENT_MODULE_LOAD_2);
if (!dr_unregister_module_load_event(module_load_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-void module_unload_event1(void *drcontext, const module_data_t *info)
+static void
+module_unload_event1(void *drcontext, const module_data_t *info)
{
inc_count_first(EVENT_MODULE_UNLOAD_1, EVENT_MODULE_UNLOAD_2);
if (!dr_unregister_module_unload_event(module_unload_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-void module_unload_event2(void *drcontext, const module_data_t *info)
+static void
+module_unload_event2(void *drcontext, const module_data_t *info)
{
inc_count_second(EVENT_MODULE_UNLOAD_2);
if (!dr_unregister_module_unload_event(module_unload_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-bool pre_syscall_event1(void *drcontext, int sysnum)
+static bool
+pre_syscall_event1(void *drcontext, int sysnum)
{
inc_count_first(EVENT_PRE_SYSCALL_1, EVENT_PRE_SYSCALL_2);
if (!dr_unregister_pre_syscall_event(pre_syscall_event1))
@@ -369,8 +376,8 @@ bool pre_syscall_event1(void *drcontext, int sysnum)
return true;
}
-static
-bool pre_syscall_event2(void *drcontext, int sysnum)
+static bool
+pre_syscall_event2(void *drcontext, int sysnum)
{
inc_count_second(EVENT_PRE_SYSCALL_2);
if (!dr_unregister_pre_syscall_event(pre_syscall_event2))
@@ -378,24 +385,24 @@ bool pre_syscall_event2(void *drcontext, int sysnum)
return true;
}
-static
-void post_syscall_event1(void *drcontext, int sysnum)
+static void
+post_syscall_event1(void *drcontext, int sysnum)
{
inc_count_first(EVENT_POST_SYSCALL_1, EVENT_POST_SYSCALL_2);
if (!dr_unregister_post_syscall_event(post_syscall_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-void post_syscall_event2(void *drcontext, int sysnum)
+static void
+post_syscall_event2(void *drcontext, int sysnum)
{
inc_count_second(EVENT_POST_SYSCALL_2);
if (!dr_unregister_post_syscall_event(post_syscall_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
-static
-bool filter_syscall_event1(void *drcontext, int sysnum)
+static bool
+filter_syscall_event1(void *drcontext, int sysnum)
{
inc_count_first(EVENT_FILTER_SYSCALL_1, EVENT_FILTER_SYSCALL_2);
if (!dr_unregister_filter_syscall_event(filter_syscall_event1))
@@ -403,8 +410,8 @@ bool filter_syscall_event1(void *drcontext, int sysnum)
return true;
}
-static
-bool filter_syscall_event2(void *drcontext, int sysnum)
+static bool
+filter_syscall_event2(void *drcontext, int sysnum)
{
inc_count_second(EVENT_FILTER_SYSCALL_2);
if (!dr_unregister_filter_syscall_event(filter_syscall_event2))
@@ -412,10 +419,39 @@ bool filter_syscall_event2(void *drcontext, int sysnum)
return true;
}
+static void
+kernel_xfer_event1(void *drcontext, const dr_kernel_xfer_info_t *info)
+{
+ inc_count_first(EVENT_KERNEL_XFER_1, EVENT_KERNEL_XFER_2);
+ if (!dr_unregister_kernel_xfer_event(kernel_xfer_event1))
+ dr_fprintf(STDERR, "unregister failed!\n");
+}
+
+static void
+kernel_xfer_event2(void *drcontext, const dr_kernel_xfer_info_t *info)
+{
+ inc_count_second(EVENT_KERNEL_XFER_2);
+ dr_log(drcontext, LOG_ALL, 2, "%s: %d %p to %p sp=%zx\n", __FUNCTION__, info->type,
+ info->source_mcontext == NULL ? 0 : info->source_mcontext->pc,
+ info->target_pc, info->target_xsp);
+ if (info->type == DR_XFER_CLIENT_REDIRECT) {
+ /* Test for exception event redirect. */
+ ASSERT(info->source_mcontext != NULL);
+ dr_mcontext_t mc = {sizeof(mc)};
+ mc.flags = DR_MC_CONTROL;
+ bool ok = dr_get_mcontext(drcontext, &mc);
+ ASSERT(ok);
+ ASSERT(mc.pc == info->target_pc);
+ ASSERT(mc.xsp == info->target_xsp);
+ mc.flags = DR_MC_ALL;
+ ok = dr_get_mcontext(drcontext, &mc);
+ ASSERT(ok);
+ }
+}
#ifdef WINDOWS
-static
-bool exception_event_redirect(void *dcontext, dr_exception_t *excpt)
+static bool
+exception_event_redirect(void *dcontext, dr_exception_t *excpt)
{
app_pc addr;
dr_mcontext_t mcontext = {sizeof(mcontext),DR_MC_ALL,};
@@ -443,8 +479,8 @@ bool exception_event_redirect(void *dcontext, dr_exception_t *excpt)
return true;
}
-static
-bool exception_event1(void *dcontext, dr_exception_t *excpt)
+static bool
+exception_event1(void *dcontext, dr_exception_t *excpt)
{
if (excpt->record->ExceptionCode == STATUS_ACCESS_VIOLATION)
inc_count_first(EVENT_EXCEPTION_1, EVENT_EXCEPTION_2);
@@ -457,8 +493,8 @@ bool exception_event1(void *dcontext, dr_exception_t *excpt)
return true;
}
-static
-bool exception_event2(void *dcontext, dr_exception_t *excpt)
+static bool
+exception_event2(void *dcontext, dr_exception_t *excpt)
{
if (excpt->record->ExceptionCode == STATUS_ACCESS_VIOLATION)
inc_count_second(EVENT_EXCEPTION_2);
@@ -654,6 +690,8 @@ void dr_init(client_id_t id)
dr_register_post_syscall_event(post_syscall_event2);
dr_register_filter_syscall_event(filter_syscall_event1);
dr_register_filter_syscall_event(filter_syscall_event2);
+ dr_register_kernel_xfer_event(kernel_xfer_event1);
+ dr_register_kernel_xfer_event(kernel_xfer_event2);
#ifdef WINDOWS
dr_register_exception_event(exception_event1);
dr_register_exception_event(exception_event2);
diff --git a/suite/tests/client-interface/events.templatex b/suite/tests/client-interface/events.templatex
index 8a35ebef5ad..95a8c0b7b0b 100644
--- a/suite/tests/client-interface/events.templatex
+++ b/suite/tests/client-interface/events.templatex
@@ -4,37 +4,39 @@ exception event redirect
Redirect success!
exit event 2
exit event 1
-module load event 1 is called 1 time\(s\)
-module load event 2 is called 1 time\(s\)
-thread init event 1 is called 1 time\(s\)
-thread init event 2 is called 1 time\(s\)
-bb event 1 is called 1 time\(s\)
-bb event 2 is called 1 time\(s\)
+module load event 1 is called 1 time
+module load event 2 is called 1 time
+thread init event 1 is called 1 time
+thread init event 2 is called 1 time
+bb event 1 is called 1 time
+bb event 2 is called 1 time
# if !defined(disable_traces)
-end trace event 1 is called 1 time\(s\)
-end trace event 2 is called 1 time\(s\)
-trace event 1 is called 1 time\(s\)
-trace event 2 is called 1 time\(s\)
+end trace event 1 is called 1 time
+end trace event 2 is called 1 time
+trace event 1 is called 1 time
+trace event 2 is called 1 time
# endif
-delete event 1 is called 1 time\(s\)
-delete event 2 is called 1 time\(s\)
+delete event 1 is called 1 time
+delete event 2 is called 1 time
/* no filter event on xp sp2+ 32-bit kernel, so optional */
-(filter syscall event 1 is called 1 time\(s\)
-)?(filter syscall event 2 is called 1 time\(s\)
-)?pre syscall event 1 is called 1 time\(s\)
-pre syscall event 2 is called 1 time\(s\)
-post syscall event 1 is called 1 time\(s\)
-post syscall event 2 is called 1 time\(s\)
-module unload event 1 is called 1 time\(s\)
-module unload event 2 is called 1 time\(s\)
-thread exit event 1 is called 1 time\(s\)
-thread exit event 2 is called 1 time\(s\)
-exception event 1 is called 1 time\(s\)
-exception event 2 is called 1 time\(s\)
-restore state event 1 is called 1 time\(s\)
-restore state event 2 is called 1 time\(s\)
-restore state ex event 1 is called 1 time\(s\)
-restore state ex event 2 is called 1 time\(s\)
+(filter syscall event 1 is called 1 time
+)?(filter syscall event 2 is called 1 time
+)?pre syscall event 1 is called 1 time
+pre syscall event 2 is called 1 time
+post syscall event 1 is called 1 time
+post syscall event 2 is called 1 time
+kernel xfer event 1 is called 1 time
+kernel xfer event 2 is called >1 time
+module unload event 1 is called 1 time
+module unload event 2 is called 1 time
+thread exit event 1 is called 1 time
+thread exit event 2 is called 1 time
+exception event 1 is called 1 time
+exception event 2 is called 1 time
+restore state event 1 is called 1 time
+restore state event 2 is called 1 time
+restore state ex event 1 is called 1 time
+restore state ex event 2 is called 1 time
#else
appdll initialized
Sending SIGUSR1
@@ -44,42 +46,44 @@ Sending SIGURG
Done
exit event 2
exit event 1
-thread exit event 1 is called 1 time\(s\)
-thread exit event 2 is called 1 time\(s\)
-fork init event 1 is called 1 time\(s\)
-fork init event 2 is called 1 time\(s\)
+thread exit event 1 is called 1 time
+thread exit event 2 is called 1 time
+fork init event 1 is called 1 time
+fork init event 2 is called 1 time
signal event redirect
Redirect success!
exit event 2
exit event 1
-module load event 1 is called 1 time\(s\)
-module load event 2 is called 1 time\(s\)
-thread init event 1 is called 1 time\(s\)
-thread init event 2 is called 1 time\(s\)
-bb event 1 is called 1 time\(s\)
-bb event 2 is called 1 time\(s\)
+module load event 1 is called 1 time
+module load event 2 is called 1 time
+thread init event 1 is called 1 time
+thread init event 2 is called 1 time
+bb event 1 is called 1 time
+bb event 2 is called 1 time
# if !defined(disable_traces)
-end trace event 1 is called 1 time\(s\)
-end trace event 2 is called 1 time\(s\)
-trace event 1 is called 1 time\(s\)
-trace event 2 is called 1 time\(s\)
+end trace event 1 is called 1 time
+end trace event 2 is called 1 time
+trace event 1 is called 1 time
+trace event 2 is called 1 time
# endif
-delete event 1 is called 1 time\(s\)
-delete event 2 is called 1 time\(s\)
-filter syscall event 1 is called 1 time\(s\)
-filter syscall event 2 is called 1 time\(s\)
-pre syscall event 1 is called 1 time\(s\)
-pre syscall event 2 is called 1 time\(s\)
-post syscall event 1 is called 1 time\(s\)
-post syscall event 2 is called 1 time\(s\)
-module unload event 1 is called 1 time\(s\)
-module unload event 2 is called 1 time\(s\)
-thread exit event 1 is called 1 time\(s\)
-thread exit event 2 is called 1 time\(s\)
-signal event 1 is called 3 time\(s\)
-signal event 2 is called 1 time\(s\)
-restore state event 1 is called 1 time\(s\)
-restore state event 2 is called 1 time\(s\)
-restore state ex event 1 is called 1 time\(s\)
-restore state ex event 2 is called 1 time\(s\)
+delete event 1 is called 1 time
+delete event 2 is called 1 time
+filter syscall event 1 is called 1 time
+filter syscall event 2 is called 1 time
+pre syscall event 1 is called 1 time
+pre syscall event 2 is called 1 time
+post syscall event 1 is called 1 time
+post syscall event 2 is called 1 time
+kernel xfer event 1 is called 1 time
+kernel xfer event 2 is called >1 time
+module unload event 1 is called 1 time
+module unload event 2 is called 1 time
+thread exit event 1 is called 1 time
+thread exit event 2 is called 1 time
+signal event 1 is called >1 time
+signal event 2 is called 1 time
+restore state event 1 is called 1 time
+restore state event 2 is called 1 time
+restore state ex event 1 is called 1 time
+restore state ex event 2 is called 1 time
#endif
diff --git a/suite/tests/client-interface/fibers.template b/suite/tests/client-interface/fibers.template
index 80053140ad5..c8f7be5f3c1 100755
--- a/suite/tests/client-interface/fibers.template
+++ b/suite/tests/client-interface/fibers.template
@@ -50,23 +50,23 @@ fls_delete val=0x12345678
fls_delete val=0xdeadbeef
exit event 2
exit event 1
-module load event 1 is called 1 time(s)
-module load event 2 is called 1 time(s)
-thread init event 1 is called 1 time(s)
-thread init event 2 is called 1 time(s)
-bb event 1 is called 1 time(s)
-bb event 2 is called 1 time(s)
-end trace event 1 is called 1 time(s)
-end trace event 2 is called 1 time(s)
-trace event 1 is called 1 time(s)
-trace event 2 is called 1 time(s)
-delete event 1 is called 1 time(s)
-delete event 2 is called 1 time(s)
-filter syscall event 1 is called 1 time(s)
-filter syscall event 2 is called 1 time(s)
-pre syscall event 1 is called 1 time(s)
-pre syscall event 2 is called 1 time(s)
-post syscall event 1 is called 1 time(s)
-post syscall event 2 is called 1 time(s)
-thread exit event 1 is called 1 time(s)
-thread exit event 2 is called 1 time(s)
+module load event 1 is called 1 time
+module load event 2 is called 1 time
+thread init event 1 is called 1 time
+thread init event 2 is called 1 time
+bb event 1 is called 1 time
+bb event 2 is called 1 time
+end trace event 1 is called 1 time
+end trace event 2 is called 1 time
+trace event 1 is called 1 time
+trace event 2 is called 1 time
+delete event 1 is called 1 time
+delete event 2 is called 1 time
+filter syscall event 1 is called 1 time
+filter syscall event 2 is called 1 time
+pre syscall event 1 is called 1 time
+pre syscall event 2 is called 1 time
+post syscall event 1 is called 1 time
+post syscall event 2 is called 1 time
+thread exit event 1 is called 1 time
+thread exit event 2 is called 1 time
diff --git a/suite/tests/client-interface/flush.dll.c b/suite/tests/client-interface/flush.dll.c
index 5a4f5454ca9..4eacdca9947 100644
--- a/suite/tests/client-interface/flush.dll.c
+++ b/suite/tests/client-interface/flush.dll.c
@@ -1,5 +1,5 @@
/* **********************************************************
- * Copyright (c) 2011-2014 Google, Inc. All rights reserved.
+ * Copyright (c) 2011-2017 Google, Inc. All rights reserved.
* Copyright (c) 2007-2010 VMware, Inc. All rights reserved.
* **********************************************************/
@@ -32,6 +32,7 @@
*/
#include "dr_api.h"
+#include "client_tools.h"
#include
#define MINSERT instrlist_meta_preinsert
@@ -279,6 +280,23 @@ dr_emit_flags_t bb_event(void* drcontext, void *tag, instrlist_t *bb,
return DR_EMIT_DEFAULT;
}
+static void
+kernel_xfer_event(void *drcontext, const dr_kernel_xfer_info_t *info)
+{
+ /* Test kernel xfer on dr_redirect_execution */
+ dr_fprintf(STDERR, "%s: type %d\n", __FUNCTION__, info->type);
+ ASSERT(info->source_mcontext != NULL);
+ dr_mcontext_t mc = {sizeof(mc)};
+ mc.flags = DR_MC_CONTROL;
+ bool ok = dr_get_mcontext(drcontext, &mc);
+ ASSERT(ok);
+ ASSERT(mc.pc == info->target_pc);
+ ASSERT(mc.xsp == info->target_xsp);
+ mc.flags = DR_MC_ALL;
+ ok = dr_get_mcontext(drcontext, &mc);
+ ASSERT(ok);
+}
+
DR_EXPORT
void dr_init(client_id_t id)
{
@@ -294,5 +312,5 @@ void dr_init(client_id_t id)
dr_register_trace_event(trace_event);
dr_register_delete_event(deleted_event);
dr_register_bb_event(bb_event);
-
+ dr_register_kernel_xfer_event(kernel_xfer_event);
}
diff --git a/suite/tests/client-interface/flush.template b/suite/tests/client-interface/flush.template
index 6a5eabb0d99..58c059415b7 100644
--- a/suite/tests/client-interface/flush.template
+++ b/suite/tests/client-interface/flush.template
@@ -1,12 +1,18 @@
#if defined(thread_private) || defined(enable_full_api)
options = use_unlink
+kernel_xfer_event: type 9
Flush completion id=100
+kernel_xfer_event: type 9
Flush completion id=200
+kernel_xfer_event: type 9
Flush completion id=300
+kernel_xfer_event: type 9
Flush completion id=400
#else
options =@&
+kernel_xfer_event: type 9
Flush completion id=200
+kernel_xfer_event: type 9
Flush completion id=400
#endif
count = 402
diff --git a/suite/tests/client-interface/signal.dll.c b/suite/tests/client-interface/signal.dll.c
index 52c57bdbf85..38663f9c90e 100644
--- a/suite/tests/client-interface/signal.dll.c
+++ b/suite/tests/client-interface/signal.dll.c
@@ -49,8 +49,44 @@ static void *child_dead;
static void *sigchld_received;
static pid_t child_pid, child_tid;
-static
-dr_signal_action_t signal_event(void *dcontext, dr_siginfo_t *info)
+static void
+redirect_xfer(void)
+{
+ /* XXX: this is not super-clean: we'll interpret this routine.
+ * Better to coordinate w/ the app: but that's more work for us.
+ */
+ dr_fprintf(STDERR, "redirected via dr_set_mcontext\n");
+}
+
+static void
+kernel_xfer_event(void *drcontext, const dr_kernel_xfer_info_t *info)
+{
+ dr_fprintf(STDERR, "%s: type %d, sig %d\n", __FUNCTION__, info->type, info->sig);
+ dr_log(drcontext, LOG_ALL, 2, "%s: %d %d %p to %p sp=%zx\n", __FUNCTION__, info->type,
+ info->sig, info->source_mcontext->pc, info->target_pc, info->target_xsp);
+ dr_mcontext_t mc = {sizeof(mc)};
+ mc.flags = DR_MC_CONTROL;
+ bool ok = dr_get_mcontext(drcontext, &mc);
+ ASSERT(ok);
+ ASSERT(mc.pc == info->target_pc);
+ ASSERT(mc.xsp == info->target_xsp);
+ mc.flags = DR_MC_ALL;
+ ok = dr_get_mcontext(drcontext, &mc);
+ ASSERT(ok);
+ /* We do one test of setting the context.
+ * XXX: We would ideally test for synch vs asynch signals too.
+ */
+ static bool set_mc_once;
+ if (info->type == DR_XFER_SIGNAL_DELIVERY && !set_mc_once) {
+ set_mc_once = true;
+ mc.pc = (app_pc)redirect_xfer;
+ ok = dr_set_mcontext(drcontext, &mc);
+ ASSERT(ok);
+ }
+}
+
+static dr_signal_action_t
+signal_event(void *dcontext, dr_siginfo_t *info)
{
static int count = -1;
count++;
@@ -141,6 +177,7 @@ void dr_init(client_id_t id)
{
dr_register_bb_event(bb_event);
dr_register_signal_event(signal_event);
+ dr_register_kernel_xfer_event(kernel_xfer_event);
/* We test syscall auto-restart (i#2659) by having another thread
* sit at a blocking read while it receives signals.
diff --git a/suite/tests/client-interface/signal.expect b/suite/tests/client-interface/signal.expect
index 418dd6aa596..98f321bdbcd 100644
--- a/suite/tests/client-interface/signal.expect
+++ b/suite/tests/client-interface/signal.expect
@@ -2,7 +2,9 @@ signal event 0 sig=17
got 2 bytes == 97 98
Sending SIGURG
signal event 1 sig=23
-Got SIGURG
+kernel_xfer_event: type 0, sig 23
+redirected via dr_set_mcontext
+kernel_xfer_event: type 1, sig 23
Sending SIGURG
signal event 2 sig=23
Sending SIGURG
@@ -18,11 +20,16 @@ signal event 7 sig=15
Redirected
Sending SIGUSR2
signal event 8 sig=12
+kernel_xfer_event: type 9, sig 12
Redirected
signal event 9 sig=11
signal event 10 sig=11
+kernel_xfer_event: type 0, sig 11
Got SIGSEGV
+kernel_xfer_event: type 1, sig 11
Sending SIGUSR1
signal event 11 sig=10
+kernel_xfer_event: type 0, sig 10
Got SIGUSR1
+kernel_xfer_event: type 1, sig 10
Done
diff --git a/suite/tests/client-interface/winxfer.c b/suite/tests/client-interface/winxfer.c
new file mode 100644
index 00000000000..f62fa736e14
--- /dev/null
+++ b/suite/tests/client-interface/winxfer.c
@@ -0,0 +1,291 @@
+/* **********************************************************
+ * Copyright (c) 2014-2017 Google, Inc. All rights reserved.
+ * Copyright (c) 2003-2010 VMware, Inc. All rights reserved.
+ * **********************************************************/
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of VMware, Inc. nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+/* This combines win32/callback.c and win32/winapc.c */
+
+#include
+#include /* for QueueUserAPC */
+#include /* for _beginthreadex */
+#include "tools.h"
+#include
+
+/***************************************************************************
+ * Callback and exception raising
+ */
+
+static volatile bool thread_ready = false;
+static volatile bool past_crash = false;
+static volatile uint last_received = 0;
+static HWND hwnd;
+static uint msgdata;
+
+static const UINT MSG_CUSTOM = WM_APP + 1;
+static const LRESULT MSG_SUCCESS = 1;
+
+static const WPARAM WP_NOP = 0;
+static const WPARAM WP_EXIT = 1;
+static const WPARAM WP_CRASH = 3;
+
+static const uint BAD_WRITE = 0x40;
+
+#ifndef WM_DWMNCRENDERINGCHANGED
+# define WM_DWMNCRENDERINGCHANGED 0x031F
+#endif
+
+/* This is where all our callbacks come. We get 4 default messages:
+ * WM_GETMINMAXINFO 0x0024
+ * WM_NCCREATE 0x0081
+ * WM_NCCALCSIZE 0x0083
+ * WM_CREATE 0x0001
+ * and then our 2 custom messages that we send.
+ *
+ * On Windows 7 we also get (i#520):
+ * WM_DWMNCRENDERINGCHANGED 0x031F
+ * and we avoid printing anything about it to simplify the test suite.
+ */
+LRESULT CALLBACK
+wnd_callback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ if (message == MSG_CUSTOM) {
+ print("in wnd_callback "PFX" %d %d\n", message, wParam, lParam);
+ if (wParam == WP_CRASH) {
+ /* ensure SendMessage returns prior to our crash */
+ ReplyMessage(TRUE);
+ print("About to crash\n");
+ /* We don't bother to pass an exception across the callback boundary
+ * as it complicates the test template due to lack of x64 support.
+ * We stick with a local exception.
+ */
+ __try {
+ *((int *)BAD_WRITE) = 4;
+ print("Should not get here\n");
+ }
+ __except (/* Only catch the bad write, to not mask DR errors (like
+ * case 10579) */
+ (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION &&
+ (GetExceptionInformation())->ExceptionRecord->
+ ExceptionInformation[0] == 1 /* write */ &&
+ (GetExceptionInformation())->ExceptionRecord->
+ ExceptionInformation[1] == BAD_WRITE) ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
+ print("Inside handler\n");
+ past_crash = true;
+ }
+ }
+ return MSG_SUCCESS;
+ } else {
+ /* lParam varies so don't make template nondet */
+ if (message != WM_DWMNCRENDERINGCHANGED)
+ print("in wnd_callback "PFX" %d\n", message, wParam);
+ return DefWindowProc(hwnd, message, wParam, lParam);
+ }
+}
+
+int WINAPI
+run_func(void * arg)
+{
+ MSG msg;
+ char *winName = "foobar";
+ WNDCLASS wndclass = {0, wnd_callback, 0, 0, NULL/* WinMain hwnd would be here */,
+ NULL, NULL,
+ NULL, NULL, winName};
+
+ if (!RegisterClass(&wndclass)) {
+ print("Unable to create window class\n");
+ return 0;
+ }
+ hwnd = CreateWindow(winName, winName, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, NULL, NULL, NULL/* WinMain hwnd would be here */,
+ NULL);
+ /* deliberately not calling ShowWindow */
+
+ /* For case 10579 we want a handled system call in this thread prior
+ * to our crash inside a callback */
+ VirtualAlloc(0, 1024, MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+
+ thread_ready = true;
+ while (true) {
+ __try {
+ if (GetMessage(&msg, NULL, 0, 0)) {
+ /* Messages not auto-sent to callbacks are processed here */
+ if ((msg.message != MSG_CUSTOM || msg.wParam != WP_NOP) &&
+ msg.message != WM_DWMNCRENDERINGCHANGED) {
+ print("Got message "PFX" %d %d\n",
+ msg.message, msg.wParam, msg.lParam);
+ }
+ last_received = msg.message;
+ if (msg.message == MSG_CUSTOM && msg.wParam == WP_EXIT)
+ break; /* Done */
+ TranslateMessage(&msg); /* convert virtual-key msgs to character msgs */
+ DispatchMessage(&msg);
+ }
+ }
+ __except (/* Only catch the bad write, to not mask DR errors (like
+ * case 10579) */
+ (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION &&
+ (GetExceptionInformation())->ExceptionRecord->ExceptionInformation[0]
+ == 1 /* write */ &&
+ (GetExceptionInformation())->ExceptionRecord->ExceptionInformation[1]
+ == BAD_WRITE) ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
+ /* This should have crossed the callback boundary
+ * On xpsp2 and earlier we never see a callback return for
+ * the crashing callback, while on 2k3sp1 we do see one.
+ */
+ print("Inside handler\n");
+ past_crash = true;
+ continue;
+ }
+ }
+ return (int) msg.wParam;
+}
+
+static int
+test_callbacks(void)
+{
+ int tid;
+ HANDLE hThread;
+ uint msgnum = 0;
+
+ print("About to create thread\n");
+ hThread = CreateThread(NULL, 0, run_func, NULL, 0, &tid);
+ if (hThread == NULL) {
+ print("Error creating thread\n");
+ return -1;
+ }
+ while (!thread_ready)
+ Sleep(0);
+
+ /* We have to send a message to a window to get a callback.
+ * We go ahead and use the blocking SendMessage for simplicity;
+ * could use SendMessageCallback and get a callback back, but have to
+ * ask for messages to receive it and then have no clear exit path.
+ */
+ if (SendMessage(hwnd, MSG_CUSTOM, WP_CRASH, msgnum++) != MSG_SUCCESS) {
+ print("Error %d posting window message\n", GetLastError());
+ return -1;
+ }
+ /* On bucephalus (win2k3sp1) I need to send a message to get the
+ * thread to go into the except block: it sits waiting in the
+ * kernel at the NtCallbackReturn from
+ * KiUserCallbackExceptionHandler, and that is where it receives
+ * the callback for this message: seems problematic natively?
+ */
+ PostThreadMessage(tid, MSG_CUSTOM, WP_NOP, msgnum++);
+ while (!past_crash)
+ Sleep(0);
+ if (SendMessage(hwnd, MSG_CUSTOM, WP_NOP, msgnum++) != MSG_SUCCESS) {
+ print("Error %d posting window message\n", GetLastError());
+ return -1;
+ }
+
+ /* Message not sent to a window is processed inside the GetMessage loop,
+ * with no callback involved. So this bit here is mainly to get the thread
+ * to exit. */
+ if (!PostThreadMessage(tid, MSG_CUSTOM, WP_EXIT, msgnum++)) {
+ print("Error %d posting thread message\n", GetLastError());
+ return -1;
+ }
+ while (last_received != MSG_CUSTOM)
+ Sleep(0);
+
+ WaitForSingleObject(hThread, INFINITE);
+ return 0;
+}
+
+/***************************************************************************
+ * APC testing
+ */
+
+static volatile BOOL synch_1 = TRUE;
+static volatile BOOL synch_2 = TRUE;
+static int result = 0;
+static ULONG_PTR apc_arg = 0;
+
+unsigned int WINAPI
+thread_func(void * arg)
+{
+ int res;
+ synch_2 = FALSE;
+ while (synch_1) {
+ /* need non alertable thread yield function */
+ SwitchToThread();
+ }
+ /* now the alertable system call */
+ res = SleepEx(100, 1);
+ /* is going to return 192 since received apc during sleep call
+ * well technically 192 is io completion interruption, but seems to
+ * report that for any interrupting APC */
+ print("SleepEx returned %d\n", res);
+ print("Apc arg = %d\n", (int) apc_arg);
+ print("Result = %d\n", result);
+ return 0;
+}
+
+void WINAPI
+apc_func(ULONG_PTR arg)
+{
+ result += 100;
+ apc_arg = arg;
+}
+
+static void
+test_apc(void)
+{
+ unsigned int tid;
+ HANDLE hThread;
+ int res;
+
+ print("Before _beginthreadex\n");
+ hThread = (HANDLE) _beginthreadex(NULL, 0, thread_func, NULL, 0, &tid);
+
+ while (synch_2) {
+ SwitchToThread();
+ }
+
+ res = QueueUserAPC(apc_func, hThread, 37);
+ print("QueueUserAPC returned %d\n", res);
+
+ synch_1 = FALSE;
+
+ WaitForSingleObject((HANDLE)hThread, INFINITE);
+ print("After _beginthreadex\n");
+}
+
+int main()
+{
+ test_callbacks();
+ test_apc();
+ print("All done\n");
+ return 0;
+}
diff --git a/suite/tests/client-interface/winxfer.dll.c b/suite/tests/client-interface/winxfer.dll.c
new file mode 100644
index 00000000000..95aad216c54
--- /dev/null
+++ b/suite/tests/client-interface/winxfer.dll.c
@@ -0,0 +1,97 @@
+/* **********************************************************
+ * Copyright (c) 2017 Google, Inc. All rights reserved.
+ * **********************************************************/
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Google, Inc. nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+/*
+ * Tests API interactions with Windows kernel-mediated events.
+ */
+
+#include "dr_api.h"
+#include "client_tools.h"
+
+#if 0 /* FIXME i#241 */
+static void
+redirect_xfer(void)
+{
+ dr_printf("redirected!\n");
+}
+#endif
+
+static void
+kernel_xfer_event(void *drcontext, const dr_kernel_xfer_info_t *info)
+{
+ dr_fprintf(STDERR, "%s: type %d\n", __FUNCTION__, info->type);
+ dr_log(drcontext, LOG_ALL, 2, "%s: %d %p to %p sp=%zx\n", __FUNCTION__, info->type,
+ info->source_mcontext == NULL ? 0 : info->source_mcontext->pc,
+ info->target_pc, info->target_xsp);
+ dr_mcontext_t mc = {sizeof(mc)};
+ mc.flags = DR_MC_CONTROL;
+ bool ok = dr_get_mcontext(drcontext, &mc);
+ ASSERT(ok);
+ ASSERT(mc.pc == info->target_pc);
+ ASSERT(mc.xsp == info->target_xsp);
+ mc.flags = DR_MC_ALL;
+ ok = dr_get_mcontext(drcontext, &mc);
+ ASSERT(ok);
+#if 0 /* FIXME i#241 */
+ /* FIXME i#241: test dr_set_mcontext. It's not easy though: it doesn't make much
+ * sense for the Ki dispatchers, there's no NtContinue in SEH64, it's not
+ * supported for cbret, and we don't have a test here for NtSetContextThread. I
+ * manually tested by sending to redirect_cbret(), confirmed a print, and then
+ * the app crashes. Making it continue is more work than I want to put in right
+ * now. Since the uses cases are few, we may want to consider disallowing
+ * setting the context?
+ */
+ if (type == DR_XFER_CALLBACK_DISPATCHER) {
+ mc.pc = (app_pc)redirect_xfer;
+ ok = dr_set_mcontext(drcontext, &mc);
+ ASSERT(ok);
+ }
+#endif
+}
+
+static bool
+exception_event(void *dcontext, dr_exception_t *excpt)
+{
+ void *fault_address = (void *)excpt->record->ExceptionInformation[1];
+ dr_fprintf(STDERR,
+ "exception %x addr "PFX"\n",
+ excpt->record->ExceptionCode,
+ (ptr_uint_t)excpt->record->ExceptionInformation[1]);
+ return true;
+}
+
+DR_EXPORT
+void dr_init(client_id_t id)
+{
+ dr_register_kernel_xfer_event(kernel_xfer_event);
+ dr_register_exception_event(exception_event);
+}
diff --git a/suite/tests/client-interface/winxfer.templatex b/suite/tests/client-interface/winxfer.templatex
new file mode 100755
index 00000000000..ab007056016
--- /dev/null
+++ b/suite/tests/client-interface/winxfer.templatex
@@ -0,0 +1,66 @@
+About to create thread
+kernel_xfer_event: type 2
+kernel_xfer_event: type 7
+kernel_xfer_event: type 5
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+kernel_xfer_event: type 5
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+kernel_xfer_event: type 6
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+in wnd_callback 0x0*0000024 0
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+in wnd_callback 0x0*0000081 0
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+in wnd_callback 0x0*0000083 0
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+in wnd_callback 0x0*0000001 0
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+in wnd_callback 0x0*0008001 3 0
+About to crash
+exception c0000005 addr 0x0*0000040
+kernel_xfer_event: type 3
+#ifndef X64
+kernel_xfer_event: type 7
+#endif
+Inside handler
+kernel_xfer_event: type 6
+kernel_xfer_event: type 5
+in wnd_callback 0x0*0008001 0 2
+kernel_xfer_event: type 6
+Got message 0x0*0008001 1 3
+Before _beginthreadex
+kernel_xfer_event: type 2
+kernel_xfer_event: type 7
+QueueUserAPC returned 1
+kernel_xfer_event: type 2
+kernel_xfer_event: type 7
+SleepEx returned 192
+Apc arg = 37
+Result = 100
+After _beginthreadex
+All done