@@ -2379,8 +2379,8 @@ options_init()
     /* .lspdata pages start out writable so no unprotect needed here */
     write_lock(&options_lock);
     ASSERT(sizeof(dynamo_options) == sizeof(options_t));
-    /* get dynamo options */
-    adjust_defaults_for_page_size(&dynamo_options);
+    /* reset options to the defaults and get current dynamo options */
+    set_dynamo_options_defaults(&dynamo_options);
     retval = get_parameter(PARAM_STR(DYNAMORIO_VAR_OPTIONS), option_string,
                            sizeof(option_string));
     if (IS_GET_PARAMETER_SUCCESS(retval))
/* **********************************************************
 * Copyright (c) 2011-2017 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
 *
 */

#include <stddef.h>

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

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 uint uint_size;
typedef uint uint_time;
typedef ptr_uint_t uint_addr;

/* Structure with all the information about an option. */
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
/* 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

/* Traits of all the options. */
#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);

/* 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.  Do not assume that it is initialized. */
options_t temp_options;

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

static void
adjust_defaults_for_page_size(options_t *options)
{
#ifndef NOT_DYNAMORIO_CORE /* XXX: clumsy fix for Windows */
    uint page_size = (uint)PAGE_SIZE;

    /* The defaults are known to be appropriate for 4 KiB pages. */
    if (page_size == 4096)
        return;

    /* XXX: This approach is not scalable or maintainable as there may in
     * future be many more options that depend on the page size.
     */

    /* Some of these are a small multiple of the page size because they include
     * one or two guard pages.
     */
    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
    options->initial_heap_unit_size =
        MAX(ALIGN_FORWARD(options->initial_heap_unit_size, page_size), 3 * page_size);
    options->initial_heap_nonpers_size =
        MAX(ALIGN_FORWARD(options->initial_heap_nonpers_size, page_size), 3 * page_size);
    options->initial_global_heap_unit_size = MAX(
        ALIGN_FORWARD(options->initial_global_heap_unit_size, page_size), 3 * page_size);
    options->heap_commit_increment =
        ALIGN_FORWARD(options->heap_commit_increment, page_size);
    options->cache_commit_increment =
        ALIGN_FORWARD(options->cache_commit_increment, page_size);
#endif /* !NOT_DYNAMORIO_CORE */
}

/* 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;
    adjust_defaults_for_page_size(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 && quote == '\0' /*not a quoted empty string*/)
        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;
        case 'G': // Giga (bytes)
        case 'g': factor = 1024 * 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';
}

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;
    }
}

/* We mark this function to be NOINLINE so that in case the compiler unrolls the
 * 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);
        /* FIXME: check argument */
    }
}

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
}

