#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #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= - 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@ - print a signed integer register after stop\n" " --real-reg=r@ - print a real register after stop\n" " --int-data=@ - print a signed integer object from DATA memory\n" " --real-data=@ - print a real object from DATA memory\n" " --raw-data=@ - dump bytes from .data, clipped at .data end\n" " --int-control=@ - print a signed integer object from CONTROL memory\n" " --real-control=@ - print a real object from CONTROL memory\n" " --raw-control=@ - dump bytes from .control, clipped at .control end\n" " --int-heap=@ - print a signed integer object from HEAP memory\n" " --real-heap=@ - print a real object from HEAP memory\n" " --raw-heap=@ - dump bytes from HEAP, clipped at HEAP end\n" " --int-input=@ - print a signed integer object from INPUT memory\n" " --real-input=@ - print a real object from INPUT memory\n" " --raw-input=@ - dump bytes from .input, clipped at .input end\n" " --int-output=@ - print a signed integer object from OUTPUT memory\n" " --real-output=@ - print a real object from OUTPUT memory\n" " --raw-output=@ - dump bytes from .output, clipped at .output end\n" " --int-trace=@ - print a signed integer object from TRACE memory\n" " --real-trace=@ - print a real object from TRACE memory\n" " --raw-trace=@ - dump bytes from .trace, clipped at .trace end\n" " --before-run= - execute host action before first guest instruction\n" " --after-init= - execute host action after INIT (or empty INIT stage)\n" " --on-step= - execute host action after every LIFE END boundary\n" " --after-run= - execute host action after final execution stop\n" " --digits= - mantissa digits for real output (default: 80)\n" " --init-only - execute INIT through END, then stop\n" " --steps= - 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@[,...]\n" " print-real-regs:r@[,...]\n" " set-int-regs:r@=[,...]\n" " set-real-regs:r@=[,...]\n" " print-int-:@[,...]\n" " print-real-:@[,...]\n" " set-int-:@=[,...]\n" " set-real-:@=[,...]\n" " print-raw-:@\n\n" " 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-* , 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() */