blob: bfe27f96fc88abe46ed9316a7b799d70d71072a5 [file] [log] [blame]
/* **********************************************************
* 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 */