| /* ********************************************************** |
| * 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 */ |