blob: e8d5a35c8edff4430a6882d9a363191640628cb6 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2013-2014 Google, Inc. All rights reserved.
* Copyright (c) 2001-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.
*/
/* Copyright (c) 2003-2007 Determina Corp. */
/* Copyright (c) 2001-2003 Massachusetts Institute of Technology */
/* Copyright (c) 2001 Hewlett-Packard Company */
/*
* x86_code.c - auxiliary C routines to assembly routines in x86.asm
*/
#include "../globals.h"
#include "../fragment.h"
#include "../dispatch.h"
#include "../monitor.h"
#include "arch.h"
#include <string.h> /* for memcpy */
/* Helper routine for the x86.asm PUSH_DR_MCONTEXT, to fill in the xmm0-5 values
* (or all for linux) (or ymm) only if necessary.
*/
void
get_xmm_vals(priv_mcontext_t *mc)
{
#ifdef X86
if (preserve_xmm_caller_saved()) {
ASSERT(proc_has_feature(FEATURE_SSE));
if (YMM_ENABLED())
get_ymm_caller_saved(&mc->ymm[0]);
else
get_xmm_caller_saved(&mc->ymm[0]);
}
#elif defined(ARM)
/* FIXME i#1551: no xmm but SIMD regs on ARM */
ASSERT_NOT_REACHED();
#endif
}
/* Just calls dynamo_thread_under_dynamo. We used to initialize dcontext here,
* but that would end up initializing it twice.
*/
static void
thread_starting(dcontext_t *dcontext)
{
ASSERT(dcontext->initialized);
dynamo_thread_under_dynamo(dcontext);
#ifdef WINDOWS
LOG(THREAD, LOG_INTERP, 2, "thread_starting: interpreting thread "TIDFMT"\n",
get_thread_id());
#endif
}
/* Initializes a dcontext with the supplied state and calls dispatch */
void
dynamo_start(priv_mcontext_t *mc)
{
priv_mcontext_t *mcontext;
dcontext_t *dcontext = get_thread_private_dcontext();
ASSERT(dcontext != NULL);
thread_starting(dcontext);
/* Signal other threads for take over. */
dynamorio_take_over_threads(dcontext);
/* Set return address */
dcontext->next_tag = mc->pc;
ASSERT(dcontext->next_tag != NULL);
/* transfer exec state to mcontext */
mcontext = get_mcontext(dcontext);
*mcontext = *mc;
/* clear pc */
mcontext->pc = 0;
DOLOG(2, LOG_TOP, {
byte *cur_esp;
GET_STACK_PTR(cur_esp);
LOG(THREAD, LOG_TOP, 2, "%s: next_tag="PFX", cur xsp="PFX", mc->xsp="PFX"\n",
__FUNCTION__, dcontext->next_tag, cur_esp, mc->xsp);
});
/* Swap stacks so dispatch is invoked outside the application. */
call_switch_stack(dcontext, dcontext->dstack, dispatch,
NULL/*not on initstack*/, true/*return on error*/);
/* In release builds, this will simply return and continue native
* execution. That's better than calling unexpected_return() which
* goes into an infinite loop.
*/
ASSERT_NOT_REACHED();
}
/* auto_setup: called by dynamo_auto_start for non-early follow children.
* This routine itself would be dynamo_auto_start except that we want
* our own go-native path separate from load_dynamo (we could still have
* this by dynamo_auto_start and jump to an asm routine for go-native,
* but keeping the entry in asm is more flexible).
* Assumptions: The saved priv_mcontext_t for the start of the app is on
* the stack, followed by a pointer to a region of memory to free
* (which can be NULL) and its size. If we decide not to take over
* this process, this routine returns; otherwise it does not return.
*/
void
auto_setup(ptr_uint_t appstack)
{
dcontext_t *dcontext;
priv_mcontext_t *mcontext;
byte *pappstack;
byte *addr;
pappstack = (byte *)appstack;
/* Our parameter points at a priv_mcontext_t struct, beyond which are
* two other fields:
pappstack --> +0 priv_mcontext_t struct
+x addr of memory to free (can be NULL)
+y sizeof memory to free
*/
automatic_startup = true;
/* we should control all threads */
control_all_threads = true;
dynamorio_app_init();
if (INTERNAL_OPTION(nullcalls)) {
dynamorio_app_exit();
return;
}
/* For apps injected using follow_children, this is where control should be
* allowed to go native for hotp_only & thin_client.
*/
if (RUNNING_WITHOUT_CODE_CACHE())
return;
/* useful to debug fork-following */
DOLOG(4, LOG_TOP, { SYSLOG_INTERNAL_INFO("dynamo auto start"); });
dcontext = get_thread_private_dcontext();
ASSERT(dcontext);
thread_starting(dcontext);
/* Despite what *should* happen, there can be other threads if a statically
* imported lib created one in its DllMain (Cygwin does this), or if a
* thread was injected from the outside. We go ahead and check for and
* take over any other threads at this time. Xref i#1304.
* XXX i#1305: we should really suspend all these other threads for DR init.
*/
dynamorio_take_over_threads(dcontext);
/* copy over the app state into mcontext */
mcontext = get_mcontext(dcontext);
*mcontext = *((priv_mcontext_t *)pappstack);
pappstack += sizeof(priv_mcontext_t);
dcontext->next_tag = mcontext->pc;
ASSERT(dcontext->next_tag != NULL);
/* free memory */
addr = (byte *) *((byte **)pappstack);
pappstack += sizeof(byte*);
if (addr != NULL) {
size_t size = *((size_t*)pappstack);
heap_error_code_t error_code;
/* since this is rx it was added to our exec list, remove now
* ASSUMPTION: no fragments in the region so no need to flush
*/
/* flushing would align for us but we have to do it ourselves here */
size_t alloc_size = ALIGN_FORWARD(size, PAGE_SIZE);
DODEBUG({
if (SHARED_FRAGMENTS_ENABLED())
ASSERT(!thread_vm_area_overlap(GLOBAL_DCONTEXT, addr, addr+alloc_size));
});
ASSERT(!thread_vm_area_overlap(dcontext, addr, addr+alloc_size));
remove_executable_region(addr, alloc_size, false/*do not have lock*/);
os_heap_free(addr, size, &error_code);
}
/* FIXME : for transparency should we zero out the appstack where we
* stored injection information? would be safe to do so here */
LOG(THREAD, LOG_INTERP, 1, "DynamoRIO auto start at 0x%08x\n",
dcontext->next_tag);
DOLOG(LOG_INTERP, 2, {
dump_mcontext(mcontext, THREAD, DUMP_NOT_XML);
});
call_switch_stack(dcontext, dcontext->dstack, dispatch,
NULL/*not on initstack*/, false/*shouldn't return*/);
ASSERT_NOT_REACHED();
}
/* Get the retstack index from the app stack and reset the mcontext to the
* original app state. The retstub saved it like this in x86.asm:
* push $retidx
* jmp back_from_native
* back_from_native:
* push mcontext
* call return_from_native(mc)
*/
int
native_get_retstack_idx(priv_mcontext_t *mc)
{
int retidx = (int) *(ptr_int_t *) mc->xsp;
mc->xsp += sizeof(void *); /* Undo the push. */
return retidx;
}
/****************************************************************************/
#ifdef UNIX
/* Called by new_thread_dynamo_start to initialize the dcontext
* structure for the current thread and start executing at the
* the pc stored in the clone_record_t * stored at *mc->xsp.
* Assumes that it is called on the dstack.
*
* CAUTION: don't add a lot of stack variables in this routine or call a lot
* of functions before get_clone_record() because get_clone_record()
* makes assumptions about the usage of stack being less than a page.
*/
void
new_thread_setup(priv_mcontext_t *mc)
{
dcontext_t *dcontext;
app_pc next_tag;
void *crec;
int rc;
/* this is where a new thread first touches other than the dstack,
* so we "enter" DR here
*/
ENTERING_DR();
/* i#149/PR 403015: clone_record_t is passed via dstack. */
crec = get_clone_record(mc->xsp);
LOG(GLOBAL, LOG_INTERP, 1,
"new_thread_setup: thread "TIDFMT", dstack "PFX" clone record "PFX"\n",
get_thread_id(), get_clone_record_dstack(crec), crec);
/* As we used dstack as app thread stack to pass clone record, we now need
* to switch back to the real app thread stack before continuing.
*/
mc->xsp = get_clone_record_app_xsp(crec);
/* clear xax/r0 (was used to hold clone record) */
ASSERT(mc->IF_X86_ELSE(xax, r0) == (reg_t) mc->pc);
mc->IF_X86_ELSE(xax, r0) = 0;
/* clear pc */
mc->pc = 0;
rc = dynamo_thread_init(get_clone_record_dstack(crec), mc
_IF_CLIENT_INTERFACE(false));
ASSERT(rc != -1); /* this better be a new thread */
dcontext = get_thread_private_dcontext();
ASSERT(dcontext != NULL);
/* set up sig handlers before starting itimer in thread_starting() (PR 537743)
* but thread_starting() calls initialize_dynamo_context() so cache next_tag
*/
next_tag = signal_thread_inherit(dcontext, (void *) crec);
ASSERT(next_tag != NULL);
thread_starting(dcontext);
dcontext->next_tag = next_tag;
call_switch_stack(dcontext, dcontext->dstack, dispatch,
NULL/*not on initstack*/, false/*shouldn't return*/);
ASSERT_NOT_REACHED();
}
# ifdef MACOS
/* Called from new_bsdthread_intercept for targeting a bsd thread user function.
* new_bsdthread_intercept stored the arg to the user thread func in
* mc->xax. We're on the app stack -- but this is a temporary solution.
* i#1403 covers intercepting in an earlier and better manner.
*/
void
new_bsdthread_setup(priv_mcontext_t *mc)
{
dcontext_t *dcontext;
app_pc next_tag;
void *crec, *func_arg;
int rc;
/* this is where a new thread first touches other than the dstack,
* so we "enter" DR here
*/
ENTERING_DR();
crec = (void *) mc->xax; /* placed there by new_bsdthread_intercept */
func_arg = (void *) get_clone_record_thread_arg(crec);
LOG(GLOBAL, LOG_INTERP, 1,
"new_thread_setup: thread "TIDFMT", dstack "PFX" clone record "PFX"\n",
get_thread_id(), get_clone_record_dstack(crec), crec);
rc = dynamo_thread_init(get_clone_record_dstack(crec), mc
_IF_CLIENT_INTERFACE(false));
ASSERT(rc != -1); /* this better be a new thread */
dcontext = get_thread_private_dcontext();
ASSERT(dcontext != NULL);
/* set up sig handlers before starting itimer in thread_starting() (PR 537743)
* but thread_starting() calls initialize_dynamo_context() so cache next_tag
*/
next_tag = signal_thread_inherit(dcontext, (void *) crec);
crec = NULL; /* now freed */
ASSERT(next_tag != NULL);
thread_starting(dcontext);
dcontext->next_tag = next_tag;
/* We assume that the only state that matters is the arg to the function. */
# ifdef X64
mc->rdi = (reg_t) func_arg;
# else
*(reg_t*)(mc->xsp + sizeof(reg_t)) = (reg_t) func_arg;
# endif
call_switch_stack(dcontext, dcontext->dstack, dispatch,
NULL/*not on initstack*/, false/*shouldn't return*/);
ASSERT_NOT_REACHED();
}
# endif /* MACOS */
#endif /* UNIX */
#ifdef WINDOWS
/* Called by nt_continue_dynamo_start when we're about to execute
* the continuation of an exception or APC: after NtContinue.
* next_pc is bogus, the real next pc has been stored in dcontext->next_tag.
* This routine is also used by NtSetContextThread.
*/
void
nt_continue_setup(priv_mcontext_t *mc)
{
app_pc next_pc;
dcontext_t *dcontext;
ENTERING_DR();
dcontext = get_thread_private_dcontext();
ASSERT(dcontext != NULL);
SELF_PROTECT_LOCAL(dcontext, WRITABLE);
/* save target in temp var during init of dcontext */
/* we have to use a different slot since next_tag ends up holding the do_syscall
* entry when entered from dispatch
*/
if (dcontext->asynch_target != NULL)
next_pc = dcontext->asynch_target;
else {
ASSERT(DYNAMO_OPTION(shared_syscalls));
next_pc = dcontext->next_tag;
}
LOG(THREAD, LOG_ASYNCH, 2, "nt_continue_setup: target is "PFX"\n", next_pc);
initialize_dynamo_context(dcontext);
dcontext->next_tag = next_pc;
ASSERT(dcontext->next_tag != NULL);
set_last_exit(dcontext, (linkstub_t *) get_asynch_linkstub());
dcontext->whereami = WHERE_TRAMPOLINE;
*get_mcontext(dcontext) = *mc;
/* clear pc */
get_mcontext(dcontext)->pc = 0;
#if defined(WINDOWS) && defined(CLIENT_INTERFACE)
/* We came straight from fcache, so swap to priv now (i#25) */
if (INTERNAL_OPTION(private_peb) && should_swap_peb_pointer())
swap_peb_pointer(dcontext, true/*to priv*/);
#endif
call_switch_stack(dcontext, dcontext->dstack, dispatch,
NULL/*not on initstack*/, false/*shouldn't return*/);
ASSERT_NOT_REACHED();
}
#endif /* WINDOWS */
/****************************************************************************/
/* C-level wrapper around the asm implementation. Shuffles arguments and
* increments stats.
* We used to use try/except on Linux and NtReadVirtualMemory on Windows, but
* this is faster than both.
*/
bool
safe_read_fast(const void *base, size_t size, void *out_buf, size_t *bytes_read)
{
byte *stop_pc;
size_t nbytes;
stop_pc = safe_read_asm(out_buf, base, size);
nbytes = stop_pc - (byte*)base;
if (bytes_read != NULL)
*bytes_read = nbytes;
return (nbytes == size);
}
bool
is_safe_read_pc(app_pc pc)
{
#ifdef X86
return (pc == (app_pc)safe_read_asm_pre ||
pc == (app_pc)safe_read_asm_mid ||
pc == (app_pc)safe_read_asm_post);
#elif defined(ARM)
return false;
#endif
}
app_pc
safe_read_resume_pc(void)
{
return (app_pc) &safe_read_asm_recover;
}
#if defined(STANDALONE_UNIT_TEST)
# define CONST_BYTE 0x1f
# define TEST_STACK_SIZE PAGE_SIZE
byte test_stack[TEST_STACK_SIZE];
static dcontext_t *static_dc;
static void
test_func(dcontext_t *dcontext)
{
byte var;
memcpy(&var, &var-1, 1); /* avoid uninit warning */
EXPECT((ptr_uint_t)dcontext, (ptr_uint_t)static_dc);
EXPECT(var , CONST_BYTE);
return;
}
static void
test_call_switch_stack(dcontext_t *dc)
{
byte* stack_ptr = test_stack + TEST_STACK_SIZE;
static_dc = dc;
print_file(STDERR, "testing asm call_switch_stack\n");
memset(test_stack, CONST_BYTE, sizeof(test_stack));
call_switch_stack(dc, stack_ptr, test_func,
NULL, true /* should return */);
}
static void
test_cpuid()
{
int cpuid_res[4] = {0};
print_file(STDERR, "testing asm cpuid\n");
EXPECT(cpuid_supported(), true);
IF_UNIX_ELSE(our_cpuid, __cpuid)(cpuid_res, 0); /* get vendor id */
/* cpuid_res[1..3] stores vendor info like "GenuineIntel" or "AuthenticAMD" for X86 */
EXPECT_NE(cpuid_res[1], 0);
EXPECT_NE(cpuid_res[2], 0);
EXPECT_NE(cpuid_res[3], 0);
}
void
unit_test_asm(dcontext_t *dc)
{
print_file(STDERR, "testing asm\n");
test_call_switch_stack(dc);
test_cpuid();
}
#endif /* STANDALONE_UNIT_TEST */