summaryrefslogtreecommitdiff
path: root/utils/mcpu-run-options.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/mcpu-run-options.c')
-rw-r--r--utils/mcpu-run-options.c1169
1 files changed, 1169 insertions, 0 deletions
diff --git a/utils/mcpu-run-options.c b/utils/mcpu-run-options.c
new file mode 100644
index 0000000..f3cb881
--- /dev/null
+++ b/utils/mcpu-run-options.c
@@ -0,0 +1,1169 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libmcpu.h>
+#include <libmpuio.h>
+
+#include "mcpu-run-options.h"
+
+void
+mcpu_run_usage( const mcpu_run_options *options )
+{
+ const char *name = options ? options->common.progname : "mcpu-run";
+
+ fprintf( stderr,
+ "usage: %s [runtime-options] -- executable [program-args...]\n"
+ " %s [runtime-options] executable [program-args...] (compatibility)\n\n"
+ "Execute a LibMCPU MELF ET_EXEC image. STACK and HEAP sizes are part of\n"
+ "the executable ABI and cannot be overridden by mcpu-run.\n\n"
+ "MCPU system options:\n"
+ " --no-config - do not read mcpu-ld.conf files\n"
+ " --sys-root=PATH - use PATH as MCPU root and do not read config files\n"
+ " --dump-config - print effective MCPU configuration and exit\n"
+ " --print-search-dirs - print effective library search directories and exit\n"
+ "\nLibrary options (must precede executable or explicit --):\n"
+ " --library-path=DIR[:DIR...] - add explicit library search directories\n"
+ " --ldpreload=<a.so:b.so> - preload ABI-v2/ABI-v3 shared objects; names use search path\n"
+ "\nEnvironment:\n"
+ " MCPU_LD_PRELOAD=a.so:b.so - preload shared objects before command-line --ldpreload\n"
+ "\nRuntime options (must precede executable or explicit --):\n"
+ " --int-reg=r<bits>@<addr> - print a signed integer register after stop\n"
+ " --real-reg=r<bits>@<addr> - print a real register after stop\n"
+ " --int-data=<bits>@<addr> - print a signed integer object from DATA memory\n"
+ " --real-data=<bits>@<addr> - print a real object from DATA memory\n"
+ " --raw-data=<bytes>@<addr> - dump bytes from .data, clipped at .data end\n"
+ " --int-control=<bits>@<addr> - print a signed integer object from CONTROL memory\n"
+ " --real-control=<bits>@<addr> - print a real object from CONTROL memory\n"
+ " --raw-control=<bytes>@<addr> - dump bytes from .control, clipped at .control end\n"
+ " --int-heap=<bits>@<addr> - print a signed integer object from HEAP memory\n"
+ " --real-heap=<bits>@<addr> - print a real object from HEAP memory\n"
+ " --raw-heap=<bytes>@<addr> - dump bytes from HEAP, clipped at HEAP end\n"
+ " --int-input=<bits>@<addr> - print a signed integer object from INPUT memory\n"
+ " --real-input=<bits>@<addr> - print a real object from INPUT memory\n"
+ " --raw-input=<bytes>@<addr> - dump bytes from .input, clipped at .input end\n"
+ " --int-output=<bits>@<addr> - print a signed integer object from OUTPUT memory\n"
+ " --real-output=<bits>@<addr> - print a real object from OUTPUT memory\n"
+ " --raw-output=<bytes>@<addr> - dump bytes from .output, clipped at .output end\n"
+ " --int-trace=<bits>@<addr> - print a signed integer object from TRACE memory\n"
+ " --real-trace=<bits>@<addr> - print a real object from TRACE memory\n"
+ " --raw-trace=<bytes>@<addr> - dump bytes from .trace, clipped at .trace end\n"
+ " --before-run=<action> - execute host action before first guest instruction\n"
+ " --after-init=<action> - execute host action after INIT (or empty INIT stage)\n"
+ " --on-step=<action> - execute host action after every LIFE END boundary\n"
+ " --after-run=<action> - execute host action after final execution stop\n"
+ " --digits=<n> - mantissa digits for real output (default: 80)\n"
+ " --init-only - execute INIT through END, then stop\n"
+ " --steps=<n> - stop after n LIFE END boundaries\n"
+ " (default: stop at first LIFE END)\n"
+ " -h, --help - show this help\n"
+ " --version - show program version\n"
+ " --dump-version - show version number only\n"
+ "\nBare preload names from MCPU_LD_PRELOAD and --ldpreload are searched in\n"
+ "--library-path order and then in the effective MCPU system library path.\n"
+ "Names containing '/' are explicit pathnames and bypass name lookup.\n"
+ "Environment preloads are ordered before command-line preloads; because the\n"
+ "rightmost equal-strength preload wins, command-line preloads override the\n"
+ "environment. Empty preload/path-list elements are errors.\n\n"
+ "Stage actions:\n"
+ " print-int-regs:r<bits>@<addr>[,...]\n"
+ " print-real-regs:r<bits>@<addr>[,...]\n"
+ " set-int-regs:r<bits>@<addr>=<value>[,...]\n"
+ " set-real-regs:r<bits>@<addr>=<value>[,...]\n"
+ " print-int-<memory>:<bits>@<addr>[,...]\n"
+ " print-real-<memory>:<bits>@<addr>[,...]\n"
+ " set-int-<memory>:<bits>@<addr>=<value>[,...]\n"
+ " set-real-<memory>:<bits>@<addr>=<value>[,...]\n"
+ " print-raw-<memory>:<bytes>@<addr>\n\n"
+ "<memory> is data, control, heap, input, output or trace. Scalar print\n"
+ "actions print their comma-separated objects on one line separated by spaces.\n"
+ "Raw actions contain exactly one dump; repeat the stage option for more dumps.\n"
+ "There are no set-raw-* actions. Values for set-int-* and set-real-* are\n"
+ "parsed by LibMPUIO mpu_sscanf(). Repeated stage options execute in command-\n"
+ "line order. Step-number filters are not implemented.\n\n"
+ "For --raw-* and print-raw-* <bytes>, K/KiB, M/MiB and G/GiB suffixes are\n"
+ "accepted. K, M and G use binary multipliers (1024, 1024^2 and 1024^3).\n"
+ "Use -- to unambiguously separate mcpu-run options from executable/argv.\n",
+ name, name );
+
+} /* End of mcpu_run_usage() */
+
+
+static int
+valid_int_bits( unsigned int bits )
+{
+ return( bits >= CPU_REGISTER_MIN_BITS &&
+ bits <= CPU_REGISTER_MAX_BITS &&
+ (bits & (bits - 1U)) == 0U );
+
+} /* End of valid_int_bits() */
+
+
+static int
+valid_real_bits( unsigned int bits )
+{
+ return( bits >= 32U &&
+ bits <= CPU_REGISTER_MAX_BITS &&
+ bits <= (unsigned int)MPU_REAL_IO_LIMIT &&
+ (bits & (bits - 1U)) == 0U );
+
+} /* End of valid_real_bits() */
+
+
+static int
+parse_u64( const char *s, __mpu_uint64_t *v )
+{
+ char *e;
+ unsigned long long x;
+
+ errno = 0;
+ x = strtoull( s, &e, 0 );
+ if( errno || e == s || *e ) return( -1 );
+
+ *v = (__mpu_uint64_t)x;
+
+ return( 0 );
+
+} /* End of parse_u64() */
+
+
+static int
+parse_size( const char *s, __mpu_uint64_t *v )
+{
+ unsigned long long x;
+ __mpu_uint64_t m = 1;
+ char *e;
+
+ if( !s || !*s || *s == '-' ) return( -1 );
+
+ errno = 0;
+ x = strtoull( s, &e, 0 );
+ if( errno || e == s ) return( -1 );
+
+ if( *e == '\0' )
+ m = 1;
+ else if( !strcmp( e, "K" ) || !strcmp( e, "KiB" ) )
+ m = __mpu_UINT64_C( 1024 );
+ else if( !strcmp( e, "M" ) || !strcmp( e, "MiB" ) )
+ m = __mpu_UINT64_C( 1024 ) * __mpu_UINT64_C( 1024 );
+ else if( !strcmp( e, "G" ) || !strcmp( e, "GiB" ) )
+ m = __mpu_UINT64_C( 1024 ) * __mpu_UINT64_C( 1024 ) * __mpu_UINT64_C( 1024 );
+ else
+ return( -1 );
+
+ if( (__mpu_uint64_t)x != x || (__mpu_uint64_t)x > UINT64_MAX / m )
+ return( -1 );
+
+ *v = (__mpu_uint64_t)x * m;
+
+ return( 0 );
+
+} /* End of parse_size() */
+
+
+
+static int
+parse_integer_register( const char *s, print_spec *spec )
+{
+ const char *at;
+ char width[32], *end;
+ size_t n;
+ unsigned long bits;
+ __mpu_uint64_t address;
+
+ if( !s || (s[0] != 'r' && s[0] != 'R') ) return( -1 );
+
+ at = strchr( s + 1, '@' );
+ if( !at ) return( -1 );
+
+ n = (size_t)(at - (s + 1));
+ if( !n || n >= sizeof( width ) ) return( -1 );
+
+ memcpy( width, s + 1, n );
+ width[n] = 0;
+
+ errno = 0;
+ bits = strtoul( width, &end, 10 );
+ if( errno || end == width || *end || bits > (unsigned long)~0U ||
+ !valid_int_bits( (unsigned)bits ) )
+ return( -1 );
+
+ if( parse_u64( at + 1, &address ) ||
+ address > CPU_REGISTER_ADDRESS_MAX ||
+ !__cpu_reg_valid( (unsigned)bits, (__mpu_uint32_t)address ) )
+ return( -1 );
+
+ spec->kind = PRINT_INTEGER_REGISTER;
+ spec->memory = MEMORY_NONE;
+ spec->bits = (unsigned)bits;
+ spec->address = address;
+
+ return( 0 );
+
+} /* End of parse_integer_register() */
+
+
+static int
+parse_real_register( const char *s, print_spec *spec )
+{
+ const char *at;
+ char width[32], *end;
+ size_t n;
+ unsigned long bits;
+ __mpu_uint64_t address;
+
+ if( !s || (s[0] != 'r' && s[0] != 'R') ) return( -1 );
+
+ at = strchr( s + 1, '@' );
+ if( !at ) return( -1 );
+
+ n = (size_t)(at - (s + 1));
+ if( !n || n >= sizeof( width ) ) return( -1 );
+
+ memcpy( width, s + 1, n );
+ width[n] = 0;
+
+ errno = 0;
+ bits = strtoul( width, &end, 10 );
+ if( errno || end == width || *end || bits > (unsigned long)~0U ||
+ !valid_real_bits( (unsigned)bits ) )
+ return( -1 );
+
+ if( parse_u64( at + 1, &address ) ||
+ address > CPU_REGISTER_ADDRESS_MAX ||
+ !__cpu_reg_valid( (unsigned)bits, (__mpu_uint32_t)address ) )
+ return( -1 );
+
+ spec->kind = PRINT_REAL_REGISTER;
+ spec->memory = MEMORY_NONE;
+ spec->bits = (unsigned)bits;
+ spec->address = address;
+
+ return( 0 );
+
+} /* End of parse_real_register() */
+
+
+static int
+parse_memory_object( const char *s, print_spec *spec,
+ int real_object, memory_kind memory )
+{
+ const char *at;
+ char width[32], *end;
+ size_t n;
+ unsigned long bits;
+ __mpu_uint64_t address;
+
+ if( !s ) return( -1 );
+ if( real_object && (*s == 'r' || *s == 'R') ) ++s;
+
+ at = strchr( s, '@' );
+ if( !at ) return( -1 );
+
+ n = (size_t)(at - s);
+ if( !n || n >= sizeof( width ) ) return( -1 );
+
+ memcpy( width, s, n );
+ width[n] = 0;
+
+ errno = 0;
+ bits = strtoul( width, &end, 10 );
+ if( errno || end == width || *end || bits > (unsigned long)~0U )
+ return( -1 );
+
+ if( real_object ? !valid_real_bits( (unsigned)bits ) :
+ !valid_int_bits( (unsigned)bits ) )
+ return( -1 );
+
+ if( parse_u64( at + 1, &address ) ) return( -1 );
+
+ spec->kind = real_object ? PRINT_REAL_MEMORY : PRINT_INTEGER_MEMORY;
+ spec->memory = memory;
+ spec->bits = (unsigned)bits;
+ spec->address = address;
+
+ return( 0 );
+
+} /* End of parse_memory_object() */
+
+
+static int
+parse_raw_memory( const char *s, print_spec *spec, memory_kind memory )
+{
+ const char *at;
+ char countbuf[32];
+ size_t n;
+ __mpu_uint64_t count, address;
+
+ if( !s ) return( -1 );
+
+ at = strchr( s, '@' );
+ if( !at ) return( -1 );
+
+ n = (size_t)(at - s);
+ if( !n || n >= sizeof( countbuf ) ) return( -1 );
+
+ memcpy( countbuf, s, n );
+ countbuf[n] = 0;
+
+ if( parse_size( countbuf, &count ) || !count ||
+ parse_u64( at + 1, &address ) )
+ return( -1 );
+
+ spec->kind = PRINT_RAW_MEMORY;
+ spec->memory = memory;
+ spec->address = address;
+ spec->count = count;
+
+ return( 0 );
+
+} /* End of parse_raw_memory() */
+
+
+static void
+free_stage_action( stage_action *action )
+{
+ size_t i;
+
+ if( !action ) return;
+
+ for( i = 0; i < action->count; ++i )
+ free( action->item[i].value );
+ free( action->item );
+ memset( action, 0, sizeof( *action ) );
+
+} /* End of free_stage_action() */
+
+
+static void
+free_stage_action_list( stage_action_list *list )
+{
+ size_t i;
+
+ if( !list ) return;
+
+ for( i = 0; i < list->count; ++i )
+ free_stage_action( &list->action[i] );
+ free( list->action );
+ memset( list, 0, sizeof( *list ) );
+
+} /* End of free_stage_action_list() */
+
+
+static int
+append_stage_action( stage_action_list *list, stage_action *action )
+{
+ stage_action *a;
+ size_t capacity;
+
+ if( list->count == list->capacity )
+ {
+ capacity = list->capacity ? list->capacity * 2U : 4U;
+ if( capacity < list->capacity || capacity > SIZE_MAX / sizeof( *a ) )
+ return( -1 );
+
+ a = (stage_action *)realloc( list->action, capacity * sizeof( *a ) );
+ if( !a ) return( -1 );
+
+ list->action = a;
+ list->capacity = capacity;
+ }
+
+ list->action[list->count++] = *action;
+ memset( action, 0, sizeof( *action ) );
+
+ return( 0 );
+
+} /* End of append_stage_action() */
+
+
+static int
+append_action_item( stage_action *action, const action_item *item )
+{
+ action_item *p;
+
+ if( action->count == SIZE_MAX / sizeof( *p ) ) return( -1 );
+
+ p = (action_item *)realloc( action->item,
+ (action->count + 1U) * sizeof( *p ) );
+ if( !p ) return( -1 );
+
+ action->item = p;
+ action->item[action->count++] = *item;
+
+ return( 0 );
+
+} /* End of append_action_item() */
+
+
+static char *
+dup_range( const char *s, size_t n )
+{
+ char *p;
+
+ p = (char *)malloc( n + 1U );
+ if( !p ) return( (char *)0 );
+
+ memcpy( p, s, n );
+ p[n] = 0;
+
+ return( p );
+
+} /* End of dup_range() */
+
+
+static int
+parse_action_object( const char *text, size_t length,
+ int is_register, int real_object, int set_object,
+ memory_kind memory, action_item *item )
+{
+ print_spec spec;
+ char *copy, *eq;
+ const char *value = (const char *)0;
+
+ memset( item, 0, sizeof( *item ) );
+ memset( &spec, 0, sizeof( spec ) );
+
+ copy = dup_range( text, length );
+ if( !copy ) return( -1 );
+
+ eq = strchr( copy, '=' );
+ if( set_object )
+ {
+ if( !eq || eq == copy || !eq[1] || strchr( eq + 1, '=' ) )
+ goto fail;
+ *eq = 0;
+ value = eq + 1;
+ }
+ else if( eq )
+ goto fail;
+
+ if( is_register )
+ {
+ if( real_object ? parse_real_register( copy, &spec ) :
+ parse_integer_register( copy, &spec ) )
+ goto fail;
+ }
+ else
+ {
+ if( parse_memory_object( copy, &spec, real_object, memory ) )
+ goto fail;
+ }
+
+ item->bits = spec.bits;
+ item->address = spec.address;
+ if( set_object )
+ {
+ item->value = dup_range( value, strlen( value ) );
+ if( !item->value ) goto fail;
+ }
+
+ free( copy );
+ return( 0 );
+
+fail:
+ free( item->value );
+ memset( item, 0, sizeof( *item ) );
+ free( copy );
+ return( -1 );
+
+} /* End of parse_action_object() */
+
+
+typedef struct __action_prefix
+{
+ const char *name;
+ action_kind kind;
+ memory_kind memory;
+ int is_register;
+ int real_object;
+ int set_object;
+ int raw_object;
+} action_prefix;
+
+
+static int
+parse_stage_action( const char *text, stage_action *action )
+{
+ static const action_prefix prefix[] =
+ {
+ { "print-int-regs:", ACTION_PRINT_INTEGER_REGISTER, MEMORY_NONE, 1, 0, 0, 0 },
+ { "print-real-regs:", ACTION_PRINT_REAL_REGISTER, MEMORY_NONE, 1, 1, 0, 0 },
+ { "set-int-regs:", ACTION_SET_INTEGER_REGISTER, MEMORY_NONE, 1, 0, 1, 0 },
+ { "set-real-regs:", ACTION_SET_REAL_REGISTER, MEMORY_NONE, 1, 1, 1, 0 },
+
+ { "print-int-data:", ACTION_PRINT_INTEGER_MEMORY, MEMORY_DATA, 0, 0, 0, 0 },
+ { "print-real-data:", ACTION_PRINT_REAL_MEMORY, MEMORY_DATA, 0, 1, 0, 0 },
+ { "set-int-data:", ACTION_SET_INTEGER_MEMORY, MEMORY_DATA, 0, 0, 1, 0 },
+ { "set-real-data:", ACTION_SET_REAL_MEMORY, MEMORY_DATA, 0, 1, 1, 0 },
+ { "print-raw-data:", ACTION_PRINT_RAW_MEMORY, MEMORY_DATA, 0, 0, 0, 1 },
+
+ { "print-int-control:", ACTION_PRINT_INTEGER_MEMORY, MEMORY_CONTROL, 0, 0, 0, 0 },
+ { "print-real-control:", ACTION_PRINT_REAL_MEMORY, MEMORY_CONTROL, 0, 1, 0, 0 },
+ { "set-int-control:", ACTION_SET_INTEGER_MEMORY, MEMORY_CONTROL, 0, 0, 1, 0 },
+ { "set-real-control:", ACTION_SET_REAL_MEMORY, MEMORY_CONTROL, 0, 1, 1, 0 },
+ { "print-raw-control:", ACTION_PRINT_RAW_MEMORY, MEMORY_CONTROL, 0, 0, 0, 1 },
+
+ { "print-int-heap:", ACTION_PRINT_INTEGER_MEMORY, MEMORY_HEAP, 0, 0, 0, 0 },
+ { "print-real-heap:", ACTION_PRINT_REAL_MEMORY, MEMORY_HEAP, 0, 1, 0, 0 },
+ { "set-int-heap:", ACTION_SET_INTEGER_MEMORY, MEMORY_HEAP, 0, 0, 1, 0 },
+ { "set-real-heap:", ACTION_SET_REAL_MEMORY, MEMORY_HEAP, 0, 1, 1, 0 },
+ { "print-raw-heap:", ACTION_PRINT_RAW_MEMORY, MEMORY_HEAP, 0, 0, 0, 1 },
+
+ { "print-int-input:", ACTION_PRINT_INTEGER_MEMORY, MEMORY_INPUT, 0, 0, 0, 0 },
+ { "print-real-input:", ACTION_PRINT_REAL_MEMORY, MEMORY_INPUT, 0, 1, 0, 0 },
+ { "set-int-input:", ACTION_SET_INTEGER_MEMORY, MEMORY_INPUT, 0, 0, 1, 0 },
+ { "set-real-input:", ACTION_SET_REAL_MEMORY, MEMORY_INPUT, 0, 1, 1, 0 },
+ { "print-raw-input:", ACTION_PRINT_RAW_MEMORY, MEMORY_INPUT, 0, 0, 0, 1 },
+
+ { "print-int-output:", ACTION_PRINT_INTEGER_MEMORY, MEMORY_OUTPUT, 0, 0, 0, 0 },
+ { "print-real-output:", ACTION_PRINT_REAL_MEMORY, MEMORY_OUTPUT, 0, 1, 0, 0 },
+ { "set-int-output:", ACTION_SET_INTEGER_MEMORY, MEMORY_OUTPUT, 0, 0, 1, 0 },
+ { "set-real-output:", ACTION_SET_REAL_MEMORY, MEMORY_OUTPUT, 0, 1, 1, 0 },
+ { "print-raw-output:", ACTION_PRINT_RAW_MEMORY, MEMORY_OUTPUT, 0, 0, 0, 1 },
+
+ { "print-int-trace:", ACTION_PRINT_INTEGER_MEMORY, MEMORY_TRACE, 0, 0, 0, 0 },
+ { "print-real-trace:", ACTION_PRINT_REAL_MEMORY, MEMORY_TRACE, 0, 1, 0, 0 },
+ { "set-int-trace:", ACTION_SET_INTEGER_MEMORY, MEMORY_TRACE, 0, 0, 1, 0 },
+ { "set-real-trace:", ACTION_SET_REAL_MEMORY, MEMORY_TRACE, 0, 1, 1, 0 },
+ { "print-raw-trace:", ACTION_PRINT_RAW_MEMORY, MEMORY_TRACE, 0, 0, 0, 1 }
+ };
+ const action_prefix *ap = (const action_prefix *)0;
+ const char *payload, *p, *comma;
+ action_item item;
+ size_t i, n;
+
+ if( !text || !*text ) return( -1 );
+ memset( action, 0, sizeof( *action ) );
+
+ for( i = 0; i < sizeof( prefix ) / sizeof( prefix[0] ); ++i )
+ {
+ n = strlen( prefix[i].name );
+ if( !strncmp( text, prefix[i].name, n ) )
+ {
+ ap = &prefix[i];
+ payload = text + n;
+ break;
+ }
+ }
+
+ if( !ap || !*payload ) return( -1 );
+
+ action->kind = ap->kind;
+ action->memory = ap->memory;
+
+ if( ap->raw_object )
+ {
+ if( strchr( payload, ',' ) || parse_raw_memory( payload, &action->raw, ap->memory ) )
+ goto fail;
+ return( 0 );
+ }
+
+ p = payload;
+ while( *p )
+ {
+ comma = strchr( p, ',' );
+ n = comma ? (size_t)(comma - p) : strlen( p );
+ if( !n || parse_action_object( p, n, ap->is_register, ap->real_object,
+ ap->set_object, ap->memory, &item ) )
+ goto fail;
+
+ if( append_action_item( action, &item ) )
+ {
+ free( item.value );
+ goto fail;
+ }
+
+ if( !comma ) break;
+ p = comma + 1;
+ if( !*p ) goto fail;
+ }
+
+ return( action->count ? 0 : -1 );
+
+fail:
+ free_stage_action( action );
+ return( -1 );
+
+} /* End of parse_stage_action() */
+
+
+static int
+parse_stage_option( const char *text, stage_action_list *list )
+{
+ stage_action action;
+
+ memset( &action, 0, sizeof( action ) );
+ if( parse_stage_action( text, &action ) ) return( -1 );
+
+ if( append_stage_action( list, &action ) )
+ {
+ free_stage_action( &action );
+ return( -1 );
+ }
+
+ return( 0 );
+
+} /* End of parse_stage_option() */
+
+
+
+void
+mcpu_run_options_init( mcpu_run_options *options, const char *progname )
+{
+ const char *env_preload;
+
+ if( options == NULL )
+ return;
+
+ memset( options, 0, sizeof(*options) );
+ mcpu_tool_options_init( &options->common, progname );
+ options->digits = 80U;
+
+ env_preload = getenv( "MCPU_LD_PRELOAD" );
+ if( env_preload != NULL && *env_preload != 0 )
+ options->env_ldpreload = env_preload;
+
+} /* End of mcpu_run_options_init() */
+
+
+void
+mcpu_run_options_free( mcpu_run_options *options )
+{
+ if( options == NULL )
+ return;
+
+ free_stage_action_list( &options->before_run );
+ free_stage_action_list( &options->after_init );
+ free_stage_action_list( &options->on_step );
+ free_stage_action_list( &options->after_run );
+ mcpu_tool_options_free( &options->common );
+
+} /* End of mcpu_run_options_free() */
+
+
+static int
+append_resolved_preload( char **list, size_t *length, const char *path )
+{
+ size_t path_length;
+ size_t extra;
+ char *p;
+
+ if( list == NULL || length == NULL || path == NULL )
+ {
+ errno = EINVAL;
+ return( -1 );
+ }
+
+ path_length = strlen( path );
+ extra = path_length + (*length != 0 ? 1U : 0U);
+ if( *length > SIZE_MAX - extra - 1U )
+ {
+ errno = EOVERFLOW;
+ return( -1 );
+ }
+
+ p = (char *)realloc( *list, *length + extra + 1U );
+ if( p == NULL )
+ return( -1 );
+
+ *list = p;
+ if( *length != 0 )
+ p[(*length)++] = ':';
+ memcpy( p + *length, path, path_length );
+ *length += path_length;
+ p[*length] = 0;
+ return( 0 );
+
+} /* End of append_resolved_preload() */
+
+
+static int
+resolve_preload_list( const mcpu_run_options *options, const char *list,
+ const char *source, char **result,
+ size_t *result_length, char *err, size_t errsz )
+{
+ const char *begin;
+
+ if( list == NULL || *list == 0 )
+ return( 0 );
+
+ begin = list;
+ for( ;; )
+ {
+ const char *colon = strchr( begin, ':' );
+ size_t length = colon ? (size_t)(colon - begin) : strlen(begin);
+ char *name;
+ char *path = NULL;
+
+ if( length == 0 )
+ {
+ snprintf( err, errsz, "empty name in %s list", source );
+ return( -1 );
+ }
+
+ name = (char *)malloc( length + 1U );
+ if( name == NULL )
+ {
+ snprintf( err, errsz, "out of memory" );
+ return( -1 );
+ }
+
+ memcpy( name, begin, length );
+ name[length] = 0;
+ if( mcpu_tool_resolve_library(&options->common, name, &path) != 0 )
+ {
+ if( errno == ENOENT && strchr(name, '/') == NULL )
+ snprintf( err, errsz, "%s: cannot find MCPU library '%s'",
+ source, name );
+ else
+ snprintf( err, errsz, "%s: cannot resolve MCPU library '%s': %s",
+ source, name, strerror(errno) );
+ free( name );
+ return( -1 );
+ }
+ free( name );
+
+ if( append_resolved_preload(result, result_length, path) != 0 )
+ {
+ snprintf( err, errsz, "cannot build effective preload list: %s",
+ strerror(errno) );
+ free( path );
+ return( -1 );
+ }
+ free( path );
+
+ if( colon == NULL )
+ break;
+ begin = colon + 1;
+ }
+
+ return( 0 );
+
+} /* End of resolve_preload_list() */
+
+
+int
+mcpu_run_resolve_preload( const mcpu_run_options *options,
+ char **resolved, char *err, size_t errsz )
+{
+ char *result = NULL;
+ size_t result_length = 0;
+
+ if( options == NULL || resolved == NULL || err == NULL || errsz == 0 )
+ {
+ errno = EINVAL;
+ return( -1 );
+ }
+
+ *resolved = NULL;
+
+ if( resolve_preload_list(options, options->env_ldpreload,
+ "MCPU_LD_PRELOAD", &result, &result_length,
+ err, errsz) != 0 )
+ goto fail;
+
+ if( resolve_preload_list(options, options->ldpreload,
+ "--ldpreload", &result, &result_length,
+ err, errsz) != 0 )
+ goto fail;
+
+ *resolved = result;
+ return( 0 );
+
+fail:
+ free( result );
+ return( -1 );
+
+} /* End of mcpu_run_resolve_preload() */
+
+
+int
+mcpu_run_handle_options( mcpu_run_options *options, int argc, char **argv )
+{
+ int i;
+ int exearg = 0;
+
+ if( options == NULL || argv == NULL )
+ {
+ errno = EINVAL;
+ return( -1 );
+ }
+
+ for( i = 1; i < argc; ++i )
+ {
+ const char *arg = argv[i];
+ int common;
+
+ if( options->exe != NULL )
+ break;
+
+ if( !strcmp( arg, "--" ) )
+ {
+ if( i + 1 >= argc )
+ {
+ fprintf( stderr, "%s: -- must be followed by an executable\n",
+ options->common.progname );
+ return( -1 );
+ }
+ exearg = ++i;
+ options->exe = argv[exearg];
+ break;
+ }
+ else if( !strcmp( arg, "-h" ) || !strcmp( arg, "--help" ) )
+ {
+ options->common.help = 1;
+ return( 0 );
+ }
+
+ common = mcpu_tool_parse_common_option( &options->common,
+ argc, argv, &i );
+ if( common < 0 )
+ return( -1 );
+ if( common > 0 )
+ {
+ if( options->common.version_action != MCPU_VERSION_NONE )
+ return( 0 );
+ continue;
+ }
+
+ if( !strncmp( arg, "--int-reg=", 10 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_integer_register(arg + 10, &options->output) )
+ {
+ fprintf( stderr, "%s: bad --int-reg operand: %s\n",
+ options->common.progname, arg + 10 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--real-reg=", 11 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_real_register(arg + 11, &options->output) )
+ {
+ fprintf( stderr, "%s: bad --real-reg operand: %s\n",
+ options->common.progname, arg + 11 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--int-data=", 11 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_memory_object(arg + 11, &options->output, 0, MEMORY_DATA) )
+ {
+ fprintf( stderr, "%s: bad --int-data operand: %s\n",
+ options->common.progname, arg + 11 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--real-data=", 12 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_memory_object(arg + 12, &options->output, 1, MEMORY_DATA) )
+ {
+ fprintf( stderr, "%s: bad --real-data operand: %s\n",
+ options->common.progname, arg + 12 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--raw-data=", 11 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_raw_memory(arg + 11, &options->output, MEMORY_DATA) )
+ {
+ fprintf( stderr, "%s: bad --raw-data operand: %s\n",
+ options->common.progname, arg + 11 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--int-control=", 14 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_memory_object(arg + 14, &options->output, 0, MEMORY_CONTROL) )
+ {
+ fprintf( stderr, "%s: bad --int-control operand: %s\n",
+ options->common.progname, arg + 14 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--real-control=", 15 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_memory_object(arg + 15, &options->output, 1, MEMORY_CONTROL) )
+ {
+ fprintf( stderr, "%s: bad --real-control operand: %s\n",
+ options->common.progname, arg + 15 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--raw-control=", 14 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_raw_memory(arg + 14, &options->output, MEMORY_CONTROL) )
+ {
+ fprintf( stderr, "%s: bad --raw-control operand: %s\n",
+ options->common.progname, arg + 14 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--int-heap=", 11 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_memory_object(arg + 11, &options->output, 0, MEMORY_HEAP) )
+ {
+ fprintf( stderr, "%s: bad --int-heap operand: %s\n",
+ options->common.progname, arg + 11 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--real-heap=", 12 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_memory_object(arg + 12, &options->output, 1, MEMORY_HEAP) )
+ {
+ fprintf( stderr, "%s: bad --real-heap operand: %s\n",
+ options->common.progname, arg + 12 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--raw-heap=", 11 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_raw_memory(arg + 11, &options->output, MEMORY_HEAP) )
+ {
+ fprintf( stderr, "%s: bad --raw-heap operand: %s\n",
+ options->common.progname, arg + 11 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--int-input=", 12 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_memory_object(arg + 12, &options->output, 0, MEMORY_INPUT) )
+ {
+ fprintf( stderr, "%s: bad --int-input operand: %s\n",
+ options->common.progname, arg + 12 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--real-input=", 13 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_memory_object(arg + 13, &options->output, 1, MEMORY_INPUT) )
+ {
+ fprintf( stderr, "%s: bad --real-input operand: %s\n",
+ options->common.progname, arg + 13 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--raw-input=", 12 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_raw_memory(arg + 12, &options->output, MEMORY_INPUT) )
+ {
+ fprintf( stderr, "%s: bad --raw-input operand: %s\n",
+ options->common.progname, arg + 12 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--int-output=", 13 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_memory_object(arg + 13, &options->output, 0, MEMORY_OUTPUT) )
+ {
+ fprintf( stderr, "%s: bad --int-output operand: %s\n",
+ options->common.progname, arg + 13 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--real-output=", 14 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_memory_object(arg + 14, &options->output, 1, MEMORY_OUTPUT) )
+ {
+ fprintf( stderr, "%s: bad --real-output operand: %s\n",
+ options->common.progname, arg + 14 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--raw-output=", 13 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_raw_memory(arg + 13, &options->output, MEMORY_OUTPUT) )
+ {
+ fprintf( stderr, "%s: bad --raw-output operand: %s\n",
+ options->common.progname, arg + 13 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--int-trace=", 12 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_memory_object(arg + 12, &options->output, 0, MEMORY_TRACE) )
+ {
+ fprintf( stderr, "%s: bad --int-trace operand: %s\n",
+ options->common.progname, arg + 12 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--real-trace=", 13 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_memory_object(arg + 13, &options->output, 1, MEMORY_TRACE) )
+ {
+ fprintf( stderr, "%s: bad --real-trace operand: %s\n",
+ options->common.progname, arg + 13 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--raw-trace=", 12 ) )
+ {
+ if( options->output.kind != PRINT_NONE ||
+ parse_raw_memory(arg + 12, &options->output, MEMORY_TRACE) )
+ {
+ fprintf( stderr, "%s: bad --raw-trace operand: %s\n",
+ options->common.progname, arg + 12 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--before-run=", 13 ) )
+ {
+ if( parse_stage_option(arg + 13, &options->before_run) )
+ {
+ fprintf( stderr, "%s: bad --before-run action: %s\n",
+ options->common.progname, arg + 13 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--after-init=", 13 ) )
+ {
+ if( parse_stage_option(arg + 13, &options->after_init) )
+ {
+ fprintf( stderr, "%s: bad --after-init action: %s\n",
+ options->common.progname, arg + 13 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--on-step=", 10 ) )
+ {
+ if( parse_stage_option(arg + 10, &options->on_step) )
+ {
+ fprintf( stderr, "%s: bad --on-step action: %s\n",
+ options->common.progname, arg + 10 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--after-run=", 12 ) )
+ {
+ if( parse_stage_option(arg + 12, &options->after_run) )
+ {
+ fprintf( stderr, "%s: bad --after-run action: %s\n",
+ options->common.progname, arg + 12 );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--library-path=", 15 ) )
+ {
+ if( !arg[15] ||
+ mcpu_tool_add_library_path(&options->common, arg + 15) != 0 )
+ {
+ fprintf( stderr, "%s: bad --library-path: %s\n",
+ options->common.progname, arg + 15 );
+ return( -1 );
+ }
+ }
+ else if( !strcmp( arg, "--library-path" ) )
+ {
+ if( ++i >= argc || argv[i][0] == 0 ||
+ mcpu_tool_add_library_path(&options->common, argv[i]) != 0 )
+ {
+ fprintf( stderr, "%s: argument missing or invalid after --library-path\n",
+ options->common.progname );
+ return( -1 );
+ }
+ }
+ else if( !strncmp( arg, "--ldpreload=", 12 ) )
+ {
+ if( options->ldpreload != NULL || !arg[12] )
+ {
+ fprintf( stderr,
+ "%s: --ldpreload must be supplied once with a non-empty ':' list\n",
+ options->common.progname );
+ return( -1 );
+ }
+ options->ldpreload = arg + 12;
+ }
+ else if( !strncmp( arg, "--steps=", 8 ) )
+ {
+ if( parse_u64(arg + 8, &options->steps) || !options->steps )
+ {
+ fprintf( stderr, "%s: --steps must be positive\n",
+ options->common.progname );
+ return( -1 );
+ }
+ options->steps_set = 1;
+ }
+ else if( !strcmp( arg, "--init-only" ) )
+ options->init_only = 1;
+ else if( !strncmp( arg, "--digits=", 9 ) )
+ {
+ char *end;
+ unsigned long n;
+
+ errno = 0;
+ n = strtoul( arg + 9, &end, 10 );
+ if( errno || end == arg + 9 || *end || !n || n > 1000000UL )
+ {
+ fprintf( stderr, "%s: bad --digits value\n",
+ options->common.progname );
+ return( -1 );
+ }
+ options->digits = (unsigned)n;
+ }
+ else if( !strncmp( arg, "--stack-size", 12 ) ||
+ !strncmp( arg, "--heap-size", 11 ) ||
+ !strncmp( arg, "--heap-limit", 12 ) )
+ {
+ fprintf( stderr,
+ "%s: STACK/HEAP sizing is part of the MELF executable ABI; "
+ "runtime size options were removed\n",
+ options->common.progname );
+ return( -1 );
+ }
+ else if( arg[0] == '-' )
+ {
+ fprintf( stderr, "%s: unknown option: %s\n",
+ options->common.progname, arg );
+ mcpu_run_usage( options );
+ return( -1 );
+ }
+ else
+ {
+ options->exe = arg;
+ exearg = i;
+ break;
+ }
+ }
+
+ if( options->common.help ||
+ mcpu_tool_has_information_action(&options->common) )
+ return( 0 );
+
+ if( options->exe == NULL )
+ {
+ mcpu_run_usage( options );
+ return( -1 );
+ }
+
+ options->program_argc = argc - exearg;
+ options->program_argv = argv + exearg;
+ return( 0 );
+
+} /* End of mcpu_run_handle_options() */