#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "mcpu-exec.h" #include "mcpu-dependency.h" #include "mcpu-run-options.h" static const char * memory_name( memory_kind memory ) { switch( memory ) { case MEMORY_DATA: return( "DATA" ); case MEMORY_CONTROL: return( "CONTROL" ); case MEMORY_HEAP: return( "HEAP" ); case MEMORY_INPUT: return( "INPUT" ); case MEMORY_OUTPUT: return( "OUTPUT" ); case MEMORY_TRACE: return( "TRACE" ); default: return( "UNKNOWN" ); } } /* End of memory_name() */ static __mpu_UNIT_t * memory_ptr( memory_kind memory ) { switch( memory ) { case MEMORY_DATA: return( __cpu_data_ptr() ); case MEMORY_CONTROL: return( __cpu_control_ptr() ); case MEMORY_HEAP: return( __cpu_heap_ptr() ); case MEMORY_INPUT: return( __cpu_input_ptr() ); case MEMORY_OUTPUT: return( __cpu_output_ptr() ); case MEMORY_TRACE: return( __cpu_trace_ptr() ); default: return( (__mpu_UNIT_t *)0 ); } } /* End of memory_ptr() */ static __mpu_uint64_t memory_size( memory_kind memory ) { switch( memory ) { case MEMORY_DATA: return( __cpu_data_size() ); case MEMORY_CONTROL: return( __cpu_control_size() ); case MEMORY_HEAP: return( __cpu_heap_size() ); case MEMORY_INPUT: return( __cpu_input_size() ); case MEMORY_OUTPUT: return( __cpu_output_size() ); case MEMORY_TRACE: return( __cpu_trace_size() ); default: return( 0 ); } } /* End of memory_size() */ static int make_integer_format( __mpu_char16_t *format, size_t size, unsigned int bits, int newline ) { char ascii[64]; int len, i; len = snprintf( ascii, sizeof( ascii ), newline ? "%%Z%ud\n" : "%%Z%ud", bits ); if( len < 0 || (size_t)len >= sizeof( ascii ) || (size_t)len + 1U > size ) return( -1 ); for( i = 0; i <= len; ++i ) format[i] = (__mpu_char16_t)(unsigned char)ascii[i]; return( 0 ); } /* End of make_integer_format() */ static __mpu_UNIT_t * memory_object_ptr( const print_spec *spec, __mpu_uint64_t bytes, const char *object_name ) { __mpu_UNIT_t *p; __mpu_uint64_t size; p = memory_ptr( spec->memory ); size = memory_size( spec->memory ); if( !p || spec->address > size || bytes > size - spec->address ) { fprintf( stderr, "mcpu-run: %s %s object is outside %s memory\n", object_name, memory_name( spec->memory ), memory_name( spec->memory ) ); return( (__mpu_UNIT_t *)0 ); } return( p + (size_t)spec->address ); } /* End of memory_object_ptr() */ static int print_integer_value( const print_spec *spec, int newline ) { __mpu_char16_t format[64]; __mpu_UNIT_t *value; __mpu_uint64_t bytes; if( make_integer_format( format, sizeof( format ) / sizeof( format[0] ), spec->bits, newline ) ) return( -1 ); bytes = spec->bits / 8U; if( spec->kind == PRINT_INTEGER_REGISTER ) value = __cpu_reg_ptr( spec->bits, (__mpu_uint32_t)spec->address ); else if( spec->kind == PRINT_INTEGER_MEMORY ) value = memory_object_ptr( spec, bytes, "integer" ); else return( -1 ); if( !value ) return( -1 ); if( fflush( stdout ) ) return( -1 ); if( mpu_dprintf( STDOUT_FILENO, format, (mpu_int *)value ) < 0 ) { fprintf( stderr, "mcpu-run: LibMPUIO integer output failed\n" ); return( -1 ); } return( 0 ); } /* End of print_integer_value() */ static int print_integer( const print_spec *spec ) { return( print_integer_value( spec, 1 ) ); } /* End of print_integer() */ static int make_real_format( __mpu_char16_t *format, size_t size, unsigned int bits, unsigned int digits, int newline ) { char ascii[96]; int len, i; len = snprintf( ascii, sizeof( ascii ), newline ? "%%.%uZ%uR\n" : "%%.%uZ%uR", digits, bits ); if( len < 0 || (size_t)len >= sizeof( ascii ) || (size_t)len + 1U > size ) return( -1 ); for( i = 0; i <= len; ++i ) format[i] = (__mpu_char16_t)(unsigned char)ascii[i]; return( 0 ); } /* End of make_real_format() */ static int print_real_value( const print_spec *spec, unsigned int digits, int newline ) { __mpu_char16_t format[96]; __mpu_UNIT_t *value; __mpu_uint64_t bytes; if( make_real_format( format, sizeof( format ) / sizeof( format[0] ), spec->bits, digits, newline ) ) return( -1 ); bytes = spec->bits / 8U; if( spec->kind == PRINT_REAL_REGISTER ) value = __cpu_reg_ptr( spec->bits, (__mpu_uint32_t)spec->address ); else if( spec->kind == PRINT_REAL_MEMORY ) value = memory_object_ptr( spec, bytes, "real" ); else return( -1 ); if( !value ) return( -1 ); if( fflush( stdout ) ) return( -1 ); if( mpu_dprintf( STDOUT_FILENO, format, (mpu_real *)value ) < 0 ) { fprintf( stderr, "mcpu-run: LibMPUIO real output failed\n" ); return( -1 ); } return( 0 ); } /* End of print_real_value() */ static int print_real( const print_spec *spec, unsigned int digits ) { return( print_real_value( spec, digits, 1 ) ); } /* End of print_real() */ static int raw_memory_range( const print_spec *spec, const cpu_exec_loaded *loaded, __mpu_UNIT_t **ptr, __mpu_uint64_t *count ) { __mpu_uint64_t begin, end, size; __mpu_UNIT_t *p; if( spec->memory == MEMORY_DATA ) { if( !loaded->has_data_section ) { fprintf( stderr, "mcpu-run: executable has no .data section\n" ); return( -1 ); } begin = loaded->data_section_begin; if( loaded->data_section_size > UINT64_MAX - begin ) return( -1 ); end = begin + loaded->data_section_size; if( spec->address < begin || spec->address >= end ) { fprintf( stderr, "mcpu-run: raw DATA address is outside .data\n" ); return( -1 ); } p = __cpu_data_ptr(); } else { begin = 0; size = memory_size( spec->memory ); p = memory_ptr( spec->memory ); end = size; if( !p || spec->address >= end ) { fprintf( stderr, "mcpu-run: raw %s address is outside %s memory\n", memory_name( spec->memory ), memory_name( spec->memory ) ); return( -1 ); } } *count = spec->count; if( *count > end - spec->address ) *count = end - spec->address; *ptr = p + (size_t)spec->address; return( 0 ); } /* End of raw_memory_range() */ static int print_raw_memory( const print_spec *spec, const cpu_exec_loaded *loaded ) { __mpu_UNIT_t *p; __mpu_uint64_t count, off; if( spec->kind != PRINT_RAW_MEMORY ) return( -1 ); if( raw_memory_range( spec, loaded, &p, &count ) ) return( -1 ); printf( "%s memory dump: (%llu bytes):\n", memory_name( spec->memory ), (unsigned long long)count ); for( off = 0; off < count; off += 16 ) { unsigned int j; unsigned int row = (unsigned int)((count - off) > 16 ? 16 : (count - off)); printf( " %04llx : ", (unsigned long long)off ); for( j = 0; j < 16; ++j ) { if( j < row ) printf( "%02x ", (unsigned int)p[(size_t)off + j] ); else printf( " " ); } printf( " | " ); for( j = 0; j < row; ++j ) { unsigned int c = (unsigned int)p[(size_t)off + j]; putchar( c >= 0x20U && c <= 0x7eU ? (int)c : '.' ); } putchar( '\n' ); } return( ferror( stdout ) ? -1 : 0 ); } /* End of print_raw_memory() */ static int write_stdout( const char *s, size_t n ) { while( n ) { ssize_t rc = write( STDOUT_FILENO, s, n ); if( rc < 0 ) { if( errno == EINTR ) continue; return( -1 ); } if( !rc ) return( -1 ); s += (size_t)rc; n -= (size_t)rc; } return( 0 ); } /* End of write_stdout() */ static __mpu_char16_t * ascii_to_ucs2( const char *s ) { __mpu_char16_t *p; size_t i, n; if( !s ) return( (__mpu_char16_t *)0 ); n = strlen( s ); if( n > (SIZE_MAX / sizeof( *p )) - 1U ) return( (__mpu_char16_t *)0 ); p = (__mpu_char16_t *)malloc( (n + 1U) * sizeof( *p ) ); if( !p ) return( (__mpu_char16_t *)0 ); for( i = 0; i < n; ++i ) { unsigned char c = (unsigned char)s[i]; if( c >= 0x80U ) { free( p ); return( (__mpu_char16_t *)0 ); } p[i] = (__mpu_char16_t)c; } p[n] = 0; return( p ); } /* End of ascii_to_ucs2() */ static int scan_action_value( const char *value, unsigned int bits, int real_object, __mpu_UNIT_t *dst ) { __mpu_char16_t *text = (__mpu_char16_t *)0; __mpu_char16_t *format = (__mpu_char16_t *)0; __mpu_uint32_t psw; char ascii[64]; size_t n; int len, consumed = -1, rc; n = strlen( value ); len = snprintf( ascii, sizeof( ascii ), real_object ? "%%Z%uR%%n" : "%%Z%ui%%n", bits ); if( len < 0 || (size_t)len >= sizeof( ascii ) ) return( -1 ); text = ascii_to_ucs2( value ); format = ascii_to_ucs2( ascii ); if( !text || !format ) goto fail; psw = __cpu_get_psw(); if( real_object ) rc = mpu_sscanf( text, format, (mpu_real *)dst, &consumed ); else rc = mpu_sscanf( text, format, (mpu_int *)dst, &consumed ); __cpu_set_psw( psw ); free( format ); free( text ); if( rc != 1 || consumed < 0 || (size_t)consumed != n ) return( -1 ); return( 0 ); fail: free( format ); free( text ); return( -1 ); } /* End of scan_action_value() */ static void make_action_print_spec( const stage_action *action, const action_item *item, print_spec *spec ) { memset( spec, 0, sizeof( *spec ) ); spec->memory = action->memory; spec->bits = item->bits; spec->address = item->address; if( action->kind == ACTION_PRINT_INTEGER_REGISTER || action->kind == ACTION_SET_INTEGER_REGISTER ) spec->kind = PRINT_INTEGER_REGISTER; else if( action->kind == ACTION_PRINT_REAL_REGISTER || action->kind == ACTION_SET_REAL_REGISTER ) spec->kind = PRINT_REAL_REGISTER; else if( action->kind == ACTION_PRINT_INTEGER_MEMORY || action->kind == ACTION_SET_INTEGER_MEMORY ) spec->kind = PRINT_INTEGER_MEMORY; else if( action->kind == ACTION_PRINT_REAL_MEMORY || action->kind == ACTION_SET_REAL_MEMORY ) spec->kind = PRINT_REAL_MEMORY; } /* End of make_action_print_spec() */ static __mpu_UNIT_t * action_object_ptr( const stage_action *action, const action_item *item, const char *object_name ) { print_spec spec; make_action_print_spec( action, item, &spec ); if( spec.kind == PRINT_INTEGER_REGISTER || spec.kind == PRINT_REAL_REGISTER ) return( __cpu_reg_ptr( spec.bits, (__mpu_uint32_t)spec.address ) ); return( memory_object_ptr( &spec, spec.bits / 8U, object_name ) ); } /* End of action_object_ptr() */ static int execute_print_action( const stage_action *action, unsigned int digits ) { print_spec spec; size_t i; /* Validate the whole action before producing any output. */ for( i = 0; i < action->count; ++i ) { if( !action_object_ptr( action, &action->item[i], (action->kind == ACTION_PRINT_REAL_REGISTER || action->kind == ACTION_PRINT_REAL_MEMORY) ? "real" : "integer" ) ) return( -1 ); } for( i = 0; i < action->count; ++i ) { make_action_print_spec( action, &action->item[i], &spec ); if( i && write_stdout( " ", 1U ) ) return( -1 ); if( spec.kind == PRINT_INTEGER_REGISTER || spec.kind == PRINT_INTEGER_MEMORY ) { if( print_integer_value( &spec, 0 ) ) return( -1 ); } else { if( print_real_value( &spec, digits, 0 ) ) return( -1 ); } } if( write_stdout( "\n", 1U ) ) return( -1 ); return( 0 ); } /* End of execute_print_action() */ typedef struct __set_value { __mpu_UNIT_t *dst; __mpu_UNIT_t *value; size_t bytes; } set_value; static int execute_set_action( const stage_action *action ) { set_value *value; size_t i, j; int real_object; value = (set_value *)calloc( action->count, sizeof( *value ) ); if( !value ) return( -1 ); real_object = (action->kind == ACTION_SET_REAL_REGISTER || action->kind == ACTION_SET_REAL_MEMORY); /* Parse and validate every assignment before changing guest state. */ for( i = 0; i < action->count; ++i ) { value[i].bytes = action->item[i].bits / 8U; value[i].dst = action_object_ptr( action, &action->item[i], real_object ? "real" : "integer" ); if( !value[i].dst ) goto fail; value[i].value = (__mpu_UNIT_t *)calloc( value[i].bytes, 1U ); if( !value[i].value ) goto fail; if( scan_action_value( action->item[i].value, action->item[i].bits, real_object, value[i].value ) ) { fprintf( stderr, "mcpu-run: cannot parse stage value '%s' as %s%u\n", action->item[i].value, real_object ? "real" : "int", action->item[i].bits ); goto fail; } } for( i = 0; i < action->count; ++i ) memcpy( value[i].dst, value[i].value, value[i].bytes ); for( i = 0; i < action->count; ++i ) free( value[i].value ); free( value ); return( 0 ); fail: for( j = 0; j < action->count; ++j ) free( value[j].value ); free( value ); return( -1 ); } /* End of execute_set_action() */ static int execute_stage_action( const stage_action *action, unsigned int digits, const cpu_exec_loaded *loaded ) { switch( action->kind ) { case ACTION_PRINT_INTEGER_REGISTER: case ACTION_PRINT_REAL_REGISTER: case ACTION_PRINT_INTEGER_MEMORY: case ACTION_PRINT_REAL_MEMORY: return( execute_print_action( action, digits ) ); case ACTION_SET_INTEGER_REGISTER: case ACTION_SET_REAL_REGISTER: case ACTION_SET_INTEGER_MEMORY: case ACTION_SET_REAL_MEMORY: return( execute_set_action( action ) ); case ACTION_PRINT_RAW_MEMORY: if( print_raw_memory( &action->raw, loaded ) ) return( -1 ); if( fflush( stdout ) ) return( -1 ); return( 0 ); default: return( -1 ); } } /* End of execute_stage_action() */ static int execute_stage_action_list( const stage_action_list *list, unsigned int digits, const cpu_exec_loaded *loaded ) { size_t i; for( i = 0; i < list->count; ++i ) if( execute_stage_action( &list->action[i], digits, loaded ) ) return( -1 ); return( 0 ); } /* End of execute_stage_action_list() */ static int execution_fault( int rc ) { fprintf( stderr, "mcpu-run: execution fault %d at PC=0x%016llx, PSW=0x%08x\n", rc, (unsigned long long)__cpu_pc, (unsigned int)__cpu_get_psw()); return( 1 ); } static int mcpu_run_execute( const mcpu_run_options *options ) { unsigned long long completed = 0; cpu_exec_loaded loaded; char err[512]; char *resolved_preload = NULL; char *resolved_modules = NULL; size_t preload_count = 0; int rc; int host_status = 0; if( options == NULL ) return( 1 ); if( mcpu_run_resolve_preload(options, &resolved_preload, err, sizeof(err)) != 0 ) { fprintf( stderr, "%s: preload resolution error: %s\n", options->common.progname, err ); return( 1 ); } if( mcpu_dependency_build_load_list(&options->common, options->exe, resolved_preload, &resolved_modules, &preload_count, err, sizeof(err)) != 0 ) { fprintf( stderr, "%s: dependency resolution error: %s\n", options->common.progname, err ); free( resolved_preload ); return( 1 ); } __cpu_init(); /* Guest argv begins with the executable spelling supplied to mcpu-run. */ if( cpu_exec_load_resolved(options->exe, resolved_modules, preload_count, options->program_argc, options->program_argv, &loaded, err, sizeof(err)) ) { fprintf( stderr, "%s: load error: %s\n", options->common.progname, err ); __cpu_free_context(); free( resolved_modules ); free( resolved_preload ); return( 1 ); } if( execute_stage_action_list(&options->before_run, options->digits, &loaded) ) { fprintf( stderr, "%s: --before-run action failed\n", options->common.progname ); host_status = 1; goto done; } if( loaded.has_init ) { __cpu_pc = loaded.init_entry; rc = __cpu_run(); if( rc < 0 ) { host_status = execution_fault( rc ); goto done; } if( rc == CPU_EXEC_EXIT ) goto graceful_exit; if( __cpu_get_stop_reason() == CPU_STOP_HALT ) goto stopped; if( rc != CPU_EXEC_END ) { fprintf( stderr, "%s: INIT did not terminate with END\n", options->common.progname ); host_status = 1; goto done; } if( __cpu_sp != loaded.startup_base || __cpu_fp != loaded.stack_size ) { fprintf( stderr, "%s: INIT did not restore top-level STACK state\n", options->common.progname ); host_status = 1; goto done; } __cpu_pc = loaded.life_entry; } if( execute_stage_action_list(&options->after_init, options->digits, &loaded) ) { fprintf( stderr, "%s: --after-init action failed\n", options->common.progname ); host_status = 1; goto done; } if( options->init_only ) goto stopped; for( ; ; ) { rc = __cpu_run(); if( rc < 0 ) { host_status = execution_fault( rc ); goto done; } if( rc == CPU_EXEC_EXIT ) goto graceful_exit; if( __cpu_get_stop_reason() == CPU_STOP_HALT ) goto stopped; if( rc != CPU_EXEC_END ) { fprintf( stderr, "%s: LIFE phase stopped unexpectedly (%d)\n", options->common.progname, rc ); host_status = 1; goto done; } ++completed; if( execute_stage_action_list(&options->on_step, options->digits, &loaded) ) { fprintf( stderr, "%s: --on-step action failed after LIFE step %llu\n", options->common.progname, completed ); host_status = 1; goto done; } if( !options->steps_set || completed >= options->steps ) goto stopped; /* Resume from the PC already advanced past END. */ } graceful_exit: host_status = (int)__cpu_get_exit_status(); if( loaded.has_fini ) { if( __cpu_sp != loaded.startup_base || __cpu_fp != loaded.stack_size ) { fprintf( stderr, "%s: LIFE did not restore top-level STACK state before FINI\n", options->common.progname ); host_status = 1; goto done; } __cpu_pc = loaded.fini_entry; rc = __cpu_run(); if( rc < 0 ) { host_status = execution_fault( rc ); goto done; } if( rc != CPU_EXEC_END ) { fprintf( stderr, "%s: FINI must terminate with END\n", options->common.progname ); host_status = 1; goto done; } if( __cpu_sp != loaded.startup_base || __cpu_fp != loaded.stack_size ) { fprintf( stderr, "%s: FINI did not restore top-level STACK state\n", options->common.progname ); host_status = 1; goto done; } } stopped: if( execute_stage_action_list(&options->after_run, options->digits, &loaded) ) { fprintf( stderr, "%s: --after-run action failed\n", options->common.progname ); host_status = 1; goto done; } if( options->output.kind == PRINT_INTEGER_REGISTER || options->output.kind == PRINT_INTEGER_MEMORY ) { if( print_integer(&options->output) ) { host_status = 1; goto done; } } else if( options->output.kind == PRINT_REAL_REGISTER || options->output.kind == PRINT_REAL_MEMORY ) { if( print_real(&options->output, options->digits) ) { host_status = 1; goto done; } } else if( options->output.kind == PRINT_RAW_MEMORY ) { if( print_raw_memory(&options->output, &loaded) ) { host_status = 1; goto done; } } done: __cpu_free_context(); free( resolved_modules ); free( resolved_preload ); if( host_status < 0 || host_status > 255 ) return( 1 ); return( host_status ); } /* End of mcpu_run_execute() */ int main( int argc, char **argv ) { mcpu_run_options options; int rc = 2; mcpu_run_options_init( &options, argv[0] ); if( mcpu_run_handle_options(&options, argc, argv) != 0 ) goto done; if( options.common.help ) { mcpu_run_usage( &options ); rc = 0; goto done; } if( options.common.version_action != MCPU_VERSION_NONE ) { if( mcpu_version_print(options.common.progname, options.common.version_action) != 0 ) { rc = 1; goto done; } rc = 0; goto done; } if( mcpu_tool_options_prepare(&options.common, argv[0]) != 0 ) { rc = 1; goto done; } if( mcpu_tool_has_information_action(&options.common) ) { if( options.common.dump_config && mcpu_tool_dump_config(&options.common, stdout) != 0 ) { rc = 1; goto done; } if( options.common.print_search_dirs && mcpu_tool_print_search_dirs(&options.common, stdout) != 0 ) { rc = 1; goto done; } rc = 0; goto done; } rc = mcpu_run_execute( &options ); done: mcpu_run_options_free( &options ); return( rc ); } /* End of main() */