/* 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;
    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());
    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;
            }
        }

        /* no matching option found */
        if (!got_badopt) {
            snprintf(badopt, BUFFER_SIZE_ELEMENTS(badopt), "%s", opt);
            NULL_TERMINATE_BUFFER(badopt);
        }
        got_badopt = true;
    }

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

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 >= %u, resetting from %u to %u", name, min, *val,
                        new_val);
        } else {
            new_val = max;
            USAGE_ERROR("%s must be >= %u and <= %u, resetting from %u to %u", 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: %u 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, 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)
{
    /* FIXME: 0x100 hack to get logmask printed in hex,
     * 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)
{
    uint value = *(const uint *)val_ptr;
    snprintf(optionbuff, MAX_OPTION_LENGTH, "-%s %d%s ", option,
             (value % 1024 == 0 ? value / 1024 : value), (value % 1024 == 0 ? "K" : "B"));
}
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 " PFX " ", 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)
{
    return DIFF_uint(ptr1, ptr2);
}
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(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)
{
    COPY_uint(ptr1, 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)
{
    *(ptr_uint_t *)(ptr1) = *(const ptr_uint_t *)(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;
    }
}

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

    int i;
    for (i = 0; i < num_options; ++i) {
        if (option_traits[i].name[0] != ' ') { /* not synthetic */
            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;
}

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

    int i;
    for (i = 0; i < num_options; ++i) {
        if (option_traits[i].affects_pcache >= pcache_effect &&
            option_traits[i].name[0] != ' ') { /* not synthetic */
            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;
}

/* 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)
{
    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)
/* 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());
    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;
}

#ifdef CLIENT_INTERFACE
void
options_enable_code_api_dependences(options_t *options)
{
    if (!options->code_api)
        return;

    /* PR 202669: larger stack size since we're saving a 512-byte
     * 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.
     */
    options->stack_size = MAX(options->stack_size, 56 * 1024);
#    ifdef UNIX
    /* We assume that clients avoid private library code, within reason, and
     * 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, 32 * 1024);
#    endif

    /* For CI builds we'll disable elision by default since we
     * 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;

    /* indcall2direct causes problems with the code manip API,
     * 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;

    /* To support clients changing syscall numbers we need to
     * be able to swap ignored for non-ignored (xref PR 307284)
     */
    options->inline_ignored_syscalls = false;

    /* Clients usually want to see all the code, regardless of bugs and
     * 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;

    /* Don't randomize dynamorio.dll */
    IF_WINDOWS(options->aslr_dr = false;)

    /* FIXME PR 215179 on getting rid of this tracing restriction. */
    options->pad_jmps_mark_no_trace = true;
}
#endif

/****************************************************************************/
#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 (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)) {
        /* 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");
        SET_DEFAULT_VALUE(stack_size);
        changed_options = true;
    }

#    if 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

    /****************************************************************************
     * 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 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 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;
    }
#    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)) {
        /* 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(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
    if (DYNAMO_OPTION(stack_guard_pages)) {
        /* XXX i#2595: this does not interact well with -vm_reserve. */
        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)) {
        /* 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 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");
        /* 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 (defined(ARM) || defined(LINUX)) && !defined(STATIC_LIBRARY)
    if (!INTERNAL_OPTION(private_loader)) {
        /* On ARM, to make DR work in gdb, we must use private loader to make
         * 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
        /* 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

#    ifdef ARM
    if (DYNAMO_OPTION(steal_reg) < 8 /* DR_REG_STOLEN_MIN */ ||
        DYNAMO_OPTION(steal_reg) > IF_X64_ELSE(29, 12) /* DR_REG_STOLEN_MAX */) {
        USAGE_ERROR("-steal_reg only supports register between r8 and r12(A32)/r29(A64)");
        dynamo_options.steal_reg = IF_X64_ELSE(28 /*r28*/, 10 /*r10*/);
        changed_options = true;
    }
#    endif

#    ifdef DEBUG
    if (INTERNAL_OPTION(log_at_fragment_count) > 0 && stats->loglevel > 1) {
        /* start out at 1 */
        if (dynamo_options.stats_loglevel <= 1)
            USAGE_ERROR("-log_at_fragment_count expects >1 delayed loglevel");
        stats->loglevel = 1;
        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;
#        if defined(INTERNAL) || defined(DEBUG) || defined(CLIENT_INTERFACE)
    disassemble_options_init();
#        endif
#    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 */
    adjust_defaults_for_page_size(&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);
#    if defined(STATIC_LIBRARY) && defined(CLIENT_INTERFACE)
    /* For dynamorio_static, we always enable code_api as it's a pain to set
     * DR runtime options -- unless otherwise requested.
     */
    options_enable_code_api_dependences(&dynamo_options);
#    endif
    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

/* Function for identifying string type. */
static bool
is_string_type(enum option_type_t type)
{
    return type == OPTION_TYPE_pathstring_t || type == OPTION_TYPE_liststring_t;
}

/* 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();
    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 /* 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 synthetic */                      \
            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 */

This is a big memcpy. I'd prefer to do this on exit and only if detaching, like we're doing the other expensive zeroing.