blob: fe9cc9aa7703adb59dbfdb11b3a48ac5f8f301c4 [file] [log] [blame]
/* ******************************************************
* Copyright (c) 2014 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 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.
*/
#include "globals.h"
#include "hashtable.h"
#include "instr.h"
#include "instr_create.h"
#include "decode_fast.h"
#include "utils.h"
#include "annotations.h"
#ifdef ANNOTATIONS /* around whole file */
#if !(defined (WINDOWS) && defined (X64))
# include "../third_party/valgrind/valgrind.h"
# include "../third_party/valgrind/memcheck.h"
#endif
#ifdef UNIX
# include <string.h>
#endif
/* Macros for identifying an annotation head and extracting the pointer to its name.
*
* IS_ANNOTATION_LABEL_INSTR(instr): Evaluates to true for any `instr` that
* could be the instruction which encodes
* the pointer to the annotation name.
* IS_ANNOTATION_LABEL_REFERENCE(opnd): Evaluates to true for any `opnd` that
* could be the operand which encodes
* the pointer to the annotation name.
* GET_ANNOTATION_LABEL_REFERENCE(src, instr_pc): Extracts the annotation name pointer.
* IS_ANNOTATION_LABEL_GOT_OFFSET_INSTR(instr): Evaluates to true for any `instr` that
* could encode the offset of the label's
* GOT entry within the GOT table.
* IS_ANNOTATION_LABEL_GOT_OFFSET_REFERENCE(opnd): Evaluates to true for any `opnd` that
* could encode the offset of the label's
* GOT entry within the GOT table.
*/
#ifdef WINDOWS
# ifdef X64
# define IS_ANNOTATION_LABEL_INSTR(instr) \
(instr_is_mov(instr) || (instr_get_opcode(instr) == OP_prefetchw))
# define IS_ANNOTATION_LABEL_REFERENCE(opnd) opnd_is_rel_addr(opnd)
# define GET_ANNOTATION_LABEL_REFERENCE(src, instr_pc) opnd_get_addr(src)
# else
# define IS_ANNOTATION_LABEL_INSTR(instr) instr_is_mov(instr)
# define IS_ANNOTATION_LABEL_REFERENCE(opnd) opnd_is_base_disp(opnd)
# define GET_ANNOTATION_LABEL_REFERENCE(src, instr_pc) ((app_pc) opnd_get_disp(src))
# endif
#else
# define IS_ANNOTATION_LABEL_INSTR(instr) instr_is_mov(instr)
# define IS_ANNOTATION_LABEL_REFERENCE(opnd) opnd_is_base_disp(opnd)
# ifdef X64
# define ANNOTATION_LABEL_REFERENCE_OPERAND_OFFSET 4
# else
# define ANNOTATION_LABEL_REFERENCE_OPERAND_OFFSET 0
# endif
# define GET_ANNOTATION_LABEL_REFERENCE(src, instr_pc) \
((app_pc) (opnd_get_disp(src) + (instr_pc) + \
ANNOTATION_LABEL_REFERENCE_OPERAND_OFFSET))
# define IS_ANNOTATION_LABEL_GOT_OFFSET_INSTR(instr) \
(instr_get_opcode(scratch) == OP_bsf || instr_get_opcode(scratch) == OP_bsr)
# define IS_ANNOTATION_LABEL_GOT_OFFSET_REFERENCE(opnd) opnd_is_base_disp(opnd)
#endif
/* Annotation label components. */
#define DYNAMORIO_ANNOTATION_LABEL "dynamorio-annotation"
#define DYNAMORIO_ANNOTATION_LABEL_LENGTH 20
#define ANNOTATION_STATEMENT_LABEL "statement"
#define ANNOTATION_STATEMENT_LABEL_LENGTH 9
#define ANNOTATION_EXPRESSION_LABEL "expression"
#define ANNOTATION_EXPRESSION_LABEL_LENGTH 10
#define ANNOTATION_VOID_LABEL "void"
#define ANNOTATION_VOID_LABEL_LENGTH 4
#define IS_ANNOTATION_STATEMENT_LABEL(annotation_name) \
(strncmp((const char *) (annotation_name), ANNOTATION_STATEMENT_LABEL, \
ANNOTATION_STATEMENT_LABEL_LENGTH) == 0)
#define IS_ANNOTATION_VOID(annotation_name) \
(strncmp((const char *) (annotation_name), ANNOTATION_VOID_LABEL":", \
ANNOTATION_VOID_LABEL_LENGTH) == 0)
/* Annotation detection factors exclusive to Windows x64. */
#if defined (WINDOWS) && defined (X64)
/* Instruction `int 2c` hints that the preceding cbr is probably an annotation head. */
# define WINDOWS_X64_ANNOTATION_HINT_BYTE 0xcd
# define X64_WINDOWS_ENCODED_ANNOTATION_HINT 0x2ccd
/* Instruction `int 3` acts as a boundary for compiler optimizations, to prevent the
* the annotation from being transformed into something unrecognizable.
*/
# define WINDOWS_X64_OPTIMIZATION_FENCE 0xcc
# define IS_ANNOTATION_HEADER(scratch, pc) \
(instr_is_cbr(scratch) && (*(ushort *) (pc) == X64_WINDOWS_ENCODED_ANNOTATION_HINT))
#endif
/* OPND_RETURN_VALUE: create the return value operand for `mov $return_value,%xax`. */
#ifdef X64
# define OPND_RETURN_VALUE(return_value) OPND_CREATE_INT64(return_value)
#else
# define OPND_RETURN_VALUE(return_value) OPND_CREATE_INT32(return_value)
#endif
/* FASTCALL_REGISTER_ARG_COUNT: Specifies the number of arguments passed in registers. */
#ifdef X64
# ifdef UNIX
# define FASTCALL_REGISTER_ARG_COUNT 6
# else /* WINDOWS x64 */
# define FASTCALL_REGISTER_ARG_COUNT 4
# endif
#else /* x86 (all) */
# define FASTCALL_REGISTER_ARG_COUNT 2
#endif
#define DYNAMORIO_ANNOTATE_RUNNING_ON_DYNAMORIO_NAME \
"dynamorio_annotate_running_on_dynamorio"
#define DYNAMORIO_ANNOTATE_LOG_NAME \
"dynamorio_annotate_log"
#define DYNAMORIO_ANNOTATE_LOG_ARG_COUNT 20
/* Facilitates timestamp substitution in `dynamorio_annotate_log()`. */
#define LOG_ANNOTATION_TIMESTAMP_TOKEN "${timestamp}"
#define LOG_ANNOTATION_TIMESTAMP_TOKEN_LENGTH 12
/* Constant factors of the Valgrind annotation, as defined in valgrind.h. */
enum {
VG_ROL_COUNT = 4,
VG_PATTERN_LENGTH = 5,
};
/* Immediate operands to the special rol instructions.
* See __SPECIAL_INSTRUCTION_PREAMBLE in valgrind.h.
*/
#ifdef X64
static const int
expected_rol_immeds[VG_PATTERN_LENGTH] = {
3,
13,
61,
51
};
#else
static const int
expected_rol_immeds[VG_PATTERN_LENGTH] = {
3,
13,
29,
19
};
#endif
typedef enum _annotation_type_t {
/* Indicates that the analyzed instruction turned out not to be an annotation head. */
ANNOTATION_TYPE_NONE,
/* To invoke an annotation as an expression, the target app calls the annotation as if
* it were a normal function. The annotation instruction sequence follows the preamble
* of each annotation function, and instrumentation replaces it with (1) a clean call
* to each registered handler for that annotation, or (2) a return value substitution,
* depending on the type of registration.
*/
ANNOTATION_TYPE_EXPRESSION,
/* To invoke an annotation as a statement, the target app calls a macro defined in
* the annotation header (via dr_annotations_asm.h), which places the annotation
* instruction sequence inline at the invocation site. The sequence includes a normal
* call to the annotation function, so instrumentation simply involves removing the
* surrounding components of the annotation to expose the call. The DR client's clean
* calls will be invoked within the annotation function itself (see above).
*/
ANNOTATION_TYPE_STATEMENT,
} annotation_type_t;
/* Specifies the exact byte position of the essential components of an annotation. */
typedef struct _annotation_layout_t {
app_pc start_pc;
annotation_type_t type;
/* Indicates whether the annotation function in the target app is of void type. */
bool is_void;
/* Points to the annotation name in the target app's data section. */
const char *name;
/* Specifies the translation of the annotation instrumentation (e.g., clean call). */
app_pc substitution_xl8;
/* Specifies the byte at which app decoding should resume following the annotation. */
app_pc resume_pc;
} annotation_layout_t;
static strhash_table_t *handlers;
/* locked under the `handlers` table lock */
static dr_annotation_handler_t *vg_handlers[DR_VG_ID__LAST];
#if !(defined (WINDOWS) && defined (X64))
/* Dispatching function for Valgrind annotations (required because id of the Valgrind
* client request object cannot be determined statically).
*/
static dr_annotation_handler_t vg_router;
static dr_annotation_receiver_t vg_receiver; /* The sole receiver for `vg_router`. */
static opnd_t vg_router_arg; /* The sole argument for the clean call to `vg_router`. */
#endif
/* Required for passing the va_list in `dynamorio_annotate_log()` to the log function. */
extern ssize_t do_file_write(file_t f, const char *fmt, va_list ap);
/*********************************************************
* INTERNAL ROUTINE DECLARATIONS
*/
#if !(defined (WINDOWS) && defined (X64))
/* Valgrind dispatcher, called by the instrumentation of the Valgrind annotations. */
static void
handle_vg_annotation(app_pc request_args);
/* Maps a Valgrind request id into a (sequential) `DR_VG_ID__*` constant. */
static dr_valgrind_request_id_t
lookup_valgrind_request(ptr_uint_t request);
static ptr_uint_t
valgrind_running_on_valgrind(dr_vg_client_request_t *request);
#endif
static bool
is_annotation_tag(dcontext_t *dcontext, IN OUT app_pc *start_pc, instr_t *scratch,
OUT const char **name);
static void
identify_annotation(dcontext_t *dcontext, IN OUT annotation_layout_t *layout,
instr_t *scratch);
/* Create argument operands for the instrumented clean call */
static void
create_arg_opnds(dr_annotation_handler_t *handler, uint num_args,
dr_annotation_calling_convention_t call_type);
#ifdef DEBUG
/* Implements `dynamorio_annotate_log()`, including substitution of the string literal
* token "${timestamp}" with the current system time.
*/
static ssize_t
annotation_printf(const char *format, ...);
#endif
/* Invoked during hashtable entry removal */
static void
free_annotation_handler(void *p);
/*********************************************************
* ANNOTATION INTEGRATION FUNCTIONS
*/
void
annotation_init()
{
handlers = strhash_hash_create(GLOBAL_DCONTEXT, 8, 80, /* favor a small table */
HASHTABLE_ENTRY_SHARED | HASHTABLE_SHARED |
HASHTABLE_RELAX_CLUSTER_CHECKS | HASHTABLE_PERSISTENT,
free_annotation_handler
_IF_DEBUG("annotation handler hashtable"));
#if !(defined (WINDOWS) && defined (X64))
vg_router.type = DR_ANNOTATION_HANDLER_CALL;
/* The Valgrind client request object is passed in %xax. */
vg_router.num_args = 1;
vg_router_arg = opnd_create_reg(DR_REG_XAX);
vg_router.args = &vg_router_arg;
vg_router.symbol_name = NULL; /* No symbols in Valgrind annotations. */
vg_router.receiver_list = &vg_receiver;
vg_receiver.instrumentation.callback = (void *) (void (*)()) handle_vg_annotation;
vg_receiver.save_fpstate = false;
vg_receiver.next = NULL;
#endif
dr_annotation_register_return(DYNAMORIO_ANNOTATE_RUNNING_ON_DYNAMORIO_NAME,
(void *) (ptr_uint_t) true);
#ifdef DEBUG
/* The logging annotation requires a debug build of DR. Arbitrarily allows up to
* 20 arguments, since the clean call must have a fixed number of them.
*/
dr_annotation_register_call(DYNAMORIO_ANNOTATE_LOG_NAME, (void *) annotation_printf,
false, DYNAMORIO_ANNOTATE_LOG_ARG_COUNT,
DR_ANNOTATION_CALL_TYPE_VARARG);
#endif
#if !(defined (WINDOWS) && defined (X64))
/* DR pretends to be Valgrind. */
dr_annotation_register_valgrind(DR_VG_ID__RUNNING_ON_VALGRIND,
valgrind_running_on_valgrind);
#endif
}
void
annotation_exit()
{
uint i;
for (i = 0; i < DR_VG_ID__LAST; i++) {
if (vg_handlers[i] != NULL)
free_annotation_handler(vg_handlers[i]);
}
strhash_hash_destroy(GLOBAL_DCONTEXT, handlers);
}
bool
instrument_annotation(dcontext_t *dcontext, IN OUT app_pc *start_pc,
OUT instr_t **substitution _IF_WINDOWS_X64(IN bool hint_is_safe))
{
annotation_layout_t layout = {0};
/* This instr_t is used for analytical decoding throughout the detection functions.
* It is passed on the stack and its contents are considered void on function entry.
*/
instr_t scratch;
#if defined (WINDOWS) && defined (X64)
app_pc hint_pc = *start_pc;
bool hint = true;
byte hint_byte;
#endif
#if defined (WINDOWS) && defined (X64)
if (hint_is_safe) {
hint_byte = *hint_pc;
} else {
if (!safe_read(hint_pc, 1, &hint_byte))
return false;
}
if (hint_byte != WINDOWS_X64_ANNOTATION_HINT_BYTE)
return false;
/* The hint is the first byte of the 2-byte instruction `int 2c`. Skip both bytes. */
layout.start_pc = hint_pc + INT_LENGTH;
layout.substitution_xl8 = layout.start_pc;
#else
layout.start_pc = *start_pc;
#endif
instr_init(dcontext, &scratch);
TRY_EXCEPT(dcontext, {
identify_annotation(dcontext, &layout, &scratch);
}, { /* EXCEPT */
LOG(THREAD, LOG_ANNOTATIONS, 2, "Failed to instrument annotation at "PFX"\n",
*start_pc);
/* layout.type is already ANNOTATION_TYPE_NONE */
});
if (layout.type != ANNOTATION_TYPE_NONE) {
LOG(GLOBAL, LOG_ANNOTATIONS, 2, "Decoded %s instance of %s\n",
(layout.type == ANNOTATION_TYPE_EXPRESSION) ? "expression" : "statement",
layout.name);
/* Notify the caller where to resume decoding app instructions. */
*start_pc = layout.resume_pc;
if (layout.type == ANNOTATION_TYPE_EXPRESSION) {
dr_annotation_handler_t *handler;
TABLE_RWLOCK(handlers, write, lock);
handler = strhash_hash_lookup(GLOBAL_DCONTEXT, handlers, layout.name);
if (handler != NULL) {
if (handler->type == DR_ANNOTATION_HANDLER_CALL) {
/* Substitute the annotation with a label pointing to the handler. */
instr_t *call = INSTR_CREATE_label(dcontext);
dr_instr_label_data_t *label_data = instr_get_label_data_area(call);
SET_ANNOTATION_HANDLER(label_data, handler);
SET_ANNOTATION_APP_PC(label_data, layout.resume_pc);
instr_set_note(call, (void *) DR_NOTE_ANNOTATION);
instr_set_meta(call);
*substitution = call;
handler->is_void = layout.is_void;
if (!handler->is_void) {
/* Append `mov $0x0,%eax` to the annotation substitution, so that
* clients and tools recognize that %xax will be written here.
* The placeholder is "ok to mangle" because it (partially)
* implements the app's annotation. The placeholder will be
* removed post-client during mangling.
*/
instr_t *return_placeholder =
INSTR_XL8(INSTR_CREATE_mov_st(dcontext,
opnd_create_reg(REG_XAX),
OPND_CREATE_INT32(0)),
layout.substitution_xl8);
instr_set_note(return_placeholder, (void *) DR_NOTE_ANNOTATION);
/* Append the placeholder manually, because the caller can call
* `instrlist_append()` with a "sublist" of instr_t.
*/
instr_set_next(*substitution, return_placeholder);
instr_set_prev(return_placeholder, *substitution);
}
} else {
/* Substitute the annotation with `mov $return_value,%eax` */
void *return_value =
handler->receiver_list->instrumentation.return_value;
*substitution =
INSTR_XL8(INSTR_CREATE_mov_imm(dcontext,
opnd_create_reg(REG_XAX),
OPND_RETURN_VALUE(return_value)),
layout.substitution_xl8);
}
} /* else no handlers, so replace the annotation with nothing */
TABLE_RWLOCK(handlers, write, unlock);
}
/* else (layout.type == ANNOTATION_TYPE_STATEMENT), in which case the only
* instrumentation is to remove the jump-over-annotation such that the annotation
* function gets called like a normal function. Instrumentation of clean calls and
* return values will then occur within the annotation function (the case above).
*/
}
instr_free(dcontext, &scratch);
return (layout.type != ANNOTATION_TYPE_NONE);
}
#if !(defined (WINDOWS) && defined (X64))
void
instrument_valgrind_annotation(dcontext_t *dcontext, instrlist_t *bb, instr_t *xchg_instr,
app_pc xchg_pc, uint bb_instr_count)
{
int i;
instr_t *return_placeholder;
instr_t *instr, *next_instr;
dr_instr_label_data_t *label_data;
LOG(THREAD, LOG_ANNOTATIONS, 2, "Matched valgrind client request pattern at "PFX"\n",
instr_get_app_pc(xchg_instr));
/* We leave the argument gathering code as app instructions, because it writes to app
* registers (xref i#1423). Now delete the `xchg` instruction, and the `rol`
* instructions--unless a previous BB contains some of the `rol`, in which case they
* must be executed to avoid messing up %xdi (the 4 `rol` compose to form a nop).
*/
instr_destroy(dcontext, xchg_instr);
if (bb_instr_count > VG_ROL_COUNT) {
instr = instrlist_last(bb);
for (i = 0; i < VG_ROL_COUNT; i++) {
ASSERT(instr != NULL && instr_get_opcode(instr) == OP_rol);
next_instr = instr_get_prev(instr);
instrlist_remove(bb, instr);
instr_destroy(dcontext, instr);
instr = next_instr;
}
}
/* Substitute the annotation tail with a label pointing to the Valgrind handler. */
instr = INSTR_CREATE_label(dcontext);
instr_set_note(instr, (void *) DR_NOTE_ANNOTATION);
label_data = instr_get_label_data_area(instr);
SET_ANNOTATION_HANDLER(label_data, &vg_router);
SET_ANNOTATION_APP_PC(label_data, xchg_pc);
instr_set_meta(instr);
instrlist_append(bb, instr);
/* Append `mov $0x0,%edx` so that clients and tools recognize that %xdx will be
* written here. The placeholder is "ok to mangle" because it (partially) implements
* the app's annotation. The placeholder will be removed post-client during mangling.
*/
return_placeholder = INSTR_XL8(INSTR_CREATE_mov_st(dcontext, opnd_create_reg(REG_XDX),
OPND_CREATE_INT32(0)),
xchg_pc);
instr_set_note(return_placeholder, (void *) DR_NOTE_ANNOTATION);
instrlist_append(bb, return_placeholder);
}
#endif
/*********************************************************
* ANNOTATION API FUNCTIONS
*/
bool
dr_annotation_register_call(const char *annotation_name, void *callee, bool save_fpstate,
uint num_args, dr_annotation_calling_convention_t call_type)
{
bool result = true;
dr_annotation_handler_t *handler;
dr_annotation_receiver_t *receiver;
TABLE_RWLOCK(handlers, write, lock);
handler = (dr_annotation_handler_t *) strhash_hash_lookup(GLOBAL_DCONTEXT, handlers,
annotation_name);
if (handler == NULL) { /* make a new handler if never registered yet */
handler = HEAP_TYPE_ALLOC(GLOBAL_DCONTEXT, dr_annotation_handler_t,
ACCT_OTHER, UNPROTECTED);
memset(handler, 0, sizeof(dr_annotation_handler_t));
handler->type = DR_ANNOTATION_HANDLER_CALL;
handler->symbol_name = dr_strdup(annotation_name HEAPACCT(ACCT_OTHER));
handler->num_args = num_args;
if (num_args == 0) {
handler->args = NULL;
} else {
handler->args = HEAP_ARRAY_ALLOC(GLOBAL_DCONTEXT, opnd_t, num_args,
ACCT_OTHER, UNPROTECTED);
create_arg_opnds(handler, num_args, call_type);
}
strhash_hash_add(GLOBAL_DCONTEXT, handlers, handler->symbol_name, handler);
}
if (handler->type == DR_ANNOTATION_HANDLER_CALL || handler->receiver_list == NULL) {
/* If the annotation previously had a return value registration, it can be changed
* to clean call instrumentation, provided the return value was unregistered.
*/
handler->type = DR_ANNOTATION_HANDLER_CALL;
receiver = HEAP_TYPE_ALLOC(GLOBAL_DCONTEXT, dr_annotation_receiver_t,
ACCT_OTHER, UNPROTECTED);
receiver->instrumentation.callback = callee;
receiver->save_fpstate = save_fpstate;
receiver->next = handler->receiver_list; /* push the new receiver onto the list */
handler->receiver_list = receiver;
} else {
result = false; /* A return value is registered, so no call can be added. */
}
TABLE_RWLOCK(handlers, write, unlock);
return result;
}
bool
dr_annotation_register_valgrind(dr_valgrind_request_id_t request_id,
ptr_uint_t (*annotation_callback)
(dr_vg_client_request_t *request))
{
dr_annotation_handler_t *handler;
dr_annotation_receiver_t *receiver;
if (request_id >= DR_VG_ID__LAST)
return false;
TABLE_RWLOCK(handlers, write, lock);
handler = vg_handlers[request_id];
if (handler == NULL) { /* make a new handler if never registered yet */
handler = HEAP_TYPE_ALLOC(GLOBAL_DCONTEXT, dr_annotation_handler_t,
ACCT_OTHER, UNPROTECTED);
memset(handler, 0, sizeof(dr_annotation_handler_t));
handler->type = DR_ANNOTATION_HANDLER_VALGRIND;
vg_handlers[request_id] = handler;
}
receiver = HEAP_TYPE_ALLOC(GLOBAL_DCONTEXT, dr_annotation_receiver_t,
ACCT_OTHER, UNPROTECTED);
receiver->instrumentation.vg_callback = annotation_callback;
receiver->save_fpstate = false;
receiver->next = handler->receiver_list; /* push the new receiver onto the list */
handler->receiver_list = receiver;
TABLE_RWLOCK(handlers, write, unlock);
return true;
}
bool
dr_annotation_register_return(const char *annotation_name, void *return_value)
{
bool result = true;
dr_annotation_handler_t *handler;
dr_annotation_receiver_t *receiver;
TABLE_RWLOCK(handlers, write, lock);
ASSERT_TABLE_SYNCHRONIZED(handlers, WRITE);
handler = (dr_annotation_handler_t *) strhash_hash_lookup(GLOBAL_DCONTEXT, handlers,
annotation_name);
if (handler == NULL) { /* make a new handler if never registered yet */
handler = HEAP_TYPE_ALLOC(GLOBAL_DCONTEXT, dr_annotation_handler_t,
ACCT_OTHER, UNPROTECTED);
memset(handler, 0, sizeof(dr_annotation_handler_t));
handler->type = DR_ANNOTATION_HANDLER_RETURN_VALUE;
handler->symbol_name = dr_strdup(annotation_name HEAPACCT(ACCT_OTHER));
strhash_hash_add(GLOBAL_DCONTEXT, handlers, handler->symbol_name, handler);
}
if (handler->receiver_list == NULL) {
/* If the annotation previously had clean call registration, it can be changed to
* return value instrumentation, provided the calls have been unregistered.
*/
handler->type = DR_ANNOTATION_HANDLER_RETURN_VALUE;
receiver = HEAP_TYPE_ALLOC(GLOBAL_DCONTEXT, dr_annotation_receiver_t,
ACCT_OTHER, UNPROTECTED);
receiver->instrumentation.return_value = return_value;
receiver->save_fpstate = false;
receiver->next = NULL; /* Return value can only have one implementation. */
handler->receiver_list = receiver;
} else {
result = false; /* Calls are registered, preventing the new return value. */
}
TABLE_RWLOCK(handlers, write, unlock);
return result;
}
bool
dr_annotation_unregister_call(const char *annotation_name, void *callee)
{
bool found = false;
dr_annotation_handler_t *handler;
TABLE_RWLOCK(handlers, write, lock);
handler = (dr_annotation_handler_t *) strhash_hash_lookup(GLOBAL_DCONTEXT, handlers,
annotation_name);
if (handler != NULL && handler->receiver_list != NULL) {
dr_annotation_receiver_t *receiver = handler->receiver_list;
if (receiver->instrumentation.callback == callee) { /* case 1: remove the head */
handler->receiver_list = receiver->next;
HEAP_TYPE_FREE(GLOBAL_DCONTEXT, receiver, dr_annotation_receiver_t,
ACCT_OTHER, UNPROTECTED);
found = true;
} else { /* case 2: remove from within the list */
while (receiver->next != NULL) {
if (receiver->next->instrumentation.callback == callee) {
dr_annotation_receiver_t *removal = receiver->next;
receiver->next = removal->next;
HEAP_TYPE_FREE(GLOBAL_DCONTEXT, removal, dr_annotation_receiver_t,
ACCT_OTHER, UNPROTECTED);
found = true;
break;
}
receiver = receiver->next;
}
} /* Leave the handler for the next registration (free it on exit) */
}
TABLE_RWLOCK(handlers, write, unlock);
return found;
}
bool
dr_annotation_unregister_return(const char *annotation_name)
{
bool found = false;
dr_annotation_handler_t *handler;
TABLE_RWLOCK(handlers, write, lock);
handler = (dr_annotation_handler_t *) strhash_hash_lookup(GLOBAL_DCONTEXT, handlers,
annotation_name);
if ((handler != NULL) && (handler->receiver_list != NULL)) {
ASSERT(handler->receiver_list->next != NULL);
HEAP_TYPE_FREE(GLOBAL_DCONTEXT, handler->receiver_list, dr_annotation_receiver_t,
ACCT_OTHER, UNPROTECTED);
handler->receiver_list = NULL;
found = true;
} /* Leave the handler for the next registration (free it on exit) */
TABLE_RWLOCK(handlers, write, unlock);
return found;
}
bool
dr_annotation_unregister_valgrind(dr_valgrind_request_id_t request_id,
ptr_uint_t (*annotation_callback)
(dr_vg_client_request_t *request))
{
bool found = false;
dr_annotation_handler_t *handler;
TABLE_RWLOCK(handlers, write, lock);
handler = vg_handlers[request_id];
if ((handler != NULL) && (handler->receiver_list != NULL)) {
dr_annotation_receiver_t *receiver = handler->receiver_list;
if (receiver->instrumentation.vg_callback == annotation_callback) {
handler->receiver_list = receiver->next; /* case 1: remove the head */
HEAP_TYPE_FREE(GLOBAL_DCONTEXT, receiver, dr_annotation_receiver_t,
ACCT_OTHER, UNPROTECTED);
found = true;
} else { /* case 2: remove from within the list */
while (receiver->next != NULL) {
if (receiver->next->instrumentation.vg_callback == annotation_callback) {
dr_annotation_receiver_t *removal = receiver->next;
receiver->next = removal->next;
HEAP_TYPE_FREE(GLOBAL_DCONTEXT, removal, dr_annotation_receiver_t,
ACCT_OTHER, UNPROTECTED);
found = true;
break;
}
receiver = receiver->next;
}
} /* Leave the handler for the next registration (free it on exit) */
}
TABLE_RWLOCK(handlers, write, unlock);
return found;
}
/*********************************************************
* ANNOTATION IMPLEMENTATIONS
*/
#if !(defined (WINDOWS) && defined (X64))
static void
handle_vg_annotation(app_pc request_args)
{
dcontext_t *dcontext = get_thread_private_dcontext();
dr_valgrind_request_id_t request_id;
dr_annotation_receiver_t *receiver;
dr_vg_client_request_t request;
ptr_uint_t result;
if (!safe_read(request_args, sizeof(dr_vg_client_request_t), &request)) {
LOG(THREAD, LOG_ANNOTATIONS, 2, "Failed to read Valgrind client request args at "
PFX". Skipping the annotation.\n", request_args);
return;
}
result = request.default_result;
request_id = lookup_valgrind_request(request.request);
if (request_id < DR_VG_ID__LAST) {
TABLE_RWLOCK(handlers, read, lock);
receiver = vg_handlers[request_id]->receiver_list;
while (receiver != NULL) {
result = receiver->instrumentation.vg_callback(&request);
receiver = receiver->next;
}
TABLE_RWLOCK(handlers, read, unlock);
} else {
LOG(THREAD, LOG_ANNOTATIONS, 2,
"Skipping unrecognized Valgrind client request id %d\n", request.request);
return;
}
/* Put the result in %xdx where the target app expects to find it. */
# ifdef CLIENT_INTERFACE
if (dcontext->client_data->mcontext_in_dcontext) {
get_mcontext(dcontext)->xdx = result;
} else
# endif
{
priv_mcontext_t *state = get_priv_mcontext_from_dstack(dcontext);
state->xdx = result;
}
}
static dr_valgrind_request_id_t
lookup_valgrind_request(ptr_uint_t request)
{
switch (request) {
case VG_USERREQ__RUNNING_ON_VALGRIND:
return DR_VG_ID__RUNNING_ON_VALGRIND;
case VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE:
return DR_VG_ID__MAKE_MEM_DEFINED_IF_ADDRESSABLE;
case VG_USERREQ__DISCARD_TRANSLATIONS:
return DR_VG_ID__DISCARD_TRANSLATIONS;
}
return DR_VG_ID__LAST;
}
static ptr_uint_t
valgrind_running_on_valgrind(dr_vg_client_request_t *request)
{
return 1; /* Pretend to be Valgrind. */
}
#endif
/*********************************************************
* INTERNAL ROUTINES
*/
/* If the app code at `*cur_pc` can be read as an encoded annotation label, then:
* (1) advance `*cur_pc` beyond the last label instruction,
* (2) point `**name` to the start of the label within the app image data section, and
* (3) return true.
* If there is no annotation label at `*cur_pc`, or failure occurs while trying to read
* it, then return false with undefined values for `*cur_pc` and `**name`.
*
* On Unix and Windows x86, the label has the form
* "<annotation-label>:<return-type>:<annotation-name>, e.g.
*
* "dynamorio-annotation:void:dynamorio_annotate_log"
* "dynamorio-annotation:const char *:some_custom_client_annotation"
*
* On Windows x64, the label has an additional token for the annotation type:
* "<annotation-label>:<annotation-type>:<return-type>:<annotation-name>, e.g.
*
* "dynamorio-annotation:statement:void:dynamorio_annotate_log"
* "dynamorio-annotation:expression:const char *:some_custom_client_annotation"
*
* The encoding of the pointer to the label varies by platform:
*
* Unix expression annotation label (two instructions form <GOT-base> + <GOT-offset>):
*
* mov $<GOT-base>,%xax
* bsr $<GOT-offset>,%xax
*
* Windows x86 expression annotation label (direct pointer to the <label>):
*
* mov $<label>,%eax
*
* Windows x64 expression annotation label (same, in 2 variations):
*
* mov $<label>,%rax Or prefetch $<label>
* prefetch %rax
*
* Decoding the label pointer proceeds as follows:
*
* (step 1) check `*cur_pc` for the opcode of the first label-encoding instruction
* (step 2) check the operand of that instruction for a label-encoding operand type
* Unix only--dereference the GOT entry:
* (step 3) check the next instruction for the label offset opcode (bsr or bsf)
* (step 4) check its operand for the offset-encoding operand type (base disp)
* (step 5) add the two operands and dereference as the label's GOT entry
* (step 6) attempt to read the decoded pointer and compare to "dynamorio-annotation"
* (note there is a special case for Windows x64, which is not inline asm)
* (step 7) if it matches, point `**name` to the character beyond the separator ':'
*
* See https://code.google.com/p/dynamorio/wiki/Annotations for complete examples.
*/
static inline bool
is_annotation_tag(dcontext_t *dcontext, IN OUT app_pc *cur_pc, instr_t *scratch,
OUT const char **name)
{
app_pc start_pc = *cur_pc;
instr_reset(dcontext, scratch);
*cur_pc = decode(dcontext, *cur_pc, scratch);
if (IS_ANNOTATION_LABEL_INSTR(scratch)) { /* step 1 */
opnd_t src = instr_get_src(scratch, 0);
if (IS_ANNOTATION_LABEL_REFERENCE(src)) { /* step 2 */
char buf[DYNAMORIO_ANNOTATION_LABEL_LENGTH + 1/*nul*/];
app_pc buf_ptr;
app_pc opnd_ptr = GET_ANNOTATION_LABEL_REFERENCE(src, start_pc);
#ifdef UNIX
app_pc got_ptr;
instr_reset(dcontext, scratch);
*cur_pc = decode(dcontext, *cur_pc, scratch);
if (!IS_ANNOTATION_LABEL_GOT_OFFSET_INSTR(scratch)) /* step 3 */
return false;
src = instr_get_src(scratch, 0);
if (!IS_ANNOTATION_LABEL_GOT_OFFSET_REFERENCE(src)) /* step 4 */
return false;
opnd_ptr += opnd_get_disp(src); /* step 5 */
if (!safe_read(opnd_ptr, sizeof(app_pc), &got_ptr))
return false;
opnd_ptr = got_ptr;
#endif
#if defined (WINDOWS) && defined (X64)
/* In Windows x64, if the `prefetch` instruction was found at
* `*cur_pc` with no intervening `mov` instruction, the label
* pointer must be an immediate operand to that `prefetch`.
*/
if (instr_get_opcode(scratch) == OP_prefetchw) { /* step 6 */
if (!safe_read(opnd_ptr, DYNAMORIO_ANNOTATION_LABEL_LENGTH, buf))
return false;
buf[DYNAMORIO_ANNOTATION_LABEL_LENGTH] = '\0';
if (strcmp(buf, DYNAMORIO_ANNOTATION_LABEL) == 0) {
*name = (const char *) (opnd_ptr + /* step 7 */
DYNAMORIO_ANNOTATION_LABEL_LENGTH
+ 1); /* skip the separator ':' */
return true;
}
} /* else the label pointer is the usual `mov` operand: */
#endif
if (!safe_read(opnd_ptr, sizeof(app_pc), &buf_ptr)) /* step 6 */
return false;
if (!safe_read(buf_ptr, DYNAMORIO_ANNOTATION_LABEL_LENGTH, buf))
return false;
buf[DYNAMORIO_ANNOTATION_LABEL_LENGTH] = '\0';
if (strcmp(buf, DYNAMORIO_ANNOTATION_LABEL) != 0)
return false;
*name = (const char *) (buf_ptr + /* step 7 */
DYNAMORIO_ANNOTATION_LABEL_LENGTH
+ 1); /* skip the separator ':' */
return true;
}
}
return false;
}
#if defined (WINDOWS) && defined (X64)
/* Identify the annotation at layout->start_pc, if any. On Windows x64, some flexibility
* is required to recognize the annotation sequence because it is compiled instead of
* explicitly inserted with inline asm (which is unavailable in MSVC for this platform).
* (step 1) check if the instruction at layout->start_pc encodes an annotation label.
* (step 2) decode arbitrary instructions up to a `prefetch`,
* following any direct branches decoded along the way.
* (step 3) skip the `int 3` following the `prefetch`.
* (step 4) set the resume pc, so execution resumes within the annotation body.
* (step 5) compare the next label token to determine the annotation type.
* Expression annotations only:
* (step 6) compare the return type to determine whether the annotation is void.
* (step 7) advance the label pointer to the name token
* (note that the Statement annotation concludes with a special case).
*/
static inline void
identify_annotation(dcontext_t *dcontext, IN OUT annotation_layout_t *layout,
instr_t *scratch)
{
app_pc cur_pc = layout->start_pc, last_call = NULL;
if (!is_annotation_tag(dcontext, &cur_pc, scratch, &layout->name)) /* step 1 */
return;
while (instr_get_opcode(scratch) != OP_prefetchw) { /* step 2 */
if (instr_is_ubr(scratch))
cur_pc = instr_get_branch_target_pc(scratch);
instr_reset(dcontext, scratch);
cur_pc = decode(dcontext, cur_pc, scratch);
}
ASSERT(*cur_pc == WINDOWS_X64_OPTIMIZATION_FENCE); /* step 3 */
cur_pc++;
layout->resume_pc = cur_pc; /* step 4 */
if (IS_ANNOTATION_STATEMENT_LABEL(layout->name)) { /* step 5 */
layout->type = ANNOTATION_TYPE_STATEMENT;
layout->name += (ANNOTATION_STATEMENT_LABEL_LENGTH + 1);
layout->name = strchr(layout->name, ':') + 1; /* step 7 */
/* If the target app contains an annotation whose argument is a function call that
* gets inlined, and that function contains the same annotation, the compiler will
* fuse the headers. See https://code.google.com/p/dynamorio/wiki/Annotations for
* a sample of fused headers. This loop identifies and skips any fused headers.
*/
while (true) {
instr_reset(dcontext, scratch);
cur_pc = decode(dcontext, cur_pc, scratch);
if (IS_ANNOTATION_HEADER(scratch, cur_pc)) {
cur_pc += INT_LENGTH;
while (instr_get_opcode(scratch) != OP_prefetchw) {
if (instr_is_ubr(scratch))
cur_pc = instr_get_branch_target_pc(scratch);
instr_reset(dcontext, scratch);
cur_pc = decode(dcontext, cur_pc, scratch);
}
ASSERT(*cur_pc == WINDOWS_X64_OPTIMIZATION_FENCE);
cur_pc++;
layout->resume_pc = cur_pc;
} else if (instr_is_cti(scratch))
break;
}
} else {
layout->type = ANNOTATION_TYPE_EXPRESSION;
layout->name += (ANNOTATION_EXPRESSION_LABEL_LENGTH + 1);
layout->is_void = IS_ANNOTATION_VOID(layout->name); /* step 6 */
layout->name = strchr(layout->name, ':') + 1; /* step 7 */
}
}
#else /* Windows x86 and all Unix */
/* Identify the annotation at layout->start_pc, if any. In summary:
* (step 1) check if the instruction at layout->start_pc encodes an annotation label.
* (step 2) determine the annotation type based on instruction opcodes
* Expression annotations only:
* (step 3) compare the return type to determine whether the annotation is void.
* (step 4) adjust the substitution xl8 to the current pc.
* (step 5) decode past the jump over the annotation body.
* (step 6) set the resume pc to the current instruction, which is a jump over the
* native version of the annotation body (specified by the target app).
* (step 7) advance the label pointer to the name token.
*/
static inline void
identify_annotation(dcontext_t *dcontext, IN OUT annotation_layout_t *layout,
instr_t *scratch)
{
app_pc cur_pc = layout->start_pc;
if (is_annotation_tag(dcontext, &cur_pc, scratch, &layout->name)) { /* step 1 */
# ifdef WINDOWS
if (*(cur_pc++) == RAW_OPCODE_pop_eax) { /* step 2 */
# else
if (instr_get_opcode(scratch) == OP_bsf) { /* step 2 */
# endif
layout->type = ANNOTATION_TYPE_STATEMENT;
} else {
layout->type = ANNOTATION_TYPE_EXPRESSION;
layout->is_void = IS_ANNOTATION_VOID(layout->name); /* step 3 */
}
layout->substitution_xl8 = cur_pc; /* step 4 */
instr_reset(dcontext, scratch);
cur_pc = decode_cti(dcontext, cur_pc, scratch); /* step 5 */
ASSERT(instr_is_ubr(scratch));
layout->resume_pc = cur_pc; /* step 6 */
layout->name = strchr(layout->name, ':') + 1; /* step 7 */
}
}
#endif
#ifdef X64
# ifdef UNIX
static inline void /* UNIX x64 */
create_arg_opnds(dr_annotation_handler_t *handler, uint num_args,
dr_annotation_calling_convention_t call_type)
{
uint i, arg_stack_location;
ASSERT(call_type == DR_ANNOTATION_CALL_TYPE_FASTCALL); /* architecture constraint */
switch (num_args) { /* Create up to six register args */
default:
case 6:
handler->args[5] = opnd_create_reg(DR_REG_R9);
case 5:
handler->args[4] = opnd_create_reg(DR_REG_R8);
case 4:
handler->args[3] = opnd_create_reg(DR_REG_XCX);
case 3:
handler->args[2] = opnd_create_reg(DR_REG_XDX);
case 2:
handler->args[1] = opnd_create_reg(DR_REG_XSI);
case 1:
handler->args[0] = opnd_create_reg(DR_REG_XDI);
}
/* Create the remaining args on the stack */
for (i = FASTCALL_REGISTER_ARG_COUNT; i < num_args; i++) {
/* The clean call will appear at the top of the annotation function body, where
* the stack arguments follow the return address and the caller's saved stack
* pointer. Rewind `i` to 0 and add 2 to skip over these pointers.
*/
arg_stack_location = sizeof(ptr_uint_t) * (i-FASTCALL_REGISTER_ARG_COUNT+2);
/* Use the stack pointer because the base pointer is a general register in x64 */
handler->args[i] = OPND_CREATE_MEMPTR(DR_REG_XSP, arg_stack_location);
}
}
# else /* WINDOWS x64 */
static inline void
create_arg_opnds(dr_annotation_handler_t *handler, uint num_args,
dr_annotation_calling_convention_t call_type)
{
uint i, arg_stack_location;
ASSERT(call_type == DR_ANNOTATION_CALL_TYPE_FASTCALL); /* architecture constraint */
switch (num_args) { /* Create up to four register args */
default:
case 4:
handler->args[3] = opnd_create_reg(DR_REG_R9);
case 3:
handler->args[2] = opnd_create_reg(DR_REG_R8);
case 2:
handler->args[1] = opnd_create_reg(DR_REG_XDX);
case 1:
handler->args[0] = opnd_create_reg(DR_REG_XCX);
}
/* Create the remaining args on the stack */
for (i = FASTCALL_REGISTER_ARG_COUNT; i < num_args; i++) {
/* The clean call will appear at the top of the annotation function body, where
* the stack arguments follow the return address and 32 bytes of empty space.
* Since `i` is already starting at 4, just add one more to reach the args.
*/
arg_stack_location = sizeof(ptr_uint_t) * (i+1);
/* Use the stack pointer because the base pointer is a general register in x64 */
handler->args[i] = OPND_CREATE_MEMPTR(DR_REG_XSP, arg_stack_location);
}
}
# endif
#else /* x86 (all) */
static inline void
create_arg_opnds(dr_annotation_handler_t *handler, uint num_args,
dr_annotation_calling_convention_t call_type)
{
uint i, arg_stack_location;
if (call_type == DR_ANNOTATION_CALL_TYPE_FASTCALL) {
switch (num_args) { /* Create 1 or 2 register args */
default:
case 2:
handler->args[1] = opnd_create_reg(DR_REG_XDX);
case 1:
handler->args[0] = opnd_create_reg(DR_REG_XCX);
}
/* Create the remaining args on the stack */
for (i = FASTCALL_REGISTER_ARG_COUNT; i < num_args; i++) {
/* The clean call will appear at the top of the annotation function body,
* where the stack args follow the return address and the caller's saved base
* pointer. Since `i` already starts at 2, use it to skip those pointers.
*/
arg_stack_location = sizeof(ptr_uint_t) * i;
handler->args[i] = OPND_CREATE_MEMPTR(DR_REG_XBP, arg_stack_location);
}
} else { /* DR_ANNOTATION_CALL_TYPE_STDCALL: Create all args on the stack */
for (i = 0; i < num_args; i++) {
/* The clean call will appear at the top of the annotation function body,
* where the stack args follow the return address and the caller's saved base
* pointer. Since `i` starts at 0, add 2 to skip those pointers.
*/
arg_stack_location = sizeof(ptr_uint_t) * (i+2);
handler->args[i] = OPND_CREATE_MEMPTR(DR_REG_XBP, arg_stack_location);
}
}
}
#endif
#ifdef DEBUG
static ssize_t
annotation_printf(const char *format, ...)
{
va_list ap;
ssize_t count;
const char *timestamp_token_start;
char *timestamped_format = NULL;
uint buffer_length = 0;
if (stats == NULL || stats->loglevel == 0)
return 0; /* No log is available for writing. */
if ((stats->logmask & LOG_VIA_ANNOTATIONS) == 0)
return 0; /* Filtered out by the user. */
/* Substitute the first instance of the timestamp token with a timestamp string.
* Additional timestamp tokens will be ignored, because it would be pointless.
*/
timestamp_token_start = strstr(format, LOG_ANNOTATION_TIMESTAMP_TOKEN);
if (timestamp_token_start != NULL) {
char timestamp_buffer[PRINT_TIMESTAMP_MAX_LENGTH];
buffer_length = (uint) (strlen(format) + PRINT_TIMESTAMP_MAX_LENGTH);
if (print_timestamp_to_buffer(timestamp_buffer, PRINT_TIMESTAMP_MAX_LENGTH) > 0) {
uint length_before_token =
(uint)((ptr_uint_t) timestamp_token_start - (ptr_uint_t) format);
/* print the timestamped format string into this heap buffer */
timestamped_format = HEAP_ARRAY_ALLOC(GLOBAL_DCONTEXT, char, buffer_length,
ACCT_OTHER, UNPROTECTED);
/* copy the original format string up to the timestamp token */
our_snprintf(timestamped_format, length_before_token, "%s", format);
/* copy the timestamp and the remainder of the original format string */
our_snprintf(timestamped_format + length_before_token,
(buffer_length - length_before_token), "%s%s", timestamp_buffer,
timestamp_token_start + LOG_ANNOTATION_TIMESTAMP_TOKEN_LENGTH);
/* use the timestamped format string */
format = (const char *) timestamped_format;
} else {
LOG(GLOBAL, LOG_ANNOTATIONS, 2, "Failed to obtain a system timestamp for "
"substitution in annotation log statements '%s'\n", format);
}
}
va_start(ap, format);
count = do_file_write(GLOBAL, format, ap);
va_end(ap);
if (timestamped_format != NULL) { /* free the timestamp heap buffer, if any */
HEAP_ARRAY_FREE(GLOBAL_DCONTEXT, format, char, buffer_length,
ACCT_OTHER, UNPROTECTED);
}
return count;
}
#endif
static void
free_annotation_handler(void *p)
{
dr_annotation_handler_t *handler = (dr_annotation_handler_t *) p;
dr_annotation_receiver_t *next, *receiver = handler->receiver_list;
while (receiver != NULL) {
next = receiver->next;
HEAP_TYPE_FREE(GLOBAL_DCONTEXT, receiver, dr_annotation_receiver_t,
ACCT_OTHER, UNPROTECTED);
receiver = next;
}
if (handler->num_args > 0) {
HEAP_ARRAY_FREE(GLOBAL_DCONTEXT, handler->args, opnd_t, handler->num_args,
ACCT_OTHER, UNPROTECTED);
}
if ((handler->type == DR_ANNOTATION_HANDLER_CALL) ||
(handler->type == DR_ANNOTATION_HANDLER_RETURN_VALUE))
dr_strfree(handler->symbol_name HEAPACCT(ACCT_OTHER));
HEAP_TYPE_FREE(GLOBAL_DCONTEXT, p, dr_annotation_handler_t, ACCT_OTHER, UNPROTECTED);
}
#endif /* ANNOTATIONS */