| /* ********************************************************** |
| * Copyright (c) 2010-2014 Google, Inc. All rights reserved. |
| * Copyright (c) 2000-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) 2000-2001 Hewlett-Packard Company */ |
| |
| /* |
| * dynamo.c -- initialization and cleanup routines for DynamoRIO |
| */ |
| |
| #include "globals.h" |
| #include "configure_defines.h" |
| #include "link.h" |
| #include "fragment.h" |
| #include "fcache.h" |
| #include "emit.h" |
| #include "dispatch.h" |
| #include "utils.h" |
| #include "monitor.h" |
| #include "vmareas.h" |
| #ifdef SIDELINE |
| # include "sideline.h" |
| #endif |
| #ifdef PAPI |
| # include "perfctr.h" |
| #endif |
| #ifdef CLIENT_INTERFACE |
| # include "instrument.h" |
| #endif |
| #include "hotpatch.h" |
| #include "moduledb.h" |
| #include "module_shared.h" |
| #include "synch.h" |
| #include "native_exec.h" |
| |
| #ifdef ANNOTATIONS |
| # include "annotations.h" |
| #endif |
| |
| #include <string.h> |
| |
| #ifdef WINDOWS |
| /* for close handle, duplicate handle, free memory and constants associated with them */ |
| /* also for nt_terminate_process_for_app() */ |
| #include "ntdll.h" |
| #include "nudge.h" /* to get generic_nudge_target() address for an assert */ |
| #endif |
| |
| #ifdef RCT_IND_BRANCH |
| # include "rct.h" |
| #endif |
| |
| #include "perscache.h" |
| |
| #ifdef VMX86_SERVER |
| # include "vmkuw.h" |
| #endif |
| |
| /* global thread-shared variables */ |
| bool dynamo_initialized = false; |
| bool dynamo_heap_initialized = false; |
| bool automatic_startup = false; |
| bool control_all_threads = false; |
| #ifdef WINDOWS |
| bool dr_early_injected = false; |
| int dr_early_injected_location = INJECT_LOCATION_Invalid; |
| bool dr_earliest_injected = false; |
| static void * dr_earliest_inject_args; |
| |
| /* should be set if we are controlling the primary thread, either by |
| * injecting initially (!dr_injected_secondary_thread), or by retaking |
| * over (dr_late_injected_primary_thread). Used only for debugging |
| * purposes, yet can't rely on !dr_injected_secondary_thread very |
| * early in the process |
| */ |
| bool dr_injected_primary_thread = false; |
| bool dr_injected_secondary_thread = false; |
| |
| /* should be set once we retakeover the primary thread for -inject_primary */ |
| bool dr_late_injected_primary_thread = false; |
| #endif /* WINDOWS */ |
| #ifdef DR_APP_EXPORTS |
| /* flags to indicate when DR is being initialized / exited using the API */ |
| bool dr_api_entry = false; |
| bool dr_api_exit = false; |
| #endif |
| #ifdef RETURN_AFTER_CALL |
| bool dr_preinjected = false; |
| #endif /* RETURN_AFTER_CALL */ |
| #ifdef UNIX |
| static bool dynamo_exiting = false; |
| #endif |
| bool dynamo_exited = false; |
| bool dynamo_exited_and_cleaned = false; |
| #ifdef DEBUG |
| bool dynamo_exited_log_and_stats = false; |
| #endif |
| /* Only used in release build to decide whether synch is needed, justifying |
| * its placement in .nspdata. If we use it for more we should protect it. |
| */ |
| DECLARE_NEVERPROT_VAR(bool dynamo_all_threads_synched, false); |
| bool dynamo_resetting = false; |
| #if defined(CLIENT_INTERFACE) || defined(STANDALONE_UNIT_TEST) |
| bool standalone_library = false; |
| #endif |
| #ifdef UNIX |
| bool post_execve = false; |
| #endif |
| /* initial stack so we don't have to use app's */ |
| byte * initstack; |
| |
| #if defined(WINDOWS) && defined(STACK_GUARD_PAGE) |
| /* PR203701: separate stack for error reporting when the dstack is exhausted */ |
| #define EXCEPTION_STACK_SIZE (2 * PAGE_SIZE) |
| DECLARE_NEVERPROT_VAR(byte *exception_stack, NULL); |
| #endif |
| |
| /*******************************************************/ |
| /* separate segment of Non-Self-Protected data to avoid data section |
| * protection issues -- we need to write to these vars in bootstrapping |
| * spots where we cannot unprotect first |
| */ |
| START_DATA_SECTION(NEVER_PROTECTED_SECTION, "w"); |
| |
| /* spinlock used in assembly trampolines when we can't spare registers for more */ |
| mutex_t initstack_mutex VAR_IN_SECTION(NEVER_PROTECTED_SECTION) |
| = INIT_SPINLOCK_FREE(initstack_mutex); |
| byte * initstack_app_xsp VAR_IN_SECTION(NEVER_PROTECTED_SECTION) = 0; |
| /* keeps track of how many threads are in cleanup_and_terminate */ |
| int exiting_thread_count VAR_IN_SECTION(NEVER_PROTECTED_SECTION) = 0; |
| |
| /* This is unprotected to allow stats to be written while the data |
| * segment is still protected (right now the only ones are selfmod stats) |
| */ |
| static dr_statistics_t nonshared_stats VAR_IN_SECTION(NEVER_PROTECTED_SECTION) |
| = {{0},}; |
| |
| /* Each lock protects its corresponding datasec_start, datasec_end, and |
| * datasec_writable variables. |
| */ |
| static mutex_t datasec_lock[DATASEC_NUM] VAR_IN_SECTION(NEVER_PROTECTED_SECTION) = {{0}}; |
| |
| /* back to normal section */ |
| END_DATA_SECTION() |
| /*******************************************************/ |
| |
| /* Like a recursive lock: 0==readonly, 1+=writable. |
| * This would be a simple array, but we need each in its own protected |
| * section, as this could be exploited. |
| */ |
| const uint datasec_writable_neverprot = 1; /* always writable */ |
| uint datasec_writable_rareprot = 1; |
| DECLARE_FREQPROT_VAR(uint datasec_writable_freqprot, 1); |
| DECLARE_CXTSWPROT_VAR(uint datasec_writable_cxtswprot, 1); |
| |
| static app_pc datasec_start[DATASEC_NUM]; |
| static app_pc datasec_end[DATASEC_NUM]; |
| |
| const uint DATASEC_SELFPROT[] = { |
| 0, |
| SELFPROT_DATA_RARE, |
| SELFPROT_DATA_FREQ, |
| SELFPROT_DATA_CXTSW, |
| }; |
| |
| const char * const DATASEC_NAMES[] = { |
| NEVER_PROTECTED_SECTION, |
| RARELY_PROTECTED_SECTION, |
| FREQ_PROTECTED_SECTION, |
| CXTSW_PROTECTED_SECTION, |
| }; |
| |
| /* kept in unprotected heap to avoid issues w/ data segment being RO */ |
| typedef struct _protect_info_t { |
| /* FIXME: this needs to be a recursive lock to handle signals |
| * and exceptions! |
| */ |
| mutex_t lock; |
| int num_threads_unprot; /* # threads in DR code */ |
| int num_threads_suspended; |
| } protect_info_t; |
| static protect_info_t *protect_info; |
| |
| static void data_section_init(void); |
| static void data_section_exit(void); |
| |
| #ifdef DEBUG /*************************/ |
| |
| #include <time.h> |
| |
| /* FIXME: not all dynamo_options references are #ifdef DEBUG |
| * are we trying to hardcode the options for a release build? |
| */ |
| # ifdef UNIX |
| /* linux include files for mmap stuff*/ |
| # include <sys/ipc.h> |
| # include <sys/types.h> |
| # include <unistd.h> |
| # endif |
| |
| static uint starttime; |
| |
| file_t main_logfile = INVALID_FILE; |
| |
| #endif /* DEBUG ****************************/ |
| |
| dr_statistics_t *stats = NULL; |
| |
| DECLARE_FREQPROT_VAR(static int num_known_threads, 0); |
| #ifdef UNIX |
| /* i#237/PR 498284: vfork threads that execve need to be separately delay-freed */ |
| DECLARE_FREQPROT_VAR(int num_execve_threads, 0); |
| #endif |
| DECLARE_FREQPROT_VAR(static uint threads_ever_count, 0); |
| |
| /* FIXME : not static so os.c can hand walk it for dump core */ |
| /* FIXME: use new generic_table_t and generic_hash_* routines */ |
| thread_record_t ** all_threads; /* ALL_THREADS_HASH_BITS-bit addressed hash table */ |
| |
| /* these locks are used often enough that we put them in .cspdata: */ |
| |
| /* not static so can be referenced in win32/os.c for SuspendThread handling, |
| * FIXME : is almost completely redundant in usage with thread_initexit_lock |
| * maybe replace this lock with thread_initexit_lock? */ |
| DECLARE_CXTSWPROT_VAR(mutex_t all_threads_lock, INIT_LOCK_FREE(all_threads_lock)); |
| /* used for synch to prevent thread creation/deletion in critical periods |
| * due to its use for flushing, this lock cannot be held while couldbelinking! |
| */ |
| DECLARE_CXTSWPROT_VAR(mutex_t thread_initexit_lock, |
| INIT_LOCK_FREE(thread_initexit_lock)); |
| |
| /* recursive to handle signals/exceptions while in DR code */ |
| DECLARE_CXTSWPROT_VAR(static recursive_lock_t thread_in_DR_exclusion, |
| INIT_RECURSIVE_LOCK(thread_in_DR_exclusion)); |
| |
| /****************************************************************************/ |
| #ifdef DEBUG |
| |
| static const char * |
| main_logfile_name(void) |
| { |
| return get_app_name_for_path(); |
| } |
| |
| static const char * |
| thread_logfile_name(void) |
| { |
| return "log"; |
| } |
| |
| #endif /* DEBUG */ |
| /****************************************************************************/ |
| |
| static void |
| statistics_pre_init(void) |
| { |
| /* until it's set up for real, point at static var |
| * really only logmask and loglevel are meaningful, so be careful! |
| * statistics_init and create_log_directory are the only routines that |
| * use stats before it's set up for real, currently |
| */ |
| /* The indirection here is left over from when we used to allow alternative |
| * locations for stats (namely shared memory for the old MIT gui). */ |
| stats = &nonshared_stats; |
| stats->process_id = get_process_id(); |
| strncpy(stats->process_name, get_application_name(), MAXIMUM_PATH); |
| stats->process_name[MAXIMUM_PATH-1] = '\0'; |
| ASSERT(strlen(stats->process_name) > 0); |
| stats->num_stats = 0; |
| } |
| |
| static void |
| statistics_init(void) |
| { |
| /* should have called statistics_pre_init() first */ |
| ASSERT(stats == &nonshared_stats); |
| ASSERT(stats->num_stats == 0); |
| #ifndef DEBUG |
| if (!DYNAMO_OPTION(global_rstats)) { |
| /* references to stat values should return 0 (static var) */ |
| return; |
| } |
| #endif |
| stats->num_stats = 0 |
| #ifdef DEBUG |
| # define STATS_DEF(desc, name) +1 |
| #else |
| # define RSTATS_DEF(desc, name) +1 |
| #endif |
| # include "statsx.h" |
| #undef STATS_DEF |
| #undef RSTATS_DEF |
| ; |
| /* We inline the stat description to make it easy for external processes |
| * to view our stats: they don't have to chase pointers, and we could put |
| * this in shared memory easily. However, we do waste some memory, but |
| * not much in release build. |
| */ |
| #ifdef DEBUG |
| # define STATS_DEF(desc, statname) \ |
| strncpy(stats->statname##_pair.name, desc, \ |
| BUFFER_SIZE_ELEMENTS(stats->statname##_pair.name)); \ |
| NULL_TERMINATE_BUFFER(stats->statname##_pair.name); |
| #else |
| # define RSTATS_DEF(desc, statname) \ |
| strncpy(stats->statname##_pair.name, desc, \ |
| BUFFER_SIZE_ELEMENTS(stats->statname##_pair.name)); \ |
| NULL_TERMINATE_BUFFER(stats->statname##_pair.name); |
| #endif |
| # include "statsx.h" |
| #undef STATS_DEF |
| #undef RSTATS_DEF |
| } |
| |
| static void |
| statistics_exit(void) |
| { |
| stats = NULL; |
| } |
| |
| dr_statistics_t * |
| get_dr_stats(void) |
| { |
| return stats; |
| } |
| |
| /* initialize per-process dynamo state; this must be called before any |
| * threads are created and before any other API calls are made; |
| * returns zero on success, non-zero on failure |
| */ |
| DYNAMORIO_EXPORT int |
| dynamorio_app_init(void) |
| { |
| int size; |
| |
| if (!dynamo_initialized /* we do enter if nullcalls is on */) { |
| |
| #ifdef WINDOWS |
| /* MUST do this before making any system calls */ |
| syscalls_init(); |
| #endif |
| /* avoid time() for libc independence */ |
| DODEBUG(starttime = query_time_seconds();); |
| |
| #ifdef UNIX |
| if (getenv(DYNAMORIO_VAR_EXECVE) != NULL) { |
| post_execve = true; |
| # ifdef VMX86_SERVER |
| /* PR 458917: our gdt slot was not cleared on exec so we need to |
| * clear it now to ensure we don't leak it and eventually run out of |
| * slots. We could alternatively call os_tls_exit() prior to |
| * execve, since syscalls use thread-private fcache_enter, but |
| * complex to recover from execve failure, so instead we pass which |
| * TLS index we had. |
| */ |
| os_tls_pre_init(atoi(getenv(DYNAMORIO_VAR_EXECVE))); |
| # endif |
| /* important to remove it, don't want to propagate to forked children, etc. */ |
| unsetenv(DYNAMORIO_VAR_EXECVE); |
| /* check that it's gone: we've had problems with unsetenv */ |
| ASSERT(getenv(DYNAMORIO_VAR_EXECVE) == NULL); |
| } else |
| post_execve = false; |
| #endif |
| |
| /* default non-zero dynamo settings (options structure is |
| * initialized to 0 automatically) |
| */ |
| #ifdef DEBUG |
| # ifndef INTERNAL |
| nonshared_stats.logmask = LOG_ALL_RELEASE; |
| # else |
| nonshared_stats.logmask = LOG_ALL; |
| # endif |
| statistics_pre_init(); |
| #endif |
| |
| config_init(); |
| options_init(); |
| #ifdef WINDOWS |
| syscalls_init_options_read(); /* must be called after options_init |
| * but before init_syscall_trampolines */ |
| #endif |
| utils_init(); |
| data_section_init(); |
| |
| #ifdef DEBUG |
| /* decision: nullcalls WILL create a dynamorio.log file and |
| * fill it with perfctr stats! |
| */ |
| if (stats->loglevel > 0) { |
| main_logfile = open_log_file(main_logfile_name(), NULL, 0); |
| LOG(GLOBAL, LOG_TOP, 1, "global log file fd=%d\n", main_logfile); |
| } else { |
| /* loglevel 0 means we don't create a log file! |
| * if the loglevel is later raised, too bad! it all goes to stderr! |
| * N.B.: when checking for no logdir, we check for empty string or |
| * first char '<'! |
| */ |
| strncpy(stats->logdir, "<none (loglevel was 0 on startup)>", MAXIMUM_PATH-1); |
| stats->logdir[MAXIMUM_PATH-1] = '\0'; /* if max no null */ |
| main_logfile = INVALID_FILE; |
| } |
| |
| # ifdef PAPI |
| /* setup hardware performance counting */ |
| hardware_perfctr_init(); |
| # endif |
| |
| DOLOG(1, LOG_TOP, { |
| print_version_and_app_info(GLOBAL); |
| }); |
| |
| /* now exit if nullcalls, now that perfctrs are set up */ |
| if (INTERNAL_OPTION(nullcalls)) { |
| print_file(main_logfile, |
| "** nullcalls is set, NOT taking over execution **\n\n"); |
| return SUCCESS; |
| } |
| |
| LOG(GLOBAL, LOG_TOP, 1, PRODUCT_NAME"'s stack size: %d Kb\n", |
| DYNAMORIO_STACK_SIZE/1024); |
| #endif /* !DEBUG */ |
| |
| /* set up exported statistics struct */ |
| #ifndef DEBUG |
| statistics_pre_init(); |
| #endif |
| statistics_init(); |
| |
| #ifdef VMX86_SERVER |
| /* Must be before {vmm_,}heap_init() */ |
| vmk_init_lib(); |
| #endif |
| vmm_heap_init_constraints(); /* before client libs are loaded! */ |
| #ifdef CLIENT_INTERFACE |
| /* PR 200207: load the client lib before callback_interception_init |
| * since the client library load would hit our own hooks (xref hotpatch |
| * cases about that) -- though -private_loader removes that issue. */ |
| /* Must be before [vmm_]heap_init() so we can register the client lib as |
| * reachable from the dr heap. Xref PR 215395. */ |
| instrument_load_client_libs(); |
| #endif |
| |
| /* initialize components (CAUTION: order is important here) */ |
| vmm_heap_init(); /* must be called even if not using vmm heap */ |
| heap_init(); |
| dynamo_heap_initialized = true; |
| |
| /* The process start event should be done after os_init() but before |
| * process_control_int() because the former initializes event logging |
| * and the latter can kill the process if a violation occurs. |
| */ |
| SYSLOG(SYSLOG_INFORMATION, |
| IF_CLIENT_INTERFACE_ELSE(INFO_PROCESS_START_CLIENT, INFO_PROCESS_START), |
| IF_CLIENT_INTERFACE_ELSE(3, 2), |
| get_application_name(), get_application_pid() |
| _IF_NOT_CLIENT_INTERFACE(get_application_md5())); |
| |
| #ifdef PROCESS_CONTROL |
| if (IS_PROCESS_CONTROL_ON()) /* Case 8594. */ |
| process_control_init(); |
| #endif |
| |
| #ifdef WINDOWS |
| /* Now that DR is set up, perform any final clean-up, before |
| * we do our address space scans. |
| */ |
| if (dr_earliest_injected) |
| earliest_inject_cleanup(dr_earliest_inject_args); |
| #endif |
| |
| dynamo_vm_areas_init(); |
| proc_init(); |
| modules_init(); /* before vm_areas_init() */ |
| os_init(); |
| config_heap_init(); /* after heap_init */ |
| |
| /* Setup for handling faults in loader_init() */ |
| /* initial stack so we don't have to use app's |
| * N.B.: we never de-allocate initstack (see comments in app_exit) |
| */ |
| initstack = (byte *) stack_alloc(DYNAMORIO_STACK_SIZE); |
| |
| #if defined(WINDOWS) && defined(STACK_GUARD_PAGE) |
| /* PR203701: separate stack for error reporting when the |
| * dstack is exhausted |
| */ |
| exception_stack = (byte *) stack_alloc(EXCEPTION_STACK_SIZE); |
| #endif |
| #ifdef WINDOWS |
| if (!INTERNAL_OPTION(noasynch)) { |
| /* We split the hooks up: first we put in just Ki* to catch |
| * exceptions in client init routines (PR 200207), but we don't want |
| * syscall hooks so client init can scan syscalls. |
| * Xref PR 216934 where this was originally down below 1st thread init, |
| * before we had GLOBAL_DCONTEXT. |
| */ |
| callback_interception_init_start(); |
| } |
| #endif /* WINDOWS */ |
| |
| /* loader initialization, finalize the private lib load. |
| * FIXME i#338: this must be before arch_init() for Windows, but Linux |
| * wants it later. |
| */ |
| loader_init(); |
| arch_init(); |
| synch_init(); |
| |
| #ifdef KSTATS |
| kstat_init(); |
| #endif |
| monitor_init(); |
| fcache_init(); |
| link_init(); |
| fragment_init(); |
| moduledb_init(); /* before vm_areas_init, after heap_init */ |
| perscache_init(); /* before vm_areas_init */ |
| native_exec_init(); /* before vm_areas_init, after arch_init */ |
| |
| if (!DYNAMO_OPTION(thin_client)) { |
| #ifdef HOT_PATCHING_INTERFACE |
| /* must init hotp before vm_areas_init() calls find_executable_vm_areas() */ |
| if (DYNAMO_OPTION(hot_patching)) |
| hotp_init(); |
| #endif |
| } |
| |
| #ifdef INTERNAL |
| { |
| char initial_options[MAX_OPTIONS_STRING]; |
| get_dynamo_options_string(&dynamo_options, |
| initial_options, sizeof(initial_options), true); |
| SYSLOG_INTERNAL_INFO("Initial options = %s", initial_options); |
| DOLOG(1, LOG_TOP, { |
| get_pcache_dynamo_options_string(&dynamo_options, initial_options, |
| sizeof(initial_options), |
| OP_PCACHE_LOCAL); |
| LOG(GLOBAL, LOG_TOP, 1, "Initial pcache-affecting options = %s\n", |
| initial_options); |
| }); |
| } |
| #endif /* INTERNAL */ |
| |
| LOG(GLOBAL, LOG_TOP, 1, "\n"); |
| |
| /* initialize thread hashtable */ |
| /* Note: for thin_client, this isn't needed if it is only going to |
| * look for spawned processes; however, if we plan to promote from |
| * thin_client to hotp_only mode (highly likely), this would be needed. |
| * For now, leave it in there unless thin_client footprint becomes an |
| * issue. |
| */ |
| size = HASHTABLE_SIZE(ALL_THREADS_HASH_BITS) * sizeof(thread_record_t*); |
| all_threads = (thread_record_t**) global_heap_alloc(size HEAPACCT(ACCT_THREAD_MGT)); |
| memset(all_threads, 0, size); |
| if (!INTERNAL_OPTION(nop_initial_bblock) |
| IF_WINDOWS(|| !check_sole_thread())) /* some other thread is already here! */ |
| bb_lock_start = true; |
| |
| #ifdef SIDELINE |
| /* initialize sideline thread after thread table is set up */ |
| if (dynamo_options.sideline) |
| sideline_init(); |
| #endif |
| |
| /* thread-specific initialization for the first thread we inject in |
| * (in a race with injected threads, sometimes it is not the primary thread) |
| */ |
| /* i#117/PR 395156: it'd be nice to have mc here but would |
| * require changing start/stop API |
| */ |
| dynamo_thread_init(NULL, NULL _IF_CLIENT_INTERFACE(false)); |
| #ifdef UNIX |
| /* i#27: we need to special-case the 1st thread */ |
| signal_thread_inherit(get_thread_private_dcontext(), NULL); |
| #endif |
| |
| /* We move vm_areas_init() below dynamo_thread_init() so we can have |
| * two things: 1) a dcontext and 2) a SIGSEGV handler, for TRY/EXCEPT |
| * inside vm_areas_init() for PR 361594's probes and for safe_read(). |
| * This means vm_areas_thread_init() runs before vm_areas_init(). |
| */ |
| if (!DYNAMO_OPTION(thin_client)) { |
| vm_areas_init(); |
| #ifdef RCT_IND_BRANCH |
| /* relies on is_in_dynamo_dll() which needs vm_areas_init */ |
| rct_init(); |
| #endif |
| } else { |
| /* This is needed to handle exceptions in thin_client mode, mostly |
| * internal ones, but can be app ones too. */ |
| dynamo_vm_areas_lock(); |
| find_dynamo_library_vm_areas(); |
| dynamo_vm_areas_unlock(); |
| } |
| |
| #ifdef ANNOTATIONS |
| annotation_init(); |
| #endif |
| #ifdef CLIENT_INTERFACE |
| /* client last, in case it depends on other inits: must be after |
| * dynamo_thread_init so the client can use a dcontext (PR 216936). |
| * Note that we *load* the client library before installing our hooks, |
| * but call the client's init routine afterward so that we correctly |
| * report crashes (PR 200207). |
| * Note: DllMain in client libraries can crash and we still won't |
| * report; better document that client libraries shouldn't have |
| * DllMain. |
| */ |
| instrument_init(); |
| /* To give clients a chance to process pcaches as we load them, we |
| * delay the loading until we've initialized the clients. |
| */ |
| vm_area_delay_load_coarse_units(); |
| #endif |
| |
| #ifdef WINDOWS |
| if (!INTERNAL_OPTION(noasynch)) |
| callback_interception_init_finish(); /* split for PR 200207: see above */ |
| #endif |
| |
| if (SELF_PROTECT_ON_CXT_SWITCH) { |
| protect_info = (protect_info_t *) |
| global_unprotected_heap_alloc(sizeof(protect_info_t) HEAPACCT(ACCT_OTHER)); |
| ASSIGN_INIT_LOCK_FREE(protect_info->lock, protect_info); |
| protect_info->num_threads_unprot = 0; /* ENTERING_DR() below will inc to 1 */ |
| protect_info->num_threads_suspended = 0; |
| if (INTERNAL_OPTION(single_privileged_thread)) { |
| /* FIXME: thread_initexit_lock must be a recursive lock! */ |
| ASSERT_NOT_IMPLEMENTED(false); |
| /* grab the lock now -- the thread that is in dynamo must be holding |
| * the lock, and we are the initial thread in dynamo! |
| */ |
| mutex_lock(&thread_initexit_lock); |
| } |
| /* ENTERING_DR will increment, so decrement first |
| * FIXME: waste of protection change since will nop-unprotect! |
| */ |
| if (TEST(SELFPROT_DATA_CXTSW, DYNAMO_OPTION(protect_mask))) |
| datasec_writable_cxtswprot = 0; |
| /* FIXME case 8073: remove once freqprot not every cxt sw */ |
| if (TEST(SELFPROT_DATA_FREQ, DYNAMO_OPTION(protect_mask))) |
| datasec_writable_freqprot = 0; |
| } |
| /* this thread is now entering DR */ |
| ENTERING_DR(); |
| |
| #ifdef WINDOWS |
| if (DYNAMO_OPTION(early_inject)) { |
| /* AFTER callback_interception_init and self protect init and |
| * ENTERING_DR() */ |
| early_inject_init(); |
| } |
| #endif |
| } |
| |
| dynamo_initialized = true; |
| |
| /* Protect .data, assuming all vars there have been initialized. */ |
| SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT); |
| |
| /* internal-only options for testing run-once (case 3990) */ |
| if (INTERNAL_OPTION(unsafe_crash_process)) { |
| SYSLOG_INTERNAL_ERROR("Crashing the process deliberately!"); |
| *((int *)PTR_UINT_MINUS_1) = 0; |
| } |
| if (INTERNAL_OPTION(unsafe_hang_process)) { |
| event_t never_signaled = create_event(); |
| SYSLOG_INTERNAL_ERROR("Hanging the process deliberately!"); |
| wait_for_event(never_signaled); |
| destroy_event(never_signaled); |
| } |
| |
| return SUCCESS; |
| } |
| |
| #ifdef UNIX |
| void |
| dynamorio_fork_init(dcontext_t *dcontext) |
| { |
| /* on a fork we want to re-initialize some data structures, especially |
| * log files, which we want a separate directory for |
| */ |
| thread_record_t **threads; |
| int i, num_threads; |
| # ifdef DEBUG |
| char parent_logdir[MAXIMUM_PATH]; |
| # endif |
| |
| /* re-cache app name, etc. that are using parent pid before we |
| * create log dirs (xref i#189/PR 452168) |
| */ |
| os_fork_init(dcontext); |
| |
| /* sanity check, plus need to set this for statistics_init: |
| * even if parent did an execve, env var should be reset by now |
| */ |
| post_execve = (getenv(DYNAMORIO_VAR_EXECVE) != NULL); |
| ASSERT(!post_execve); |
| |
| # ifdef DEBUG |
| /* copy stats->logdir |
| * stats->logdir is static, so current copy is fine, don't need |
| * frozen copy |
| */ |
| strncpy(parent_logdir, stats->logdir, MAXIMUM_PATH-1); |
| stats->logdir[MAXIMUM_PATH-1] = '\0'; /* if max no null */ |
| # endif |
| |
| if (get_log_dir(PROCESS_DIR, NULL, NULL)) { |
| /* we want brand new log dir */ |
| enable_new_log_dir(); |
| create_log_dir(PROCESS_DIR); |
| } |
| |
| # ifdef DEBUG |
| /* just like dynamorio_app_init, create main_logfile before stats */ |
| if (stats->loglevel > 0) { |
| /* we want brand new log files. os_fork_init() closed inherited files. */ |
| main_logfile = open_log_file(main_logfile_name(), NULL, 0); |
| print_file(main_logfile, "%s\n", dynamorio_version_string); |
| print_file(main_logfile, "New log file for child %d forked by parent %d\n", |
| get_thread_id(), get_parent_id()); |
| print_file(main_logfile, "Parent's log dir: %s\n", parent_logdir); |
| } |
| |
| stats->process_id = get_process_id(); |
| |
| if (stats->loglevel > 0) { |
| /* FIXME: share these few lines of code w/ dynamorio_app_init? */ |
| LOG(GLOBAL, LOG_TOP, 1, "Running: %s\n", stats->process_name); |
| # ifndef _WIN32_WCE |
| LOG(GLOBAL, LOG_TOP, 1, "DYNAMORIO_OPTIONS: %s\n", option_string); |
| # endif |
| } |
| # endif /* DEBUG */ |
| |
| /* must re-hash parent entry in threads table, plus no longer have any |
| * other threads (fork -> we're alone in address space), so clear |
| * out entire thread table, then add child |
| */ |
| mutex_lock(&thread_initexit_lock); |
| get_list_of_threads_ex(&threads, &num_threads, true/*include execve*/); |
| for (i=0; i<num_threads; i++) { |
| if (threads[i] == dcontext->thread_record) |
| remove_thread(threads[i]->id); |
| else |
| dynamo_other_thread_exit(threads[i]); |
| } |
| mutex_unlock(&thread_initexit_lock); |
| global_heap_free(threads, num_threads*sizeof(thread_record_t*) |
| HEAPACCT(ACCT_THREAD_MGT)); |
| |
| add_thread(get_process_id(), get_thread_id(), true/*under dynamo control*/, |
| dcontext); |
| |
| GLOBAL_STAT(num_threads) = 1; |
| # ifdef DEBUG |
| if (stats->loglevel > 0) { |
| /* need a new thread-local logfile */ |
| dcontext->logfile = open_log_file(thread_logfile_name(), NULL, 0); |
| print_file(dcontext->logfile, "%s\n", dynamorio_version_string); |
| print_file(dcontext->logfile, "New log file for child %d forked by parent %d\n", |
| get_thread_id(), get_parent_id()); |
| LOG(THREAD, LOG_TOP|LOG_THREADS, 1, |
| "THREAD %d (dcontext "PFX")\n\n", get_thread_id(), dcontext); |
| } |
| # endif |
| num_threads = 1; |
| |
| /* FIXME: maybe should have a callback list for who wants to be notified |
| * on a fork -- probably everyone who makes a log file on init. |
| */ |
| fragment_fork_init(dcontext); |
| /* this must be called after dynamo_other_thread_exit() above */ |
| signal_fork_init(dcontext); |
| |
| # ifdef CLIENT_INTERFACE |
| if (!IS_INTERNAL_STRING_OPTION_EMPTY(client_lib)) { |
| instrument_fork_init(dcontext); |
| } |
| # endif |
| } |
| #endif /* UNIX */ |
| |
| #if defined(CLIENT_INTERFACE) || defined(STANDALONE_UNIT_TEST) |
| /* To make DynamoRIO useful as a library for a standalone client |
| * application (as opposed to a client library that works with |
| * DynamoRIO in executing a target application). This makes DynamoRIO |
| * useful as an IA-32 disassembly library, etc. |
| */ |
| dcontext_t * |
| standalone_init(void) |
| { |
| dcontext_t *dcontext; |
| standalone_library = true; |
| /* We have release-build stats now so this is not just DEBUG */ |
| stats = &nonshared_stats; |
| #if defined(INTERNAL) && defined(DEADLOCK_AVOIDANCE) |
| /* avoid issues w/ GLOBAL_DCONTEXT instead of thread dcontext */ |
| dynamo_options.deadlock_avoidance = false; |
| #endif |
| #ifdef WINDOWS |
| /* MUST do this before making any system calls */ |
| if (!syscalls_init()) |
| return NULL; /* typically b/c of unsupported OS version */ |
| #endif |
| config_init(); |
| options_init(); |
| vmm_heap_init(); |
| heap_init(); |
| dynamo_heap_initialized = true; |
| dynamo_vm_areas_init(); |
| proc_init(); |
| os_init(); |
| config_heap_init(); |
| |
| #ifdef STANDALONE_UNIT_TEST |
| os_tls_init(); |
| dcontext = create_new_dynamo_context(true/*initial*/, NULL); |
| set_thread_private_dcontext(dcontext); |
| /* sanity check */ |
| ASSERT(get_thread_private_dcontext() == dcontext); |
| |
| heap_thread_init(dcontext); |
| |
| # ifdef DEBUG |
| /* FIXME: share code w/ main init routine? */ |
| nonshared_stats.logmask = LOG_ALL; |
| options_init(); |
| if (stats->loglevel > 0) { |
| char initial_options[MAX_OPTIONS_STRING]; |
| main_logfile = open_log_file(main_logfile_name(), NULL, 0); |
| print_file(main_logfile, "%s\n", dynamorio_version_string); |
| print_file(main_logfile, "Log file for standalone unit test\n"); |
| get_dynamo_options_string(&dynamo_options, |
| initial_options, sizeof(initial_options), true); |
| SYSLOG_INTERNAL_INFO("Initial options = %s", initial_options); |
| print_file(main_logfile, "\n"); |
| } |
| # endif /* DEBUG */ |
| #else |
| /* rather than ask the user to call some thread-init routine in |
| * every thread, we just use global dcontext everywhere (i#548) |
| */ |
| dcontext = GLOBAL_DCONTEXT; |
| #endif |
| |
| /* since we do not export any dr_standalone_exit(), we clean up any .1config |
| * file right now. the only loss if that we can't synch options: but that |
| * should be less important for standalone. we disabling synching. |
| */ |
| /* options are never made read-only for standalone */ |
| dynamo_options.dynamic_options = false; |
| |
| dynamo_initialized = true; |
| |
| return dcontext; |
| } |
| |
| void |
| standalone_exit(void) |
| { |
| /* should clean up here */ |
| config_exit(); |
| } |
| #endif |
| |
| /* Perform exit tasks that require full thread data structs, which we have |
| * already cleaned up by the time we reach dynamo_shared_exit() for both |
| * debug and detach paths. |
| */ |
| void |
| dynamo_process_exit_with_thread_info(void) |
| { |
| perscache_fast_exit(); /* "fast" b/c called in release as well */ |
| } |
| |
| /* shared between app_exit and detach */ |
| int |
| dynamo_shared_exit(IF_WINDOWS_(thread_record_t *toexit) |
| IF_WINDOWS_ELSE_NP(bool detach_stacked_callbacks, void)) |
| { |
| DEBUG_DECLARE(uint endtime); |
| /* set this now, could already be set */ |
| dynamo_exited = true; |
| |
| /* avoid time() for libc independence */ |
| DODEBUG(endtime = query_time_seconds();); |
| LOG(GLOBAL, LOG_STATS, 1, "\n#### Statistics for entire process:\n"); |
| LOG(GLOBAL, LOG_STATS, 1, "Total running time: %d seconds\n", |
| endtime - starttime); |
| |
| #ifdef PAPI |
| hardware_perfctr_exit(); |
| #endif |
| #ifdef DEBUG |
| # if defined(INTERNAL) && defined(X86) |
| print_optimization_stats(); |
| # endif /* INTERNAL && X86 */ |
| DOLOG(1, LOG_STATS, { |
| dump_global_stats(false); |
| }); |
| #endif /* DEBUG */ |
| |
| if (SELF_PROTECT_ON_CXT_SWITCH) { |
| DELETE_LOCK(protect_info->lock); |
| global_unprotected_heap_free(protect_info, sizeof(protect_info_t) HEAPACCT(ACCT_OTHER)); |
| } |
| |
| dynamo_exited_and_cleaned = true; |
| |
| /* call all component exit routines (CAUTION: order is important here) */ |
| |
| DELETE_RECURSIVE_LOCK(thread_in_DR_exclusion); |
| DOSTATS({ |
| LOG(GLOBAL, LOG_TOP|LOG_THREADS, 1, |
| "fcache_stats_exit: before fragment cleanup\n"); |
| DOLOG(1, LOG_CACHE, fcache_stats_exit();); |
| }); |
| #ifdef RCT_IND_BRANCH |
| if (!DYNAMO_OPTION(thin_client)) |
| rct_exit(); |
| #endif |
| fragment_exit(); |
| #ifdef ANNOTATIONS |
| annotation_exit(); |
| #endif |
| #ifdef CLIENT_INTERFACE |
| /* We tell the client as soon as possible in case it wants to use services from other |
| * components. Must be after fragment_exit() so that the client gets all the |
| * fragment_deleted() callbacks (xref PR 228156). FIXME - might be issues with the |
| * client trying to use api routines that depend on fragment state. |
| */ |
| instrument_exit(); |
| #endif |
| |
| /* we want dcontext around for loader_exit() */ |
| if (get_thread_private_dcontext() != NULL) |
| loader_thread_exit(get_thread_private_dcontext()); |
| loader_exit(); |
| |
| #ifdef WINDOWS |
| if (toexit != NULL) { |
| /* free detaching thread's dcontext */ |
| mutex_lock(&thread_initexit_lock); |
| dynamo_other_thread_exit(toexit, false); |
| mutex_unlock(&thread_initexit_lock); |
| } |
| #endif |
| |
| if (IF_WINDOWS_ELSE(!detach_stacked_callbacks, true)) { |
| /* We don't fully free cur thread until after client exit event (PR 536058) */ |
| if (thread_lookup(get_thread_id()) == NULL) { |
| LOG(GLOBAL, LOG_TOP|LOG_THREADS, 1, |
| "Current thread never under DynamoRIO control, not exiting it\n"); |
| } else { |
| /* call thread_exit even if !under_dynamo_control, could have |
| * been at one time |
| */ |
| /* exit this thread now */ |
| dynamo_thread_exit(); |
| } |
| } |
| /* now that the final thread is exited, free the all_threads memory */ |
| mutex_lock(&all_threads_lock); |
| global_heap_free(all_threads, |
| HASHTABLE_SIZE(ALL_THREADS_HASH_BITS) * sizeof(thread_record_t*) |
| HEAPACCT(ACCT_THREAD_MGT)); |
| all_threads = NULL; |
| mutex_unlock(&all_threads_lock); |
| |
| #ifdef WINDOWS |
| # ifdef CLIENT_INTERFACE |
| /* for -private_loader we do this here to catch more exit-time crashes */ |
| if (!INTERNAL_OPTION(noasynch) && INTERNAL_OPTION(private_loader)) |
| callback_interception_unintercept(); |
| # endif |
| /* callback_interception_exit must be after fragment exit for CLIENT_INTERFACE so |
| * that fragment_exit->frees fragments->instrument_fragment_deleted-> |
| * hide_tag_from_fragment->is_intercepted_app_pc won't crash. xref PR 228156 */ |
| if (!INTERNAL_OPTION(noasynch)) { |
| callback_interception_exit(); |
| } |
| #endif |
| link_exit(); |
| fcache_exit(); |
| monitor_exit(); |
| synch_exit(); |
| arch_exit(IF_WINDOWS(detach_stacked_callbacks)); |
| #ifdef CALL_PROFILE |
| /* above os_exit to avoid eventlog_mutex trigger if we're the first to |
| * create a log file |
| */ |
| profile_callers_exit(); |
| #endif |
| os_fast_exit(); |
| os_slow_exit(); |
| native_exec_exit(); /* before vm_areas_exit for using dynamo_areas */ |
| vm_areas_exit(); |
| perscache_slow_exit(); /* fast called in dynamo_process_exit_with_thread_info() */ |
| modules_exit(); /* after aslr_exit() from os_slow_exit(), |
| * after vm_areas & perscache exits */ |
| moduledb_exit(); /* before heap_exit */ |
| #ifdef HOT_PATCHING_INTERFACE |
| if (DYNAMO_OPTION(hot_patching)) |
| hotp_exit(); |
| #endif |
| #if defined(WINDOWS) && defined(STACK_GUARD_PAGE) && defined(DEBUG) |
| /* Free exception stack before calling heap_exit */ |
| stack_free(exception_stack, EXCEPTION_STACK_SIZE); |
| exception_stack = NULL; |
| #endif |
| config_heap_exit(); |
| heap_exit(); |
| vmm_heap_exit(); |
| diagnost_exit(); |
| data_section_exit(); |
| /* funny dependences: options exit just frees lock, not destroying |
| * any options that are needed for other exits, so do it prior to |
| * checking locks in debug build |
| */ |
| options_exit(); |
| utils_exit(); |
| config_exit(); |
| |
| #ifdef KSTATS |
| kstat_exit(); |
| #endif |
| |
| DELETE_LOCK(all_threads_lock); |
| DELETE_LOCK(thread_initexit_lock); |
| |
| DOLOG(1, LOG_STATS, { |
| /* dump after cleaning up to make it easy to check if stats that |
| * are inc-ed and dec-ed actually come down to 0 |
| */ |
| dump_global_stats(false); |
| }); |
| |
| statistics_exit(); |
| #ifdef DEBUG |
| # ifdef DEADLOCK_AVOIDANCE |
| ASSERT(locks_not_closed() == 0); |
| # endif |
| dynamo_exited_log_and_stats = true; |
| if (main_logfile != STDERR) { |
| /* do it this way just in case someone tries to log to the global file |
| * right now */ |
| file_t file_temp = main_logfile; |
| main_logfile = INVALID_FILE; |
| close_log_file(file_temp); |
| } |
| #else |
| # ifdef DEADLOCK_AVOIDANCE |
| ASSERT(locks_not_closed() == 0); |
| # endif |
| #endif /* DEBUG */ |
| |
| dynamo_initialized = false; |
| return SUCCESS; |
| } |
| |
| int |
| dynamorio_app_exit(void) |
| { |
| return dynamo_process_exit(); |
| } |
| |
| /* synchs with all threads using synch type synch_res. |
| * also sets dynamo_exited to true. |
| * does not resume the threads but does release the thread_initexit_lock. |
| */ |
| static void |
| synch_with_threads_at_exit(thread_synch_state_t synch_res) |
| { |
| int num_threads; |
| thread_record_t **threads; |
| DEBUG_DECLARE(bool ok;) |
| LOG(GLOBAL, LOG_TOP|LOG_THREADS, 1, |
| "\nsynch_with_threads_at_exit: cleaning up %d un-terminated threads\n", |
| get_num_threads()); |
| |
| #if defined(CLIENT_INTERFACE) && defined(WINDOWS) |
| /* make sure client nudges are finished */ |
| wait_for_outstanding_nudges(); |
| #endif |
| |
| /* xref case 8747, requesting suspended is preferable to terminated and it |
| * doesn't make a difference here which we use (since the process is about |
| * to die). |
| * On Linux, however, we do not have dependencies on OS thread |
| * properties like we do on Windows (TEB, etc.), and our suspended |
| * threads use their sigstacks and ostd data structs, making cleanup |
| * while still catching other leaks more difficult: thus it's |
| * simpler to terminate and then clean up. FIXME: by terminating |
| * we'll raise SIGCHLD that may not have been raised natively if the |
| * whole group went down in a single SYS_exit_group. Instead we |
| * could have the suspended thread move from the sigstack-reliant |
| * loop to a stack-free loop (xref i#95). |
| */ |
| IF_UNIX(dynamo_exiting = true;) /* include execve-exited vfork threads */ |
| DEBUG_DECLARE(ok =) |
| synch_with_all_threads(synch_res, |
| &threads, &num_threads, |
| /* Case 6821: other synch-all-thread uses that |
| * only care about threads carrying fcache |
| * state can ignore us |
| */ |
| THREAD_SYNCH_NO_LOCKS_NO_XFER, |
| /* if we fail to suspend a thread (e.g., privilege |
| * problems) ignore it. FIXME: retry instead? */ |
| THREAD_SYNCH_SUSPEND_FAILURE_IGNORE); |
| ASSERT(ok); |
| ASSERT(threads == NULL && num_threads == 0); /* We asked for CLEANED */ |
| /* the synch_with_all_threads function grabbed the |
| * thread_initexit_lock for us! */ |
| /* do this now after all threads we know about are killed and |
| * while we hold the thread_initexit_lock so any new threads that |
| * are waiting on it won't get in our way (see thread_init()) */ |
| dynamo_exited = true; |
| end_synch_with_all_threads(threads, num_threads, false/*don't resume*/); |
| } |
| |
| #ifdef DEBUG |
| /* cleanup after the application has exited */ |
| static int |
| dynamo_process_exit_cleanup(void) |
| { |
| /* CAUTION: this should only be invoked after all app threads have stopped */ |
| if (!dynamo_exited && !INTERNAL_OPTION(nullcalls)) { |
| dcontext_t *dcontext; |
| |
| APP_EXPORT_ASSERT(dynamo_initialized, "Improper DynamoRIO initialization"); |
| |
| dcontext = get_thread_private_dcontext(); |
| |
| /* we deliberately do NOT clean up initstack (which was |
| * allocated using a separate mmap and so is not part of some |
| * large unit that is de-allocated), as it is used in special |
| * circumstances to call us...FIXME: is this memory leak ok? |
| * is there a better solution besides assuming the app stack? |
| */ |
| |
| #ifdef SIDELINE |
| if (dynamo_options.sideline) { |
| /* exit now to make thread cleanup simpler */ |
| sideline_exit(); |
| } |
| #endif |
| |
| /* perform exit tasks that require full thread data structs */ |
| dynamo_process_exit_with_thread_info(); |
| |
| if (INTERNAL_OPTION(single_privileged_thread)) { |
| mutex_unlock(&thread_initexit_lock); |
| } |
| |
| /* if ExitProcess called before all threads terminated, they won't |
| * all have gone through dynamo_thread_exit, so clean them up now |
| * so we can get stats about them |
| * |
| * we don't check control_all_threads b/c we're just killing |
| * the threads we know about here |
| */ |
| synch_with_threads_at_exit(IF_WINDOWS_ELSE |
| (THREAD_SYNCH_SUSPENDED_AND_CLEANED, |
| THREAD_SYNCH_TERMINATED_AND_CLEANED)); |
| /* now that APC interception point is unpatched and |
| * dynamorio_exited is set and we've killed all the theads we know |
| * about, assumption is that no other threads will be running in |
| * dynamorio code from here on out (esp. when we get into shared exit) |
| * that will do anything that could be dangerous (could possibly be |
| * a thread in the APC interception code prior to reaching thread_init |
| * but it will only global log and do thread_lookup which should be |
| * safe throughout) */ |
| |
| /* In order to pass the client a dcontext in the process exit event |
| * we do some thread cleanup early for the final thread so we can delay |
| * the rest (PR 536058). This is a little risky in that we |
| * clean up dcontext->fragment_field, which is used for lots of |
| * things like couldbelinking. |
| */ |
| dynamo_thread_exit_pre_client(get_thread_private_dcontext(), get_thread_id()); |
| |
| #ifdef WINDOWS |
| /* FIXME : our call un-interception isn't atomic so (miniscule) chance |
| * of something going wrong if new thread is just hitting its init APC |
| */ |
| /* w/ the app's loader we must remove our LdrUnloadDll hook |
| * before we unload the client lib (and thus we miss client |
| * exit crashes): xref PR 200207. |
| */ |
| if (!INTERNAL_OPTION(noasynch) |
| IF_CLIENT_INTERFACE(&& !INTERNAL_OPTION(private_loader))) { |
| callback_interception_unintercept(); |
| } |
| #else /* UNIX */ |
| unhook_vsyscall(); |
| #endif /* UNIX */ |
| |
| return dynamo_shared_exit(IF_WINDOWS_(NULL) /* not detaching */ |
| IF_WINDOWS(false /* not detaching */)); |
| } |
| return SUCCESS; |
| } |
| #endif /* DEBUG */ |
| |
| int |
| dynamo_nullcalls_exit(void) |
| { |
| /* this routine is used when nullcalls is turned on |
| * simply to get perfctr numbers in a log file |
| */ |
| ASSERT(INTERNAL_OPTION(nullcalls)); |
| #ifdef PAPI |
| hardware_perfctr_exit(); |
| #endif |
| |
| #ifdef DEBUG |
| if (main_logfile != STDERR) { |
| close_log_file(main_logfile); |
| main_logfile = INVALID_FILE; |
| } |
| #endif /* DEBUG */ |
| |
| dynamo_exited = true; |
| return SUCCESS; |
| } |
| |
| /* called when we see that the process is about to exit */ |
| int |
| dynamo_process_exit(void) |
| { |
| #ifndef DEBUG |
| bool each_thread; |
| #endif |
| SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT); |
| synchronize_dynamic_options(); |
| SYSLOG(SYSLOG_INFORMATION, INFO_PROCESS_STOP, |
| 2, get_application_name(), get_application_pid()); |
| #ifdef DEBUG |
| if (!dynamo_exited) { |
| if (INTERNAL_OPTION(nullcalls)) { |
| /* if nullcalls is on we still do perfctr stats, and this is |
| * the only place we can print them out and exit |
| */ |
| dynamo_nullcalls_exit(); |
| } |
| else { |
| /* we don't check automatic_startup -- even if the app_ |
| * interface is used, we are about to be gone from the process |
| * address space, so we clean up now |
| */ |
| LOG(GLOBAL, LOG_TOP, 1, |
| "\ndynamo_process_exit from thread "TIDFMT" -- cleaning up dynamo\n", |
| get_thread_id()); |
| dynamo_process_exit_cleanup(); |
| } |
| } |
| |
| return SUCCESS; |
| |
| #else |
| if (dynamo_exited) |
| return SUCCESS; |
| |
| /* don't need to do much! |
| * we didn't create any IPC objects or anything that might be persistent |
| * beyond our death, we're not holding any systemwide locks, etc. |
| */ |
| |
| /* It is not clear whether the Event Log service can handle well unterminated connections */ |
| |
| /* Do we need profile data for each thread? |
| * Note that windows prof_pcs duplicates the thread walk in os_exit() |
| * FIXME: should combine that thread walk with this one |
| */ |
| each_thread = TRACEDUMP_ENABLED(); |
| # ifdef UNIX |
| each_thread = each_thread || INTERNAL_OPTION(profile_pcs); |
| # endif |
| # ifdef KSTATS |
| each_thread = each_thread || DYNAMO_OPTION(kstats); |
| # endif |
| # ifdef CLIENT_INTERFACE |
| each_thread = each_thread || |
| /* If we don't need a thread exit event, avoid the possibility of |
| * racy crashes (PR 470957) by not calling instrument_thread_exit() |
| */ |
| (!INTERNAL_OPTION(nullcalls) && dr_thread_exit_hook_exists() && |
| !DYNAMO_OPTION(skip_thread_exit_at_exit)); |
| # endif |
| |
| if (DYNAMO_OPTION(synch_at_exit) |
| /* by default we synch if any exit event exists */ |
| IF_CLIENT_INTERFACE(|| (!DYNAMO_OPTION(multi_thread_exit) && |
| dr_exit_hook_exists()) |
| || (!DYNAMO_OPTION(skip_thread_exit_at_exit) && |
| dr_thread_exit_hook_exists()))) { |
| /* needed primarily for CLIENT_INTERFACE but technically all configurations |
| * can have racy crashes at exit time (xref PR 470957) |
| */ |
| synch_with_threads_at_exit(IF_WINDOWS_ELSE |
| (THREAD_SYNCH_SUSPENDED_AND_CLEANED, |
| THREAD_SYNCH_TERMINATED_AND_CLEANED)); |
| } else |
| dynamo_exited = true; |
| |
| if (each_thread) { |
| thread_record_t **threads; |
| int num, i; |
| mutex_lock(&thread_initexit_lock); |
| get_list_of_threads(&threads, &num); |
| |
| for (i = 0; i < num; i++) { |
| #ifdef CLIENT_SIDELINE |
| if (IS_CLIENT_THREAD(threads[i]->dcontext)) |
| continue; |
| #endif |
| /* FIXME: separate trace dump from rest of fragment cleanup code */ |
| if (TRACEDUMP_ENABLED() IF_CLIENT_INTERFACE(|| true)) |
| /* We always want to call this for CI builds so we can get the |
| * dr_fragment_deleted() callbacks. |
| */ |
| fragment_thread_exit(threads[i]->dcontext); |
| # ifdef UNIX |
| if (INTERNAL_OPTION(profile_pcs)) |
| pcprofile_thread_exit(threads[i]->dcontext); |
| # endif |
| # ifdef KSTATS |
| if (DYNAMO_OPTION(kstats)) |
| kstat_thread_exit(threads[i]->dcontext); |
| # endif |
| # ifdef CLIENT_INTERFACE |
| /* Inform client of all thread exits */ |
| if (!INTERNAL_OPTION(nullcalls) && !DYNAMO_OPTION(skip_thread_exit_at_exit)) |
| instrument_thread_exit_event(threads[i]->dcontext); |
| # endif |
| } |
| global_heap_free(threads, num*sizeof(thread_record_t*) |
| HEAPACCT(ACCT_THREAD_MGT)); |
| mutex_unlock(&thread_initexit_lock); |
| } |
| |
| /* PR 522783: must be before we clear dcontext (if CLIENT_INTERFACE)! */ |
| /* must also be prior to fragment_exit so we actually freeze pcaches (i#703) */ |
| dynamo_process_exit_with_thread_info(); |
| |
| /* FIXME: separate trace dump from rest of fragment cleanup code. For client |
| * interface we need to call fragment_exit to get all the fragment deleted events. */ |
| if (TRACEDUMP_ENABLED() IF_CLIENT_INTERFACE(|| dr_fragment_deleted_hook_exists())) |
| fragment_exit(); |
| |
| /* Inform client of process exit */ |
| #ifdef CLIENT_INTERFACE |
| if (!INTERNAL_OPTION(nullcalls)) { |
| # ifdef WINDOWS |
| /* instrument_exit() unloads the client library, so make sure |
| * LdrUnloadDll isn't hooked if using the app loader. |
| */ |
| if (!INTERNAL_OPTION(noasynch) |
| IF_CLIENT_INTERFACE(&& !INTERNAL_OPTION(private_loader))) { |
| callback_interception_unintercept(); |
| } |
| # endif |
| /* Must be after fragment_exit() so that the client gets all the |
| * fragment_deleted() callbacks (xref PR 228156). FIXME - might be issues |
| * with the client trying to use api routines that depend on fragment state. |
| */ |
| instrument_exit(); |
| /* for -private_loader we do this here to catch more exit-time crashes */ |
| # ifdef WINDOWS |
| if (!INTERNAL_OPTION(noasynch) |
| IF_CLIENT_INTERFACE(&& INTERNAL_OPTION(private_loader))) |
| callback_interception_unintercept(); |
| # endif |
| } |
| #endif |
| |
| #ifdef CALL_PROFILE |
| profile_callers_exit(); |
| #endif |
| # ifdef KSTATS |
| if (DYNAMO_OPTION(kstats)) |
| kstat_exit(); |
| # endif |
| /* so make sure eventlog connection is terminated (if present) */ |
| os_fast_exit(); |
| |
| return SUCCESS; |
| #endif /* !DEBUG */ |
| } |
| |
| dcontext_t * |
| create_new_dynamo_context(bool initial, byte *dstack_in) |
| { |
| dcontext_t *dcontext; |
| size_t alloc = sizeof(dcontext_t) + proc_get_cache_line_size(); |
| void *alloc_start = (void *) |
| ((TEST(SELFPROT_GLOBAL, dynamo_options.protect_mask) && |
| !TEST(SELFPROT_DCONTEXT, dynamo_options.protect_mask)) ? |
| /* if protecting global but not dcontext, put whole thing in unprot mem */ |
| global_unprotected_heap_alloc(alloc HEAPACCT(ACCT_OTHER)) : |
| global_heap_alloc(alloc HEAPACCT(ACCT_OTHER))); |
| dcontext = (dcontext_t*) proc_bump_to_end_of_cache_line((ptr_uint_t)alloc_start); |
| ASSERT(proc_is_cache_aligned(dcontext)); |
| #ifdef X86 |
| /* 264138: ensure xmm/ymm slots are aligned so we can use vmovdqa */ |
| ASSERT(ALIGNED(get_mcontext(dcontext)->ymm, YMM_REG_SIZE)); |
| /* also ensure we don't have extra padding beyond x86.asm defines */ |
| ASSERT(sizeof(priv_mcontext_t) == IF_X64_ELSE(18,10)*sizeof(reg_t) + |
| PRE_XMM_PADDING + XMM_SLOTS_SIZE); |
| #elif defined(ARM) |
| /* FIXME i#1551: add arm alignment check if any */ |
| #endif /* X86/ARM */ |
| |
| /* Put here all one-time dcontext field initialization |
| * Make sure to update create_callback_dcontext to shared |
| * fields across callback dcontexts for the same thread. |
| */ |
| /* must set to 0 so can tell if initialized for callbacks! */ |
| memset(dcontext, 0x0, sizeof(dcontext_t)); |
| dcontext->allocated_start = alloc_start; |
| |
| /* we share a single dstack across all callbacks */ |
| if (initial) { |
| if (dstack_in == NULL) |
| dcontext->dstack = (byte *) stack_alloc(DYNAMORIO_STACK_SIZE); |
| else |
| dcontext->dstack = dstack_in; /* xref i#149/PR 403015 */ |
| } else { |
| /* dstack may be pre-allocated only at thread init, not at callback */ |
| ASSERT(dstack_in == NULL); |
| } |
| if (TEST(SELFPROT_DCONTEXT, dynamo_options.protect_mask)) { |
| dcontext->upcontext.separate_upcontext = |
| global_unprotected_heap_alloc(sizeof(unprotected_context_t) HEAPACCT(ACCT_OTHER)); |
| /* don't need to initialize upcontext */ |
| LOG(GLOBAL, LOG_TOP, 2, "new dcontext="PFX", dcontext->upcontext="PFX"\n", |
| dcontext, dcontext->upcontext.separate_upcontext); |
| dcontext->upcontext_ptr = dcontext->upcontext.separate_upcontext; |
| } else |
| dcontext->upcontext_ptr = &(dcontext->upcontext.upcontext); |
| #ifdef HOT_PATCHING_INTERFACE |
| /* Set the hot patch exception state to be empty/unused. */ |
| DODEBUG(memset(&dcontext->hotp_excpt_state, -1, sizeof(dr_jmp_buf_t));); |
| #endif |
| ASSERT(dcontext->try_except.try_except_state == NULL); |
| |
| DODEBUG({dcontext->logfile = INVALID_FILE;}); |
| dcontext->owning_thread = get_thread_id(); |
| #ifdef UNIX |
| dcontext->owning_process = get_process_id(); |
| #endif |
| /* thread_record is set in add_thread */ |
| /* all of the thread-private fcache and hashtable fields are shared |
| * among all dcontext instances of a thread, so the caller must |
| * set those fields |
| */ |
| /* rest of dcontext initialization happens in initialize_dynamo_context(), |
| * which is executed for each dr_app_start() and each |
| * callback start |
| */ |
| return dcontext; |
| } |
| |
| static void |
| delete_dynamo_context(dcontext_t *dcontext, bool free_stack) |
| { |
| if (free_stack) { |
| ASSERT(dcontext->dstack != NULL); |
| ASSERT(!is_currently_on_dstack(dcontext)); |
| stack_free(dcontext->dstack, DYNAMORIO_STACK_SIZE); |
| } /* else will be cleaned up by caller */ |
| |
| ASSERT(dcontext->try_except.try_except_state == NULL); |
| |
| if (TEST(SELFPROT_DCONTEXT, dynamo_options.protect_mask)) { |
| global_unprotected_heap_free(dcontext->upcontext.separate_upcontext, |
| sizeof(unprotected_context_t) HEAPACCT(ACCT_OTHER)); |
| } |
| if (TEST(SELFPROT_GLOBAL, dynamo_options.protect_mask) && |
| !TEST(SELFPROT_DCONTEXT, dynamo_options.protect_mask)) { |
| /* if protecting global but not dcontext, we put whole thing in unprot mem */ |
| global_unprotected_heap_free(dcontext->allocated_start, |
| sizeof(dcontext_t) + proc_get_cache_line_size() |
| HEAPACCT(ACCT_OTHER)); |
| } else { |
| global_heap_free(dcontext->allocated_start, |
| sizeof(dcontext_t) + proc_get_cache_line_size() |
| HEAPACCT(ACCT_OTHER)); |
| } |
| } |
| |
| /* This routine is called not only at thread initialization, |
| * but for every callback, etc. that gets a fresh execution |
| * environment! |
| */ |
| void |
| initialize_dynamo_context(dcontext_t *dcontext) |
| { |
| /* we can't just zero out the whole thing b/c we have persistent state |
| * (fields kept across callbacks, like dstack, module-private fields, next & prev, etc.) |
| */ |
| memset(dcontext->upcontext_ptr, 0, sizeof(unprotected_context_t)); |
| dcontext->initialized = true; |
| dcontext->whereami = WHERE_APP; |
| dcontext->next_tag = NULL; |
| dcontext->native_exec_postsyscall = NULL; |
| memset(dcontext->native_retstack, 0, sizeof(dcontext->native_retstack)); |
| dcontext->native_retstack_cur = 0; |
| dcontext->isa_mode = DEFAULT_ISA_MODE; |
| dcontext->sys_num = 0; |
| #ifdef WINDOWS |
| #ifdef CLIENT_INTERFACE |
| dcontext->app_errno = 0; |
| #endif |
| dcontext->sys_param_base = NULL; |
| /* always initialize aslr_context */ |
| dcontext->aslr_context.sys_aslr_clobbered = 0; |
| dcontext->aslr_context.randomized_section_handle = INVALID_HANDLE_VALUE; |
| dcontext->aslr_context.original_image_section_handle = INVALID_HANDLE_VALUE; |
| dcontext->aslr_context.original_section_base = ASLR_INVALID_SECTION_BASE; |
| # ifdef DEBUG |
| dcontext->aslr_context.last_app_section_handle = INVALID_HANDLE_VALUE; |
| # endif |
| /* note that aslr_context.last_child_padded is preserved across callbacks */ |
| dcontext->ignore_enterexit = false; |
| #else |
| dcontext->sys_param0 = 0; |
| dcontext->sys_param1 = 0; |
| dcontext->sys_param2 = 0; |
| #endif |
| |
| #ifdef UNIX |
| dcontext->signals_pending = false; |
| #endif |
| |
| /* all thread-private fields are initialized in dynamo_thread_init |
| * or in create_callback_dcontext because they must be initialized differently |
| * in those two cases |
| */ |
| |
| set_last_exit(dcontext, (linkstub_t *) get_starting_linkstub()); |
| |
| #ifdef PROFILE_RDTSC |
| dcontext->start_time = (uint64) 0; |
| dcontext->prev_fragment = NULL; |
| dcontext->cache_frag_count = (uint64) 0; |
| { |
| int i; |
| for (i=0; i<10; i++) { |
| dcontext->cache_time[i] = (uint64) 0; |
| dcontext->cache_count[i] = (uint64) 0; |
| } |
| } |
| #endif |
| #ifdef DEBUG |
| dcontext->in_opnd_disassemble = false; |
| #endif |
| #ifdef WINDOWS |
| /* Other pieces of DR -- callback & APC handling, detach -- test |
| * asynch_target to determine where the next app pc to execute is |
| * stored. Init it to 0 to indicate that this context's most recent |
| * syscall was not executed from handle_system_call(). |
| */ |
| dcontext->asynch_target = NULL; |
| /* next_saved and prev_unused are zeroed out when dcontext is |
| * created; we shouldn't zero them here, they may have valid data |
| */ |
| dcontext->valid = true; |
| #endif |
| #ifdef HOT_PATCHING_INTERFACE |
| dcontext->nudge_thread = false; /* Fix for case 5367. */ |
| #endif |
| #ifdef CHECK_RETURNS_SSE2 |
| /* initialize sse2 index with 0 |
| * go ahead and use eax, it's dead (about to return) |
| */ |
| # ifdef UNIX |
| asm("movl $0, %eax"); |
| asm("pinsrw $7,%eax,%xmm7"); |
| # else |
| # error NYI |
| # endif |
| #endif |
| /* We don't need to initialize dcontext->coarse_exit as it is only |
| * read when last_exit indicates a coarse exit, which sets the fields. |
| */ |
| } |
| |
| #ifdef WINDOWS |
| /* on windows we use a new dcontext for each callback context */ |
| dcontext_t * |
| create_callback_dcontext(dcontext_t *old_dcontext) |
| { |
| dcontext_t *new_dcontext = create_new_dynamo_context(false, NULL); |
| new_dcontext->valid = false; |
| /* all of these fields are shared among all dcontexts of a thread: */ |
| new_dcontext->owning_thread = old_dcontext->owning_thread; |
| #ifdef UNIX |
| new_dcontext->owning_process = old_dcontext->owning_process; |
| #endif |
| new_dcontext->thread_record = old_dcontext->thread_record; |
| /* now that we have clean stack usage we can share a single stack */ |
| ASSERT(old_dcontext->dstack != NULL); |
| new_dcontext->dstack = old_dcontext->dstack; |
| new_dcontext->isa_mode = old_dcontext->isa_mode; |
| new_dcontext->link_field = old_dcontext->link_field; |
| new_dcontext->monitor_field = old_dcontext->monitor_field; |
| new_dcontext->fcache_field = old_dcontext->fcache_field; |
| new_dcontext->fragment_field = old_dcontext->fragment_field; |
| new_dcontext->heap_field = old_dcontext->heap_field; |
| new_dcontext->vm_areas_field = old_dcontext->vm_areas_field; |
| new_dcontext->os_field = old_dcontext->os_field; |
| new_dcontext->synch_field = old_dcontext->synch_field; |
| /* case 8958: copy win32_start_addr in case we produce a forensics file |
| * from within a callback. |
| */ |
| new_dcontext->win32_start_addr = old_dcontext->win32_start_addr; |
| #ifdef CLIENT_INTERFACE |
| /* FlsData is persistent across callbacks */ |
| new_dcontext->app_fls_data = old_dcontext->app_fls_data; |
| new_dcontext->priv_fls_data = old_dcontext->priv_fls_data; |
| new_dcontext->app_nt_rpc = old_dcontext->app_nt_rpc; |
| new_dcontext->priv_nt_rpc = old_dcontext->priv_nt_rpc; |
| new_dcontext->app_nls_cache = old_dcontext->app_nls_cache; |
| new_dcontext->priv_nls_cache = old_dcontext->priv_nls_cache; |
| IF_X64(new_dcontext->app_stack_limit = old_dcontext->app_stack_limit); |
| new_dcontext->teb_base = old_dcontext->teb_base; |
| #endif |
| #ifdef UNIX |
| new_dcontext->signal_field = old_dcontext->signal_field; |
| new_dcontext->pcprofile_field = old_dcontext->pcprofile_field; |
| #endif |
| new_dcontext->private_code = old_dcontext->private_code; |
| #ifdef CLIENT_INTERFACE |
| new_dcontext->client_data = old_dcontext->client_data; |
| #endif |
| #ifdef DEBUG |
| new_dcontext->logfile = old_dcontext->logfile; |
| new_dcontext->thread_stats = old_dcontext->thread_stats; |
| #endif |
| #ifdef DEADLOCK_AVOIDANCE |
| new_dcontext->thread_owned_locks = old_dcontext->thread_owned_locks; |
| #endif |
| #ifdef KSTATS |
| new_dcontext->thread_kstats = old_dcontext->thread_kstats; |
| #endif |
| /* at_syscall is real time based, not app context based, so shared |
| * |
| * FIXME: Yes need to share when swapping at NtCallbackReturn, but |
| * want to keep old so when return from cb will do post-syscall for |
| * syscall that triggered cb in the first place! |
| * Plus, new cb calls initialize_dynamo_context(), which clears this field |
| * anyway! This all works now b/c we don't have alertable syscalls |
| * that we do post-syscall processing on. |
| */ |
| new_dcontext->upcontext_ptr->at_syscall = old_dcontext->upcontext_ptr->at_syscall; |
| #ifdef HOT_PATCHING_INTERFACE /* Fix for case 5367. */ |
| /* hotp_excpt_state should be unused at this point. If it is used, it can |
| * be only because a hot patch made a system call with a callback. This is |
| * a bug because hot patches can't do system calls, let alone one with |
| * callbacks. |
| */ |
| DOCHECK(1, { |
| dr_jmp_buf_t empty; |
| memset(&empty, -1, sizeof(dr_jmp_buf_t)); |
| ASSERT(memcmp(&old_dcontext->hotp_excpt_state, &empty, |
| sizeof(dr_jmp_buf_t)) == 0); |
| }); |
| new_dcontext->nudge_thread = old_dcontext->nudge_thread; |
| #endif |
| /* our exceptions should be handled within one DR context switch */ |
| ASSERT(old_dcontext->try_except.try_except_state == NULL); |
| new_dcontext->local_state = old_dcontext->local_state; |
| #ifdef WINDOWS |
| new_dcontext->aslr_context.last_child_padded = |
| old_dcontext->aslr_context.last_child_padded; |
| #endif |
| |
| LOG(new_dcontext->logfile, LOG_TOP, 2, |
| "made new dcontext "PFX" (old="PFX")\n", new_dcontext, old_dcontext); |
| return new_dcontext; |
| } |
| #endif |
| |
| bool |
| is_thread_initialized(void) |
| { |
| #if defined(UNIX) && defined(HAVE_TLS) |
| /* We don't want to pay the get_thread_id() cost on every |
| * get_thread_private_dcontext() when we only really need the |
| * check for this call here, so we explicitly check. |
| */ |
| if (get_tls_thread_id() != get_sys_thread_id()) |
| return false; |
| #endif |
| return (get_thread_private_dcontext() != NULL); |
| } |
| |
| |
| bool |
| is_thread_known(thread_id_t tid) |
| { |
| return (thread_lookup(tid) != NULL); |
| } |
| |
| #ifdef UNIX |
| /* i#237/PR 498284: a thread about to execute SYS_execve should be considered |
| * exited, but we can't easily clean up it for real immediately |
| */ |
| void |
| mark_thread_execve(thread_record_t *tr, bool execve) |
| { |
| ASSERT((execve && !tr->execve) || (!execve && tr->execve)); |
| tr->execve = execve; |
| mutex_lock(&all_threads_lock); |
| if (execve) { |
| /* since we free on a second vfork we should never accumulate |
| * more than one |
| */ |
| ASSERT(num_execve_threads == 0); |
| num_execve_threads++; |
| } else { |
| ASSERT(num_execve_threads > 0); |
| num_execve_threads--; |
| } |
| mutex_unlock(&all_threads_lock); |
| } |
| #endif /* UNIX */ |
| |
| int |
| get_num_threads(void) |
| { |
| return num_known_threads IF_UNIX(- num_execve_threads); |
| } |
| |
| bool |
| is_last_app_thread(void) |
| { |
| return (get_num_threads() == |
| IF_CLIENT_INTERFACE(get_num_client_threads() +) 1); |
| } |
| |
| /* This routine takes a snapshot of all the threads known to DR, |
| * NOT LIMITED to those currently under DR control! |
| * It returns an array of thread_record_t* and the length of the array |
| * The caller must free the array using global_heap_free |
| * The caller must hold the thread_initexit_lock to ensure that threads |
| * are not created or destroyed before the caller is done with the list |
| * The caller CANNOT be could_be_linking, else a deadlock with flushing |
| * can occur (unless the caller is the one flushing) |
| */ |
| static void |
| get_list_of_threads_common(thread_record_t ***list, int *num |
| _IF_UNIX(bool include_execve)) |
| { |
| int i, cur = 0, max_num; |
| thread_record_t *tr; |
| thread_record_t **mylist; |
| |
| /* Only a flushing thread can get the thread snapshot while being |
| * couldbelinking -- else a deadlock w/ flush! |
| * FIXME: this assert should be on any acquisition of thread_initexit_lock! |
| */ |
| ASSERT(is_self_flushing() || !is_self_couldbelinking()); |
| ASSERT(all_threads != NULL); |
| ASSERT_OWN_MUTEX(true, &thread_initexit_lock); |
| |
| mutex_lock(&all_threads_lock); |
| /* Do not include vfork threads that exited via execve, unless we're exiting */ |
| max_num = IF_UNIX_ELSE((include_execve || dynamo_exiting) ? |
| num_known_threads : get_num_threads(), |
| get_num_threads()); |
| mylist = (thread_record_t **) global_heap_alloc(max_num*sizeof(thread_record_t*) |
| HEAPACCT(ACCT_THREAD_MGT)); |
| for (i = 0; i < HASHTABLE_SIZE(ALL_THREADS_HASH_BITS); i++) { |
| for (tr = all_threads[i]; tr != NULL; tr = tr->next) { |
| /* include those for which !tr->under_dynamo_control */ |
| /* don't include those that exited for execve. there should be |
| * no race b/c vfork suspends the parent. xref i#237/PR 498284. |
| */ |
| if (IF_UNIX_ELSE(!tr->execve || include_execve || dynamo_exiting, true)) { |
| mylist[cur] = tr; |
| cur++; |
| } |
| } |
| } |
| |
| ASSERT(cur > 0); |
| IF_WINDOWS(ASSERT(cur == max_num)); |
| if (cur < max_num) { |
| mylist = (thread_record_t **) |
| global_heap_realloc(mylist, max_num, cur, sizeof(thread_record_t*) |
| HEAPACCT(ACCT_THREAD_MGT)); |
| } |
| |
| *num = cur; |
| *list = mylist; |
| mutex_unlock(&all_threads_lock); |
| } |
| |
| void |
| get_list_of_threads(thread_record_t ***list, int *num) |
| { |
| get_list_of_threads_common(list, num _IF_UNIX(false)); |
| } |
| |
| #ifdef UNIX |
| void |
| get_list_of_threads_ex(thread_record_t ***list, int *num, bool include_execve) |
| { |
| get_list_of_threads_common(list, num, include_execve); |
| } |
| #endif |
| |
| /* assumes caller can ensure that thread is either suspended or self to |
| * avoid races |
| */ |
| thread_record_t * |
| thread_lookup(thread_id_t tid) |
| { |
| thread_record_t *tr; |
| uint hindex; |
| |
| /* check that caller is self or has initexit_lock |
| * FIXME: no way to tell who has initexit_lock |
| */ |
| ASSERT(mutex_testlock(&thread_initexit_lock) || tid == get_thread_id()); |
| |
| hindex = HASH_FUNC_BITS(tid, ALL_THREADS_HASH_BITS); |
| mutex_lock(&all_threads_lock); |
| if (all_threads == NULL) { |
| tr = NULL; |
| } else { |
| tr = all_threads[hindex]; |
| } |
| while (tr != NULL) { |
| if (tr->id == tid) { |
| mutex_unlock(&all_threads_lock); |
| return tr; |
| } |
| tr = tr->next; |
| } |
| mutex_unlock(&all_threads_lock); |
| return NULL; |
| } |
| |
| /* assumes caller can ensure that thread is either suspended or self to |
| * avoid races |
| */ |
| uint |
| get_thread_num(thread_id_t tid) |
| { |
| thread_record_t *tr = thread_lookup(tid); |
| if (tr != NULL) |
| return tr->num; |
| else |
| return 0; /* yes can't distinguish from 1st thread, who cares */ |
| } |
| |
| void |
| add_thread(IF_WINDOWS_ELSE_NP(HANDLE hthread, process_id_t pid), |
| thread_id_t tid, bool under_dynamo_control, |
| dcontext_t *dcontext) |
| { |
| thread_record_t *tr; |
| uint hindex; |
| |
| ASSERT(all_threads != NULL); |
| |
| /* add entry to thread hashtable */ |
| tr = (thread_record_t *) global_heap_alloc(sizeof(thread_record_t) |
| HEAPACCT(ACCT_THREAD_MGT)); |
| #ifdef WINDOWS |
| /* we duplicate the thread pseudo-handle, this should give us full rights |
| * Note that instead asking explicitly for THREAD_ALL_ACCESS or just for |
| * THREAD_TERMINATE|THREAD_SUSPEND_RESUME|THREAD_GET_CONTEXT|THREAD_SET_CONTEXT |
| * does not seem able to acquire more rights than simply duplicating the |
| * app handle gives. |
| */ |
| LOG(GLOBAL, LOG_THREADS, 1, "Thread %d app handle rights: "PFX"\n", |
| tid, nt_get_handle_access_rights(hthread)); |
| duplicate_handle(NT_CURRENT_PROCESS, hthread, NT_CURRENT_PROCESS, |
| &tr->handle, 0, 0, |
| DUPLICATE_SAME_ACCESS|DUPLICATE_SAME_ATTRIBUTES); |
| /* We prob. only need TERMINATE (for kill thread), SUSPEND/RESUME/GET_CONTEXT |
| * (for synchronizing), and SET_CONTEXT (+ synchronizing requirements, for |
| * detach). All access includes this and quite a bit more. */ |
| # if 0 |
| /* eventually should be a real assert, but until we have a story for the |
| * injected detach threads, have to ifdef out even the ASSERT_CURIOSITY |
| * (even a syslog internal warning is prob. to noisy for QA) */ |
| ASSERT_CURIOSITY(TESTALL(THREAD_ALL_ACCESS, nt_get_handle_access_rights(tr->handle))); |
| # endif |
| LOG(GLOBAL, LOG_THREADS, 1, "Thread %d our handle rights: "PFX"\n", |
| tid, nt_get_handle_access_rights(tr->handle)); |
| tr->retakeover = false; |
| #else |
| tr->pid = pid; |
| tr->execve = false; |
| #endif |
| tr->id = tid; |
| ASSERT(tid != INVALID_THREAD_ID); /* ensure os never assigns invalid id to a thread */ |
| tr->under_dynamo_control = under_dynamo_control; |
| tr->dcontext = dcontext; |
| if (dcontext != NULL) /* we allow NULL for dr_create_client_thread() */ |
| dcontext->thread_record = tr; |
| |
| mutex_lock(&all_threads_lock); |
| tr->num = threads_ever_count++; |
| hindex = HASH_FUNC_BITS(tr->id, ALL_THREADS_HASH_BITS); |
| tr->next = all_threads[hindex]; |
| all_threads[hindex] = tr; |
| /* must be inside all_threads_lock to avoid race w/ get_list_of_threads */ |
| RSTATS_ADD_PEAK(num_threads, 1); |
| RSTATS_INC(num_threads_created); |
| num_known_threads++; |
| mutex_unlock(&all_threads_lock); |
| } |
| |
| /* return false if couldn't find the thread */ |
| bool |
| remove_thread(IF_WINDOWS_(HANDLE hthread) thread_id_t tid) |
| { |
| thread_record_t *tr = NULL, *prevtr; |
| uint hindex = HASH_FUNC_BITS(tid, ALL_THREADS_HASH_BITS); |
| |
| ASSERT(all_threads != NULL); |
| |
| mutex_lock(&all_threads_lock); |
| for (tr = all_threads[hindex], prevtr = NULL; tr; prevtr = tr, tr = tr->next) { |
| if (tr->id == tid) { |
| if (prevtr) |
| prevtr->next = tr->next; |
| else |
| all_threads[hindex] = tr->next; |
| /* must be inside all_threads_lock to avoid race w/ get_list_of_threads */ |
| RSTATS_DEC(num_threads); |
| #ifdef UNIX |
| if (tr->execve) { |
| ASSERT(num_execve_threads > 0); |
| num_execve_threads--; |
| } |
| #endif |
| num_known_threads--; |
| #ifdef WINDOWS |
| close_handle(tr->handle); |
| #endif |
| global_heap_free(tr, sizeof(thread_record_t) HEAPACCT(ACCT_THREAD_MGT)); |
| break; |
| } |
| } |
| mutex_unlock(&all_threads_lock); |
| return (tr != NULL); |
| } |
| |
| /* this bool is protected by reset_pending_lock */ |
| DECLARE_FREQPROT_VAR(static bool reset_at_nth_thread_triggered, false); |
| |
| /* thread-specific initialization |
| * if dstack_in is NULL, then a dstack is allocated; else dstack_in is used |
| * as the thread's dstack |
| * mc can be NULL for the initial thread |
| * returns -1 if current thread has already been initialized |
| */ |
| int |
| dynamo_thread_init(byte *dstack_in, priv_mcontext_t *mc |
| _IF_CLIENT_INTERFACE(bool client_thread)) |
| { |
| dcontext_t *dcontext; |
| /* due to lock issues (see below) we need another var */ |
| bool reset_at_nth_thread_pending = false; |
| bool under_dynamo_control = true; |
| APP_EXPORT_ASSERT(dynamo_initialized || dynamo_exited || |
| get_num_threads() == 0 IF_CLIENT_INTERFACE(|| client_thread), |
| PRODUCT_NAME" not initialized"); |
| |
| if (INTERNAL_OPTION(nullcalls)) |
| return SUCCESS; |
| |
| /* note that ENTERING_DR is assumed to have already happened: in apc handler |
| * for win32, in new_thread_setup for linux, in main init for 1st thread |
| */ |
| #if defined(WINDOWS) && defined(DR_APP_EXPORTS) |
| /* We need to identify a thread we intercepted in its APC when we |
| * take over all threads on dr_app_start(). Stack and pc checks aren't |
| * simple b/c it can be in ntdll waiting on a lock. |
| */ |
| if (dr_api_entry) |
| os_take_over_mark_thread(get_thread_id()); |
| #endif |
| |
| /* Try to handle externally injected threads */ |
| if (dynamo_initialized && !bb_lock_start) |
| pre_second_thread(); |
| |
| /* synch point so thread creation can be prevented for critical periods */ |
| mutex_lock(&thread_initexit_lock); |
| |
| /* The assumption is that if dynamo_exited, then we are about to exit and |
| * clean up, initializing this thread then would be dangerous, better to |
| * wait here for the app to die. Is safe with detach, since a thread |
| * should never reach here when dynamo_exited is true during detach */ |
| /* under current implementation of process exit, can happen only under |
| * debug build, or app_start app_exit interface */ |
| while (dynamo_exited) { |
| /* logging should be safe, though might not actually result in log |
| * message */ |
| DODEBUG_ONCE(LOG(GLOBAL, LOG_THREADS, 1, |
| "Thread %d reached initialization point while dynamo exiting, " |
| "waiting for app to exit\n", get_thread_id());); |
| mutex_unlock(&thread_initexit_lock); |
| os_thread_yield(); |
| /* just in case we want to support exited and then restarted at some |
| * point */ |
| mutex_lock(&thread_initexit_lock); |
| } |
| |
| |
| if (is_thread_initialized()) { |
| mutex_unlock(&thread_initexit_lock); |
| #if defined(WINDOWS) && defined(DR_APP_EXPORTS) |
| if (dr_api_entry) |
| os_take_over_unmark_thread(get_thread_id()); |
| #endif |
| return -1; |
| } |
| |
| os_tls_init(); |
| dcontext = create_new_dynamo_context(true/*initial*/, dstack_in); |
| initialize_dynamo_context(dcontext); |
| set_thread_private_dcontext(dcontext); |
| /* sanity check */ |
| ASSERT(get_thread_private_dcontext() == dcontext); |
| |
| /* set local state pointer for access from other threads */ |
| dcontext->local_state = get_local_state(); |
| |
| /* set initial mcontext, if known */ |
| if (mc != NULL) |
| *get_mcontext(dcontext) = *mc; |
| |
| /* For hotp_only, the thread should run native, not under dr. However, |
| * the core should still get control of the thread at hook points to track |
| * what the application is doing & at patched points to execute hot patches. |
| * It is the same for thin_client except that there are fewer hooks, only to |
| * follow children. |
| */ |
| if (RUNNING_WITHOUT_CODE_CACHE()) |
| under_dynamo_control = false; |
| |
| /* add entry to thread hashtable before creating logdir so have thread num. |
| * otherwise we'd like to do this only after we'd fully initialized the thread, but we |
| * hold the thread_initexit_lock, so nobody should be listing us -- thread_lookup |
| * on other than self, or a thread list, should only be done while the initexit_lock |
| * is held. CHECK: is this always correct? thread_lookup does have an assert |
| * to try and enforce but cannot tell who has the lock. |
| */ |
| add_thread(IF_WINDOWS_ELSE(NT_CURRENT_THREAD, get_process_id()), get_thread_id(), |
| under_dynamo_control, dcontext); |
| #if defined(WINDOWS) && defined(DR_APP_EXPORTS) |
| /* Now that the thread is in the main thread table we don't need to remember it */ |
| if (dr_api_entry) |
| os_take_over_unmark_thread(get_thread_id()); |
| #endif |
| |
| LOG(GLOBAL, LOG_TOP|LOG_THREADS, 1, |
| "\ndynamo_thread_init: %d thread(s) now, dcontext="PFX", #=%d, id=" |
| TIDFMT", pid="PIDFMT"\n\n", |
| GLOBAL_STAT(num_threads), dcontext, get_thread_num(get_thread_id()), |
| get_thread_id(), get_process_id()); |
| |
| DOLOG(1, LOG_STATS, { |
| dump_global_stats(false); |
| }); |
| #ifdef DEBUG |
| if (stats->loglevel > 0) { |
| dcontext->logfile = open_log_file(thread_logfile_name(), NULL, 0); |
| print_file(dcontext->logfile, "%s\n", dynamorio_version_string); |
| } else { |
| dcontext->logfile = INVALID_FILE; |
| } |
| DOLOG(1, LOG_TOP|LOG_THREADS, { |
| LOG(THREAD, LOG_TOP|LOG_THREADS, 1, PRODUCT_NAME" built with: %s\n", DYNAMORIO_DEFINES); |
| LOG(THREAD, LOG_TOP|LOG_THREADS, 1, PRODUCT_NAME" built on: %s\n", dynamorio_buildmark); |
| }); |
| |
| LOG(THREAD, LOG_TOP|LOG_THREADS, 1, |
| "%sTHREAD %d (dcontext "PFX")\n\n", |
| IF_CLIENT_INTERFACE_ELSE(client_thread ? "CLIENT " : "", ""), |
| get_thread_id(), dcontext); |
| LOG(THREAD, LOG_TOP|LOG_THREADS, 1, |
| "DR stack is "PFX"-"PFX"\n", dcontext->dstack - DYNAMORIO_STACK_SIZE, dcontext->dstack); |
| #endif |
| |
| #ifdef DEADLOCK_AVOIDANCE |
| locks_thread_init(dcontext); |
| #endif |
| heap_thread_init(dcontext); |
| DOSTATS({ stats_thread_init(dcontext); }); |
| #ifdef KSTATS |
| kstat_thread_init(dcontext); |
| #endif |
| os_thread_init(dcontext); |
| arch_thread_init(dcontext); |
| synch_thread_init(dcontext); |
| |
| if (!DYNAMO_OPTION(thin_client)) |
| vm_areas_thread_init(dcontext); |
| |
| monitor_thread_init(dcontext); |
| fcache_thread_init(dcontext); |
| link_thread_init(dcontext); |
| fragment_thread_init(dcontext); |
| |
| /* This lock has served its purposes: A) a barrier to thread creation for those |
| * iterating over threads, B) mutex for add_thread, and C) mutex for synch_field |
| * to be set up. |
| * So we release it to shrink the time spent w/ this big lock, in particular |
| * to avoid holding it while running private lib thread init code (i#875). |
| */ |
| mutex_unlock(&thread_initexit_lock); |
| |
| #ifdef CLIENT_INTERFACE |
| /* Set up client data needed in loader_thread_init for IS_CLIENT_THREAD */ |
| instrument_client_thread_init(dcontext, client_thread); |
| #endif |
| |
| loader_thread_init(dcontext); |
| |
| if (!DYNAMO_OPTION(thin_client)) { |
| #ifdef CLIENT_INTERFACE |
| /* put client last, may depend on other thread inits. |
| * Note that we are calling this prior to instrument_init() |
| * now (PR 216936), which is required to initialize |
| * the client dcontext field prior to instrument_init(). |
| */ |
| instrument_thread_init(dcontext, client_thread, mc != NULL); |
| #endif |
| |
| #ifdef SIDELINE |
| if (dynamo_options.sideline) { |
| /* wake up sideline thread -- ok to call if thread already awake */ |
| sideline_start(); |
| } |
| #endif |
| } |
| |
| /* must check # threads while holding thread_initexit_lock, yet cannot |
| * call fcache_reset_all_caches_proactively while holding it due to |
| * rank order of reset_pending_lock which we must also hold -- so we |
| * set a local bool reset_at_nth_thread_pending |
| */ |
| if (DYNAMO_OPTION(reset_at_nth_thread) != 0 && !reset_at_nth_thread_triggered |
| && (uint) get_num_threads() == DYNAMO_OPTION(reset_at_nth_thread)) { |
| mutex_lock(&reset_pending_lock); |
| if (!reset_at_nth_thread_triggered) { |
| reset_at_nth_thread_triggered = true; |
| reset_at_nth_thread_pending = true; |
| } |
| mutex_unlock(&reset_pending_lock); |
| } |
| |
| DOLOG(1, LOG_STATS, { |
| dump_thread_stats(dcontext, false); |
| }); |
| |
| if (reset_at_nth_thread_pending) { |
| mutex_lock(&reset_pending_lock); |
| /* fcache_reset_all_caches_proactively() will unlock */ |
| fcache_reset_all_caches_proactively(RESET_ALL); |
| } |
| return SUCCESS; |
| } |
| |
| /* We don't free cur thread until after client exit event (PR 536058) except for |
| * fragment_thread_exit(). Since this is called outside of dynamo_thread_exit() |
| * on process exit we assume fine to skip enter_threadexit(). |
| */ |
| void |
| dynamo_thread_exit_pre_client(dcontext_t *dcontext, thread_id_t id) |
| { |
| /* fcache stats needs to examine fragment state, so run it before |
| * fragment exit, but real fcache exit needs to be after fragment exit |
| */ |
| #ifdef DEBUG |
| fcache_thread_exit_stats(dcontext); |
| #endif |
| /* must abort now to avoid deleting possibly un-deletable fragments |
| * monitor_thread_exit remains later b/c of monitor_remove_fragment calls |
| */ |
| trace_abort_and_delete(dcontext); |
| fragment_thread_exit(dcontext); |
| #ifdef CLIENT_INTERFACE |
| instrument_thread_exit_event(dcontext); |
| #endif |
| } |
| |
| /* thread-specific cleanup */ |
| /* Note : if this routine is not called by thread id, then other_thread should |
| * be true and the calling thread should hold the thread_initexit_lock |
| */ |
| static int |
| dynamo_thread_exit_common(dcontext_t *dcontext, thread_id_t id, |
| IF_WINDOWS_(bool detach_stacked_callbacks) bool other_thread) |
| { |
| dcontext_t *dcontext_tmp; |
| #ifdef WINDOWS |
| dcontext_t *dcontext_next; |
| int num_dcontext; |
| #endif |
| bool on_dstack = !other_thread && is_currently_on_dstack(dcontext); |
| /* cache this now for use after freeing dcontext */ |
| local_state_t *local_state = dcontext->local_state; |
| |
| if (INTERNAL_OPTION(nullcalls) || dcontext == NULL) |
| return SUCCESS; |
| |
| /* make sure don't get into deadlock w/ flusher */ |
| enter_threadexit(dcontext); |
| |
| /* synch point so thread exiting can be prevented for critical periods */ |
| /* see comment at start of method for other thread exit */ |
| if (!other_thread) |
| mutex_lock(&thread_initexit_lock); |
| |
| ASSERT_OWN_MUTEX(true, &thread_initexit_lock); |
| #ifdef WINDOWS |
| /* need to clean up thread stack before clean up other thread data, but |
| * after we're made nolinking |
| */ |
| os_thread_stack_exit(dcontext); |
| /* free the thread's application stack if requested */ |
| if (dcontext->free_app_stack) { |
| byte *base; |
| /* only used for nudge threads currently */ |
| ASSERT(dcontext->nudge_target == generic_nudge_target); |
| if (get_stack_bounds(dcontext, &base, NULL)) { |
| NTSTATUS res; |
| ASSERT(base != NULL); |
| res = nt_free_virtual_memory(base); |
| ASSERT(NT_SUCCESS(res)); |
| } else { |
| /* stack should be available here */ |
| ASSERT_NOT_REACHED(); |
| } |
| } |
| #endif |
| |
| #ifdef SIDELINE |
| /* N.B.: do not clean up any data structures while sideline thread |
| * is still running! put it to sleep for duration of this routine! |
| */ |
| if (!DYNAMO_OPTION(thin_client)) { |
| if (dynamo_options.sideline) { |
| /* put sideline thread to sleep */ |
| sideline_stop(); |
| /* sideline_stop will not return until sideline thread is asleep */ |
| } |
| } |
| #endif |
| |
| LOG(GLOBAL, LOG_TOP|LOG_THREADS, 1, |
| "\ndynamo_thread_exit (thread #%d id="TIDFMT"): %d thread(s) now\n\n", |
| get_thread_num(id), id, GLOBAL_STAT(num_threads)-1); |
| |
| DOLOG(1, LOG_STATS, { |
| dump_global_stats(false); |
| }); |
| |
| LOG(THREAD, LOG_STATS|LOG_THREADS, 1, "\n## Statistics for this thread:\n"); |
| |
| #ifdef PROFILE_RDTSC |
| if (dynamo_options.profile_times) { |
| int i; |
| ASSERT(dcontext); |
| LOG(THREAD, LOG_STATS|LOG_THREADS, 1, "\nTop ten cache times:\n"); |
| for (i=0; i<10; i++) { |
| if (dcontext->cache_time[i] > (uint64) 0) { |
| uint top_part, bottom_part; |
| divide_int64_print(dcontext->cache_time[i], kilo_hertz, false, |
| 3, &top_part, &bottom_part); |
| LOG(THREAD, LOG_STATS|LOG_THREADS, 1, |
| "\t#%2d = %6u.%.3u ms, %9d hits\n", |
| i+1, top_part, bottom_part, (int)dcontext->cache_count[i]); |
| } |
| } |
| LOG(THREAD, LOG_STATS|LOG_THREADS, 1, "\n"); |
| } |
| #endif |
| |
| /* In order to pass the client a dcontext in the process exit event |
| * we do some thread cleanup early for the final thread so we can delay |
| * the rest (PR 536058) |
| */ |
| if (!dynamo_exited_and_cleaned) |
| dynamo_thread_exit_pre_client(dcontext, id); |
| #ifdef CLIENT_INTERFACE |
| /* PR 243759: don't free client_data until after all fragment deletion events */ |
| if (!DYNAMO_OPTION(thin_client)) |
| instrument_thread_exit(dcontext); |
| #endif |
| |
| /* i#920: we can't take segment/timer/asynch actions for other threads. |
| * This must be called after dynamo_thread_exit_pre_client where |
| * we called event callbacks. |
| */ |
| if (!other_thread) |
| dynamo_thread_not_under_dynamo(dcontext); |
| |
| /* We clean up priv libs prior to setting tls dc to NULL so we can use |
| * TRY_EXCEPT when calling the priv lib entry routine |
| */ |
| if (!dynamo_exited || |
| (other_thread && |
| (IF_WINDOWS_ELSE(!doing_detach, true) || |
| dcontext->owning_thread != get_thread_id()))) /* else already did this */ |
| loader_thread_exit(dcontext); |
| |
| /* set tls dc to NULL prior to cleanup, to avoid problems handling |
| * alarm signals received during cleanup (we'll suppress if tls |
| * dc==NULL which seems the right thing to do: not worth our |
| * effort to pass to another thread if thread-group-shared alarm, |
| * and if thread-private then thread would have exited soon |
| * anyway). see PR 596127. |
| */ |
| /* make sure we invalidate the dcontext before releasing the memory */ |
| /* when cleaning up other threads, we cannot set their dcs to null, |
| * but we only do this at dynamorio_app_exit so who cares |
| */ |
| /* This must be called after instrument_thread_exit, which uses |
| * get_thread_private_dcontext for app/dr state checks. |
| */ |
| if (id == get_thread_id()) |
| set_thread_private_dcontext(NULL); |
| |
| fcache_thread_exit(dcontext); |
| link_thread_exit(dcontext); |
| monitor_thread_exit(dcontext); |
| if (!DYNAMO_OPTION(thin_client)) |
| vm_areas_thread_exit(dcontext); |
| synch_thread_exit(dcontext); |
| arch_thread_exit(dcontext _IF_WINDOWS(detach_stacked_callbacks)); |
| os_thread_exit(dcontext, other_thread); |
| DOLOG(1, LOG_STATS, { |
| dump_thread_stats(dcontext, false); |
| }); |
| #ifdef KSTATS |
| kstat_thread_exit(dcontext); |
| #endif |
| DOSTATS({ stats_thread_exit(dcontext); }); |
| heap_thread_exit(dcontext); |
| #ifdef DEADLOCK_AVOIDANCE |
| locks_thread_exit(dcontext); |
| #endif |
| |
| #ifdef DEBUG |
| if (dcontext->logfile != INVALID_FILE) { |
| os_flush(dcontext->logfile); |
| close_log_file(dcontext->logfile); |
| } |
| #endif |
| |
| /* remove thread from threads hashtable */ |
| remove_thread(IF_WINDOWS_(NT_CURRENT_THREAD) id); |
| |
| dcontext_tmp = dcontext; |
| #ifdef WINDOWS |
| /* clean up all the dcs */ |
| num_dcontext = 0; |
| # ifdef DCONTEXT_IN_EDI |
| /* go to one end of list */ |
| while (dcontext_tmp->next_saved) |
| dcontext_tmp = dcontext_tmp->next_saved; |
| # else |
| /* already at one end of list */ |
| # endif |
| |
| /* delete through to other end */ |
| while (dcontext_tmp) { |
| num_dcontext++; |
| dcontext_next = dcontext_tmp->prev_unused; |
| delete_dynamo_context(dcontext_tmp, |
| dcontext_tmp == dcontext/*do not free dup cb stacks*/ |
| && !on_dstack/*do not free own stack*/); |
| dcontext_tmp = dcontext_next; |
| } |
| LOG(GLOBAL, LOG_STATS|LOG_THREADS, 1, "\tdynamo contexts used: %d\n", |
| num_dcontext); |
| #else /* UNIX */ |
| if (!other_thread) |
| set_thread_private_dcontext(NULL); |
| delete_dynamo_context(dcontext_tmp, !on_dstack/*do not free own stack*/); |
| #endif /* UNIX */ |
| os_tls_exit(local_state, other_thread); |
| |
| #ifdef SIDELINE |
| /* see notes above -- we can now wake up sideline thread */ |
| if (dynamo_options.sideline && get_num_threads() > 0) { |
| sideline_start(); |
| } |
| #endif |
| if (!other_thread) { |
| mutex_unlock(&thread_initexit_lock); |
| /* FIXME: once thread_initexit_lock is released, we're not on |
| * thread list, and a terminate targeting us could kill us in the middle |
| * of this call -- but this can't come before the unlock b/c the lock's |
| * in the data segment! (see case 3121) |
| * (note we do not re-protect for process exit, see !dynamo_exited check |
| * in exiting_dynamorio) |
| */ |
| if (!on_dstack) { |
| EXITING_DR(); |
| /* else, caller will clean up stack and then call EXITING_DR(), |
| * probably via dynamo_thread_stack_free_and_exit(), as the stack free |
| * must be done before the exit |
| */ |
| } |
| } |
| |
| return SUCCESS; |
| } |
| |
| int |
| dynamo_thread_exit(void) |
| { |
| dcontext_t *dcontext = get_thread_private_dcontext(); |
| return dynamo_thread_exit_common(dcontext, get_thread_id(), IF_WINDOWS_(false) false); |
| } |
| |
| /* NOTE : you must hold thread_initexit_lock to call this function! */ |
| int |
| dynamo_other_thread_exit(thread_record_t *tr _IF_WINDOWS(bool detach_stacked_callbacks)) |
| { |
| /* FIXME: Usually a safe spot for cleaning other threads should be |
| * under num_exits_dir_syscall, but for now rewinding all the way |
| */ |
| KSTOP_REWIND_DC(tr->dcontext, thread_measured); |
| KSTART_DC(tr->dcontext, thread_measured); |
| return dynamo_thread_exit_common(tr->dcontext, tr->id, |
| IF_WINDOWS_(detach_stacked_callbacks) true); |
| } |
| |
| /* Called from another stack to finish cleaning up a thread. |
| * The final steps are to free the stack and perform the exit hook. |
| */ |
| void |
| dynamo_thread_stack_free_and_exit(byte *stack) |
| { |
| if (stack != NULL) { |
| stack_free(stack, DYNAMORIO_STACK_SIZE); |
| /* ASSUMPTION: if stack is NULL here, the exit was done earlier |
| * (fixes case 6967) |
| */ |
| EXITING_DR(); |
| } |
| } |
| |
| #ifdef DR_APP_EXPORTS |
| /* API routine to initialize DR */ |
| DR_APP_API int |
| dr_app_setup(void) |
| { |
| /* FIXME: we either have to disallow the client calling this with |
| * more than one thread running, or we have to suspend all the threads. |
| * We should share the suspend-and-takeover loop (and for dr_app_setup_and_start |
| * share the takeover portion) from dr_app_start(). |
| */ |
| dr_api_entry = true; |
| return dynamorio_app_init(); |
| } |
| |
| /* API routine to exit DR */ |
| DR_APP_API int |
| dr_app_cleanup(void) |
| { |
| thread_record_t *tr; |
| |
| /* XXX: The dynamo_thread_[not_]under_dynamo() routines are not idempotent, |
| * and must be balanced! On Linux, they track the shared itimer refcount, |
| * so a mismatch will lead to a refleak or negative refcount. |
| * dynamorio_app_exit() will call dynamo_thread_not_under_dynamo(), so we |
| * must ensure that we are under DR before calling it. Therefore, we |
| * require that the caller call dr_app_stop() before calling |
| * dr_app_cleanup(). However, we cannot make a usage assertion to that |
| * effect without addressing the FIXME comments in |
| * dynamo_thread_not_under_dynamo() about updating tr->under_dynamo_control. |
| */ |
| tr = thread_lookup(get_thread_id()); |
| if (tr != NULL && tr->dcontext != NULL) |
| dynamo_thread_under_dynamo(tr->dcontext); |
| |
| SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT); |
| dr_api_exit = true; |
| SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT); /* to keep properly nested */ |
| return dynamorio_app_exit(); |
| } |
| |
| /* Called by dr_app_start in arch-specific assembly file */ |
| void |
| dr_app_start_helper(priv_mcontext_t *mc) |
| { |
| apicheck(dynamo_initialized, PRODUCT_NAME" not initialized"); |
| LOG(GLOBAL, LOG_TOP, 1, "dr_app_start in thread "TIDFMT"\n", get_thread_id()); |
| |
| if (!INTERNAL_OPTION(nullcalls)) { |
| /* Adjust the app stack to account for the return address + alignment. |
| * See dr_app_start in x86.asm. |
| */ |
| mc->xsp += DYNAMO_START_XSP_ADJUST; |
| dynamo_start(mc); |
| /* the interpreter takes over from here */ |
| } |
| } |
| |
| /* dummy routine that returns control to the app if it is currently |
| * under dynamo control |
| */ |
| DR_APP_API void |
| dr_app_stop(void) |
| { |
| /* the application regains control in here */ |
| } |
| |
| DR_APP_API int |
| dr_app_setup_and_start(void) |
| { |
| int r = dr_app_setup(); |
| if (r == SUCCESS) |
| dr_app_start(); |
| return r; |
| } |
| #endif |
| |
| /* For use by threads that start and stop whether dynamo controls them |
| */ |
| void |
| dynamo_thread_under_dynamo(dcontext_t *dcontext) |
| { |
| ASSERT(dcontext != NULL); |
| /* FIXME: mark under_dynamo_control? |
| * see comments in not routine below |
| */ |
| os_thread_under_dynamo(dcontext); |
| #ifdef SIDELINE |
| if (dynamo_options.sideline) { |
| /* wake up sideline thread -- ok to call if thread already awake */ |
| sideline_start(); |
| } |
| #endif |
| dcontext->currently_stopped = false; |
| } |
| |
| /* For use by threads that start and stop whether dynamo controls them. |
| * This must be called by the owner of dcontext and not another |
| * non-executing thread. |
| * XXX i#95: for detach we'll need to send a signal and have the |
| * target thread run this on its own (ditto for os_tls_exit()). |
| */ |
| void |
| dynamo_thread_not_under_dynamo(dcontext_t *dcontext) |
| { |
| ASSERT_MESSAGE(CHKLVL_ASSERTS+1/*expensive*/, "can only act on executing thread", |
| dcontext == get_thread_private_dcontext()); |
| if (dcontext == NULL) |
| return; |
| dcontext->currently_stopped = true; |
| os_thread_not_under_dynamo(dcontext); |
| #ifdef SIDELINE |
| /* FIXME: if # active threads is 0, then put sideline thread to sleep! */ |
| if (dynamo_options.sideline) { |
| /* put sideline thread to sleep */ |
| sideline_stop(); |
| } |
| #endif |
| #ifdef DEBUG |
| os_flush(dcontext->logfile); |
| #endif |
| } |
| |
| #define MAX_TAKE_OVER_ATTEMPTS 4 |
| |
| /* Take over other threads in the current process. |
| */ |
| void |
| dynamorio_take_over_threads(dcontext_t *dcontext) |
| { |
| /* We repeatedly check if there are other threads in the process, since |
| * while we're checking one may be spawning additional threads. |
| */ |
| bool found_threads; |
| uint attempts = 0; |
| |
| /* XXX i#1305: we should suspend all the other threads for DR init to |
| * satisfy the parts of the init process that assume there are no races. |
| */ |
| do { |
| found_threads = os_take_over_all_unknown_threads(dcontext); |
| attempts++; |
| if (found_threads && !bb_lock_start) |
| bb_lock_start = true; |
| } while (found_threads && attempts < MAX_TAKE_OVER_ATTEMPTS); |
| |
| if (found_threads) { |
| SYSLOG(SYSLOG_WARNING, INTERNAL_SYSLOG_WARNING, |
| 3, get_application_name(), get_application_pid(), |
| "Failed to take over all threads after multiple attempts"); |
| ASSERT_NOT_REACHED(); |
| } |
| } |
| |
| /* Called by dynamorio_app_take_over in arch-specific assembly file */ |
| void |
| dynamorio_app_take_over_helper(priv_mcontext_t *mc) |
| { |
| static bool have_taken_over = false; /* ASSUMPTION: not an actual write */ |
| SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT); |
| APP_EXPORT_ASSERT(dynamo_initialized, PRODUCT_NAME" not initialized"); |
| #ifdef RETURN_AFTER_CALL |
| /* FIXME : this is set after dynamo_initialized, so a slight race with |
| * an injected thread turning on .C protection before the main thread |
| * sets this. */ |
| dr_preinjected = true; /* currently only relevant on Win32 */ |
| #endif |
| |
| if (!INTERNAL_OPTION(nullcalls) && !have_taken_over) { |
| have_taken_over = true; |
| LOG(GLOBAL, LOG_TOP, 1, "dynamorio_app_take_over\n"); |
| /* set this flag to indicate that we should run until the program dies: */ |
| automatic_startup = true; |
| |
| if (DYNAMO_OPTION(inject_primary)) |
| take_over_primary_thread(); |
| |
| /* who knows when this was called -- no guarantee we control all threads -- |
| * unless we were auto-injected (preinject library calls this routine) |
| */ |
| control_all_threads = automatic_startup; |
| SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT); |
| |
| /* Adjust the app stack to account for the return address + alignment. |
| * See dynamorio_app_take_over in x86.asm. |
| */ |
| mc->xsp += DYNAMO_START_XSP_ADJUST; |
| |
| /* For hotp_only and thin_client, the app should run native, except |
| * for our hooks. |
| * This is where apps hooked using appinit key are let go native. |
| * Even though control is going to native app code, we want |
| * automatic_startup and control_all_threads set. |
| */ |
| if (!RUNNING_WITHOUT_CODE_CACHE()) |
| dynamo_start(mc); |
| /* the interpreter takes over from here */ |
| } else |
| SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT); |
| } |
| |
| #ifdef WINDOWS |
| extern app_pc parent_early_inject_address; /* from os.c */ |
| |
| /* in arch-specific assembly file */ |
| void dynamorio_app_take_over(void); |
| |
| DYNAMORIO_EXPORT void |
| dynamorio_app_init_and_early_takeover(uint inject_location, void *restore_code) |
| { |
| int res; |
| ASSERT(!dynamo_initialized && !dynamo_exited); |
| /* This routine combines dynamorio_app_init() and dynamrio_app_takeover into |
| * a single routine that also handles any early injection cleanup needed. */ |
| ASSERT_NOT_IMPLEMENTED(inject_location != INJECT_LOCATION_KiUserApc); |
| /* currently only Ldr* hook points are known to work */ |
| ASSERT_CURIOSITY(INJECT_LOCATION_IS_LDR(inject_location)); |
| /* See notes in os.c DLLMain. When early injected we are unable to find |
| * the address of LdrpLoadDll so we use the parent's value which is passed |
| * to us at the start of restore_code. FIXME - if we start using multiple |
| * inject locations we'll probably have to ensure we always pass this. |
| */ |
| if (INJECT_LOCATION_IS_LDR(inject_location)) { |
| parent_early_inject_address = *(app_pc *)restore_code; |
| } |
| dr_early_injected = true; |
| dr_early_injected_location = inject_location; |
| res = dynamorio_app_init(); |
| ASSERT(res == SUCCESS); |
| ASSERT(dynamo_initialized && !dynamo_exited); |
| LOG(GLOBAL, LOG_TOP, 1, "dynamorio_app_init_and_early_take_over\n"); |
| /* FIXME - restore code needs to be freed, but we have to return through it |
| * first... could instead duplicate its tail here if we wrap this |
| * routine in asm or eqv. pass the continuation state in as args. */ |
| ASSERT(inject_location != INJECT_LOCATION_KiUserApc); |
| dynamorio_app_take_over(); |
| } |
| |
| /* Called with DR library mapped in but without its imports processed. |
| */ |
| void |
| dynamorio_earliest_init_takeover_C(byte *arg_ptr) |
| { |
| int res; |
| bool earliest_inject; |
| |
| /* Windows-specific code for the most part */ |
| earliest_inject = earliest_inject_init(arg_ptr); |
| |
| /* Initialize now that DR dll imports are hooked up */ |
| if (earliest_inject) { |
| dr_earliest_injected = true; |
| dr_earliest_inject_args = arg_ptr; |
| } else |
| dr_early_injected = true; |
| res = dynamorio_app_init(); |
| ASSERT(res == SUCCESS); |
| ASSERT(dynamo_initialized && !dynamo_exited); |
| LOG(GLOBAL, LOG_TOP, 1, "dynamorio_earliest_init_takeover\n"); |
| |
| /* earliest_inject_cleanup() is called within dynamorio_app_init() to avoid |
| * confusing the exec areas scan |
| */ |
| |
| /* Take over at retaddr |
| * |
| * XXX i#626: app_takeover sets preinjected for rct (should prob. rename) |
| * which needs to be done whenever we takeover not at the bottom of the |
| * callstack. For earliest won't need to set this if we takeover |
| * in such a way as to handle the return back to our hook code without a |
| * violation -- though currently we will see 3 rets (return from |
| * dynamorio_app_take_over(), return from here, and return from |
| * dynamorio_earliest_init_takeover() to app hook code). |
| * Should we have dynamorio_earliest_init_takeover() set up an |
| * mcontext that we can go to directly instead of interpreting |
| * the returns in our own code? That would make tools that shadow |
| * callstacks simpler too. |
| */ |
| dynamorio_app_take_over(); |
| } |
| #endif /* WINDOWS */ |
| |
| /*************************************************************************** |
| * SELF-PROTECTION |
| */ |
| |
| /* FIXME: even with -single_privileged_thread, we aren't fully protected, |
| * because there's a window between us resuming the other threads and |
| * returning to our caller where another thread could clobber our return |
| * address or something. |
| */ |
| static void |
| dynamorio_protect(void) |
| { |
| ASSERT(SELF_PROTECT_ON_CXT_SWITCH); |
| LOG(GLOBAL, LOG_DISPATCH, 4, "dynamorio_protect thread="TIDFMT"\n", get_thread_id()); |
| /* we don't protect local heap here, that's done lazily */ |
| |
| mutex_lock(&protect_info->lock); |
| ASSERT(protect_info->num_threads_unprot > 0); |
| /* FIXME: nice to also catch double enters but would need to track more info */ |
| if (protect_info->num_threads_unprot <= 0) { |
| /* Defensive code to prevent crashes from double exits (the theory |
| * for case 7631/8030). However, this precludes an extra exit+enter |
| * pair from working properly (though an extra enter+exit will continue |
| * to work), though such a pair would have crashed if another thread |
| * had entered in the interim anyway. |
| */ |
| protect_info->num_threads_unprot = 0; |
| mutex_unlock(&protect_info->lock); |
| return; |
| } |
| protect_info->num_threads_unprot--; |
| if (protect_info->num_threads_unprot > 0) { |
| /* other threads still in DR, cannot protect global memory */ |
| LOG(GLOBAL, LOG_DISPATCH, 4, "dynamorio_protect: not last thread => nop\n"); |
| mutex_unlock(&protect_info->lock); |
| return; |
| } |
| |
| SELF_PROTECT_GLOBAL(READONLY); |
| |
| if (INTERNAL_OPTION(single_privileged_thread)) { |
| /* FIXME: want to resume threads and allow thread creation only |
| * _after_ protect data segment, but lock is in data segment! |
| */ |
| if (protect_info->num_threads_suspended > 0) { |
| thread_record_t *tr; |
| int i, num = 0; |
| /* we do not need to grab the all_threads_lock because |
| * no threads can be added or removed so who cares if we |
| * access the data structure simultaneously with another |
| * reader of it |
| */ |
| for (i = 0; i < HASHTABLE_SIZE(ALL_THREADS_HASH_BITS); i++) { |
| for (tr = all_threads[i]; tr; tr = tr->next) { |
| if (tr->under_dynamo_control) { |
| os_thread_resume(all_threads[i]); |
| num++; |
| } |
| } |
| } |
| ASSERT(num == protect_info->num_threads_suspended); |
| protect_info->num_threads_suspended = 0; |
| } |
| |
| /* thread init/exit can proceed now */ |
| mutex_unlock(&thread_initexit_lock); |
| } |
| |
| /* FIXME case 8073: temporary until we put in unprots in the |
| * right places. if we were to leave this here we'd want to combine |
| * .fspdata and .cspdata for more efficient prot changes. |
| */ |
| SELF_PROTECT_DATASEC(DATASEC_FREQ_PROT); |
| SELF_PROTECT_DATASEC(DATASEC_CXTSW_PROT); |
| |
| mutex_unlock(&protect_info->lock); |
| } |
| |
| static void |
| dynamorio_unprotect(void) |
| { |
| ASSERT(SELF_PROTECT_ON_CXT_SWITCH); |
| |
| mutex_lock(&protect_info->lock); /* lock in unprot heap, not data segment, so safe! */ |
| protect_info->num_threads_unprot++; |
| if (protect_info->num_threads_unprot == 1) { |
| /* was protected, so we need to do the unprotection */ |
| SELF_UNPROTECT_DATASEC(DATASEC_CXTSW_PROT); |
| /* FIXME case 8073: temporary until we put in unprots in the |
| * right places. if we were to leave this here we'd want to combine |
| * .fspdata and .cspdata for more efficient prot changes. |
| */ |
| SELF_UNPROTECT_DATASEC(DATASEC_FREQ_PROT); |
| |
| if (INTERNAL_OPTION(single_privileged_thread)) { |
| /* FIXME: want to suspend all other threads _before_ unprotecting anything, |
| * but need to guarantee no new threads while we're suspending them, |
| * and can't do that without setting a lock => need data segment! |
| */ |
| mutex_lock(&thread_initexit_lock); |
| |
| if (get_num_threads() > 1) { |
| thread_record_t *tr; |
| int i; |
| /* current multiple-thread solution: suspend all other threads! */ |
| ASSERT(protect_info->num_threads_suspended == 0); |
| /* we do not need to grab the all_threads_lock because |
| * no threads can be added or removed so who cares if we |
| * access the data structure simultaneously with another |
| * reader of it |
| */ |
| for (i = 0; i < HASHTABLE_SIZE(ALL_THREADS_HASH_BITS); i++) { |
| for (tr = all_threads[i]; tr; tr = tr->next) { |
| if (tr->under_dynamo_control) { |
| DEBUG_DECLARE(bool ok =) |
| os_thread_suspend(all_threads[i]); |
| ASSERT(ok); |
| protect_info->num_threads_suspended++; |
| } |
| } |
| } |
| } |
| /* we don't unlock or resume threads until we re-enter cache */ |
| } |
| |
| SELF_PROTECT_GLOBAL(WRITABLE); |
| } |
| /* we don't re-protect local heap here, that's done at points where |
| * it was protected lazily |
| */ |
| mutex_unlock(&protect_info->lock); |
| LOG(GLOBAL, LOG_DISPATCH, 4, "dynamorio_unprotect thread="TIDFMT"\n", get_thread_id()); |
| } |
| |
| #ifdef DEBUG |
| const char * |
| get_data_section_name(app_pc pc) |
| { |
| uint i; |
| for (i=0; i<DATASEC_NUM; i++) { |
| if (pc >= datasec_start[i] && pc < datasec_end[i]) |
| return DATASEC_NAMES[i]; |
| } |
| return NULL; |
| } |
| |
| bool |
| check_should_be_protected(uint sec) |
| { |
| /* Blindly asserting that a data section is protected is racy as |
| * another thread could be in an unprot window. We use some |
| * heuristics to try and identify bugs where a section is left |
| * unprot, but it's not easy. |
| */ |
| if (/* case 8107: for INJECT_LOCATION_LdrpLoadImportModule we |
| * load a helper library and end up in dispatch() for |
| * syscall_while_native before DR is initialized. |
| */ |
| !dynamo_initialized || |
| #ifdef WINDOWS |
| /* case 8113: detach currently unprots .data prior to its |
| * thread synch, so don't count anything after that |
| */ |
| doing_detach || |
| #endif |
| !TEST(DATASEC_SELFPROT[sec], DYNAMO_OPTION(protect_mask)) || |
| DATASEC_PROTECTED(sec)) |
| return true; |
| STATS_INC(datasec_not_prot); |
| /* FIXME: even checking get_num_threads()==1 is still racy as a thread could |
| * exit, and it's not worth grabbing thread_initexit_lock here.. |
| */ |
| if (threads_ever_count == 1 |
| #ifdef DR_APP_EXPORTS |
| /* For start/stop, can be other threads running around so we bail on |
| * perfect protection |
| */ |
| && !dr_api_entry |
| #endif |
| ) |
| return false; |
| /* FIXME: no count of threads in DR or anything so can't conclude much |
| * Just return true and hope developer looks at datasec_not_prot stats. |
| * We do have an ASSERT_CURIOSITY on the stat in data_section_exit(). |
| */ |
| return true; |
| } |
| |
| # ifdef WINDOWS |
| /* Assumed to only be called about DR dll writable regions */ |
| bool |
| data_sections_enclose_region(app_pc start, app_pc end) |
| { |
| /* Rather than solve the general enclose problem by sorting, |
| * we subtract each piece we find. |
| * It used to be that on 32-bit .data|.fspdata|.cspdata|.nspdata formed |
| * the only writable region, with .pdata between .data and .fspdata on 64. |
| * But building with VS2012, I'm seeing the sections in other orders (i#1075). |
| * And with x64 reachability we moved the interception buffer in .data, |
| * and marking it +rx results in sub-section calls to here. |
| */ |
| int i; |
| bool found_start = false, found_end = false; |
| ssize_t sz = end - start; |
| for (i = 0; i < DATASEC_NUM; i++) { |
| if (datasec_start[i] <= end && datasec_end[i] >= start) { |
| byte *overlap_start = MAX(datasec_start[i], start); |
| byte *overlap_end = MIN(datasec_end[i], end); |
| sz -= overlap_end - overlap_start; |
| } |
| } |
| return sz == 0; |
| } |
| # endif /* WINDOWS */ |
| #endif /* DEBUG */ |
| |
| static void |
| get_data_section_bounds(uint sec) |
| { |
| /* FIXME: on linux we should include .got and .dynamic in one of our |
| * sections, requiring specifying the order of sections (case 3789)! |
| * Should use an ld script to ensure that .nspdata is last, or find a unique |
| * attribute to force separation (perhaps mark as rwx, then |
| * remove the x at init time?) ld 2.15 puts it at the end, but |
| * ld 2.13 puts .got and .dynamic after it! For now we simply |
| * don't protect subsequent guys. |
| * On win32 there are no other rw sections, fortunately. |
| */ |
| ASSERT(sec >=0 && sec < DATASEC_NUM); |
| /* for DEBUG we use for data_sections_enclose_region() */ |
| ASSERT(IF_WINDOWS(IF_DEBUG(true ||)) |
| TEST(DATASEC_SELFPROT[sec], dynamo_options.protect_mask)); |
| mutex_lock(&datasec_lock[sec]); |
| ASSERT(datasec_start[sec] == NULL); |
| get_named_section_bounds(get_dynamorio_dll_start(), DATASEC_NAMES[sec], |
| &datasec_start[sec], &datasec_end[sec]); |
| mutex_unlock(&datasec_lock[sec]); |
| ASSERT(ALIGNED(datasec_start[sec], PAGE_SIZE)); |
| ASSERT(ALIGNED(datasec_end[sec], PAGE_SIZE)); |
| ASSERT(datasec_start[sec] < datasec_end[sec]); |
| #ifdef WINDOWS |
| if (IF_DEBUG(true ||) TEST(DATASEC_SELFPROT[sec], dynamo_options.protect_mask)) |
| merge_writecopy_pages(datasec_start[sec], datasec_end[sec]); |
| #endif |
| } |
| |
| #ifdef UNIX |
| /* We get into problems if we keep a .section open across string literals, etc. |
| * (such as when wrapping a function to get its local-scope statics in that section), |
| * but the VAR_IN_SECTION does the real work for us, just so long as we have one |
| * .section decl somewhere. |
| */ |
| DECLARE_DATA_SECTION(RARELY_PROTECTED_SECTION, "w") |
| DECLARE_DATA_SECTION(FREQ_PROTECTED_SECTION, "w") |
| DECLARE_DATA_SECTION(NEVER_PROTECTED_SECTION, "w") |
| END_DATA_SECTION_DECLARATIONS() |
| #endif |
| |
| static void |
| data_section_init(void) |
| { |
| uint i; |
| for (i=0; i<DATASEC_NUM; i++) { |
| if (datasec_start[i] != NULL) { |
| /* We were called early due to an early syslog. |
| * We still retain our slightly later normal init position so we can |
| * log, etc. in normal runs. |
| */ |
| return; |
| } |
| ASSIGN_INIT_LOCK_FREE(datasec_lock[i], datasec_selfprot_lock); |
| /* for DEBUG we use for data_sections_enclose_region() */ |
| if (IF_WINDOWS(IF_DEBUG(true ||)) |
| TEST(DATASEC_SELFPROT[i], dynamo_options.protect_mask)) { |
| get_data_section_bounds(i); |
| } |
| } |
| DOCHECK(1, { |
| /* ensure no overlaps */ |
| uint j; |
| for (i=0; i<DATASEC_NUM; i++) { |
| for (j=i+1; j<DATASEC_NUM; j++) { |
| ASSERT(datasec_start[i] >= datasec_end[j] || |
| datasec_start[j] >= datasec_end[i]); |
| } |
| } |
| }); |
| } |
| |
| static void |
| data_section_exit(void) |
| { |
| uint i; |
| DOSTATS({ |
| /* There can't have been that many races. |
| * A failure to re-protect should result in a ton of dispatch |
| * entrances w/ .data unprot, so should show up here. |
| * However, an app with threads that are initializing in DR and thus |
| * unprotected .data while other threads are running new code (such as |
| * on attach) can easily rack up hundreds of unprot cache entrances. |
| */ |
| ASSERT_CURIOSITY(GLOBAL_STAT(datasec_not_prot) < 5000); |
| }); |
| for (i=0; i<DATASEC_NUM; i++) |
| DELETE_LOCK(datasec_lock[i]); |
| } |
| |
| #define DATASEC_WRITABLE_MOD(which, op) \ |
| ((which) == DATASEC_RARELY_PROT ? (datasec_writable_rareprot op) : \ |
| ((which) == DATASEC_CXTSW_PROT ? (datasec_writable_cxtswprot op) : \ |
| ((which) == DATASEC_FREQ_PROT ? (datasec_writable_freqprot op) : \ |
| (ASSERT_NOT_REACHED(), datasec_writable_neverprot)))) |
| |
| /* WARNING: any DO_ONCE will call this routine, so don't call anything here |
| * that has a DO_ONCE, to avoid deadlock! |
| */ |
| void |
| protect_data_section(uint sec, bool writable) |
| { |
| ASSERT(sec >=0 && sec < DATASEC_NUM); |
| ASSERT(TEST(DATASEC_SELFPROT[sec], dynamo_options.protect_mask)); |
| /* We can be called very early before data_section_init() so init here |
| * (data_section_init() has no dependences). |
| */ |
| if (datasec_start[sec] == NULL) { |
| /* should only happen early in init */ |
| ASSERT(!dynamo_initialized); |
| data_section_init(); |
| } |
| mutex_lock(&datasec_lock[sec]); |
| ASSERT(datasec_start[sec] != NULL); |
| /* if using libc, we cannot print while data segment is read-only! |
| * thus, if making it writable, do that first, otherwise do it last. |
| * w/ ntdll this is not a problem. |
| */ |
| /* Remember that multiple threads can be doing (unprotect,protect) pairs of |
| * calls simultaneously. The datasec_lock makes each individual call atomic, |
| * and if all calls are properly nested, our use of counters should result in |
| * the proper protection only after the final protect call and not in the |
| * middle of some other thread's writes to the data section. |
| */ |
| if (writable) { |
| /* On-context-switch protection has a separate mechanism for |
| * only protecting when the final thread leaves DR |
| */ |
| ASSERT_CURIOSITY(DATASEC_WRITABLE(sec) <= 2); /* shouldn't nest too deep! */ |
| if (DATASEC_WRITABLE(sec) == 0) { |
| make_writable(datasec_start[sec], datasec_end[sec] - datasec_start[sec]); |
| STATS_INC(datasec_prot_changes); |
| } else |
| STATS_INC(datasec_prot_wasted_calls); |
| (void)DATASEC_WRITABLE_MOD(sec, ++); |
| } |
| LOG(TEST(DATASEC_SELFPROT[sec], SELFPROT_ON_CXT_SWITCH) ? THREAD_GET : GLOBAL, |
| LOG_VMAREAS, TEST(DATASEC_SELFPROT[sec], SELFPROT_ON_CXT_SWITCH) ? 3U : 2U, |
| "protect_data_section: thread "TIDFMT" %s (recur %d, stat %d) %s %s %d\n", |
| get_thread_id(), DATASEC_WRITABLE(sec) == 1 ? "changing" : "nop", |
| DATASEC_WRITABLE(sec), GLOBAL_STAT(datasec_not_prot), |
| DATASEC_NAMES[sec], writable ? "rw" : "r", DATASEC_WRITABLE(sec)); |
| if (!writable) { |
| ASSERT(DATASEC_WRITABLE(sec) > 0); |
| (void)DATASEC_WRITABLE_MOD(sec, --); |
| if (DATASEC_WRITABLE(sec) == 0) { |
| make_unwritable(datasec_start[sec], datasec_end[sec] - datasec_start[sec]); |
| STATS_INC(datasec_prot_changes); |
| } else |
| STATS_INC(datasec_prot_wasted_calls); |
| } |
| mutex_unlock(&datasec_lock[sec]); |
| } |
| |
| /* enter/exit DR hooks */ |
| void |
| entering_dynamorio(void) |
| { |
| if (SELF_PROTECT_ON_CXT_SWITCH) |
| dynamorio_unprotect(); |
| ASSERT(HOOK_ENABLED); |
| LOG(GLOBAL, LOG_DISPATCH, 3, "entering_dynamorio thread="TIDFMT"\n", get_thread_id()); |
| STATS_INC(num_entering_DR); |
| if (INTERNAL_OPTION(single_thread_in_DR)) { |
| acquire_recursive_lock(&thread_in_DR_exclusion); |
| LOG(GLOBAL, LOG_DISPATCH, 3, "entering_dynamorio thread="TIDFMT" count=%d\n", |
| get_thread_id(), thread_in_DR_exclusion.count); |
| } |
| } |
| |
| void |
| exiting_dynamorio(void) |
| { |
| ASSERT(HOOK_ENABLED); |
| LOG(GLOBAL, LOG_DISPATCH, 3, "exiting_dynamorio thread="TIDFMT"\n", get_thread_id()); |
| STATS_INC(num_exiting_DR); |
| if (INTERNAL_OPTION(single_thread_in_DR)) { |
| /* thread init/exit can proceed now */ |
| LOG(GLOBAL, LOG_DISPATCH, 3, "exiting_dynamorio thread="TIDFMT" count=%d\n", |
| get_thread_id(), thread_in_DR_exclusion.count - 1); |
| release_recursive_lock(&thread_in_DR_exclusion); |
| } |
| if (SELF_PROTECT_ON_CXT_SWITCH && !dynamo_exited) |
| dynamorio_protect(); |
| } |
| |
| /* Note this includes any stack guard pages */ |
| bool |
| is_on_initstack(byte *esp) |
| { |
| return (esp <= initstack && esp > initstack - DYNAMORIO_STACK_SIZE); |
| } |
| |
| /* Note this includes any stack guard pages */ |
| bool |
| is_on_dstack(dcontext_t *dcontext, byte *esp) |
| { |
| return (esp <= dcontext->dstack && |
| esp > dcontext->dstack - DYNAMORIO_STACK_SIZE); |
| } |
| |
| bool |
| is_currently_on_dstack(dcontext_t *dcontext) |
| { |
| byte *cur_esp; |
| GET_STACK_PTR(cur_esp); |
| return is_on_dstack(dcontext, cur_esp); |
| } |
| |
| void |
| pre_second_thread(void) |
| { |
| /* i#1111: nop-out bb_building_lock until 2nd thread created. |
| * While normally we'll call this in the primary thread while not holding |
| * the lock, it's possible on Windows for an externally injected thread |
| * (or for a thread sneakily created by some native_exec code w/o going |
| * through ntdll wrappers) to appear. We solve the problem of the main |
| * thread currently holding bb_building_lock and us turning its |
| * unlock into an error by the bb_lock_would_have bool in |
| * SHARED_BB_UNLOCK(). |
| */ |
| if (!bb_lock_start) { |
| mutex_lock(&bb_building_lock); |
| SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT); |
| bb_lock_start = true; |
| SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT); |
| mutex_unlock(&bb_building_lock); |
| } |
| } |
| |