| /* ********************************************************** |
| * Copyright (c) 2011-2014 Google, Inc. All rights reserved. |
| * Copyright (c) 2003-2010 VMware, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| /* Copyright (c) 2003-2007 Determina Corp. */ |
| |
| /* |
| * options.c |
| * |
| * Options definition and handling of corresponding command line options |
| * |
| */ |
| |
| #ifndef NOT_DYNAMORIO_CORE |
| # include "globals.h" |
| # include "fcache.h" |
| # include "monitor.h" |
| # include "moduledb.h" /* for the process control defines */ |
| # include "disassemble.h" |
| # include <string.h> |
| |
| #else /* NOT_DYNAMORIO_CORE */ |
| # include "configure.h" |
| # include <stdio.h> /* snprintf, sscanf */ |
| # include <string.h> |
| |
| # ifdef WINDOWS |
| # define inline __inline |
| # define snprintf _snprintf |
| # define WIN32_LEAN_AND_MEAN |
| # include <windows.h> /* no longer included in globals_shared.h */ |
| # endif |
| |
| #include "globals_shared.h" |
| |
| typedef unsigned int uint; |
| |
| # define print_file(f,s,x) |
| |
| # ifndef bool |
| typedef char bool; |
| # endif |
| # ifndef true |
| # define true (1) |
| # define false (0) |
| # endif |
| # ifndef NULL |
| # define NULL (0) |
| # endif |
| |
| struct stats_type { |
| int logmask; |
| int loglevel; |
| } thestats; |
| |
| struct stats_type *stats = &thestats; |
| |
| /* define away core only features, depending on use outside the core |
| * may want to use some of these, NOTE build environments outside of the |
| * core can't handle VARARG macros! */ |
| # define ASSERT(x) |
| /* right now all OPTION_PARSE_ERROR's have 6 arguments |
| * once libutil, etc. are all using vc8 we can use ... |
| */ |
| # define OPTION_PARSE_ERROR(a, b, c, d, e, f) |
| |
| static void ignore_varargs_function(char *format, ...) { } |
| /* right now all FATAL_USAGE_ERROR's have 4 arguments */ |
| # define FATAL_USAGE_ERROR(a, b, c, d) |
| /* USAGE_ERROR unfortunately needs a quick fix now */ |
| # define USAGE_ERROR 1 ? (void)0 : ignore_varargs_function |
| # define DOLOG(a, b, c) |
| |
| #include "options.h" |
| |
| #endif /* NOT_DYNAMORIO_CORE */ |
| |
| |
| #ifndef EXPOSE_INTERNAL_OPTIONS |
| /* default values for internal options are kept in a separate struct */ |
| # define OPTION_COMMAND_INTERNAL(type, name, default_value, command_line_option, statement, description, flag, pcache) default_value, |
| # define OPTION_COMMAND(type, name, default_value, command_line_option, \ |
| statement, description, flag, pcache) /* nothing */ |
| /* read only source for default internal option values and names |
| * no lock needed since never written |
| */ |
| const internal_options_t default_internal_options = { |
| # include "optionsx.h" |
| }; |
| #undef OPTION_COMMAND_INTERNAL |
| #undef OPTION_COMMAND |
| #endif |
| |
| |
| #ifdef EXPOSE_INTERNAL_OPTIONS |
| # define OPTION_COMMAND_INTERNAL OPTION_COMMAND |
| #else |
| /* DON'T FIXME: In order to support easy switching of an internal |
| option into user accessible one we could waste some memory by |
| allocating fields in options_t even for INTERNAL options. That would |
| let us have INTERNAL_OPTION be a more flexible macro that can use |
| either the constant value, or the dynamically set variable |
| depending on the option declaration in optionsx.h. However, |
| it increases the code size of this file (there is also a minor increase |
| in other object files since more option fields need longer than 8-bit offsets) |
| For now we can live without this. |
| */ |
| # define OPTION_COMMAND_INTERNAL(type, name, default_value, command_line_option, statement, description, flag, pcache) /* nothing, */ |
| #endif |
| |
| |
| /* all to default values */ |
| #define OPTION_COMMAND(type, name, default_value, command_line_option, \ |
| statement, description, flag, pcache) default_value, |
| /* read only source for default option values and names |
| * no lock needed since never written |
| */ |
| const options_t default_options = { |
| # include "optionsx.h" |
| }; |
| |
| #ifndef NOT_DYNAMORIO_CORE /*****************************************/ |
| |
| # define SELF_PROTECT_OPTIONS() SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT) |
| # define SELF_UNPROTECT_OPTIONS() SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT) |
| /* WARNING: testing positive direction is racy (other threads unprot .data |
| * for brief windows), negative is reliable |
| */ |
| # define OPTIONS_PROTECTED() (DATASEC_PROTECTED(DATASEC_RARELY_PROT)) |
| |
| /* Holds a copy of the last read option string from the registry, NOT a |
| * canonical option string. |
| */ |
| char option_string[MAX_OPTIONS_STRING] = {0,}; |
| # define ASSERT_OWN_OPTIONS_LOCK(b, l) ASSERT_OWN_WRITE_LOCK(b, l) |
| # define CORE_STATIC static |
| /* official options */ |
| options_t dynamo_options = { |
| # include "optionsx.h" |
| }; |
| /* Temporary string, static to save stack space in synchronize_dynamic_options(). |
| * FIXME case 8074: should protect this better as w/o DR dll randomization attacker |
| * can repeatedly try to clobber. Move to heap? Or shrink stack space elsewhere |
| * and put back as synchronize_dynamic_options() local var. |
| */ |
| DECLARE_FREQPROT_VAR(char new_option_string[MAX_OPTIONS_STRING], {0,}); |
| /* temporary structure */ |
| options_t temp_options = { |
| # include "optionsx.h" |
| }; |
| |
| /* dynamo_options and temp_options, the two writable option structures, |
| * are both protected by this lock, which is kept outside of the protected |
| * section to ease bootstrapping issues |
| */ |
| DECLARE_CXTSWPROT_VAR(read_write_lock_t options_lock, INIT_READWRITE_LOCK(options_lock)); |
| |
| #else /* !NOT_DYNAMORIO_CORE ****************************************/ |
| # define ASSERT_OWN_OPTIONS_LOCK(b, l) |
| # define CORE_STATIC |
| #endif /* !NOT_DYNAMORIO_CORE ****************************************/ |
| |
| #undef OPTION_COMMAND |
| |
| |
| /* INITIALIZATION */ |
| |
| /* sets defaults just like the above initialization */ |
| CORE_STATIC void |
| set_dynamo_options_defaults(options_t *options) |
| { |
| ASSERT_OWN_OPTIONS_LOCK(options==&dynamo_options || options==&temp_options, &options_lock); |
| *options = default_options; |
| } |
| #undef OPTION_COMMAND_INTERNAL |
| |
| /* For all other purposes OPTION_COMMAND_INTERNAL should be equivalent to either OPTION_COMMAND or nothing */ |
| #ifdef EXPOSE_INTERNAL_OPTIONS |
| # define OPTION_COMMAND_INTERNAL OPTION_COMMAND |
| #else |
| # define OPTION_COMMAND_INTERNAL(type, name, default_value, command_line_option, statement, description, flag, pcache) /* nothing */ |
| #endif |
| |
| /* PARSING HANDLER */ |
| /* returns the space delimited or quote-delimited word |
| * starting at strpos in the string str, or NULL if none |
| * wordbuf is a caller allocated buffer of size wordbuflen that will hold |
| the copied word from the original string, since it assumes it cannot modify it |
| */ |
| static char* |
| getword_common(const char *str, const char **strpos, char *wordbuf, uint wordbuflen, |
| bool external/*whether called from outside the option parser*/) |
| { |
| uint i = 0; |
| const char *pos = *strpos; |
| char quote = '\0'; |
| |
| if (pos < str || pos >= str + strlen(str)) |
| return NULL; |
| |
| if (*pos == '\0') |
| return NULL; /* no more words */ |
| |
| /* eat leading spaces */ |
| while (*pos == ' ' || *pos == '\t' || |
| *pos == '\n' || *pos == '\r') { |
| pos++; |
| } |
| |
| /* extract the word */ |
| if (*pos == '\'' || *pos == '\"' || *pos == '`') { |
| quote = *pos; |
| pos++; /* don't include surrounding quotes in word */ |
| } |
| while (*pos) { |
| if (quote != '\0') { |
| /* if quoted, only end on matching quote */ |
| if (*pos == quote) { |
| pos++; /* include the quote */ |
| break; |
| } |
| } else { |
| /* if not quoted, end on whitespace */ |
| if (*pos == ' ' || *pos == '\t' || |
| *pos == '\n' || *pos == '\r') |
| break; |
| } |
| if (i<wordbuflen-1) { |
| wordbuf[i++] = *pos; |
| pos++; |
| } else { |
| if (!external) { |
| OPTION_PARSE_ERROR(ERROR_OPTION_TOO_LONG_TO_PARSE, 4, |
| get_application_name(), get_application_pid(), |
| strpos, IF_DEBUG_ELSE("Terminating", "Continuing")); |
| } |
| /* just return truncated form */ |
| break; |
| } |
| } |
| if (i == 0) |
| return NULL; /* no more words */ |
| |
| ASSERT(i<wordbuflen); |
| wordbuf[i] = '\0'; |
| *strpos = pos; |
| |
| return wordbuf; |
| } |
| |
| /* internal version */ |
| static char * |
| getword(const char *str, const char **strpos, char *wordbuf, uint wordbuflen) |
| { |
| return getword_common(str, strpos, wordbuf, wordbuflen, false/*internal*/); |
| } |
| |
| /* exported version */ |
| char * |
| parse_word(const char *str, const char **strpos, char *wordbuf, uint wordbuflen) |
| { |
| return getword_common(str, strpos, wordbuf, wordbuflen, true/*external*/); |
| } |
| |
| #define ISBOOL_bool 1 |
| #define ISBOOL_uint 0 |
| #define ISBOOL_uint_size 0 |
| #define ISBOOL_uint_time 0 |
| #define ISBOOL_uint_addr 0 |
| #define ISBOOL_pathstring_t 0 |
| #define ISBOOL_liststring_t 0 |
| |
| static void |
| parse_bool(bool *var, void *value) |
| { |
| *var = *(bool*)value; |
| } |
| |
| static void |
| parse_uint(uint *var, void *value) |
| { |
| uint num; |
| char *opt = (char*)value; |
| |
| if ((sscanf(opt, "0x%x", &num) == 1) || |
| (sscanf(opt, "%d", &num) == 1)) { /* atoi(opt) */ |
| *var = num; |
| } else { |
| /* var should be pre-initialized to default */ |
| OPTION_PARSE_ERROR(ERROR_OPTION_BAD_NUMBER_FORMAT, 4, |
| get_application_name(), get_application_pid(), opt, |
| IF_DEBUG_ELSE("Terminating", "Continuing")); |
| } |
| } |
| |
| static void |
| parse_uint_size(uint *var, void *value) |
| { |
| char unit; |
| int num; |
| int factor = 0; |
| |
| if (sscanf((char*)value, "%d%c", &num, &unit) == 1) { |
| /* no unit specifier, default unit is Kilo for compatibility */ |
| factor = 1024; |
| } else { |
| switch (unit) { |
| case 'B': // plain bytes |
| case 'b': factor = 1; break; |
| case 'K': // Kilo (bytes) |
| case 'k': factor = 1024; break; |
| case 'M': // Mega (bytes) |
| case 'm': factor = 1024*1024; break; |
| default: |
| /* var should be pre-initialized to default */ |
| OPTION_PARSE_ERROR(ERROR_OPTION_UNKNOWN_SIZE_SPECIFIER, 4, |
| get_application_name(), get_application_pid(), |
| (char *)value, IF_DEBUG_ELSE("Terminating", "Continuing")); |
| return; |
| } |
| } |
| *var = num * factor; |
| } |
| |
| static void |
| parse_uint_time(uint *var, void *value) |
| { |
| char unit; |
| int num; |
| int factor = 0; |
| |
| if (sscanf((char*)value, "%d%c", &num, &unit) == 1) { |
| /* no unit specifier, default unit is milliseconds */ |
| factor = 1; |
| } else { |
| switch (unit) { |
| case 's': factor = 1000; break; // seconds in milliseconds |
| case 'm': factor = 1000*60; break; // minutes in milliseconds |
| default: |
| /* var should be pre-initialized to default */ |
| OPTION_PARSE_ERROR(ERROR_OPTION_UNKNOWN_TIME_SPECIFIER, 4, |
| get_application_name(), get_application_pid(), |
| (char *)value, IF_DEBUG_ELSE("Terminating", "Continuing")); |
| return; |
| } |
| } |
| *var = num * factor; |
| } |
| |
| static void |
| parse_uint_addr(ptr_uint_t *var, void *value) |
| { |
| ptr_uint_t num; |
| char *opt = (char*)value; |
| |
| if (sscanf(opt, PIFX, &num) == 1) { /* atoi(opt) */ |
| *var = num; |
| } else { |
| /* var should be pre-initialized to default */ |
| OPTION_PARSE_ERROR(ERROR_OPTION_BAD_NUMBER_FORMAT, 4, |
| get_application_name(), get_application_pid(), opt, |
| IF_DEBUG_ELSE("Terminating", "Continuing")); |
| } |
| } |
| |
| static inline void |
| parse_pathstring_t(pathstring_t *var, void *value) { |
| strncpy(*var, (char*)value, MAX_PATH_OPTION_LENGTH-1); |
| if (strlen((char *)value) > MAX_PATH_OPTION_LENGTH) { |
| OPTION_PARSE_ERROR(ERROR_OPTION_TOO_LONG_TO_PARSE, 4, |
| get_application_name(), get_application_pid(), |
| (char *)value, IF_DEBUG_ELSE("Terminating", "Continuing")); |
| } |
| /* truncate if max (strncpy doesn't put NULL) */ |
| (*var)[MAX_PATH_OPTION_LENGTH-1] = '\0'; |
| } |
| |
| static void |
| parse_liststring_t(liststring_t *var, void *value) { |
| /* Case 5727: append by default (separating via ';') for liststring_t, as |
| * opposed to what we do for all other option types where the final |
| * specifier overwrites all previous. The special prefix '#' can be used to |
| * indicate overwrite. |
| */ |
| size_t len; |
| if (*((char *)value) == '#') { |
| strncpy(*var, (((char*)value)+1), MAX_LIST_OPTION_LENGTH-1); |
| len = strlen(((char *)value)+1); |
| } else { |
| len = strlen(*var) + strlen((char *)value); |
| if (*var[0] != '\0') { |
| len++; /*;*/ |
| strncat(*var, ";", MAX_LIST_OPTION_LENGTH-1 - strlen(*var)); |
| } |
| strncat(*var, (char*)value, MAX_LIST_OPTION_LENGTH-1 - strlen(*var)); |
| } |
| if (len >= MAX_LIST_OPTION_LENGTH) { |
| /* FIXME: no longer is value always the single too-long factor |
| * (could be appending a short option to a very long string), so |
| * should we change the message to "option is too long, truncating"? |
| */ |
| OPTION_PARSE_ERROR(ERROR_OPTION_TOO_LONG_TO_PARSE, 4, |
| get_application_name(), get_application_pid(), |
| *var, IF_DEBUG_ELSE("list Terminating", "Continuing")); |
| } |
| /* truncate if max (strncpy doesn't put NULL, even though strncat does) */ |
| (*var)[MAX_LIST_OPTION_LENGTH-1] = '\0'; |
| } |
| |
| |
| #define OPTION_COMMAND(type, name, default_value, command_line_option, \ |
| statement, description, flag, pcache) { \ |
| value = NULL; \ |
| if (ISBOOL_##type) { \ |
| if (!strcmp(opt+1, command_line_option)) { \ |
| value = &value_true; \ |
| } else if (!strncmp(opt+1, "no_", 3) \ |
| && !strcmp(opt+4, command_line_option)) { \ |
| value = &value_false; \ |
| } \ |
| } else { \ |
| if (!strcmp(opt+1, command_line_option)) { \ |
| value = getword(optstr, &pos, wordbuffer, sizeof(wordbuffer));\ |
| /* FIXME: check argument */ \ |
| } \ |
| } \ |
| if (value) { \ |
| parse_##type(&options->name, value); \ |
| statement; \ |
| continue; /* match found */ \ |
| } \ |
| } |
| |
| /* PR 330860: the for_this_process bool is read in OPTION_COMMAND statements */ |
| static int |
| set_dynamo_options_common(options_t *options, const char *optstr, bool for_this_process) |
| { |
| char *opt; |
| const char *pos = optstr; |
| const char *prev_pos; |
| bool got_badopt = false; |
| char badopt[MAX_OPTION_LENGTH]; |
| |
| char wordbuffer[MAX_OPTION_LENGTH]; |
| |
| /* used in the OPTION_COMMAND define above, declared here to save stack |
| * space FIXME : value_true and value_false could be static const if |
| * we mark the value arguments to the parse functions const */ |
| void *value = NULL; |
| bool value_true = true, value_false = false; |
| |
| if (optstr == NULL) |
| return 0; |
| |
| ASSERT_OWN_OPTIONS_LOCK(options==&dynamo_options || options==&temp_options, &options_lock); |
| ASSERT(!OPTIONS_PROTECTED()); |
| prev_pos = pos; |
| while ((opt = getword(optstr, &pos, wordbuffer, sizeof(wordbuffer))) != NULL) { |
| if (opt[0]=='-') { |
| /* N.B.: case 7853,7863: this expansion results in excessive stack |
| * usage under gcc. If strcmp is intrinsic, gcc 4.1 uses 3 local |
| * slots per strcmp, and doesn't overlap them at all. With -O2 it |
| * reduces it to 1 slot per strcmp, though it should be 0! But |
| * gcc 3.4 only makes it intrinsic at -O1+, so -O2 fixes the |
| * problem for 4.1 but makes it appear in 3.4! For now we |
| * explicitly use -fno-builtin-strcmp to handle all gcc versions. |
| * Long-term we may need our own strcmp inline just for this |
| * expansion that knows it needs no per-cmp slot. |
| */ |
| #include "optionsx.h" // will continue if a match is found |
| } |
| |
| /* no matching option found */ |
| if (!got_badopt) { |
| snprintf(badopt, BUFFER_SIZE_ELEMENTS(badopt), "%s", opt); |
| NULL_TERMINATE_BUFFER(badopt); |
| } |
| got_badopt = true; |
| |
| prev_pos = pos; |
| } |
| |
| /* we only report the first bad option */ |
| if (got_badopt) { |
| OPTION_PARSE_ERROR(ERROR_OPTION_UNKNOWN, 4, get_application_name(), |
| get_application_pid(), badopt, |
| IF_DEBUG_ELSE("Terminating", "Continuing")); |
| } |
| |
| return (int)got_badopt; |
| } |
| #undef OPTION_COMMAND |
| |
| CORE_STATIC int |
| set_dynamo_options(options_t *options, const char *optstr) |
| { |
| return set_dynamo_options_common(options, optstr, true); |
| } |
| |
| #if !defined(NOT_DYNAMORIO_CORE) && defined(WINDOWS) |
| static int |
| set_dynamo_options_other_process(options_t *options, const char *optstr) |
| { |
| return set_dynamo_options_common(options, optstr, false); |
| } |
| #endif |
| |
| /* max==0 means no max and 0 is an ok value */ |
| /* if option is incompatible, will try to touch up the option |
| * by assigning min to make it valid returns true if changed the |
| * option value |
| */ |
| bool |
| check_param_bounds(uint *val, uint min, uint max, const char *name) |
| { |
| bool ret = false; |
| uint new_val; |
| if ((max == 0 && *val != 0 && *val < min) || |
| (max > 0 && (*val < min || *val > max))) { |
| if (max == 0) { |
| new_val = min; |
| USAGE_ERROR("%s must be >= %d, resetting from %d to %d", |
| name, min, *val, new_val); |
| } else { |
| new_val = min; |
| USAGE_ERROR("%s must be >= %d and <= %d, resetting from %d to %d", |
| name, min, max, *val, new_val); |
| } |
| *val = new_val; |
| ret = true; |
| } |
| DOLOG(1, LOG_CACHE, { |
| if (*val == 0) { |
| LOG(GLOBAL, LOG_CACHE, 1, "%s: <unlimited>\n", name); |
| } else { |
| LOG(GLOBAL, LOG_CACHE, 1, "%s: %d KB\n", name, *val/1024); |
| } |
| }); |
| return ret; |
| } |
| |
| /* Print an option string from an option struct. |
| * Xref case 7939, in DEBUG builds the ? : creates an unshared implicit local |
| * that led to huge stack usage for update_dynamic_options() so we use |
| * methods instead of macros for those. While we could make these inline |
| * (which DEBUG will ignore and in release will result in the same code as |
| * MACRO version) these are way off the hot path and this way cuts the |
| * size of the release build dll by ~7kb. |
| */ |
| static void |
| PRINT_STRING_bool(char *optionbuff, bool value, const char *option) |
| { |
| snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s%s ", |
| value ? "" : "no_", option); |
| } |
| static void |
| PRINT_STRING_uint(char *optionbuff, uint value, const char *option) |
| { |
| /* FIXME: 0x100 hack to get logmask printed in hex, |
| * loglevel etc in decimal */ |
| snprintf(optionbuff, MAX_OPTION_LENGTH, |
| (value > 0x100 ? "-%s 0x%x " : "-%s %d "), option, value); |
| } |
| #define PRINT_STRING_uint_size(optionbuff,value,option) \ |
| snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s %d%s ", option, \ |
| ((value) % 1024 == 0 ? (value)/1024 : (value)), \ |
| ((value) % 1024 == 0 ? "K" : "B")) |
| #define PRINT_STRING_uint_time(optionbuff,value,option) \ |
| snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s %d ", option, (value)) |
| #define PRINT_STRING_uint_addr(optionbuff,value,option) \ |
| snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s "PFX" ", option, (value)); |
| #define PRINT_STRING_pathstring_t(optionbuff,value,option) snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s '%s' ", option, (value)); |
| #define PRINT_STRING_liststring_t(optionbuff,value,option) snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s '%s' ", option, (value)); |
| |
| |
| #define DIFF_bool(value1,value2) ( value1 != value2 ) |
| #define DIFF_uint(value1,value2) ( value1 != value2 ) |
| #define DIFF_uint_size(value1,value2) ( value1 != value2 ) |
| #define DIFF_uint_time(value1,value2) ( value1 != value2 ) |
| #define DIFF_uint_addr(value1,value2) ( value1 != value2 ) |
| #define DIFF_pathstring_t(value1,value2) (strcmp(value1,value2)) |
| #define DIFF_liststring_t(value1,value2) (strcmp(value1,value2)) |
| |
| #define COPY_bool(value1,value2) ( value1 = value2 ) |
| #define COPY_uint(value1,value2) ( value1 = value2 ) |
| #define COPY_uint_size(value1,value2) ( value1 = value2 ) |
| #define COPY_uint_time(value1,value2) ( value1 = value2 ) |
| #define COPY_uint_addr(value1,value2) ( value1 = value2 ) |
| #define COPY_pathstring_t(value1,value2) (strncpy(value1,value2,sizeof(value1))) |
| #define COPY_liststring_t(value1,value2) (strncpy(value1,value2,sizeof(value1))) |
| |
| /* Keep in synch with get_pcache_dynamo_options_string */ |
| void |
| get_dynamo_options_string(options_t *options, char *opstr, int len, bool minimal) |
| { |
| char optionbuff[MAX_OPTION_LENGTH]; |
| opstr[0] = 0; |
| |
| #define OPTION_COMMAND(type, name, default_value, \ |
| command_line_option, statement, \ |
| description, flag, pcache) { \ |
| if (command_line_option[0] != ' ' && /* not synthethic */ \ |
| (!minimal || \ |
| DIFF_##type(options->name,default_options.name))) { \ |
| PRINT_STRING_##type(optionbuff, options->name, \ |
| command_line_option); \ |
| NULL_TERMINATE_BUFFER(optionbuff); \ |
| strncat(opstr, optionbuff, len-strlen(opstr)-1); \ |
| } \ |
| } |
| #include "optionsx.h" |
| #undef OPTION_COMMAND |
| |
| |
| opstr[len-1] = 0; |
| } |
| |
| /* Fills opstr with a minimal string of only |
| * persistent-cache-affecting options whose effect is >= pcache_effect |
| * and that are different from the defaults. |
| * Keep in synch with get_dynamo_options_string. |
| */ |
| void |
| get_pcache_dynamo_options_string(options_t *options, char *opstr, int len, |
| op_pcache_t pcache_effect) |
| { |
| char optionbuff[MAX_OPTION_LENGTH]; |
| opstr[0] = 0; |
| |
| #define OPTION_COMMAND(type, name, default_value, \ |
| command_line_option, statement, \ |
| description, flag, pcache) { \ |
| if (command_line_option[0] != ' ' && /* not synthethic */ \ |
| (op_pcache_t) OPTION_AFFECTS_PCACHE_##name >= pcache_effect && \ |
| DIFF_##type(options->name,default_options.name)) { \ |
| PRINT_STRING_##type(optionbuff, options->name, \ |
| command_line_option); \ |
| NULL_TERMINATE_BUFFER(optionbuff); \ |
| strncat(opstr, optionbuff, len-strlen(opstr)-1); \ |
| } \ |
| } |
| #include "optionsx.h" |
| #undef OPTION_COMMAND |
| |
| opstr[len-1] = 0; |
| } |
| |
| /* Returns whether any persistent-cache-affecting options whose effect |
| * is == pcache_effect were passed in that are different from the defaults. |
| */ |
| bool |
| has_pcache_dynamo_options(options_t *options, op_pcache_t pcache_effect) |
| { |
| #define OPTION_COMMAND(type, name, default_value, \ |
| command_line_option, statement, \ |
| description, flag, pcache) { \ |
| if (command_line_option[0] != ' ' && /* not synthethic */ \ |
| (op_pcache_t) OPTION_AFFECTS_PCACHE_##name == pcache_effect && \ |
| DIFF_##type(options->name,default_options.name)) { \ |
| return true; \ |
| } \ |
| } |
| #include "optionsx.h" |
| #undef OPTION_COMMAND |
| return false; |
| } |
| |
| enum {OPTION_TYPE_STATIC, OPTION_TYPE_DYNAMIC}; |
| |
| #if defined(DEBUG) && defined(INTERNAL) |
| /* Used in update_dynamic_options() below. Usage is thread-safe as potential |
| * accesses are protected by the options_lock. */ |
| static char optionbuff[MAX_OPTION_LENGTH]; |
| static char new_optionbuff[MAX_OPTION_LENGTH]; |
| #endif |
| |
| /* need to see if any dynamic options have changed and copy them over */ |
| /* returns number of updated dynamic options */ |
| static int |
| update_dynamic_options(options_t *options, options_t *new_options) |
| { |
| int updated = 0; |
| |
| ASSERT_OWN_OPTIONS_LOCK(options==&dynamo_options || options==&temp_options, &options_lock); |
| ASSERT(!OPTIONS_PROTECTED()); |
| |
| #define OPTION_COMMAND(type, name, default_value, \ |
| command_line_option, statement, \ |
| description, flag, pcache) { \ |
| if (OPTION_TYPE_DYNAMIC == OPTION_TYPE_##flag) { \ |
| if (DIFF_##type(options->name, new_options->name)) { \ |
| COPY_##type(options->name, new_options->name); \ |
| updated++; \ |
| } \ |
| } else { \ |
| DOLOG(2, LOG_TOP, { \ |
| if (DIFF_##type(options->name, new_options->name)) { \ |
| PRINT_STRING_##type(optionbuff, options->name, \ |
| command_line_option); \ |
| NULL_TERMINATE_BUFFER(optionbuff); \ |
| PRINT_STRING_##type(new_optionbuff, new_options->name,\ |
| command_line_option); \ |
| NULL_TERMINATE_BUFFER(new_optionbuff); \ |
| LOG(GLOBAL, LOG_TOP, 2, \ |
| "Updating dynamic options : Ignoring static option change (%.*s changed to %.*s)\n", \ |
| MAX_LOG_LENGTH/2-80, optionbuff, \ |
| MAX_LOG_LENGTH/2-80, new_optionbuff); \ |
| } \ |
| }); \ |
| } \ |
| } |
| |
| #include "optionsx.h" |
| #undef OPTION_COMMAND |
| |
| return updated; |
| } |
| |
| /****************************************************************************/ |
| #ifndef NOT_DYNAMORIO_CORE |
| /* compare short_name, usually module name, against a list option of the combined |
| * default option (that could be overridden) and append list that is usually used |
| */ |
| list_default_or_append_t |
| check_list_default_and_append(liststring_t default_list, |
| liststring_t append_list, |
| const char *short_name) |
| { |
| list_default_or_append_t onlist = LIST_NO_MATCH; |
| ASSERT(short_name != NULL); |
| /* The wildcard '*' is currently expected to be tested by callers |
| * to allow modules without a PE name. FIXME: Alternatively could |
| * check whether either list is * and also handle NULL name. |
| * |
| * FIXME: case 3858 about providing a substitute PE name |
| */ |
| /* inlined IS_STRING_OPTION_EMPTY */ |
| if (!(default_list[0] == '\0')) { |
| string_option_read_lock(); |
| LOG(THREAD_GET, LOG_INTERP|LOG_VMAREAS, 2, |
| "check_list_default_and_append: module %s vs default list %s\n", |
| short_name, default_list); |
| if (check_filter(default_list, short_name)) |
| onlist = LIST_ON_DEFAULT; |
| string_option_read_unlock(); |
| } |
| if (!onlist && |
| !(append_list[0] == '\0')) { |
| string_option_read_lock(); |
| LOG(THREAD_GET, LOG_INTERP|LOG_VMAREAS, 2, |
| "check_list_default_and_append: module %s vs append list %s\n", |
| short_name, append_list); |
| if (check_filter(append_list, short_name)) |
| onlist = LIST_ON_APPEND; |
| string_option_read_unlock(); |
| } |
| return onlist; |
| } |
| |
| /* security options have to be enabled to be blocking or reporting */ |
| #define SECURITY_OPTION_CONSISTENT(security_option) do { \ |
| if (!TEST(OPTION_ENABLED, DYNAMO_OPTION(security_option)) && \ |
| TESTANY(OPTION_BLOCK|OPTION_REPORT, DYNAMO_OPTION(security_option))) { \ |
| USAGE_ERROR("Incompatible settings for %s", #security_option); \ |
| dynamo_options.security_option = OPTION_DISABLED; \ |
| changed_options = true; \ |
| } \ |
| } while (0) |
| |
| /* check for incompatible options */ |
| static bool |
| check_option_compatibility_helper(int recurse_count) |
| { |
| bool changed_options = false; |
| #ifdef EXPOSE_INTERNAL_OPTIONS |
| if (!INTERNAL_OPTION(inline_calls) && !DYNAMO_OPTION(disable_traces)) { |
| /* cannot disable inlining of calls and build traces (currently) */ |
| USAGE_ERROR("-no_inline_calls not compatible with -disable_traces, setting to default"); |
| SET_DEFAULT_VALUE(inline_calls); |
| SET_DEFAULT_VALUE(disable_traces); |
| changed_options = true; |
| } |
| if (INTERNAL_OPTION(tracedump_binary) && INTERNAL_OPTION(tracedump_text)) { |
| USAGE_ERROR("Cannot set both -tracedump_binary and -tracedump_text, setting to default"); |
| SET_DEFAULT_VALUE(tracedump_binary); |
| SET_DEFAULT_VALUE(tracedump_text); |
| changed_options = true; |
| } |
| if (INTERNAL_OPTION(trace_threshold) > USHRT_MAX) { |
| USAGE_ERROR("trace threshold (%d) must be <= USHRT_MAX (%d), setting to max", |
| INTERNAL_OPTION(trace_threshold), USHRT_MAX, USHRT_MAX); |
| /* user was probably trying to make the threshold very high, set it to |
| * max |
| */ |
| /* from derek : this could wreak havoc w/ trace building fencepost |
| * cases... in the case where if head gets hot but somebody |
| * else is building trace w/ it you wait, and end up inc-ing counter |
| * again, in which case would wrap around and not be hot! |
| * (THCI already has problem w/ that b/c it only checks for == not >= |
| * (to avoid eflags)) |
| */ |
| /* FIXME : we may want to set to USHRT_MAX-10 or some such, same with |
| * check above */ |
| dynamo_options.trace_threshold = USHRT_MAX; |
| changed_options = true; |
| } |
| if (INTERNAL_OPTION(trace_counter_on_delete) > INTERNAL_OPTION(trace_threshold)) { |
| USAGE_ERROR("trace_counter_on_delete cannot be > trace_threshold"); |
| SET_DEFAULT_VALUE(trace_counter_on_delete); |
| changed_options = true; |
| } |
| if (INTERNAL_OPTION(alt_hash_func) >= HASH_FUNCTION_ENUM_MAX) { |
| USAGE_ERROR("Invalid selection (%d) for shared cache hash func, must be < %d", |
| INTERNAL_OPTION(alt_hash_func), |
| HASH_FUNCTION_ENUM_MAX); |
| SET_DEFAULT_VALUE(alt_hash_func); |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(inline_bb_ibl) && |
| DYNAMO_OPTION(shared_bbs) && |
| !DYNAMO_OPTION(atomic_inlined_linking)) { |
| USAGE_ERROR("-inline_bb_ibl requires -atomic_inlined_linking when -shared_bbs"); |
| dynamo_options.atomic_inlined_linking = true; |
| changed_options = true; |
| } |
| # ifdef SHARING_STUDY |
| if (INTERNAL_OPTION(fragment_sharing_study) && SHARED_FRAGMENTS_ENABLED()) { |
| USAGE_ERROR("-fragment_sharing_study requires only private fragments"); |
| dynamo_options.fragment_sharing_study = false; |
| changed_options = true; |
| } |
| # endif |
| #endif /* EXPOSE_INTERNAL_OPTIONS */ |
| |
| if (!ALIGNED(DYNAMO_OPTION(stack_size), PAGE_SIZE)) { |
| USAGE_ERROR("-stack_size must be at least 12K and a multiple of the page size (4k)"); |
| SET_DEFAULT_VALUE(stack_size); |
| changed_options = true; |
| } |
| |
| #if defined(PROFILE_LINKCOUNT) || defined(TRACE_HEAD_CACHE_INCR) || defined(CUSTOM_EXIT_STUBS) |
| if (DYNAMO_OPTION(pad_jmps)) { |
| USAGE_ERROR("-pad_jmps not supported in this build yet"); |
| } |
| #endif |
| |
| #if defined(UNIX) || defined(CLIENT_INTERFACE) |
| if (DYNAMO_OPTION(pad_jmps) && !DYNAMO_OPTION(pad_jmps_mark_no_trace) |
| && DYNAMO_OPTION(enable_traces) |
| # ifndef UNIX |
| && INTERNAL_OPTION(code_api) |
| # endif |
| ) { |
| USAGE_ERROR("-pad_jmps isn't safe with code_api or on Linux without -pad_jmps_mark_no_trace when traces are enabled"); |
| } |
| #endif |
| |
| #ifdef PROFILE_LINKCOUNT |
| if (DYNAMO_OPTION(profile_counts) && |
| TESTANY(SELFPROT_LOCAL|SELFPROT_GLOBAL, DYNAMO_OPTION(protect_mask))) { |
| /* May not be able to write to some linkstubs! */ |
| USAGE_ERROR("-prof_counts incompatible with heap write protection"); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * warn of unfinished and untested self-protection options |
| * FIXME: update once these features are complete |
| */ |
| #ifdef UNIX |
| if (dynamo_options.protect_mask != 0) { |
| USAGE_ERROR("selfprot not supported on unix: case 8023"); |
| dynamo_options.protect_mask = 0; |
| changed_options = true; |
| } |
| #endif |
| #if defined(WINDOWS) && !defined(NOLIBC) |
| if (TEST(SELFPROT_ANY_DATA_SECTION, dynamo_options.protect_mask)) { |
| /* since libc routines are embedded in our dll and we run |
| * them from the code cache we hit write faults |
| */ |
| USAGE_ERROR("Cannot protect data segment w/ a non-NOLIBC build"); |
| dynamo_options.protect_mask &= ~SELFPROT_ANY_DATA_SECTION; |
| changed_options = true; |
| } |
| #endif |
| if ( |
| #ifdef WINDOWS |
| /* FIXME: CACHE isn't multithread safe yet */ |
| TEST(SELFPROT_CACHE, dynamo_options.protect_mask) || |
| #endif |
| /* FIXME: LOCAL has some unresolved issues w/ new heap units, etc. */ |
| TEST(SELFPROT_LOCAL, dynamo_options.protect_mask) || |
| TEST(SELFPROT_DCONTEXT, dynamo_options.protect_mask)) { |
| ASSERT_NOT_TESTED(); |
| } |
| /* warn of incompatible options */ |
| if (TEST(SELFPROT_DCONTEXT, dynamo_options.protect_mask) && |
| !TEST(SELFPROT_GLOBAL, dynamo_options.protect_mask)) { |
| USAGE_ERROR("dcontext is only actually protected if global is as well"); |
| /* FIXME: turn off dcontext? or let upcontext be split anyway? */ |
| } |
| /* FIXME: better way to enforce these incompatibilities w/ certain builds |
| * than by turning off protection? Should we halt instead? |
| */ |
| if (TEST(SELFPROT_DCONTEXT, dynamo_options.protect_mask) && SHARED_FRAGMENTS_ENABLED()) { |
| /* FIXME: get all shared gen routines to properly handle unprotected_context_t */ |
| USAGE_ERROR("Shared cache does not support protecting dcontext yet"); |
| dynamo_options.protect_mask &= ~SELFPROT_DCONTEXT; |
| changed_options = true; |
| } |
| #ifdef TRACE_HEAD_CACHE_INCR |
| if (TESTANY(SELFPROT_LOCAL|SELFPROT_GLOBAL, dynamo_options.protect_mask)) { |
| USAGE_ERROR("Cannot protect heap in a TRACE_HEAD_CACHE_INCR build"); |
| dynamo_options.protect_mask &= ~(SELFPROT_LOCAL|SELFPROT_GLOBAL); |
| changed_options = true; |
| } |
| #endif |
| #ifdef CLIENT_INTERFACE |
| /* case 9714: The client interface is compatible with the current default |
| * protect_mask of 0x101, but is incompatible with the following: |
| */ |
| if (TESTANY(SELFPROT_DATA_CXTSW | SELFPROT_GLOBAL | SELFPROT_DCONTEXT | |
| SELFPROT_LOCAL | SELFPROT_CACHE | SELFPROT_STACK, |
| dynamo_options.protect_mask)) { |
| USAGE_ERROR("CLIENT_INTERFACE incompatible with protect_mask %x at this time", |
| dynamo_options.protect_mask); |
| dynamo_options.protect_mask &= ~(SELFPROT_DATA_CXTSW | SELFPROT_GLOBAL | |
| SELFPROT_DCONTEXT | SELFPROT_LOCAL | |
| SELFPROT_CACHE | SELFPROT_STACK); |
| changed_options = true; |
| } |
| #endif |
| #ifdef CUSTOM_TRACES |
| if (PRIVATE_TRACES_ENABLED() && DYNAMO_OPTION(shared_bbs)) { |
| /* Due to complications with shadowing, we do not support |
| * private traces and shared bbs |
| */ |
| USAGE_ERROR("CUSTOM_TRACES incompatible with private traces and shared bbs"); |
| dynamo_options.shared_bbs = false; |
| changed_options = true; |
| } |
| #endif |
| #ifdef IA32_ON_IA64 |
| if (dynamo_options.protect_mask != 0) { |
| USAGE_ERROR("IA32_ON_IA64 incompatible with self-protection at this time"); |
| dynamo_options.protect_mask = 0; |
| changed_options = true; |
| } |
| #endif |
| /****************************************************************************/ |
| |
| #if defined(PROFILE_RDTSC) && defined(SIDELINE) |
| if (dynamo_options.profile_times && dynamo_options.sideline) { |
| USAGE_ERROR("-profile_times incompatible with -sideline, setting to defaults"); |
| SET_DEFAULT_VALUE(profile_times); |
| SET_DEFAULT_VALUE(sideline); |
| changed_options = true; |
| } |
| #endif |
| |
| #ifdef UNIX |
| # ifndef HAVE_TLS |
| if (SHARED_FRAGMENTS_ENABLED()) { |
| USAGE_ERROR("shared fragments not supported on this OS"); |
| dynamo_options.shared_bbs = false; |
| dynamo_options.shared_traces = false; |
| changed_options = true; |
| } |
| # ifdef X64 |
| /* PR 361894: we do not support x64 without TLS (xref PR 244737) */ |
| # error X64 requires HAVE_TLS |
| # endif |
| # endif |
| |
| # if !defined(HAVE_MEMINFO) && defined(PROGRAM_SHEPHERDING) |
| /* PR 235433: without +x info we cannot support code origins */ |
| if (DYNAMO_OPTION(code_origins)) { |
| USAGE_ERROR("-code_origins not supported on this OS"); |
| dynamo_options.code_origins = false; |
| changed_options = true; |
| } |
| /* FIXME: We can't support certain GBOP policies, either. Anything else? */ |
| # endif |
| #endif |
| |
| /* Manipulate all of the options needed for -shared_traces. */ |
| if (DYNAMO_OPTION(shared_traces)) { |
| if (!DYNAMO_OPTION(private_ib_in_tls)) { |
| SYSLOG_INTERNAL_INFO("-shared_traces requires -private_ib_in_tls, enabling"); |
| dynamo_options.private_ib_in_tls = true; |
| changed_options = true; |
| } |
| if (!DYNAMO_OPTION(shared_trace_ibl_routine)) { |
| SYSLOG_INTERNAL_INFO("-shared_traces requires -shared_trace_ibl_routine, enabling"); |
| dynamo_options.shared_trace_ibl_routine = true; |
| changed_options = true; |
| } |
| if (!DYNAMO_OPTION(atomic_inlined_linking)) { |
| SYSLOG_INTERNAL_INFO("-shared_traces requires -atomic_inlined_linking, enabling"); |
| dynamo_options.atomic_inlined_linking = true; |
| changed_options = true; |
| } |
| } |
| #ifdef EXPOSE_INTERNAL_OPTIONS |
| # ifdef DEADLOCK_AVOIDANCE |
| if (INTERNAL_OPTION(mutex_callstack) > MAX_MUTEX_CALLSTACK) { |
| USAGE_ERROR("-mutex_callstack is compiled with MAX_MUTEX_CALLSTACK=%d", MAX_MUTEX_CALLSTACK); |
| dynamo_options.mutex_callstack = MAX_MUTEX_CALLSTACK; |
| changed_options = true; |
| } |
| # endif |
| if (INTERNAL_OPTION(unsafe_ignore_eflags_ibl) && |
| !INTERNAL_OPTION(unsafe_ignore_eflags_prefix)) { |
| USAGE_ERROR("-unsafe_ignore_eflags_ibl requires -unsafe_ignore_eflags_prefix, enabling"); |
| dynamo_options.unsafe_ignore_eflags_prefix = true; |
| changed_options = true; |
| } |
| # ifdef X64 |
| /* saving in the trace and restoring in ibl means that |
| * -unsafe_ignore_eflags_{trace,ibl} must be equivalent |
| */ |
| if ((INTERNAL_OPTION(unsafe_ignore_eflags_ibl) && |
| !INTERNAL_OPTION(unsafe_ignore_eflags_trace)) || |
| (!INTERNAL_OPTION(unsafe_ignore_eflags_ibl) && |
| INTERNAL_OPTION(unsafe_ignore_eflags_trace))) { |
| USAGE_ERROR("-unsafe_ignore_eflags_ibl must match -unsafe_ignore_eflags_trace " |
| "for x64: enabling both"); |
| dynamo_options.unsafe_ignore_eflags_trace = true; |
| dynamo_options.unsafe_ignore_eflags_ibl = true; |
| changed_options = true; |
| } |
| # endif |
| #endif /* EXPOSE_INTERNAL_OPTIONS */ |
| if (RUNNING_WITHOUT_CODE_CACHE() && DYNAMO_OPTION(enable_reset)) { |
| /* No reset for hotp_only and thin_client modes; case 8389. */ |
| USAGE_ERROR("-enable_reset can't be used with -hotp_only or -thin_client"); |
| DISABLE_RESET(&dynamo_options); |
| } |
| if (DYNAMO_OPTION(reset_at_vmm_percent_free_limit) > 100) { |
| USAGE_ERROR("-reset_at_vmm_percent_free_limit is percentage value, " |
| "can't be > 100"); |
| dynamo_options.reset_at_vmm_percent_free_limit = 100; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(reset_at_commit_percent_free_limit) > 100) { |
| USAGE_ERROR("-reset_at_commit_percent_free_limit is percentage value, can't be > 100"); |
| dynamo_options.reset_at_commit_percent_free_limit = 100; |
| changed_options = true; |
| } |
| if (!DYNAMO_OPTION(enable_reset)) { |
| if (DYNAMO_OPTION(reset_at_nth_thread)) { |
| USAGE_ERROR("-reset_at_nth_thread requires -enable_reset, enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| #ifdef EXPOSE_INTERNAL_OPTIONS |
| else if (INTERNAL_OPTION(reset_at_fragment_count)) { |
| USAGE_ERROR("-reset_at_fragment_count requires -enable_reset, enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| #endif /* EXPOSE_INTERNAL_OPTIONS */ |
| else if (DYNAMO_OPTION(reset_at_switch_to_os_at_vmm_limit)) { |
| USAGE_ERROR("-reset_at_switch_to_os_at_vmm_limit requires -enable_reset, " |
| " enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| else if (DYNAMO_OPTION(reset_at_vmm_percent_free_limit) != 0) { |
| USAGE_ERROR("-reset_at_vmm_percent_free_limit requires -enable_reset, " |
| " enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| else if (DYNAMO_OPTION(reset_at_vmm_free_limit) != 0) { |
| USAGE_ERROR("-reset_at_vmm_free_limit requires -enable_reset, enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| else if (DYNAMO_OPTION(reset_at_vmm_full)) { |
| USAGE_ERROR("-reset_at_vmm_full requires -enable_reset, enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| else if (DYNAMO_OPTION(reset_at_commit_percent_free_limit) != 0) { |
| USAGE_ERROR("-reset_at_commit_percent_free_limit requires -enable_reset," |
| " enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| else if (DYNAMO_OPTION(reset_at_commit_free_limit) != 0) { |
| USAGE_ERROR("-reset_at_commit_free_limit requires -enable_reset, enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| else if (DYNAMO_OPTION(reset_every_nth_pending) > 0) { |
| USAGE_ERROR("-reset_every_nth_pending requires -enable_reset, enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| else if (DYNAMO_OPTION(reset_at_nth_bb_unit) > 0) { |
| USAGE_ERROR("-reset_at_nth_bb_unit requires -enable_reset, enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| else if (DYNAMO_OPTION(reset_at_nth_trace_unit) > 0) { |
| USAGE_ERROR("-reset_at_nth_trace_unit requires -enable_reset, enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| else if (DYNAMO_OPTION(reset_every_nth_bb_unit) > 0) { |
| USAGE_ERROR("-reset_every_nth_bb_unit requires -enable_reset, enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| else if (DYNAMO_OPTION(reset_every_nth_trace_unit) > 0) { |
| USAGE_ERROR("-reset_every_nth_trace_unit requires -enable_reset, enabling"); |
| dynamo_options.enable_reset = true; |
| changed_options = true; |
| } |
| } |
| |
| #ifdef TRACE_HEAD_CACHE_INCR |
| if (dynamo_options.shared_traces) { |
| USAGE_ERROR("Cannot share traces in a TRACE_HEAD_CACHE_INCR build"); |
| dynamo_options.shared_traces = false; |
| changed_options = true; |
| } |
| #endif |
| /* FIXME We support only shared BBs as IBTs when trace building is on. */ |
| if (DYNAMO_OPTION(bb_ibl_targets) && !DYNAMO_OPTION(disable_traces) && |
| !DYNAMO_OPTION(shared_bbs)) { |
| USAGE_ERROR("-bb_ibl_targets w/traces not supported w/-no_shared_bbs, disabling"); |
| dynamo_options.bb_ibl_targets = false; |
| changed_options = true; |
| } |
| /* We need private_ib_in_tls for shared BB IBTs. */ |
| if (DYNAMO_OPTION(bb_ibl_targets) && DYNAMO_OPTION(shared_bbs) && |
| !DYNAMO_OPTION(private_ib_in_tls)) { |
| SYSLOG_INTERNAL_INFO("-bb_ibl_targets w/traces requires -private_ib_in_tls, enabling"); |
| dynamo_options.private_ib_in_tls = true; |
| changed_options = true; |
| } |
| /* We need shared tables for shared BB IBTs when trace building is on. */ |
| if (DYNAMO_OPTION(bb_ibl_targets) && DYNAMO_OPTION(shared_bbs) && |
| !DYNAMO_OPTION(disable_traces) && !DYNAMO_OPTION(shared_bb_ibt_tables)) { |
| SYSLOG_INTERNAL_INFO("-bb_ibl_targets -shared_bbs w/traces requires -shared_bb_ibt_tables, enabling"); |
| dynamo_options.shared_bb_ibt_tables = true; |
| changed_options = true; |
| } |
| /* If we're still using BBs as IBTs when trace building is on and want to |
| * add traces to the BB IBT tables, don't let private traces get added to |
| * a shared table. |
| */ |
| if (DYNAMO_OPTION(bb_ibl_targets) && !DYNAMO_OPTION(disable_traces) && |
| DYNAMO_OPTION(bb_ibt_table_includes_traces) && |
| DYNAMO_OPTION(shared_bb_ibt_tables) && !DYNAMO_OPTION(shared_traces)) { |
| SYSLOG_INTERNAL_INFO("-bb_ibt_table_includes_traces -shared_bb_ibt_tables requires -shared_traces, disabling -bb_ibt_table_includes_traces"); |
| dynamo_options.bb_ibt_table_includes_traces = false; |
| changed_options = true; |
| } |
| /* When using BBs as IBTs when trace building is on and adding traces to |
| * the BB IBT table, BBs & traces must use the same type of prefix. */ |
| if (DYNAMO_OPTION(bb_ibl_targets) && !DYNAMO_OPTION(disable_traces) && |
| DYNAMO_OPTION(bb_ibt_table_includes_traces) && |
| (DYNAMO_OPTION(trace_single_restore_prefix) != DYNAMO_OPTION(bb_single_restore_prefix))) { |
| SYSLOG_INTERNAL_INFO("For -bb_ibl_targets -bb_ibt_table_includes_traces, traces & BBs must use identical prefixes"); |
| /* FIXME We could either set trace_single_restore_prefix and |
| * bb_single_restore_prefix to the same value or use |
| * -no_bb_ibt_table_includes_traces. For now, we do the latter as |
| * it's less disruptive overall -- the trace prefix setting isn't |
| * modified and full prefixes are not used on BBs, limiting the |
| * cache/memory size increase. We need to measure to determine |
| * the proper thing to do. |
| */ |
| SYSLOG_INTERNAL_INFO("Disabling -bb_ibt_table_includes_traces"); |
| dynamo_options.bb_ibt_table_includes_traces = false; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(syscalls_synch_flush) && !DYNAMO_OPTION(shared_deletion)) { |
| /* right now syscalls_synch_flush only affects shared_deletion, so we want |
| * to disable it when shared_deletion is off -- but don't yell at user so |
| * not a USAGE_ERROR, simply an info event |
| */ |
| SYSLOG_INTERNAL_INFO("-syscalls_synch_flush requires -shared_deletion, disabling"); |
| dynamo_options.syscalls_synch_flush = false; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(free_private_stubs) && !DYNAMO_OPTION(separate_private_stubs)) { |
| USAGE_ERROR("-free_private_stubs requires -separate_private_stubs, disabling"); |
| dynamo_options.free_private_stubs = false; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(unsafe_free_shared_stubs) && !DYNAMO_OPTION(separate_shared_stubs)) { |
| USAGE_ERROR("-unsafe_free_shared_stubs requires -separate_shared_stubs, disabling"); |
| dynamo_options.unsafe_free_shared_stubs = false; |
| changed_options = true; |
| } |
| #ifdef EXPOSE_INTERNAL_OPTIONS |
| if (!DYNAMO_OPTION(indirect_stubs)) { |
| # ifdef PROGRAM_SHEPHERDING |
| if (DYNAMO_OPTION(ret_after_call) || |
| DYNAMO_OPTION(rct_ind_call) != OPTION_DISABLED || |
| DYNAMO_OPTION(rct_ind_jump) != OPTION_DISABLED) { |
| USAGE_ERROR("C, E, and F policies require -indirect_stubs, enabling"); |
| dynamo_options.indirect_stubs = true; |
| changed_options = true; |
| } |
| # endif |
| # ifdef PROFILE_LINKCOUNT |
| if (INTERNAL_OPTION(profile_counts)) { |
| USAGE_ERROR("-prof_counts require -indirect_stubs, enabling"); |
| dynamo_options.indirect_stubs = true; |
| changed_options = true; |
| } |
| # endif |
| # ifdef CUSTOM_EXIT_STUBS |
| USAGE_ERROR("CUSTOM_EXIT_STUBS requires -indirect_stubs, enabling"); |
| dynamo_options.indirect_stubs = true; |
| changed_options = true; |
| # endif |
| #ifdef HASHTABLE_STATISTICS |
| if ((!DYNAMO_OPTION(shared_traces) && DYNAMO_OPTION(inline_trace_ibl)) || |
| (!DYNAMO_OPTION(shared_bbs) && DYNAMO_OPTION(inline_bb_ibl))) { |
| USAGE_ERROR("private inlined ibl requires -indirect_stubs, enabling"); |
| dynamo_options.indirect_stubs = true; |
| changed_options = true; |
| } |
| #endif |
| } |
| #endif |
| if ((DYNAMO_OPTION(finite_shared_bb_cache) || |
| DYNAMO_OPTION(finite_shared_trace_cache)) && |
| !DYNAMO_OPTION(cache_shared_free_list)) { |
| USAGE_ERROR("-finite_shared_{bb,trace}_cache requires -cache_shared_free_list, enabling"); |
| dynamo_options.cache_shared_free_list = true; |
| changed_options = true; |
| } |
| #ifdef WINDOWS |
| if (DYNAMO_OPTION(shared_fragment_shared_syscalls) && |
| !DYNAMO_OPTION(shared_syscalls)) { |
| SYSLOG_INTERNAL_INFO("-shared_fragment_shared_syscalls requires -shared_syscalls, disabling"); |
| dynamo_options.shared_fragment_shared_syscalls = false; |
| changed_options = true; |
| } |
| # ifdef X64 |
| if (!DYNAMO_OPTION(shared_fragment_shared_syscalls)) { |
| /* we use tls for the continuation pc, and the shared gencode, always */ |
| USAGE_ERROR("-shared_fragment_shared_syscalls is required for x64"); |
| dynamo_options.shared_fragment_shared_syscalls = true; |
| changed_options = true; |
| } |
| if (!DYNAMO_OPTION(private_ib_in_tls)) { |
| USAGE_ERROR("-private_ib_in_tls is required for x64"); |
| dynamo_options.private_ib_in_tls = true; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(x86_to_x64_ibl_opt) && !DYNAMO_OPTION(x86_to_x64)) { |
| SYSLOG_INTERNAL_INFO("-x86_to_x64 is required for x86_to_x64_ibl_opt. disabling -x86_to_x64_ibl_opt."); |
| dynamo_options.x86_to_x64_ibl_opt = false; |
| changed_options = true; |
| } |
| # endif |
| /* We retain shared_fragment_shared_syscalls as a separate option since |
| * it can be used -- but isn't required -- for shared BBs only mode. */ |
| if (SHARED_FRAGMENTS_ENABLED() && |
| DYNAMO_OPTION(shared_syscalls) && |
| !DYNAMO_OPTION(shared_fragment_shared_syscalls)) { |
| SYSLOG_INTERNAL_INFO("-shared_{bbs|traces} w/-shared_syscalls requires -shared_fragment_shared_syscalls, enabling"); |
| dynamo_options.shared_fragment_shared_syscalls = true; |
| changed_options = true; |
| } |
| if (SHARED_IBT_TABLES_ENABLED() && DYNAMO_OPTION(shared_syscalls) && |
| !DYNAMO_OPTION(shared_fragment_shared_syscalls)) { |
| SYSLOG_INTERNAL_INFO("-shared_{bb|trace}_ibt_tables requires -shared_fragment_shared_syscalls, enabling"); |
| dynamo_options.shared_fragment_shared_syscalls = true; |
| changed_options = true; |
| } |
| /* Don't leave -shared_fragment_shared_syscalls on if we're not using shared |
| * fragments: case 8027. */ |
| /* FIXME We could try to eliminate the info msg by pulling this logic and |
| * associated processing into an OPTION_COMMAND (but OPTION_COMMAND has |
| * its own imperfections). |
| */ |
| if (DYNAMO_OPTION(shared_fragment_shared_syscalls) && |
| /* x64 uses -shared_fragment_shared_syscalls always */ |
| IF_X64_ELSE(false, !SHARED_FRAGMENTS_ENABLED())) { |
| SYSLOG_INTERNAL_INFO("-shared_fragment_shared_syscalls requires -shared_{bbs|traces}, disabling"); |
| dynamo_options.shared_fragment_shared_syscalls = false; |
| changed_options = true; |
| } |
| /* We don't yet support shared BBs and private traces targeting shared syscall |
| * simultaneously: case 5436. */ |
| if (DYNAMO_OPTION(shared_syscalls) && DYNAMO_OPTION(shared_bbs) && |
| !DYNAMO_OPTION(shared_traces) && !DYNAMO_OPTION(disable_traces)) { |
| SYSLOG_INTERNAL_INFO("-shared_syscalls not supported with -shared_bbs -no_shared_traces, disabling"); |
| dynamo_options.shared_syscalls = false; |
| changed_options = true; |
| } |
| # ifdef EXPOSE_INTERNAL_OPTIONS |
| if (INTERNAL_OPTION(shared_syscalls_fastpath) && |
| !DYNAMO_OPTION(shared_syscalls)) { |
| SYSLOG_INTERNAL_INFO("-shared_syscalls_fastpath requires -shared_syscalls, disabling"); |
| dynamo_options.shared_syscalls_fastpath = false; |
| changed_options = true; |
| } |
| if (INTERNAL_OPTION(shared_syscalls_fastpath) && |
| !DYNAMO_OPTION(disable_traces)) { |
| SYSLOG_INTERNAL_INFO("-shared_syscalls_fastpath requires -disable_traces, disabling"); |
| dynamo_options.shared_syscalls_fastpath = false; |
| changed_options = true; |
| } |
| # endif |
| #endif |
| if (DYNAMO_OPTION(shared_bb_ibt_tables) && !DYNAMO_OPTION(shared_bbs)) { |
| SYSLOG_INTERNAL_INFO("-shared_bb_ibt_tables requires -shared_bbs, disabling"); |
| dynamo_options.shared_bb_ibt_tables = false; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(shared_bb_ibt_tables) && !DYNAMO_OPTION(bb_ibl_targets)) { |
| SYSLOG_INTERNAL_INFO("-shared_bb_ibt_tables requires -bb_ibl_targets, disabling"); |
| dynamo_options.shared_bb_ibt_tables = false; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(shared_trace_ibt_tables) && !DYNAMO_OPTION(shared_traces)) { |
| SYSLOG_INTERNAL_INFO("-shared_bb_ibt_tables requires -shared_traces, disabling"); |
| dynamo_options.shared_trace_ibt_tables = false; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(shared_trace_ibt_tables) && |
| DYNAMO_OPTION(trace_ibt_groom) > 0) { |
| USAGE_ERROR("-trace_ibt_groom incompatible -shared_trace_ibt_tables, disabling"); |
| dynamo_options.trace_ibt_groom = 0; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(shared_bb_ibt_tables) && DYNAMO_OPTION(bb_ibt_groom) > 0) { |
| USAGE_ERROR("-bb_ibt_groom incompatible -shared_bb_ibt_tables, disabling"); |
| dynamo_options.bb_ibt_groom = 0; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(bb_ibt_groom) != 0 |
| && DYNAMO_OPTION(bb_ibt_groom) > DYNAMO_OPTION(private_bb_ibl_targets_load)) { |
| SYSLOG_INTERNAL_INFO("-bb_ibt_groom > private_bb_ibl_targets_load, disabling"); |
| dynamo_options.bb_ibt_groom = 0; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(trace_ibt_groom) != 0 |
| && DYNAMO_OPTION(trace_ibt_groom) > DYNAMO_OPTION(private_ibl_targets_load)) { |
| SYSLOG_INTERNAL_INFO("-trace_ibt_groom > private_ibl_targets_load, disabling"); |
| dynamo_options.trace_ibt_groom = 0; |
| changed_options = true; |
| } |
| #if defined(UNIX) && defined(HAVE_TLS) |
| if (!DYNAMO_OPTION(ibl_table_in_tls)) { |
| /* xref PR 211147 */ |
| SYSLOG_INTERNAL_INFO("-no_ibl_table_in_tls invalid on unix, disabling"); |
| dynamo_options.ibl_table_in_tls = true; |
| changed_options = true; |
| } |
| #endif |
| if (DYNAMO_OPTION(IAT_elide) && !DYNAMO_OPTION(IAT_convert)) { |
| USAGE_ERROR("-IAT_elide requires -IAT_convert, enabling"); |
| dynamo_options.IAT_convert = true; |
| changed_options = true; |
| } |
| |
| if (DYNAMO_OPTION(sandbox_writable) && DYNAMO_OPTION(sandbox_non_text)) { |
| USAGE_ERROR("-sandbox_writable and -sandbox_non_text are mutually exclusive, using -sandbox_non_text"); |
| dynamo_options.sandbox_writable = false; |
| dynamo_options.sandbox_non_text = true; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(sandbox2ro_threshold) == 1U) { |
| /* since we inc the counter before executing a selfmod fragment, |
| * a threshold of 1 can result in no progress |
| */ |
| USAGE_ERROR("-sandbox2ro_threshold cannot be 1, changing to 2"); |
| dynamo_options.sandbox2ro_threshold = 2U; |
| changed_options = true; |
| } |
| |
| #ifdef WINDOWS |
| # ifdef PROGRAM_SHEPHERDING |
| if (DYNAMO_OPTION(IAT_convert) && !DYNAMO_OPTION(emulate_IAT_writes)) { |
| /* FIXME: case 1948 we should in fact depend on emulate_IAT_read */ |
| USAGE_ERROR("-IAT_convert requires -emulate_IAT_writes, enabling"); |
| dynamo_options.emulate_IAT_writes = true; |
| changed_options = true; |
| } |
| # else |
| if (DYNAMO_OPTION(IAT_convert)) { |
| /* FIXME: case 1948 we should in fact depend on emulate_IAT_read */ |
| USAGE_ERROR("-IAT_convert requires unavailable -emulate_IAT_writes, disabling IAT_convert"); |
| dynamo_options.IAT_convert = false; |
| changed_options = true; |
| } |
| # endif /* PROGRAM_SHEPHERDING */ |
| #endif /* WINDOWS */ |
| |
| #ifdef WINDOWS |
| /* In theory ignore syscalls should work for int system calls, and also for |
| * sysenter system calls when Sygate SPA is not installed [though haven't |
| * tested]. However, ignored sysenter system calls when SPA is installed |
| * may lead to them reporting/blocking violations on certain platforms as |
| * the necessary mangling is too much at this point (FIXME). */ |
| if (DYNAMO_OPTION(ignore_syscalls) && DYNAMO_OPTION(sygate_sysenter)) { |
| USAGE_ERROR("-ignore_syscalls can't be used with -sygate_sysenter"); |
| dynamo_options.ignore_syscalls = false; |
| changed_options = true; |
| } |
| /* shared/ignore syscall writes to sysenter_storage dcontext field which |
| * should be in upcontext or something FIXME */ |
| if (DYNAMO_OPTION(sygate_sysenter) && |
| TEST(SELFPROT_DCONTEXT, DYNAMO_OPTION(protect_mask))) { |
| USAGE_ERROR("-sygate_sysenter incompatbile with -protect_mask dc"); |
| dynamo_options.protect_mask &= ~SELFPROT_DCONTEXT; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(hook_conflict) > HOOKED_TRAMPOLINE_MAX || |
| DYNAMO_OPTION(hook_conflict) == HOOKED_TRAMPOLINE_HOOK_DEEPER) { |
| USAGE_ERROR("-hook_conflict invalid or unsupported value"); |
| SET_DEFAULT_VALUE(hook_conflict); |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(native_exec_hook_conflict) > HOOKED_TRAMPOLINE_MAX || |
| DYNAMO_OPTION(native_exec_hook_conflict) == HOOKED_TRAMPOLINE_CHAIN) { |
| USAGE_ERROR("-native_exec_hook_conflict invalid or unsupported value"); |
| SET_DEFAULT_VALUE(native_exec_hook_conflict); |
| changed_options = true; |
| } |
| # ifdef CLIENT_INTERFACE |
| if (INTERNAL_OPTION(private_peb) && !INTERNAL_OPTION(private_loader)) { |
| /* The private peb is set up in loader.c */ |
| USAGE_ERROR("-private_peb requires -private_loader"); |
| dynamo_options.private_peb = false; |
| changed_options = true; |
| } |
| # endif |
| #endif |
| |
| #ifdef WINDOWS |
| SECURITY_OPTION_CONSISTENT(apc_policy); |
| SECURITY_OPTION_CONSISTENT(thread_policy); |
| #endif |
| #ifdef RETURN_AFTER_CALL |
| SECURITY_OPTION_CONSISTENT(rct_ret_unreadable); |
| #endif |
| #ifdef RCT_IND_BRANCH |
| SECURITY_OPTION_CONSISTENT(rct_ind_call); |
| SECURITY_OPTION_CONSISTENT(rct_ind_jump); |
| if (!DYNAMO_OPTION(ret_after_call) |
| && TEST(OPTION_ENABLED, DYNAMO_OPTION(rct_ind_jump))) { |
| SYSLOG_INTERNAL_INFO(".F depends on .C after calls, disabling .F"); |
| dynamo_options.rct_ind_jump = OPTION_DISABLED; |
| changed_options = true; |
| } |
| #endif |
| |
| if (DYNAMO_OPTION(ibl_hash_func_offset) > IBL_HASH_FUNC_OFFSET_MAX) { |
| USAGE_ERROR("-ibl_hash_func_offset currently can only be 0, 1, 2, or 3" |
| IF_X64(" or 4")); |
| dynamo_options.ibl_hash_func_offset = IBL_HASH_FUNC_OFFSET_MAX; |
| changed_options = true; |
| } |
| |
| #ifdef HOT_PATCHING_INTERFACE |
| /* -hot_patching controls all code relating to reading policies, modes, |
| * loading dlls, nudging, etc. can't do -hotp_only without those. |
| */ |
| if (DYNAMO_OPTION(hotp_only) && !DYNAMO_OPTION(hot_patching)) { |
| USAGE_ERROR("-hotp_only depends on -hot_patching, enabling -hot_patching"); |
| dynamo_options.hot_patching = true; |
| changed_options = true; |
| } |
| /* -hotp_only can't rely on interp to identify/trap system calls as app |
| * image will be patched directly, i.e., no interp. -native_exec_syscalls |
| * is needed to gain control for important app system calls. |
| */ |
| if (DYNAMO_OPTION(hotp_only) && !DYNAMO_OPTION(native_exec_syscalls)) { |
| USAGE_ERROR("-hotp_only depends on -native_exec_syscalls, " |
| "enabling -native_exec_syscalls"); |
| dynamo_options.native_exec_syscalls = true; |
| changed_options = true; |
| } |
| # ifdef RCT_IND_BRANCH |
| if (DYNAMO_OPTION(hotp_only) && |
| (DYNAMO_OPTION(rct_ind_call) != OPTION_DISABLED || |
| DYNAMO_OPTION(rct_ind_jump) != OPTION_DISABLED)) { |
| USAGE_ERROR("-rct_ind_{call,jump} incompatible w/ -hotp_only, disabling"); |
| dynamo_options.rct_ind_call = OPTION_DISABLED; |
| dynamo_options.rct_ind_jump = OPTION_DISABLED; |
| changed_options = true; |
| } |
| # endif |
| # ifdef RETURN_AFTER_CALL |
| if (DYNAMO_OPTION(hotp_only) && DYNAMO_OPTION(ret_after_call)) { |
| USAGE_ERROR("-ret_after_call incompatible w/ -hotp_only, disabling"); |
| dynamo_options.ret_after_call = false; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(borland_SEH_rct) && !DYNAMO_OPTION(process_SEH_push)) { |
| USAGE_ERROR("-borland_SEH_rct requires -process_SEH_push, enabling"); |
| dynamo_options.process_SEH_push = true; |
| changed_options = true; |
| } |
| # endif |
| # ifdef KSTATS |
| /* case 6837: FIXME: remove once -hotp_only -kstats work */ |
| if (DYNAMO_OPTION(hotp_only) && DYNAMO_OPTION(kstats)) { |
| USAGE_ERROR("-hotp_only doesn't support -kstats"); |
| dynamo_options.kstats = false; |
| changed_options = true; |
| } |
| # endif /* KSTATS */ |
| # ifdef CLIENT_INTERFACE |
| /* Probe API needs hot_patching. Also, for the time being at least, |
| * liveshields shouldn't be on when probe api is on. |
| */ |
| if (DYNAMO_OPTION(probe_api)) { |
| if (!DYNAMO_OPTION(hot_patching)) { |
| USAGE_ERROR("-probe_api needs -hot_patching"); |
| dynamo_options.hot_patching = true; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(liveshields)) { |
| USAGE_ERROR("-probe_api and -liveshields aren't compatible"); |
| dynamo_options.liveshields = false; |
| changed_options = true; |
| } |
| } |
| # endif /* CLIENT_INTERFACE */ |
| #endif /* HOT_PATCHING_INTERFACE */ |
| #ifdef CLIENT_INTERFACE |
| /* i#660/PR 226578 - Probe API doesn't flush pcaches conflicting with hotpatches. */ |
| if (DYNAMO_OPTION(probe_api) && DYNAMO_OPTION(use_persisted)) { |
| USAGE_ERROR("-probe_api and -use_persisted aren't compatible"); |
| dynamo_options.use_persisted = false; |
| changed_options = true; |
| } |
| # ifdef UNIX |
| /* PR 304708: we intercept all signals for a better client interface */ |
| if (DYNAMO_OPTION(code_api) && !DYNAMO_OPTION(intercept_all_signals)) { |
| USAGE_ERROR("-code_api requires -intercept_all_signals"); |
| dynamo_options.intercept_all_signals = true; |
| changed_options = true; |
| } |
| # endif |
| #endif /* CLIENT_INTERFACE */ |
| #ifdef CALL_PROFILE |
| if (DYNAMO_OPTION(prof_caller) > MAX_CALL_PROFILE_DEPTH) { |
| USAGE_ERROR("-prof_caller must be <= %d", MAX_CALL_PROFILE_DEPTH); |
| dynamo_options.prof_caller = MAX_CALL_PROFILE_DEPTH; |
| changed_options = true; |
| } |
| #endif |
| |
| #ifdef WINDOWS_PC_SAMPLE |
| if (DYNAMO_OPTION(prof_pcs_global) < 8 || DYNAMO_OPTION(prof_pcs_global) > 32) { |
| USAGE_ERROR("-prof_pcs_global must be >=8 and <= 32, setting to default"); |
| SET_DEFAULT_VALUE(prof_pcs_global); |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(prof_pcs_stubs) < 2 || DYNAMO_OPTION(prof_pcs_stubs) > 32) { |
| USAGE_ERROR("-prof_pcs_stubs must be >= 2 and <= 32, setting to default"); |
| /* maybe better to clamp to closest bound */ |
| SET_DEFAULT_VALUE(prof_pcs_stubs); |
| changed_options = true; |
| } |
| #endif |
| |
| #if defined(UNIX) && defined(CLIENT_INTERFACE) |
| if (DYNAMO_OPTION(early_inject) && !DYNAMO_OPTION(private_loader)) { |
| USAGE_ERROR("-early_inject requires -private_loader, turning on -private_loader"); |
| dynamo_options.private_loader = true; |
| changed_options = true; |
| } |
| #endif /* UNIX && CLIENT_INTERFACE */ |
| |
| #ifdef WINDOWS |
| if (DYNAMO_OPTION(inject_at_create_process) && !DYNAMO_OPTION(early_inject)) { |
| USAGE_ERROR("-inject_at_create_process requires -early_inject, setting to defaults"); |
| SET_DEFAULT_VALUE(inject_at_create_process); |
| SET_DEFAULT_VALUE(early_inject); |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(follow_systemwide) && !DYNAMO_OPTION(early_inject) && |
| !IS_STRING_OPTION_EMPTY(block_mod_load_list_default) && |
| !check_filter(DYNAMO_OPTION(block_mod_load_list_default), |
| "dynamorio.dll")) { |
| USAGE_ERROR("follow_systemwide is dangerous without -early_inject unless -block_mod_load_list[_default] includes dynamorio.dll"); |
| dynamo_options.follow_systemwide = false; |
| changed_options = true; |
| } |
| # ifndef NOT_DYNAMORIO_CORE /* can't check without get_os_version etc. */ |
| if (DYNAMO_OPTION(early_inject)) { |
| /* using early inject */ |
| if (DYNAMO_OPTION(early_inject_location) == |
| INJECT_LOCATION_LdrpLoadImportModule || |
| (DYNAMO_OPTION(early_inject_location) == |
| INJECT_LOCATION_LdrDefault && |
| (get_os_version() == WINDOWS_VERSION_NT || |
| get_os_version() == WINDOWS_VERSION_2000))) { |
| /* we will be using INJECT_LOCATION_LdrpLoadImportModule for |
| * child processes */ |
| if (!dr_early_injected || |
| dr_early_injected_location != |
| INJECT_LOCATION_LdrpLoadImportModule) { |
| /* can't get address from parent */ |
| /* our method of finding the address relies on |
| * -native_exec_syscalls */ |
| if (!DYNAMO_OPTION(native_exec_syscalls)) { |
| USAGE_ERROR("early_inject_location LdrpLoadImportModule requires -native_exec_syscalls for first process in chain"); |
| /* FIXME is this the best remediation choice? */ |
| dynamo_options.native_exec_syscalls = true; |
| changed_options = true; |
| /* FIXME - check that helper dlls exist, need a way of |
| * finding systemroot for that. */ |
| } |
| } |
| } |
| } |
| if (DYNAMO_OPTION(early_inject_location) > INJECT_LOCATION_MAX) { |
| USAGE_ERROR("invalid value for -early_inject_location, setting default"); |
| SET_DEFAULT_VALUE(early_inject_location); |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(early_inject_location) == INJECT_LOCATION_LdrCustom && |
| DYNAMO_OPTION(early_inject_address) == 0) { |
| USAGE_ERROR("early_inject_location LdrCustom requires setting -early_inject_address"); |
| SET_DEFAULT_VALUE(early_inject_location); |
| changed_options = true; |
| } |
| if ((DYNAMO_OPTION(follow_children) || DYNAMO_OPTION(follow_systemwide) || |
| DYNAMO_OPTION(follow_explicit_children)) && |
| get_os_version() >= WINDOWS_VERSION_VISTA && |
| !DYNAMO_OPTION(inject_at_create_process) && |
| !DYNAMO_OPTION(vista_inject_at_create_process)) { |
| /* We won't follow into child processes. Won't affect the current |
| * proccess so only a warning. */ |
| SYSLOG_INTERNAL_WARNING("Vista+ requires -vista_inject_at_create_process " |
| "to follow into child processes"); |
| } |
| #ifdef PROCESS_CONTROL |
| if (IS_PROCESS_CONTROL_ON() && !DYNAMO_OPTION(follow_systemwide)) { |
| /* Process control can happen even in slisted processes, so |
| * thin_client need not be true. To reliably control all processes, |
| * dr must exist in all of them, so follow_systemwide and runall must |
| * be true. |
| */ |
| USAGE_ERROR("-process_controls needs -follow_systemwide"); |
| dynamo_options.follow_systemwide = true; |
| changed_options = true; |
| |
| /* FIXME: assert that the global rununder registry key is set to |
| * rununder_all, but how? |
| */ |
| } |
| if (IS_PROCESS_CONTROL_ON() && DYNAMO_OPTION(pc_num_hashes) < 100) { |
| USAGE_ERROR("-pc_num_hashes must be at least 100 to minimize auto " |
| "shut off of process control"); |
| dynamo_options.pc_num_hashes = 100; |
| changed_options = true; |
| } |
| #endif /* PROCESS_CONTROL */ |
| if (DYNAMO_OPTION(thin_client)) { |
| /* Note: can't change all these options here because the recursion |
| * exceeds the limit, so leaving it to the user to fix it. |
| |
| * If thin_client is specified, it will override client, low, and all |
| * the options shown below. The check for client/low check will only |
| * fix those options that won't be fixed by the subsequent if, i.e., |
| * vm* options, that is why there is no else if |
| */ |
| /* Is there any option for high/server? */ |
| if (DYNAMO_OPTION(client) || DYNAMO_OPTION(low)) { |
| USAGE_ERROR("-thin_client won't work with -client or -low"); |
| dynamo_options.client = false; |
| dynamo_options.low = false; |
| dynamo_options.vm_size = 2*1024*1024; |
| dynamo_options.vm_base = 0; |
| dynamo_options.vm_max_offset = 0; |
| changed_options = true; |
| } |
| #ifdef HOT_PATCHING_INTERFACE |
| if (DYNAMO_OPTION(hot_patching) || DYNAMO_OPTION(hotp_only)) { |
| USAGE_ERROR("-thin_client doesn't support hot patching"); |
| dynamo_options.hot_patching = false; |
| dynamo_options.hotp_only = false; |
| changed_options = true; |
| } |
| #endif |
| #ifdef GBOP |
| if (DYNAMO_OPTION(gbop)) { |
| USAGE_ERROR("-thin_client doesn't support gbop"); |
| dynamo_options.gbop = 0; |
| changed_options = true; |
| } |
| #endif |
| #ifdef WINDOWS |
| if (DYNAMO_OPTION(aslr)) { |
| USAGE_ERROR("-thin_client doesn't support aslr "); |
| dynamo_options.aslr = 0; |
| changed_options = true; |
| } |
| #endif |
| if (!DYNAMO_OPTION(native_exec_syscalls)) { |
| USAGE_ERROR("-thin_client needs -native_exec_syscalls"); |
| dynamo_options.native_exec_syscalls = true; |
| changed_options = true; |
| } |
| #ifdef KSTATS |
| /* Same issue as hotp_only; case 6837. */ |
| if (DYNAMO_OPTION(kstats)) { |
| USAGE_ERROR("-thin_client doesn't support -kstats"); |
| dynamo_options.kstats = false; |
| changed_options = true; |
| } |
| #endif |
| |
| /* FIXME: not tested on vista where ldr_init_thunk is hooked first |
| * and has a different process creation mechanism; case 8576. |
| */ |
| } |
| # endif /* !NOT_DYNAMORIO_CORE */ |
| #endif /* WINDOWS */ |
| |
| #ifdef CLIENT_INTERFACE |
| if (!IS_INTERNAL_STRING_OPTION_EMPTY(client_lib) && |
| !(INTERNAL_OPTION(code_api) || INTERNAL_OPTION(probe_api) |
| IF_PROG_SHEP(|| DYNAMO_OPTION(security_api)))) { |
| USAGE_ERROR("-client_lib requires at least one API flag"); |
| } |
| #endif |
| |
| if (DYNAMO_OPTION(coarse_units)) { |
| #ifdef CUSTOM_EXIT_STUBS |
| USAGE_ERROR("-coarse_units incompatible with CUSTOM_EXIT_STUBS: disabling"); |
| dynamo_options.coarse_units = false; |
| changed_options = true; |
| #endif |
| #ifdef CLIENT_INTERFACE |
| if (DYNAMO_OPTION(bb_prefixes)) { |
| /* coarse_units doesn't support prefixes in general. |
| * the variation by addr prefix according to processor type |
| * is also not stored in pcaches. |
| */ |
| USAGE_ERROR("-coarse_units incompatible with -bb_prefixes: disabling"); |
| dynamo_options.coarse_units = false; |
| changed_options = true; |
| } |
| #endif |
| if (!DYNAMO_OPTION(shared_bbs)) { |
| USAGE_ERROR("-coarse_units requires -shared_bbs, enabling"); |
| dynamo_options.shared_bbs = true; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(inline_bb_ibl)) { |
| USAGE_ERROR("-coarse_units not compatible with -inline_bb_ibl, disabling"); |
| dynamo_options.inline_bb_ibl = false; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(bb_ibl_targets) && !DYNAMO_OPTION(disable_traces)) { |
| /* case 147/9636: NYI */ |
| USAGE_ERROR("-coarse_units not compatible with -bb_ibl_targets in " |
| "presence of traces, disabling"); |
| dynamo_options.bb_ibl_targets = false; |
| changed_options = true; |
| } |
| #ifdef EXPOSE_INTERNAL_OPTIONS |
| if (!DYNAMO_OPTION(indirect_stubs)) { |
| /* FIXME case 8827: wouldn't be hard to support, just need to ensure the |
| * shared use of the ibl fake stubs is properly separated in dispatch |
| */ |
| USAGE_ERROR("case 8827: -coarse_units requires -indirect_stubs, enabling"); |
| dynamo_options.indirect_stubs = true; |
| changed_options = true; |
| } |
| if (INTERNAL_OPTION(store_translations)) { |
| /* FIXME case 9707: NYI */ |
| USAGE_ERROR("case 9707: -coarse_units does not support -store_translations, " |
| "disabling"); |
| dynamo_options.store_translations = false; |
| changed_options = true; |
| } |
| #endif |
| if (DYNAMO_OPTION(IAT_elide)) { |
| /* FIXME case 9710: NYI */ |
| USAGE_ERROR("case 9710: -coarse_units does not support -IAT_elide, " |
| "disabling"); |
| dynamo_options.IAT_elide = false; |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(unsafe_freeze_elide_sole_ubr) && |
| !DYNAMO_OPTION(coarse_freeze_elide_ubr)) { |
| USAGE_ERROR("-unsafe_freeze_elide_sole_ubr requires " |
| "-coarse_freeze_elide_ubr, enabling"); |
| dynamo_options.coarse_freeze_elide_ubr = true; |
| changed_options = true; |
| } |
| # ifdef PROGRAM_SHEPHERDING |
| if (DYNAMO_OPTION(coarse_merge_iat) && |
| !DYNAMO_OPTION(executable_if_rx_text) |
| IF_WINDOWS(&& !DYNAMO_OPTION(executable_after_load))) { |
| /* case 8640: relies on -executable_{if_rx_text,after_load} */ |
| USAGE_ERROR("-coarse_merge_iat requires " |
| "-executable_{if_rx_text,after_load}; disabling"); |
| dynamo_options.coarse_merge_iat = false; |
| changed_options = true; |
| } |
| # endif |
| } |
| |
| if (!DYNAMO_OPTION(persist_per_user) && |
| (DYNAMO_OPTION(validate_owner_dir) || |
| DYNAMO_OPTION(validate_owner_file))) { |
| USAGE_ERROR("-no_persist_per_user is insecure\n" |
| "disabling validation, you are on your own!"); |
| dynamo_options.validate_owner_file = false; |
| dynamo_options.validate_owner_dir = false; |
| changed_options = true; |
| } |
| |
| #ifdef DGC_DIAGNOSTICS |
| if (INTERNAL_OPTION(mangle_app_seg)) { |
| /* i#107: -mangle_app_seg use a fragment flag FRAG_HAS_MOV_SEG |
| * that shares the same value with FRAG_DYNGEN_RESTRICTED used |
| * in DGC_DIAGNOSTICS, so they cannot be used together. |
| */ |
| USAGE_ERROR("-mangle_app_seg not compatible with DGC_DIAGNOSTICS; " |
| "disabling\n"); |
| dynamo_options.mangle_app_seg = false; |
| changed_options = true; |
| } |
| #endif |
| |
| #if defined(UNIX) && defined(CLIENT_INTERFACE) |
| if (INTERNAL_OPTION(private_loader)) { |
| if (!INTERNAL_OPTION(mangle_app_seg)) { |
| USAGE_ERROR("-private_loader requires -mangle_app_seg"); |
| dynamo_options.mangle_app_seg = true; |
| changed_options = true; |
| } |
| if (INTERNAL_OPTION(client_lib_tls_size) < 1) { |
| USAGE_ERROR("client_lib_tls_size is too small, set back to default"); |
| dynamo_options.client_lib_tls_size = 1; |
| changed_options = true; |
| } |
| # define MAX_NUM_LIB_TLS_PAGES 4 |
| if (INTERNAL_OPTION(client_lib_tls_size) > MAX_NUM_LIB_TLS_PAGES) { |
| USAGE_ERROR("client_lib_tls_size is too big, set to be maximum"); |
| dynamo_options.client_lib_tls_size = MAX_NUM_LIB_TLS_PAGES; |
| changed_options = true; |
| } |
| } |
| #endif |
| |
| if (DYNAMO_OPTION(native_exec_opt)) { |
| #ifdef WINDOWS |
| /* i#1238-c#1: we do not support inline optimization in Windows */ |
| USAGE_ERROR("-native_exec_opt is not supported in Windows"); |
| dynamo_options.native_exec_opt = false; |
| changed_options = true; |
| #endif |
| #ifdef KSTATS |
| /* i#1238-c#4: we do not support inline optimization with kstats */ |
| if (DYNAMO_OPTION(kstats)) { |
| USAGE_ERROR("-native_exec_opt does not support -kstats"); |
| dynamo_options.kstats = false; |
| changed_options = true; |
| } |
| #endif |
| if (!DYNAMO_OPTION(disable_traces)) { |
| USAGE_ERROR("-native_exec_opt does not support traces"); |
| DISABLE_TRACES((&dynamo_options)); |
| changed_options = true; |
| } |
| } |
| |
| #ifdef X64 |
| if (DYNAMO_OPTION(x86_to_x64)) { |
| /* i#1494: to avoid decode_fragment messing up the 32-bit/64-bit mode, |
| * we do not support any cases of using decode_fragment, including |
| * trace and coarse_units (coarse-grain code cache management). |
| */ |
| if (!DYNAMO_OPTION(disable_traces)) { |
| USAGE_ERROR("-x86_to_x64 does not support traces"); |
| DISABLE_TRACES((&dynamo_options)); |
| changed_options = true; |
| } |
| if (DYNAMO_OPTION(coarse_units)) { |
| USAGE_ERROR("-coarse_units incompatible with -x86_to_x64: disabling"); |
| DISABLE_COARSE_UNITS((&dynamo_options)); |
| changed_options = true; |
| } |
| } |
| #endif |
| |
| #ifndef NOT_DYNAMORIO_CORE |
| /* fcache param checks rather involved, leave them in fcache.c */ |
| /* case 7626: don't short-circuit checks, as later ones may be needed */ |
| changed_options = fcache_check_option_compatibility() || changed_options; |
| changed_options = heap_check_option_compatibility() || changed_options; |
| |
| changed_options = os_check_option_compatibility() || changed_options; |
| disassemble_options_init(); |
| #endif |
| |
| if (changed_options) { |
| if (recurse_count > 5) { |
| /* prevent infinite loop, should never recurse this many times */ |
| FATAL_USAGE_ERROR(OPTION_VERIFICATION_RECURSION, 2, get_application_name(), get_application_pid()); |
| } else { |
| check_option_compatibility_helper(recurse_count+1); |
| } |
| } |
| return !changed_options; |
| } |
| |
| /* returns true if changed any options */ |
| static bool |
| check_option_compatibility() |
| { |
| ASSERT_OWN_OPTIONS_LOCK(true, &options_lock); |
| ASSERT(!OPTIONS_PROTECTED()); |
| return check_option_compatibility_helper(0); |
| } |
| |
| /* returns true if changed any options */ |
| static bool |
| check_dynamic_option_compatibility() |
| { |
| ASSERT_OWN_OPTIONS_LOCK(true, &options_lock); |
| /* NOTE : use non-synch form of USAGE_ERROR in here to avoid |
| * infinite recursion */ |
| return false; /* nothing to check for yet */ |
| } |
| |
| /* initialize dynamo options */ |
| int |
| options_init() |
| { |
| int ret = 0, retval; |
| |
| /* .lspdata pages start out writable so no unprotect needed here */ |
| write_lock(&options_lock); |
| ASSERT(sizeof(dynamo_options) == sizeof(options_t)); |
| /* get dynamo options */ |
| retval = get_parameter(PARAM_STR(DYNAMORIO_VAR_OPTIONS), option_string, |
| sizeof(option_string)); |
| if (IS_GET_PARAMETER_SUCCESS(retval)) |
| ret = set_dynamo_options(&dynamo_options, option_string); |
| check_option_compatibility(); |
| /* options will be protected when DR init is completed */ |
| write_unlock(&options_lock); |
| return ret; |
| } |
| |
| /* clean up dynamo options state */ |
| void |
| options_exit() |
| { |
| DELETE_READWRITE_LOCK(options_lock); |
| } |
| |
| /* this function returns holding the options lock */ |
| void |
| options_make_writable() |
| { |
| ASSERT_DO_NOT_OWN_WRITE_LOCK(true, &options_lock); |
| write_lock(&options_lock); |
| SELF_UNPROTECT_OPTIONS(); |
| } |
| |
| /* assumes caller holds the options lock -- typically by calling |
| * options_make_writable() beforehand |
| */ |
| void |
| options_restore_readonly() |
| { |
| ASSERT_OWN_WRITE_LOCK(true, &options_lock); |
| SELF_PROTECT_OPTIONS(); |
| write_unlock(&options_lock); |
| } |
| |
| /* updates dynamic options and returns if any were changed */ |
| int |
| synchronize_dynamic_options() |
| { |
| int updated, retval; |
| bool compatibility_fixup = false; |
| |
| if (!dynamo_options.dynamic_options) |
| return 0; |
| |
| /* dynamic options */ |
| STATS_INC(option_synchronizations); |
| |
| /* make entire sequence atomic, esp since we're using a shared temp structure |
| * to save stack space. |
| * if we already have the lock, we must be in the middle of an update, |
| * so this becomes a nop. |
| */ |
| if (self_owns_write_lock(&options_lock) || |
| /* avoid hangs reporting errors or warnings by using a trylock (xref i#1198) */ |
| (!dynamo_initialized && options_lock.num_readers > 0)) { |
| STATS_INC(option_synchronizations_nop); |
| return 0; |
| } |
| |
| write_lock(&options_lock); |
| |
| /* check again now that we hold write lock in case was modified */ |
| if (!dynamo_options.dynamic_options) { |
| STATS_INC(option_synchronizations_nop); |
| write_unlock(&options_lock); |
| return 0; |
| } |
| |
| /* get options */ |
| retval = get_parameter_ex(PARAM_STR(DYNAMORIO_VAR_OPTIONS), new_option_string, |
| sizeof(new_option_string), true/*ignore cache*/); |
| if (IS_GET_PARAMETER_FAILURE(retval)) { |
| STATS_INC(option_synchronizations_nop); |
| write_unlock(&options_lock); |
| return 0; |
| } |
| |
| if (strcmp(option_string, new_option_string) == 0) { |
| STATS_INC(option_synchronizations_nop); |
| write_unlock(&options_lock); |
| return 0; |
| } |
| |
| SELF_UNPROTECT_OPTIONS(); |
| set_dynamo_options_defaults(&temp_options); |
| set_dynamo_options(&temp_options, new_option_string); |
| updated = update_dynamic_options(&dynamo_options, &temp_options); |
| compatibility_fixup = check_dynamic_option_compatibility(); |
| /* option_string holds a copy of the last read registry value */ |
| strncpy(option_string, new_option_string, BUFFER_SIZE_ELEMENTS(option_string)); |
| NULL_TERMINATE_BUFFER(option_string); |
| SELF_PROTECT_OPTIONS(); |
| |
| LOG(GLOBAL, LOG_ALL, 2 , "synchronize_dynamic_options: %s, updated = %d\n", new_option_string, updated); |
| |
| #ifdef EXPOSE_INTERNAL_OPTIONS |
| if (updated) { |
| get_dynamo_options_string(&dynamo_options, |
| new_option_string, sizeof(new_option_string), true); |
| SYSLOG_INTERNAL_NO_OPTION_SYNCH(SYSLOG_INFORMATION, |
| "Updated options = \"%s\"%s", |
| new_option_string, |
| compatibility_fixup ? |
| " after required compatibility fixups!" : ""); |
| write_unlock(&options_lock); |
| } else |
| #endif /* EXPOSE_INTERNAL_OPTIONS */ |
| write_unlock(&options_lock); |
| |
| return updated; |
| } |
| |
| # ifdef WINDOWS |
| /* Currently used to get child options to prevent aslr_dr for thin_client |
| * processes. Assumes other processes, though there is nothing wrong with |
| * using this to read the current process's options; we just guard against it |
| * because it is already done in other places like init and dynamic option |
| * update. |
| * Return value : Pointer to the global temp_options struct; so don't try to |
| * free it! |
| * Note : The CALLER IS RESPONSIBLE for unlocking the options_lock |
| * (write lock held) and shouldn't rely on the returned |
| * pointer after that. |
| */ |
| const options_t * |
| get_process_options(HANDLE process_handle) |
| { |
| uint err; |
| |
| /* Shouldn't be using this for the current process. */ |
| ASSERT(process_handle != NT_CURRENT_PROCESS && |
| process_handle != NT_CURRENT_THREAD && process_handle != NULL); |
| ASSERT(!READWRITE_LOCK_HELD(&options_lock)); |
| |
| write_lock(&options_lock); |
| SELF_UNPROTECT_OPTIONS(); |
| |
| /* Making an assumption that the core will be the same for the parent and |
| * child if set_dynamo_options_default is to work correctly. I think it is |
| * reasonable. FIXME: match parent & child cores & then use set default, |
| * what otherwise? |
| */ |
| set_dynamo_options_defaults(&temp_options); |
| err = get_process_parameter(process_handle, PARAM_STR(DYNAMORIO_VAR_OPTIONS), |
| new_option_string, sizeof(new_option_string)); |
| /* PR 330860: be sure not to set for this process */ |
| if (IS_GET_PARAMETER_SUCCESS(err)) |
| set_dynamo_options_other_process(&temp_options, new_option_string); |
| |
| /* FIXME: options_t compatibility check isn't done because that function |
| * operates directly on dynamo_options! As this is currently used only to |
| * detect if child is in thin_client we don't have to fix it because no |
| * option turns on thin_client and because fixing the compatibility checker |
| * so close to 4.2 release isn't a good idea. Case 9193 tracks this. |
| */ |
| |
| SELF_PROTECT_OPTIONS(); |
| |
| /* Note: we are delibrately not unlocking options_lock; caller will do it. |
| * This is done so as to not expose a lot of the options module |
| * functionality outside when having to access another process's options |
| * temporarily. */ |
| return &temp_options; |
| } |
| # endif /* WINDOWS */ |
| |
| # ifdef CLIENT_INTERFACE |
| /* i#771: Allow the client to query all DR runtime options. */ |
| DR_API |
| bool |
| dr_get_string_option(const char *option_name, char *buf OUT, size_t len) |
| { |
| bool found = false; |
| CLIENT_ASSERT(buf != NULL, "invalid parameter"); |
| string_option_read_lock(); |
| #define OPTION_COMMAND(type, name, default_value, command_line_option, \ |
| statement, description, flag, pcache) \ |
| if (IS_OPTION_STRING(name) && !found && \ |
| strcmp(option_name, #name) == 0) { \ |
| strncpy(buf, (const char*)&dynamo_options.name, len); \ |
| found = true; \ |
| } |
| #include "optionsx.h" |
| #undef OPTION_COMMAND |
| string_option_read_unlock(); |
| if (len > 0) |
| buf[len-1] = '\0'; |
| return found; |
| } |
| |
| DR_API |
| bool |
| dr_get_integer_option(const char *option_name, uint64 *val OUT) |
| { |
| bool found = false; |
| CLIENT_ASSERT(val != NULL, "invalid parameter"); |
| *val = 0; |
| /* gcc warns about casting strings to uint64 because uint64 isn't |
| * pointer-sized, so we cast to ptr_uint_t. We don't have any uint64 |
| * options in 32-bit, so this will never truncate. |
| * XXX: If we ever have signed integer options we'll need to sign extend |
| * here instead of zero extending. |
| */ |
| #define OPTION_COMMAND(type, name, default_value, command_line_option, \ |
| statement, description, flag, pcache) \ |
| if (!IS_OPTION_STRING(name) && !found && \ |
| strcmp(option_name, #name) == 0) { \ |
| *val = (ptr_uint_t)dynamo_options.name; \ |
| found = true; \ |
| } |
| #include "optionsx.h" |
| #undef OPTION_COMMAND |
| return found; |
| } |
| # endif /* CLIENT_INTERFACE */ |
| |
| #endif /* NOT_DYNAMORIO_CORE */ |
| |
| |
| #ifdef STANDALONE_UNIT_TEST |
| |
| static void |
| show_dynamo_options(bool minimal) |
| { |
| /* Printing all options requires a large buffer. This is test code, so we |
| * can still put this on the stack. |
| */ |
| char opstring[8*MAX_OPTIONS_STRING]; |
| |
| get_dynamo_options_string(&dynamo_options, opstring, |
| sizeof(opstring), minimal); |
| /* we exceed write_file's internal buffer size */ |
| os_write(STDERR, opstring, strlen(opstring)); |
| } |
| |
| /* USAGE Show descriptions of all available options */ |
| static void |
| show_dynamo_option_descriptions() |
| { |
| #define OPTION_COMMAND(type, name, default_value, \ |
| command_line_option, statement, \ |
| description, flag, pcache) \ |
| if (command_line_option[0] != ' ') { /* not synthethic */ \ |
| print_file(STDERR, "-%-20s %s\n", command_line_option, description); \ |
| } |
| #include "optionsx.h" |
| #undef OPTION_COMMAND |
| } |
| |
| void |
| unit_test_options(void) |
| { |
| char buf[MAX_OPTIONS_STRING]; |
| options_t new_options; |
| int updated; |
| |
| write_lock(&options_lock); /* simplicity: just grab whole time */ |
| SELF_UNPROTECT_OPTIONS(); |
| |
| /* FIXME: actually use asserts for automated testing that does not require |
| * visual inspection |
| */ |
| |
| /* FIXME: test invalid options -- w/o dying! */ |
| |
| print_file(STDERR, "default---\n"); |
| show_dynamo_options(false); |
| print_file(STDERR, "\nbefore first set---\n"); |
| set_dynamo_options(&dynamo_options, |
| "-loglevel 1 -logmask 0x10 -block_mod_load_list 'mylib.dll;evilbad.dll;really_long_name_for_a_dll.dll' -stderr_mask 12"); |
| show_dynamo_options(true); |
| |
| print_file(STDERR, "\nbefore second set---\n"); |
| set_dynamo_options(&dynamo_options, |
| "-logmask 17 -cache_bb_max 20 -cache_trace_max 20M -svchost_timeout 3m"); |
| show_dynamo_options(true); |
| |
| set_dynamo_options_defaults(&new_options); |
| set_dynamo_options(&new_options, |
| "-logmask 7 -cache_bb_max 20 -cache_trace_max 20M -svchost_timeout 3m"); |
| updated = update_dynamic_options(&dynamo_options, &new_options); |
| print_file(STDERR, "updated %d\n", updated); |
| show_dynamo_options(true); |
| |
| show_dynamo_option_descriptions(); |
| |
| get_dynamo_options_string(&dynamo_options, buf, MAXIMUM_PATH, 1); |
| print_file(STDERR, "options string: %s\n", buf); |
| |
| get_dynamo_options_string(&dynamo_options, buf, MAXIMUM_PATH, 0); |
| print_file(STDERR, "full options string: %s\n", buf); |
| |
| set_dynamo_options_defaults(&dynamo_options); |
| get_dynamo_options_string(&dynamo_options, buf, MAXIMUM_PATH, 1); |
| print_file(STDERR, "default ops string: %s\n", buf); |
| SELF_PROTECT_OPTIONS(); |
| write_unlock(&options_lock); |
| } |
| |
| #endif /* STANDALONE_UNIT_TEST */ |