* Copyright (c) 2011-2022 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.
*/
* options.c
*
* Options definition and handling of corresponding command line options
*
*/
#include <stddef.h>
#ifndef NOT_DYNAMORIO_CORE
# include "globals.h"
# include "fcache.h"
# include "monitor.h"
# include "moduledb.h"
# include "disassemble.h"
#else
# include "configure.h"
# include <stdio.h>
# ifdef WINDOWS
# define inline __inline
# define snprintf _snprintf
# define WIN32_LEAN_AND_MEAN
# include <windows.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 ((void *)0)
# endif
struct stats_type {
int logmask;
int loglevel;
} thestats;
struct stats_type *d_r_stats = &thestats;
* may want to use some of these, NOTE build environments outside of the
* core can't handle VARARG macros! */
# define ASSERT(x)
* 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, ...)
{
}
# define FATAL_USAGE_ERROR(a, b, c, d)
# define USAGE_ERROR 1 ? (void)0 : ignore_varargs_function
# define DOLOG(a, b, c)
# include "options.h"
#endif
typedef enum option_type_t {
OPTION_TYPE_bool,
OPTION_TYPE_uint,
OPTION_TYPE_uint_addr,
OPTION_TYPE_uint_size,
OPTION_TYPE_uint_time,
OPTION_TYPE_pathstring_t,
OPTION_TYPE_liststring_t,
} option_type_t;
typedef enum option_modifier_t {
OPTION_MOD_STATIC,
OPTION_MOD_DYNAMIC
} option_modifier_t;
typedef ptr_uint_t uint_size;
typedef uint uint_time;
typedef ptr_uint_t uint_addr;
typedef struct _option_t {
const char *name;
uint offset;
uint size;
option_type_t type;
op_pcache_t affects_pcache;
option_modifier_t modifier;
} option_t;
#ifndef EXPOSE_INTERNAL_OPTIONS
# 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)
* no lock needed since never written
*/
const internal_options_t default_internal_options = {
# include "optionsx.h"
};
# undef OPTION_COMMAND_INTERNAL
# undef OPTION_COMMAND
#endif
* have them in an enum in the header.
*/
#undef OPTION_STRING
#undef EMPTY_STRING
#define OPTION_STRING(x) 0
#define EMPTY_STRING 0
#define liststring_t int
#define pathstring_t int
#define OPTION_COMMAND(type, name, default_value, command_line_option, statement, \
description, flag, pcache) \
const type OPTION_DEFAULT_VALUE_##name = default_value;
#define OPTION_COMMAND_INTERNAL(type, name, default_value, command_line_option, \
statement, description, flag, pcache) \
const type OPTION_DEFAULT_VALUE_##name = default_value;
#include "optionsx.h"
#undef liststring_t
#undef pathstring_t
#undef OPTION_COMMAND
#undef OPTION_COMMAND_INTERNAL
#undef OPTION_STRING
#undef EMPTY_STRING
#define OPTION_STRING(x) x
#define EMPTY_STRING \
{ \
0 \
}
#ifdef EXPOSE_INTERNAL_OPTIONS
# define OPTION_COMMAND_INTERNAL OPTION_COMMAND
#else
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)
#endif
#define OPTION_COMMAND(type, name, default_value, command_line_option, statement, \
description, flag, pcache) \
{ command_line_option, \
offsetof(options_t, name), \
sizeof(type), \
OPTION_TYPE_##type, \
pcache, \
OPTION_MOD_##flag },
static const option_t option_traits[] = {
#include "optionsx.h"
};
#undef OPTION_COMMAND
static const int num_options = sizeof(option_traits) / sizeof(option_t);
#define OPTION_COMMAND(type, name, default_value, command_line_option, statement, \
description, flag, pcache) \
default_value,
* 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)
* for brief windows), negative is reliable
*/
# define OPTIONS_PROTECTED() (DATASEC_PROTECTED(DATASEC_RARELY_PROT))
* canonical option string.
*/
char d_r_option_string[MAX_OPTIONS_STRING] = {
0,
};
# define ASSERT_OWN_OPTIONS_LOCK(b, l) ASSERT_OWN_WRITE_LOCK(b, l)
# define CORE_STATIC static
options_t dynamo_options = {
# include "optionsx.h"
};
* 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,
});
options_t temp_options;
* 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
# define ASSERT_OWN_OPTIONS_LOCK(b, l)
# define CORE_STATIC
#endif
#undef OPTION_COMMAND
static void
adjust_defaults_for_page_size(options_t *options)
{
#ifndef NOT_DYNAMORIO_CORE
uint page_size = (uint)PAGE_SIZE;
if (page_size == 4096)
return;
* future be many more options that depend on the page size.
*/
* pages by only having them for thread-shared allocations (i#4424).
* Since 99% of our allocs are in the vmm, there should still be enough guards
* sprinkled to be quite helpful, and we have separate stack guard pages.
*/
options->per_thread_guard_pages = false;
options->vmm_block_size = ALIGN_FORWARD(options->vmm_block_size, page_size);
options->stack_size = MAX(ALIGN_FORWARD(options->stack_size, page_size), page_size);
# ifdef UNIX
options->signal_stack_size =
MAX(ALIGN_FORWARD(options->signal_stack_size, page_size), page_size);
# endif
* we keep them as small as we can to avoid wasting space.
* We'd need sub-page allocs (i#4415) to go any smaller.
*/
options->initial_heap_unit_size =
MAX(ALIGN_FORWARD(options->initial_heap_unit_size, page_size), page_size);
options->initial_heap_nonpers_size =
MAX(ALIGN_FORWARD(options->initial_heap_nonpers_size, page_size), page_size);
options->initial_global_heap_unit_size = MAX(
ALIGN_FORWARD(options->initial_global_heap_unit_size, page_size), 8 * page_size);
options->max_heap_unit_size =
MAX(ALIGN_FORWARD(options->max_heap_unit_size, page_size), 64 * page_size);
options->heap_commit_increment =
ALIGN_FORWARD(options->heap_commit_increment, page_size);
* same for 32-bit as well: the shared cache these days should have large units.
*/
options->cache_shared_bb_unit_max =
MAX(ALIGN_FORWARD(options->cache_shared_bb_unit_max, page_size), 8 * page_size);
options->cache_shared_bb_unit_init =
MAX(ALIGN_FORWARD(options->cache_shared_bb_unit_init, page_size), 8 * page_size);
options->cache_shared_bb_unit_upgrade = MAX(
ALIGN_FORWARD(options->cache_shared_bb_unit_upgrade, page_size), 8 * page_size);
options->cache_shared_bb_unit_quadruple = MAX(
ALIGN_FORWARD(options->cache_shared_bb_unit_quadruple, page_size), 8 * page_size);
options->cache_shared_trace_unit_max = MAX(
ALIGN_FORWARD(options->cache_shared_trace_unit_max, page_size), 8 * page_size);
options->cache_shared_trace_unit_init = MAX(
ALIGN_FORWARD(options->cache_shared_trace_unit_init, page_size), 8 * page_size);
options->cache_shared_trace_unit_upgrade =
MAX(ALIGN_FORWARD(options->cache_shared_trace_unit_upgrade, page_size),
8 * page_size);
options->cache_shared_trace_unit_quadruple =
MAX(ALIGN_FORWARD(options->cache_shared_trace_unit_quadruple, page_size),
8 * page_size);
options->cache_bb_unit_max =
MAX(ALIGN_FORWARD(options->cache_bb_unit_max, page_size), page_size);
options->cache_bb_unit_init =
MAX(ALIGN_FORWARD(options->cache_bb_unit_init, page_size), page_size);
options->cache_bb_unit_upgrade =
MAX(ALIGN_FORWARD(options->cache_bb_unit_upgrade, page_size), page_size);
options->cache_bb_unit_quadruple =
MAX(ALIGN_FORWARD(options->cache_bb_unit_quadruple, page_size), page_size);
options->cache_trace_unit_max =
MAX(ALIGN_FORWARD(options->cache_trace_unit_max, page_size), page_size);
options->cache_trace_unit_init =
MAX(ALIGN_FORWARD(options->cache_trace_unit_init, page_size), page_size);
options->cache_trace_unit_upgrade =
MAX(ALIGN_FORWARD(options->cache_trace_unit_upgrade, page_size), page_size);
options->cache_trace_unit_quadruple =
MAX(ALIGN_FORWARD(options->cache_trace_unit_quadruple, page_size), page_size);
options->cache_commit_increment =
ALIGN_FORWARD(options->cache_commit_increment, page_size);
#endif
}
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;
adjust_defaults_for_page_size(options);
}
#undef OPTION_COMMAND_INTERNAL
* 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)
#endif
* 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 )
{
uint i = 0;
const char *pos = *strpos;
char quote = '\0';
if (pos < str || pos >= str + strlen(str))
return NULL;
if (*pos == '\0')
return NULL;
while (*pos == ' ' || *pos == '\t' || *pos == '\n' || *pos == '\r') {
pos++;
}
if (*pos == '\'' || *pos == '\"' || *pos == '`') {
quote = *pos;
pos++;
}
while (*pos) {
if (quote != '\0') {
if (*pos == quote) {
pos++;
break;
}
} else {
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"));
}
break;
}
}
if (i == 0 && quote == '\0' )
return NULL;
ASSERT(i < wordbuflen);
wordbuf[i] = '\0';
*strpos = pos;
return wordbuf;
}
static char *
getword(const char *str, const char **strpos, char *wordbuf, uint wordbuflen)
{
return getword_common(str, strpos, wordbuf, wordbuflen, false );
}
char *
d_r_parse_word(const char *str, const char **strpos, char *wordbuf, uint wordbuflen)
{
return getword_common(str, strpos, wordbuf, wordbuflen, true );
}
#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)) {
*var = num;
} else {
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(ptr_uint_t *var, void *value)
{
char unit;
ptr_int_t num;
ptr_int_t factor = 0;
if (sscanf((char *)value, SZFMT "%c", &num, &unit) == 1) {
factor = 1024;
} else {
switch (unit) {
case 'B':
case 'b': factor = 1; break;
case 'K':
case 'k': factor = 1024; break;
case 'M':
case 'm': factor = 1024 * 1024; break;
case 'G':
case 'g': factor = 1024ULL * 1024 * 1024; break;
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) {
factor = 1;
} else {
switch (unit) {
case 's': factor = 1000; break;
case 'm': factor = 1000 * 60; break;
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) {
*var = num;
} else {
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"));
}
(*var)[MAX_PATH_OPTION_LENGTH - 1] = '\0';
}
static void
parse_liststring_t(liststring_t *var, void *value)
{
* 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) {
* (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"));
}
(*var)[MAX_LIST_OPTION_LENGTH - 1] = '\0';
}
static void
parse_by_type(enum option_type_t type, void *ptr1, void *ptr2)
{
switch (type) {
case OPTION_TYPE_bool: parse_bool(ptr1, ptr2); break;
case OPTION_TYPE_uint: parse_uint(ptr1, ptr2); break;
case OPTION_TYPE_uint_size: parse_uint_size(ptr1, ptr2); break;
case OPTION_TYPE_uint_time: parse_uint_time(ptr1, ptr2); break;
case OPTION_TYPE_uint_addr: parse_uint_addr(ptr1, ptr2); break;
case OPTION_TYPE_pathstring_t: parse_pathstring_t(ptr1, ptr2); break;
case OPTION_TYPE_liststring_t: parse_liststring_t(ptr1, ptr2); break;
}
}
* loop where this function is used, this function is not copied over there
* mutliple times. Copying over this code increases code size significantly
* especially since strcmp() is declared either as macro or as inline.
*/
static NOINLINE void
set_bool_opt(const char *opt, const char *command_line_option, bool *value_true,
bool *value_false, void **value)
{
if (strcmp(opt + 1, command_line_option) == 0) {
*value = value_true;
} else if (strncmp(opt + 1, "no_", 3) == 0 &&
strcmp(opt + 4, command_line_option) == 0) {
*value = value_false;
}
}
static NOINLINE void
set_nonbool_opt(const char *opt, const char *command_line_option, const char *optstr,
const char **pos, char *wordbuffer, int max_option_length, void **value)
{
if (strcmp(opt + 1, command_line_option) == 0) {
*value = getword(optstr, pos, wordbuffer, max_option_length);
}
}
static NOINLINE void
run_option_command(int index, options_t *options, bool for_this_process)
{
int j = 0;
#define OPTION_COMMAND(type, name, default_value, command_line_option, statement, \
description, flag, pcache) \
if (index == j) { \
statement; \
} \
++j;
#include "optionsx.h"
#undef OPTION_COMMAND
}
static int
set_dynamo_options_common(options_t *options, const char *optstr, bool for_this_process)
{
char *opt;
const char *pos = optstr;
bool got_badopt = false;
char badopt[MAX_OPTION_LENGTH];
char wordbuffer[MAX_OPTION_LENGTH];
* 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());
while ((opt = getword(optstr, &pos, wordbuffer, sizeof(wordbuffer))) != NULL) {
if (opt[0] == '-') {
value = NULL;
int i = 0;
for (i = 0; i < num_options; ++i) {
if (option_traits[i].type == OPTION_TYPE_bool) {
set_bool_opt(opt, option_traits[i].name, &value_true, &value_false,
&value);
} else {
set_nonbool_opt(opt, option_traits[i].name, optstr, &pos, wordbuffer,
sizeof(wordbuffer), &value);
}
if (value != NULL) {
void *optptr = (char *)(options) + option_traits[i].offset;
parse_by_type(option_traits[i].type, optptr, value);
run_option_command(i, options, for_this_process);
break;
}
}
if (value != NULL) {
continue;
}
}
if (!got_badopt) {
snprintf(badopt, BUFFER_SIZE_ELEMENTS(badopt), "%s", opt);
NULL_TERMINATE_BUFFER(badopt);
}
got_badopt = true;
}
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;
}
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
* by assigning min to make it valid returns true if changed the
* option value
*/
bool
check_param_bounds(ptr_uint_t *val, ptr_uint_t min, ptr_uint_t max, const char *name)
{
bool ret = false;
ptr_uint_t 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 >= " SZFMT ", resetting from " SZFMT " to " SZFMT,
name, min, *val, new_val);
} else {
new_val = max;
USAGE_ERROR("%s must be >= " SZFMT " and <= " SZFMT ", resetting from " SZFMT
" to " SZFMT,
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: " SZFMT " KB\n", name, *val / 1024);
}
});
return ret;
}
* 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, const void *val_ptr, const char *option)
{
bool value = *(const bool *)val_ptr;
snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s%s ", value ? "" : "no_", option);
}
static void
PRINT_STRING_uint(char *optionbuff, const void *val_ptr, const char *option)
{
* loglevel etc in decimal */
uint value = *(const uint *)val_ptr;
snprintf(optionbuff, MAX_OPTION_LENGTH, (value > 0x100 ? "-%s 0x%x " : "-%s %u "),
option, value);
}
static void
PRINT_STRING_uint_size(char *optionbuff, const void *val_ptr, const char *option)
{
ptr_uint_t value = *(const ptr_uint_t *)val_ptr;
char code = 'B';
if (value >= 1024ULL * 1024 * 1024 && value % 1024ULL * 1024 * 1024 == 0) {
value = value / (1024ULL * 1024 * 1024);
code = 'G';
} else if (value >= 1024 * 1024 && value % 1024 * 1024 == 0) {
value = value / (1024 * 1024);
code = 'M';
} else if (value >= 1024 && value % 1024 == 0) {
value = value / 1024;
code = 'K';
}
snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s " SZFMT "%c ", option, value, code);
}
static void
PRINT_STRING_uint_time(char *optionbuff, const void *val_ptr, const char *option)
{
uint value = *(const uint *)val_ptr;
snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s %d ", option, value);
}
static void
PRINT_STRING_uint_addr(char *optionbuff, const void *val_ptr, const char *option)
{
ptr_uint_t value = *(const ptr_uint_t *)val_ptr;
snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s " PIFX " ", option, value);
}
static void
PRINT_STRING_pathstring_t(char *optionbuff, const void *val_ptr, const char *option)
{
snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s '%s' ", option,
(*(const pathstring_t *)val_ptr));
}
static void
PRINT_STRING_liststring_t(char *optionbuff, const void *val_ptr, const char *option)
{
snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s '%s' ", option,
(*(const liststring_t *)val_ptr));
}
static void
print_option_type(enum option_type_t type, char *optionbuff, const void *val_ptr,
const char *option)
{
switch (type) {
case OPTION_TYPE_bool: PRINT_STRING_bool(optionbuff, val_ptr, option); break;
case OPTION_TYPE_uint: PRINT_STRING_uint(optionbuff, val_ptr, option); break;
case OPTION_TYPE_uint_size:
PRINT_STRING_uint_size(optionbuff, val_ptr, option);
break;
case OPTION_TYPE_uint_time:
PRINT_STRING_uint_time(optionbuff, val_ptr, option);
break;
case OPTION_TYPE_uint_addr:
PRINT_STRING_uint_addr(optionbuff, val_ptr, option);
break;
case OPTION_TYPE_pathstring_t:
PRINT_STRING_pathstring_t(optionbuff, val_ptr, option);
break;
case OPTION_TYPE_liststring_t:
PRINT_STRING_liststring_t(optionbuff, val_ptr, option);
break;
}
}
static int
DIFF_bool(const void *ptr1, const void *ptr2)
{
bool val1 = *(const bool *)(ptr1);
bool val2 = *(const bool *)(ptr2);
return val1 != val2;
}
static int
DIFF_uint(const void *ptr1, const void *ptr2)
{
uint val1 = *(const uint *)(ptr1);
uint val2 = *(const uint *)(ptr2);
return val1 != val2;
}
static int
DIFF_uint_size(const void *ptr1, const void *ptr2)
{
ptr_uint_t val1 = *(const ptr_uint_t *)(ptr1);
ptr_uint_t val2 = *(const ptr_uint_t *)(ptr2);
return val1 != val2;
}
static int
DIFF_uint_time(const void *ptr1, const void *ptr2)
{
return DIFF_uint(ptr1, ptr2);
}
static int
DIFF_uint_addr(const void *ptr1, const void *ptr2)
{
return DIFF_uint_size(ptr1, ptr2);
}
static int
DIFF_pathstring_t(const void *ptr1, const void *ptr2)
{
const char *val1 = (const char *)ptr1;
const char *val2 = (const char *)ptr2;
return strcmp(val1, val2);
}
static int
DIFF_liststring_t(const void *ptr1, const void *ptr2)
{
const char *val1 = (const char *)ptr1;
const char *val2 = (const char *)ptr2;
return strcmp(val1, val2);
}
static int
diff_by_type(enum option_type_t type, const void *ptr1, const void *ptr2)
{
switch (type) {
case OPTION_TYPE_bool: return DIFF_bool(ptr1, ptr2);
case OPTION_TYPE_uint: return DIFF_uint(ptr1, ptr2);
case OPTION_TYPE_uint_size: return DIFF_uint_size(ptr1, ptr2);
case OPTION_TYPE_uint_time: return DIFF_uint_time(ptr1, ptr2);
case OPTION_TYPE_uint_addr: return DIFF_uint_addr(ptr1, ptr2);
case OPTION_TYPE_pathstring_t: return DIFF_pathstring_t(ptr1, ptr2);
case OPTION_TYPE_liststring_t: return DIFF_liststring_t(ptr1, ptr2);
}
return 0;
}
static void
COPY_bool(void *ptr1, const void *ptr2)
{
*(bool *)(ptr1) = *(const bool *)(ptr2);
}
static void
COPY_uint(void *ptr1, const void *ptr2)
{
*(uint *)(ptr1) = *(const uint *)(ptr2);
}
static void
COPY_uint_size(void *ptr1, const void *ptr2)
{
*(ptr_uint_t *)(ptr1) = *(const ptr_uint_t *)(ptr2);
}
static void
COPY_uint_time(void *ptr1, const void *ptr2)
{
COPY_uint(ptr1, ptr2);
}
static void
COPY_uint_addr(void *ptr1, const void *ptr2)
{
COPY_uint_size(ptr1, ptr2);
}
static void
COPY_pathstring_t(void *ptr1, const void *ptr2)
{
char *val1 = (char *)ptr1;
const char *val2 = (const char *)ptr2;
strncpy(val1, val2, sizeof(pathstring_t));
}
static void
COPY_liststring_t(void *ptr1, const void *ptr2)
{
char *val1 = (char *)ptr1;
const char *val2 = (const char *)ptr2;
strncpy(val1, val2, sizeof(liststring_t));
}
static void
copy_by_type(enum option_type_t type, void *ptr1, const void *ptr2)
{
switch (type) {
case OPTION_TYPE_bool: COPY_bool(ptr1, ptr2); break;
case OPTION_TYPE_uint: COPY_uint(ptr1, ptr2); break;
case OPTION_TYPE_uint_size: COPY_uint_size(ptr1, ptr2); break;
case OPTION_TYPE_uint_time: COPY_uint_time(ptr1, ptr2); break;
case OPTION_TYPE_uint_addr: COPY_uint_addr(ptr1, ptr2); break;
case OPTION_TYPE_pathstring_t: COPY_pathstring_t(ptr1, ptr2); break;
case OPTION_TYPE_liststring_t: COPY_liststring_t(ptr1, ptr2); break;
}
}
void
get_dynamo_options_string(options_t *options, char *opstr, int len, bool minimal)
{
char optionbuff[MAX_OPTION_LENGTH];
opstr[0] = 0;
int i;
for (i = 0; i < num_options; ++i) {
if (option_traits[i].name[0] != ' ') {
const void *val1 = (char *)(options) + option_traits[i].offset;
const void *val2 = (char *)(&default_options) + option_traits[i].offset;
if (!minimal || diff_by_type(option_traits[i].type, val1, val2)) {
print_option_type(option_traits[i].type, optionbuff, val1,
option_traits[i].name);
NULL_TERMINATE_BUFFER(optionbuff);
strncat(opstr, optionbuff, len - strlen(opstr) - 1);
}
}
}
opstr[len - 1] = 0;
}
* 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;
int i;
for (i = 0; i < num_options; ++i) {
if (option_traits[i].affects_pcache >= pcache_effect &&
option_traits[i].name[0] != ' ') {
const void *val1 = (char *)(options) + option_traits[i].offset;
const void *val2 = (char *)(&default_options) + option_traits[i].offset;
if (diff_by_type(option_traits[i].type, val1, val2)) {
print_option_type(option_traits[i].type, optionbuff, val1,
option_traits[i].name);
NULL_TERMINATE_BUFFER(optionbuff);
strncat(opstr, optionbuff, len - strlen(opstr) - 1);
}
}
}
opstr[len - 1] = 0;
}
* 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)
{
int i;
for (i = 0; i < num_options; ++i) {
if (option_traits[i].affects_pcache == pcache_effect) {
const void *val1 = (char *)(options) + option_traits[i].offset;
const void *val2 = (char *)(&default_options) + option_traits[i].offset;
if (diff_by_type(option_traits[i].type, val1, val2)) {
return true;
}
}
}
return false;
}
#if defined(DEBUG) && defined(INTERNAL)
* accesses are protected by the options_lock. */
static char optionbuff[MAX_OPTION_LENGTH];
static char new_optionbuff[MAX_OPTION_LENGTH];
#endif
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());
int i;
for (i = 0; i < num_options; ++i) {
void *val1 = (char *)(options) + option_traits[i].offset;
const void *val2 = (char *)(new_options) + option_traits[i].offset;
if (OPTION_MOD_DYNAMIC == option_traits[i].modifier) {
if (diff_by_type(option_traits[i].type, val1, val2)) {
copy_by_type(option_traits[i].type, val1, val2);
updated++;
}
} else {
DOLOG(2, LOG_TOP, {
if (diff_by_type(option_traits[i].type, val1, val2)) {
print_option_type(option_traits[i].type, optionbuff, val1,
option_traits[i].name);
NULL_TERMINATE_BUFFER(optionbuff);
print_option_type(option_traits[i].type, new_optionbuff, val2,
option_traits[i].name);
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);
}
});
}
}
return updated;
}
void
options_enable_code_api_dependences(options_t *options)
{
if (!options->code_api)
return;
* buffer on the stack when saving fp state.
* Also, C++ RTL initialization (even when a C++
* client does little else) can take a lot of stack space.
* Furthermore, dbghelp.dll usage via drsyms has been observed
* to require 36KB, which is already beyond the minimum to
* share gencode in the same 64K alloc as the stack.
*
* XXX: if we raise this beyond 56KB we should adjust the
* logic in heap_mmap_reserve_post_stack() to handle sharing the
* tail end of a multi-64K-region stack.
*/
#ifndef NOT_DYNAMORIO_CORE
options->stack_size = MAX(options->stack_size, ALIGN_FORWARD(56 * 1024, PAGE_SIZE));
#endif
#ifdef UNIX
* don't need as much space when handling signals. We still raise the
* limit a little while saving some per-thread space.
*/
options->signal_stack_size =
MAX(options->signal_stack_size, ALIGN_FORWARD(32 * 1024, PAGE_SIZE));
#endif
* expect most CI users will prefer a view of the
* instruction stream that's as unmodified as possible.
* Also xref PR 214169: eliding calls presents a confusing
* view of basic blocks since clients see both the call
* and the called function in the same block. TODO PR
* 214169: pass both sides to the client and merge
* internally to get the best of both worlds.
*/
options->max_elide_jmp = 0;
options->max_elide_call = 0;
* so disable by default (xref PR 214051 & PR 214169).
* Even if we address those issues, we may want to keep
* disabled if we expect users will be confused by this
* optimization.
*/
options->indcall2direct = false;
* be able to swap ignored for non-ignored (xref PR 307284)
*/
options->inline_ignored_syscalls = false;
* perf issues, so we empty the default native exec list when using
* -code_api. The user can override this behavior by passing their
* own -native_exec_list.
* However the .pexe section thing on Vista is too dangerous so we
* leave that on. */
memset(options->native_exec_default_list, 0,
sizeof(options->native_exec_default_list));
options->native_exec_managed_code = false;
IF_WINDOWS(options->aslr_dr = false;)
}
#ifndef NOT_DYNAMORIO_CORE
* 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);
* 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
*/
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;
}
# 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)
static bool
check_option_compatibility_helper(int recurse_count)
{
bool changed_options = false;
# ifdef AARCH64
if (!DYNAMO_OPTION(bb_prefixes)) {
USAGE_ERROR("bb_prefixes must be true on AArch64");
dynamo_options.bb_prefixes = true;
changed_options = true;
}
# endif
# ifdef EXPOSE_INTERNAL_OPTIONS
if (DYNAMO_OPTION(vmm_block_size) < MIN_VMM_BLOCK_SIZE) {
USAGE_ERROR("vmm_block_size (%d) must be >= %d, setting to min",
DYNAMO_OPTION(vmm_block_size), MIN_VMM_BLOCK_SIZE);
dynamo_options.vmm_block_size = MIN_VMM_BLOCK_SIZE;
changed_options = true;
}
if (!INTERNAL_OPTION(inline_calls) && !DYNAMO_OPTION(disable_traces)) {
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);
* max
*/
* 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))
*/
* 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
if (!ALIGNED(DYNAMO_OPTION(stack_size), PAGE_SIZE)) {
USAGE_ERROR("-stack_size must be at least 12K and a multiple of the page size");
SET_DEFAULT_VALUE(stack_size);
changed_options = true;
}
# if defined(TRACE_HEAD_CACHE_INCR)
if (DYNAMO_OPTION(pad_jmps)) {
USAGE_ERROR("-pad_jmps not supported in this build yet");
}
# endif
* warn of unfinished and untested self-protection options
* FIXME: update once these features are complete
*/
if (
# ifdef WINDOWS
TEST(SELFPROT_CACHE, dynamo_options.protect_mask) ||
# endif
TEST(SELFPROT_LOCAL, dynamo_options.protect_mask) ||
TEST(SELFPROT_DCONTEXT, dynamo_options.protect_mask)) {
ASSERT_NOT_TESTED();
}
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");
}
* than by turning off protection? Should we halt instead?
*/
if (TEST(SELFPROT_DCONTEXT, dynamo_options.protect_mask) &&
SHARED_FRAGMENTS_ENABLED()) {
USAGE_ERROR("Shared cache does not support protecting dcontext yet");
dynamo_options.protect_mask &= ~SELFPROT_DCONTEXT;
changed_options = true;
}
# if defined(MACOS) && defined(AARCH64)
if (TEST(SELFPROT_GENCODE, dynamo_options.protect_mask)) {
USAGE_ERROR("memory protection changes incompatible with MAP_JIT");
dynamo_options.protect_mask &= ~SELFPROT_GENCODE;
changed_options = true;
}
# endif
# 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
* 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 support 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;
}
if (PRIVATE_TRACES_ENABLED() && DYNAMO_OPTION(shared_bbs)) {
* private traces and shared bbs if we allow clients to make custom
* traces (which is always enabled).
*/
USAGE_ERROR("private traces incompatible with shared bbs");
dynamo_options.shared_bbs = false;
changed_options = true;
}
# 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;
}
# if defined(X64) && !(defined(MACOS) && defined(AARCH64))
# error X64 requires HAVE_TLS
# endif
# endif
# if !defined(HAVE_MEMINFO) && defined(PROGRAM_SHEPHERDING)
if (DYNAMO_OPTION(code_origins)) {
USAGE_ERROR("-code_origins not supported on this OS");
dynamo_options.code_origins = false;
changed_options = true;
}
# endif
# endif
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
* -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
# ifdef X64
if (DYNAMO_OPTION(heap_in_lower_4GB) && !DYNAMO_OPTION(reachable_heap)) {
USAGE_ERROR("-heap_in_lower_4GB requires -reachable_heap: "
"enabling.");
dynamo_options.reachable_heap = true;
changed_options = true;
}
# endif
if (RUNNING_WITHOUT_CODE_CACHE() && DYNAMO_OPTION(enable_reset)) {
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
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
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;
}
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;
}
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;
}
* 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;
}
* 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");
* 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)) {
* 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 ARM
USAGE_ERROR("ARM requires -indirect_stubs, enabling");
dynamo_options.indirect_stubs = true;
changed_options = true;
# endif
# 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 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;
}
# if defined(X64) || defined(ARM)
if (!DYNAMO_OPTION(private_ib_in_tls)) {
USAGE_ERROR("-private_ib_in_tls is required for x64 and ARM");
dynamo_options.private_ib_in_tls = true;
changed_options = true;
}
# endif
# 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)) {
USAGE_ERROR("-shared_fragment_shared_syscalls is required for x64");
dynamo_options.shared_fragment_shared_syscalls = 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
* 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;
}
* fragments: case 8027. */
* associated processing into an OPTION_COMMAND (but OPTION_COMMAND has
* its own imperfections).
*/
if (DYNAMO_OPTION(shared_fragment_shared_syscalls) &&
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;
}
* 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)) {
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) {
* 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
if (DYNAMO_OPTION(stack_guard_pages)) {
USAGE_ERROR("-stack_guard_pages is not supported on Windows");
dynamo_options.stack_guard_pages = false;
changed_options = true;
}
# ifdef PROGRAM_SHEPHERDING
if (DYNAMO_OPTION(IAT_convert) && !DYNAMO_OPTION(emulate_IAT_writes)) {
USAGE_ERROR("-IAT_convert requires -emulate_IAT_writes, enabling");
dynamo_options.emulate_IAT_writes = true;
changed_options = true;
}
# else
if (DYNAMO_OPTION(IAT_convert)) {
USAGE_ERROR("-IAT_convert requires unavailable -emulate_IAT_writes, "
"disabling IAT_convert");
dynamo_options.IAT_convert = false;
changed_options = true;
}
# endif
# endif
# ifdef X64
if (DYNAMO_OPTION(satisfy_w_xor_x) &&
(DYNAMO_OPTION(coarse_enable_freeze) || DYNAMO_OPTION(use_persisted))) {
USAGE_ERROR("-satisfy_w_xor_x does not support persistent caches");
dynamo_options.satisfy_w_xor_x = false;
changed_options = true;
}
# else
if (DYNAMO_OPTION(satisfy_w_xor_x)) {
USAGE_ERROR("-satisfy_w_xor_x is not supported on 32-bit");
dynamo_options.satisfy_w_xor_x = false;
changed_options = true;
}
# endif
# ifdef WINDOWS
if (DYNAMO_OPTION(satisfy_w_xor_x)) {
USAGE_ERROR("-satisfy_w_xor_x is not supported on Windows");
dynamo_options.satisfy_w_xor_x = false;
changed_options = true;
}
# endif
# ifdef WINDOWS
* 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;
}
* 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;
}
if (INTERNAL_OPTION(private_peb) && !INTERNAL_OPTION(private_loader)) {
USAGE_ERROR("-private_peb requires -private_loader");
dynamo_options.private_peb = false;
changed_options = true;
}
# 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
* 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;
}
* 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
if (DYNAMO_OPTION(hotp_only) && DYNAMO_OPTION(kstats)) {
USAGE_ERROR("-hotp_only doesn't support -kstats");
dynamo_options.kstats = false;
changed_options = true;
}
# endif
* 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
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
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
# ifdef UNIX
if (DYNAMO_OPTION(max_pending_signals) < 1) {
USAGE_ERROR("-max_pending_signals must be at least 1");
dynamo_options.max_pending_signals = 1;
changed_options = true;
}
# endif
# 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");
SET_DEFAULT_VALUE(prof_pcs_stubs);
changed_options = true;
}
# endif
# ifdef UNIX
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
# 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
if (DYNAMO_OPTION(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))) {
* child processes */
if (!dr_early_injected ||
dr_early_injected_location != INJECT_LOCATION_LdrpLoadImportModule) {
* -native_exec_syscalls */
if (!DYNAMO_OPTION(native_exec_syscalls)) {
USAGE_ERROR("early_inject_location LdrpLoadImportModule requires "
"-native_exec_syscalls for first process in chain");
dynamo_options.native_exec_syscalls = true;
changed_options = true;
* 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)) {
* 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)) {
* 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;
* 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
if (DYNAMO_OPTION(thin_client)) {
* 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
*/
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
if (DYNAMO_OPTION(kstats)) {
USAGE_ERROR("-thin_client doesn't support -kstats");
dynamo_options.kstats = false;
changed_options = true;
}
# endif
* and has a different process creation mechanism; case 8576.
*/
}
# endif
# endif
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");
}
if (DYNAMO_OPTION(coarse_units)) {
if (DYNAMO_OPTION(bb_prefixes)) {
* 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;
}
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)) {
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)) {
* 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)) {
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)) {
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))) {
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)) {
* 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
# ifdef UNIX
# if (defined(ARM) || defined(LINUX)) && !defined(STATIC_LIBRARY)
if (!INTERNAL_OPTION(private_loader)) {
* the TLS format match what gdb wants to see.
* On Linux, we just don't want the libdl.so dependence for -early.
*/
if (IF_ARM_ELSE(true, DYNAMO_OPTION(early_inject))) {
USAGE_ERROR("-private_loader must be true on ARM or on Linux");
dynamo_options.private_loader = true;
changed_options = true;
}
}
# endif
if (INTERNAL_OPTION(private_loader)) {
# ifdef X86
if (!INTERNAL_OPTION(mangle_app_seg)) {
USAGE_ERROR("-private_loader requires -mangle_app_seg");
dynamo_options.mangle_app_seg = true;
changed_options = true;
}
# endif
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
USAGE_ERROR("-native_exec_opt is not supported in Windows");
dynamo_options.native_exec_opt = false;
changed_options = true;
# endif
# ifdef 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)) {
* 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
# ifdef ARM
if (DYNAMO_OPTION(steal_reg) < 8 ||
DYNAMO_OPTION(steal_reg) > IF_X64_ELSE(29, 12) ) {
USAGE_ERROR("-steal_reg only supports register between r8 and r12(A32)/r29(A64)");
dynamo_options.steal_reg = IF_X64_ELSE(28 , 10 );
changed_options = true;
}
# endif
# ifdef DEBUG
if (INTERNAL_OPTION(log_at_fragment_count) > 0 && d_r_stats->loglevel > 1) {
if (dynamo_options.stats_loglevel <= 1)
USAGE_ERROR("-log_at_fragment_count expects >1 delayed loglevel");
d_r_stats->loglevel = 1;
changed_options = true;
}
# endif
# ifndef NOT_DYNAMORIO_CORE
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) {
FATAL_USAGE_ERROR(OPTION_VERIFICATION_RECURSION, 2, get_application_name(),
get_application_pid());
} else {
check_option_compatibility_helper(recurse_count + 1);
}
}
return !changed_options;
}
static bool
check_option_compatibility()
{
ASSERT_OWN_OPTIONS_LOCK(true, &options_lock);
ASSERT(!OPTIONS_PROTECTED());
return check_option_compatibility_helper(0);
}
static bool
check_dynamic_option_compatibility()
{
ASSERT_OWN_OPTIONS_LOCK(true, &options_lock);
* infinite recursion */
return false;
}
int
options_init()
{
int ret = 0, retval;
d_r_write_lock(&options_lock);
ASSERT(sizeof(dynamo_options) == sizeof(options_t));
adjust_defaults_for_page_size(&dynamo_options);
retval = d_r_get_parameter(PARAM_STR(DYNAMORIO_VAR_OPTIONS), d_r_option_string,
sizeof(d_r_option_string));
if (IS_GET_PARAMETER_SUCCESS(retval))
ret = set_dynamo_options(&dynamo_options, d_r_option_string);
# ifdef STATIC_LIBRARY
* DR runtime options -- unless otherwise requested.
*/
options_enable_code_api_dependences(&dynamo_options);
# endif
check_option_compatibility();
d_r_write_unlock(&options_lock);
return ret;
}
* as those are used in other exit routines called later. We have a separate
* options_detach() for that.
*/
void
options_exit()
{
DELETE_READWRITE_LOCK(options_lock);
}
void
options_detach()
{
SELF_UNPROTECT_OPTIONS();
dynamo_options = default_options;
}
void
options_make_writable()
{
ASSERT_DO_NOT_OWN_WRITE_LOCK(true, &options_lock);
d_r_write_lock(&options_lock);
SELF_UNPROTECT_OPTIONS();
}
* options_make_writable() beforehand
*/
void
options_restore_readonly()
{
ASSERT_OWN_WRITE_LOCK(true, &options_lock);
SELF_PROTECT_OPTIONS();
d_r_write_unlock(&options_lock);
}
int
synchronize_dynamic_options()
{
int updated, retval;
if (!dynamo_options.dynamic_options)
return 0;
STATS_INC(option_synchronizations);
* 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) ||
(!dynamo_initialized && options_lock.num_readers > 0)) {
STATS_INC(option_synchronizations_nop);
return 0;
}
d_r_write_lock(&options_lock);
if (!dynamo_options.dynamic_options) {
STATS_INC(option_synchronizations_nop);
d_r_write_unlock(&options_lock);
return 0;
}
retval = get_parameter_ex(PARAM_STR(DYNAMORIO_VAR_OPTIONS), new_option_string,
sizeof(new_option_string), true );
if (IS_GET_PARAMETER_FAILURE(retval)) {
STATS_INC(option_synchronizations_nop);
d_r_write_unlock(&options_lock);
return 0;
}
if (strcmp(d_r_option_string, new_option_string) == 0) {
STATS_INC(option_synchronizations_nop);
d_r_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);
# if defined(EXPOSE_INTERNAL_OPTIONS) && defined(INTERNAL)
bool compatibility_fixup =
# endif
check_dynamic_option_compatibility();
strncpy(d_r_option_string, new_option_string,
BUFFER_SIZE_ELEMENTS(d_r_option_string));
NULL_TERMINATE_BUFFER(d_r_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!" : "");
d_r_write_unlock(&options_lock);
} else
# endif
d_r_write_unlock(&options_lock);
return updated;
}
# ifdef WINDOWS
* 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;
ASSERT(process_handle != NT_CURRENT_PROCESS && process_handle != NT_CURRENT_THREAD &&
process_handle != NULL);
ASSERT(!READWRITE_LOCK_HELD(&options_lock));
d_r_write_lock(&options_lock);
SELF_UNPROTECT_OPTIONS();
* 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));
if (IS_GET_PARAMETER_SUCCESS(err))
set_dynamo_options_other_process(&temp_options, new_option_string);
* 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();
* 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
static bool
is_string_type(enum option_type_t type)
{
return type == OPTION_TYPE_pathstring_t || type == OPTION_TYPE_liststring_t;
}
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();
int i;
CLIENT_ASSERT(num_options >= 0, "invalid number of options");
for (i = 0; i < num_options; ++i) {
if (is_string_type(option_traits[i].type) &&
strcmp(option_name, option_traits[i].name) == 0) {
const void *val = (char *)(&dynamo_options) + option_traits[i].offset;
CLIENT_ASSERT(val != NULL, "invalid address");
strncpy(buf, val, len);
found = true;
break;
}
}
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)
{
CLIENT_ASSERT(val != NULL, "invalid parameter");
*val = 0;
int i = 0;
for (i = 0; i < num_options; ++i) {
if (!is_string_type(option_traits[i].type) &&
strcmp(option_name, option_traits[i].name) == 0) {
const void *dopts_ptr = (char *)(&dynamo_options) + option_traits[i].offset;
memcpy((void *)val, dopts_ptr, option_traits[i].size);
return true;
}
}
return false;
}
#endif
#ifdef STANDALONE_UNIT_TEST
static void
show_dynamo_options(bool minimal)
{
* can still put this on the stack.
*/
char opstring[8 * MAX_OPTIONS_STRING];
get_dynamo_options_string(&dynamo_options, opstring, sizeof(opstring), minimal);
os_write(STDERR, opstring, strlen(opstring));
}
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] != ' ') { \
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;
d_r_write_lock(&options_lock);
SELF_UNPROTECT_OPTIONS();
* visual inspection
*/
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);
# ifdef X64
set_dynamo_options(&dynamo_options, "-vmheap_size 16384M -persist_short_digest 8K");
EXPECT_EQ(dynamo_options.vmheap_size, 16 * 1024 * 1024 * 1024ULL);
char opstring[MAX_OPTIONS_STRING];
* We include a smaller option to ensure we avoid printing out "0G".
*/
get_dynamo_options_string(&dynamo_options, opstring, sizeof(opstring), true);
EXPECT_EQ(0, strcmp(opstring, "-vmheap_size 16G -persist_short_digest 8K "));
# endif
SELF_PROTECT_OPTIONS();
d_r_write_unlock(&options_lock);
}
#endif