* Add 'avrr' and 'avrc' commands to setup vm regs

* Many minor random fixes
* RVm api has an own architecture description file
  - r_vm_set_arch(vm, arch, bits);
  - add x86-16, x86-32, x86-64, arm-32
This commit is contained in:
pancake 2010-08-23 00:48:44 +02:00
parent 4d50a86855
commit 8f6bdc3560
16 changed files with 265 additions and 107 deletions

8
TODO
View File

@ -60,15 +60,9 @@ TODO edu
TODO pancake
------------
* r_asm must provide a template configuration file for r_vm
- r_asm must provide evaluable strings as internal representation (microcode)
* Import r_vm register values from flags or from r_debug->r_reg
- r_vm must use mmu cache when emulating code
- use the one from r_io? and deprecate vm->mmu_cache?
- as r_debug does for r_reg
.-----------------.
| r_asm > r_vm |
| r_debug > r_reg |
`-----------------'
{
* Implement RAnalCall (analyze function arguments, return values, propagate types..)
- define number of arguments for given function

View File

@ -347,7 +347,7 @@ static void r_print_disasm(RPrint *p, RCore *core, ut64 addr, ut8 *buf, int len,
switch (analop.type) {
case R_ANAL_OP_TYPE_SWI:
{
int eax = (int)r_vm_reg_get (core->vm, "eax");
int eax = (int)r_vm_reg_get (core->vm, core->vm->cpu.ret); //"eax");
RSyscallItem *si = r_syscall_get (core->syscall, eax, (int)analop.value);
if (si) {
//DEBUG r_cons_printf (" ; sc[0x%x][%d]=%s(", (int)analop.value, eax, si->name);
@ -2050,6 +2050,8 @@ static int cmd_anal(void *data, const char *input) {
" avx N ; execute N instructions from cur seek\n"
" av- ; restart vm using asm.arch\n"
" avr eax ; show register eax\n"
" avrr eax ; set return register\n" // TODO .merge avrr and avrc
" avrc eip esp ebp ; set basic cpu registers PC, SP, BP\n"
" avra ; show register aliases\n"
" avra al eax=0xff ; define 'get' alias for register 'al'\n"
" avrt ; list valid register types\n"
@ -2742,7 +2744,7 @@ static int cmd_meta(void *data, const char *input) {
case ' ':
{
int size;
const char *fmt= NULL;
const char *fmt = NULL;
const char *ptr, *name = input+2;
ptr = strchr (name, ' ');
if (ptr) {
@ -2940,6 +2942,7 @@ static int r_core_cmd_pipe(RCore *core, char *radare_cmd, char *shell_cmd) {
return status;
#else
#warning r_core_cmd_pipe UNIMPLEMENTED FOR THIS PLATFORM
eprintf ("r_core_cmd_pipe: unimplemented for this platform\n");
return -1;
#endif
}

View File

@ -115,6 +115,7 @@ static int config_asmarch_callback(void *user, void *data) {
if (!r_syscall_setup (core->syscall, node->value,
r_config_get (core->config, "asm.os")))
eprintf ("asm.arch: Cannot setup syscall os/arch for '%s'\n", node->value);
r_vm_set_arch (core->vm, node->value, core->assembler->bits);
return R_TRUE;
}
@ -143,6 +144,8 @@ static int config_asm_bits_callback(void *user, void *data) {
}
if (!r_anal_set_bits (core->anal, node->i_value))
eprintf ("asm.arch: Cannot setup '%i' bits analysis engine\n", (int)node->i_value);
if(core->assembler->cur)
r_vm_set_arch (core->vm, core->assembler->cur->name, node->i_value);
// TODO: change debugger backend bit profile here
return ret;
}

View File

@ -87,7 +87,6 @@ typedef struct r_asm_plugin_t {
int *bits;
int (*init)(void *user);
int (*fini)(void *user);
const char *(*profile)();
int (*disassemble)(RAsm *a, struct r_asm_aop_t *aop, ut8 *buf, ut64 len);
int (*assemble)(RAsm *a, struct r_asm_aop_t *aop, const char *buf);
RAsmModifyCallback modify;

View File

@ -83,9 +83,11 @@ static ut64 r_vm_get_math(struct r_vm_t *vm, const char *str);
R_API void r_vm_print(struct r_vm_t *vm, int type);
R_API int r_vm_import(struct r_vm_t *vm, int in_vm);
R_API void r_vm_cpu_call(struct r_vm_t *vm, ut64 addr);
R_API int r_vm_set_arch(RVm *vm, const char *name, int bits);
R_API RVm *r_vm_new();
R_API void r_vm_reset(RVm *vm);
R_API int r_vm_init(struct r_vm_t *vm, int init);
R_API int r_vm_cmd_eval(RVm *vm, const char *cmd);
R_API int r_vm_eval_cmp(struct r_vm_t *vm, const char *str);
R_API int r_vm_eval_eq(struct r_vm_t *vm, const char *str, const char *val);
R_API int r_vm_eval_single(struct r_vm_t *vm, const char *str);

View File

@ -24,7 +24,7 @@ R_API struct r_io_t *r_io_new() {
}
R_API int r_io_is_listener(RIO *io) {
if (io->plugin->listener)
if (io && io->plugin && io->plugin->listener)
return io->plugin->listener (io);
return R_FALSE;
}

View File

@ -92,7 +92,7 @@ R_API int r_syscall_setup_file(RSyscall *ctx, const char *path) {
R_API RSyscallItem *r_syscall_get(RSyscall *ctx, int num, int swi) {
int i;
for (i=0; ctx->sysptr[i].num; i++) {
for (i=0; ctx->sysptr[i].name; i++) {
if (num == ctx->sysptr[i].num && \
(swi == -1 || swi == ctx->sysptr[i].swi))
return &ctx->sysptr[i];

View File

@ -1,6 +1,9 @@
NAME=r_vm
DEPS=r_util
OBJ=vm.o mmu.o reg.o extra.o setup.o stack.o op.o
OBJ=p/plugins.h vm.o mmu.o reg.o extra.o setup.o stack.o op.o
include ../rules.mk
p/plugins.h:
cd p && ${MAKE} plugins.h

View File

@ -1,4 +1,4 @@
/* radare - LGPL - Copyright 2008-2009 pancake<nopcode.org> */
/* radare - LGPL - Copyright 2008-2010 pancake<nopcode.org> */
#include "r_vm.h"
@ -24,6 +24,7 @@ R_API int r_vm_op_eval(struct r_vm_t *vm, const char *str) {
memcpy(s, str, len);
r_str_subchr (s, ',', 0);
r_str_subchr (s, '\t', 0);
r_str_subchr (s, '#', 0);
nargs = r_str_word_set0(s);
arg0 = r_str_word_get0(s, 0);
@ -80,7 +81,7 @@ R_API int r_vm_op_eval(struct r_vm_t *vm, const char *str) {
}
/* TODO : Allow to remove and so on */
R_API int r_vm_op_cmd(struct r_vm_t *vm, const char *op) {
R_API int r_vm_op_cmd(RVm *vm, const char *op) {
char *cmd, *ptr;
int len = strlen (op)+1;
if (*op==' ')
@ -89,9 +90,13 @@ R_API int r_vm_op_cmd(struct r_vm_t *vm, const char *op) {
memcpy (cmd, op, len);
ptr = strchr (cmd, ' ');
if (ptr) {
// XXX: merge those two functions in one
ptr[0]='\0';
// eprintf("vm: opcode '%s' added\n", cmd);
r_vm_op_add (vm, cmd, ptr+1);
if (!memcmp (op, "avo", 3)) {
r_vm_op_add (vm, cmd, ptr+1);
if (vm->log)
eprintf("vm: opcode '%s' added\n", cmd);
} else r_vm_cmd_reg (vm, op);
} else r_vm_cmd_op_help ();
return 0;
}

15
libr/vm/p/Makefile Normal file
View File

@ -0,0 +1,15 @@
PLUGINS=x86.16 x86.32 x86.64 arm.32
all: plugins.h
plugins.h:
:> plugins.h
for a in ${PLUGINS} ; do \
echo "static const char *vmprofile_$${a} = \\" | sed -e 's,\.,_,g' > $${a}.h ; \
cat $${a}.vm | sed -e 's,^,\t",' -e 's,\.,_,g' -e 's,$$,\\n",g' >> $${a}.h ; \
echo ";" >> $${a}.h ; \
echo "#include \"$${a}.h\"" >> plugins.h ; \
done
clean:
rm -f *.h

31
libr/vm/p/arm.32.vm Normal file
View File

@ -0,0 +1,31 @@
avr+ r0 int32
avr+ r1 int32
avr+ r2 int32
avr+ r3 int32
avr+ r4 int32
avr+ r5 int32
avr+ r6 int32
avr+ r7 int32
avr+ r8 int32
avr+ r9 int32
avr+ r10 int32
avr+ r11 int32
avr+ r12 int32
avr+ r13 int32
avr+ r14 int32
avr+ r15 int32
avra pc r15
avra sp r14
avo mov $1=$2
avo cmp tmp=$1,$1-=$2,$1=tmp
avo sub $1=$2-$3
avo add $1=$2+$3
avo strb $2=$1
avo bne jnz $1
avo beq jz $1
avo b jmp $1
avo bl call $1
avo svc int $1
avrc pc sp bp
avrf zf
avrr r0

29
libr/vm/p/x86.16.vm Normal file
View File

@ -0,0 +1,29 @@
avo mov $1=$2
avo lea $1=$2
avo add $1=$1+$2
avo sub $1=$1-$2
avo jmp eip=$1
avo push esp=esp-4,[esp]=$1
avo pop $1=[esp],esp=esp+4
avo call esp=esp-4,[esp]=eip+$$$,eip=$1
avo ret eip=[esp],esp=esp+4
avr+ eax int32
avr+ ebx int32
avr+ ecx int32
avr+ edx int32
avr+ esi int32
avr+ edi int32
avr+ eip int32
avr+ esp int32
avr+ ebp int32
avr+ ax int16
avr+ zf bit
avr+ cf bit
avra ax ax=eax&0xffff eax=eax>16,eax=eax<16 eax=eax|ax
avr+ al int8
avr+ ah int8
avra al al=eax&0xff al=al&0xff,eax=eax>16,eax=eax<16,eax=eax|al
avra ah ah=eax&0xff00,ah=ah>8 eax=eax&0xFFFF00ff,ah=ah<8,eax=eax|ah,ah=ah>8
avrc eip esp ebp
avrf zf
avrr eax

30
libr/vm/p/x86.32.vm Normal file
View File

@ -0,0 +1,30 @@
avo mov $1=$2
avo lea $1=$2
avo add $1=$1+$2
avo sub $1=$1-$2
avo jmp eip=$1
avo cmp tmp=$1,$1-=$2,$1=tmp
avo push esp=esp-4,[esp]=$1
avo pop $1=[esp],esp=esp+4
avo call esp=esp-4,[esp]=eip+$$$,eip=$1
avo ret eip=[esp],esp=esp+4
avr+ eax int32
avr+ ebx int32
avr+ ecx int32
avr+ edx int32
avr+ esi int32
avr+ edi int32
avr+ eip int32
avr+ esp int32
avr+ ebp int32
avr+ ax int16
avr+ zf bit
avr+ cf bit
avra ax ax=eax&0xffff eax=eax>16,eax=eax<16 eax=eax|ax
avr+ al int8
avr+ ah int8
avra al al=eax&0xff al=al&0xff,eax=eax>16,eax=eax<16,eax=eax|al
avra ah ah=eax&0xff00,ah=ah>8 eax=eax&0xFFFF00ff,ah=ah<8,eax=eax|ah,ah=ah>8
avrc eip esp ebp
avrf zf
avrr eax

30
libr/vm/p/x86.64.vm Normal file
View File

@ -0,0 +1,30 @@
avo mov $1=$2
avo lea $1=$2
avo add $1=$1+$2
avo sub $1=$1-$2
avo jmp rip=$1
avo push rsp=rsp-4,[rsp]=$1
avo pop $1=[rsp],rsp=rsp+4
avo call rsp=esp-4,[rsp]=rip+$$$,rip=$1
avo ret rip=[rsp],rsp=rsp+4
avr+ rax int64
avr+ rbx int64
avr+ rcx int64
avr+ rdx int64
avr+ rsi int64
avr+ rdi int64
avr+ rip int64
avr+ rsp int64
avr+ rbp int64
avr+ eax int32
avr+ ax int16
avr+ zf bit
avr+ cf bit
avra ax ax=eax&0xffff eax=eax>16,eax=eax<16 eax=eax|ax
avr+ al int8
avr+ ah int8
avra al al=eax&0xff al=al&0xff,rax=eax>16,eax=eax<16,eax=eax|al
avra ah ah=eax&0xff00,ah=ah>8 eax=eax&0xFFFF00ff,ah=ah<8,eax=eax|ah,ah=ah>8
avrc rip rsp rbp
avrf zf
avrr rax

View File

@ -63,6 +63,7 @@ R_API int r_vm_reg_del(struct r_vm_t *vm, const char *name) {
R_API int r_vm_reg_set(struct r_vm_t *vm, const char *name, ut64 value) {
struct list_head *pos;
if(name)
list_for_each(pos, &vm->regs) {
struct r_vm_reg_t *r = list_entry(pos, struct r_vm_reg_t, list);
if (!strcmp(name, r->name)) {
@ -118,24 +119,59 @@ int r_vm_reg_alias(struct r_vm_t *vm, const char *name, const char *get, const c
return 1;
}
}
fprintf(stderr, "Register '%s' not defined.\n", name);
eprintf ("Register '%s' not defined.\n", name);
return 0;
}
R_API int r_vm_cmd_eval(RVm *vm, const char *cmd) {
char *next;
do {
next = strchr (cmd,'\n');
if (next) {
*next=0;
next++;
}
if (strlen(cmd)>2)
r_vm_cmd_reg (vm, cmd+2);
cmd = next;
} while (next);
return 1;
}
R_API int r_vm_cmd_reg(struct r_vm_t *vm, const char *_str) {
char *str, *ptr;
int len;
len = strlen(_str)+1;
str = alloca(len);
memcpy(str, _str, len);
len = strlen (_str)+1;
str = alloca (len);
memcpy (str, _str, len);
if (str==NULL || str[0]=='\0') {
/* show all registers */
r_vm_print(vm, -1);
r_vm_print (vm, -1);
return 0;
}
if (str[0]=='o') {
r_vm_cmd_op (vm, str+2);
return 0;
}
strcpy(str, str+1);
switch(str[0]) {
case 'r':
r_vm_setup_ret (vm, str+2);
break;
case 'c':
{
char *sp, *bp, *pc = str+2;
sp = strchr(pc, ' ');
if (!sp) return 0;
*sp=0;sp++;
bp = strchr(sp, ' ');
if (!sp) return 0;
*bp=0;bp++;
r_vm_setup_cpu (vm, pc, sp, bp);
}
break;
case 'a':
if (str[1]==' ') {
char *get,*set;
@ -174,6 +210,9 @@ R_API int r_vm_cmd_reg(struct r_vm_t *vm, const char *_str) {
INIT_LIST_HEAD(&vm->regs); // XXX Memory leak
else r_vm_reg_del(vm, str);
break;
case 'f':
r_vm_setup_flags(vm, str+2);
break;
default:
for(;str&&*str==' ';str=str+1);
ptr = strchr(str, '=');
@ -191,7 +230,7 @@ R_API int r_vm_cmd_reg(struct r_vm_t *vm, const char *_str) {
r_vm_print(vm, r_vm_reg_type_i(str+1));
} else {
/* show single registers */
printf("%s = 0x%08"PFMT64x"\n", str, r_vm_reg_get(vm, str));
eprintf("%s = 0x%08"PFMT64x"\n", str, r_vm_reg_get(vm, str));
}
}
}

View File

@ -1,6 +1,7 @@
/* radare - LGPL - Copyright 2008-2010 pancake<nopcode.org> */
#include "r_vm.h"
#include "p/plugins.h"
/* TODO: move into r_vm_t */
int vm_arch = -1;
@ -14,8 +15,8 @@ static ut64 r_vm_get_value(struct r_vm_t *vm, const char *str) {
struct aop_t aop;
char w[32];
if (str[2]=='$') { // $$$
ret = r_vm_reg_get(vm, vm->cpu.pc);
arch_aop(ret , config.block,&aop);
ret = r_vm_reg_get (vm, vm->cpu.pc);
arch_aop (ret , config.block, &aop);
return aop.length;
} else // $$
return config.seek;
@ -23,11 +24,11 @@ static ut64 r_vm_get_value(struct r_vm_t *vm, const char *str) {
}
if (str[0]=='0' && str[1]=='x')
sscanf(str, "0x%"PFMT64x"", &ret);
sscanf (str, "0x%"PFMT64x"", &ret);
else
if (str[0]>='0' && str[0]<='9')
sscanf(str, "%"PFMT64d"", &ret);
else ret = r_vm_reg_get(vm, str);
sscanf (str, "%"PFMT64d"", &ret);
else ret = r_vm_reg_get (vm, str);
return ret;
}
@ -100,10 +101,10 @@ R_API void r_vm_print(RVm *vm, int type) {
list_for_each (pos, &vm->regs) {
struct r_vm_reg_t *r = list_entry(pos, struct r_vm_reg_t, list);
if (type == -2) {
printf("f vm.%s @ 0x%08"PFMT64x"\n", r->name, r->value);
eprintf("f vm.%s @ 0x%08"PFMT64x"\n", r->name, r->value);
} else {
if (type == -1 || type == r->type)
printf(".%s\t%s = 0x%08"PFMT64x"\n",
eprintf(".%s\t%s = 0x%08"PFMT64x"\n",
r_vm_reg_type(r->type), r->name,
(r->get!=NULL)?r_vm_reg_get(vm, r->name):r->value);
}
@ -135,9 +136,9 @@ R_API ut64 r_vm_reg_get(struct r_vm_t *vm, const char *name) {
if (name[len-1]==']')
len--;
list_for_each(pos, &vm->regs) {
struct r_vm_reg_t *r = list_entry(pos, struct r_vm_reg_t, list);
if (!strncmp(name, r->name, len)) {
list_for_each (pos, &vm->regs) {
RVmReg *r = list_entry(pos, struct r_vm_reg_t, list);
if (!strncmp (name, r->name, len)) {
if (vm->rec==NULL && r->get != NULL) {
vm->rec = r;
r_vm_eval(vm, r->get);
@ -175,11 +176,47 @@ R_API void r_vm_cpu_call(struct r_vm_t *vm, ut64 addr) {
R_API RVm *r_vm_new() {
RVm *vm = R_NEW (RVm);
r_vm_init (vm, 1);
if (vm) r_vm_init (vm, 1);
return vm;
}
R_API int r_vm_init(struct r_vm_t *vm, int init) {
R_API int r_vm_set_arch(RVm *vm, const char *name, int bits) {
const char *profile = NULL;
if (strstr (name, "x86")) {
switch (bits) {
case 16:
profile = vmprofile_x86_16;
break;
case 32:
profile = vmprofile_x86_32;
break;
case 64:
profile = vmprofile_x86_64;
break;
}
} else
if (strstr (name, "arm")) {
switch (bits) {
case 16:
//profile = vmprofile_arm_16;
break;
case 32:
profile = vmprofile_arm_32;
break;
}
}
if (profile) {
char *str = strdup (profile);
r_vm_init (vm, 2);
r_vm_cmd_eval (vm, str);
free (str);
} else eprintf ("No profile found for '%s' %d\n", name, bits);
return 0;
}
// This is conceptually rotten
R_API int r_vm_init(RVm *vm, int init) {
#if 0
if (config.arch != vm_arch)
init = 1;
@ -191,80 +228,16 @@ R_API int r_vm_init(struct r_vm_t *vm, int init) {
INIT_LIST_HEAD (&vm->regs);
INIT_LIST_HEAD (&vm->ops);
memset (&vm->cpu, '\0', sizeof(RVmCpu));
if (init==2)
return 0;
}
//vm_mmu_real(vm, config_get_i("vm.realio"));
/* vm_dbg_arch_x86_nregs */
/* XXX: this is hardcoded ..should be moved outside or as in plugins */
switch (1) { //config.arch) {
#if 0
case ARCH_X86_64:
vm_reg_add("rax", R_VMREG_INT64, 0);
vm_reg_add("rbx", R_VMREG_INT64, 0);
vm_reg_add("rcx", R_VMREG_INT64, 0);
vm_reg_add("rdx", R_VMREG_INT64, 0);
vm_reg_add("rdi", R_VMREG_INT64, 0);
vm_reg_add("rsi", R_VMREG_INT64, 0);
vm_reg_add("rip", R_VMREG_INT64, 0);
case ARCH_X86:
#endif
default:
//fprintf(stderr,"R_VM: Initialized\n");
r_vm_op_add(vm,"mov", "$1=$2");
r_vm_op_add(vm,"lea", "$1=$2");
r_vm_op_add(vm,"add", "$1=$1+$2");
r_vm_op_add(vm,"sub", "$1=$1-$2");
r_vm_op_add(vm,"jmp", "eip=$1");
r_vm_op_add(vm,"push", "esp=esp-4,[esp]=$1");
r_vm_op_add(vm,"pop", "$1=[esp],esp=esp+4");
r_vm_op_add(vm,"call", "esp=esp-4,[esp]=eip+$$$,eip=$1");
r_vm_op_add(vm,"ret", "eip=[esp],esp=esp+4");
r_vm_reg_add(vm,"eax", R_VMREG_INT32, 0);
r_vm_reg_add(vm,"ax", R_VMREG_INT16, 0);
r_vm_reg_alias(vm, "ax","ax=eax&0xffff", "eax=eax>16,eax=eax<16,eax=eax|ax");
r_vm_reg_add(vm,"al", R_VMREG_INT8, 0);
r_vm_reg_alias(vm, "al","al=eax&0xff", "al=al&0xff,eax=eax>16,eax=eax<16,eax=eax|al");
r_vm_reg_add(vm,"ah", R_VMREG_INT8, 0);
r_vm_reg_alias(vm, "ah","ah=eax&0xff00,ah=ah>8", "eax=eax&0xFFFF00ff,ah=ah<8,eax=eax|ah,ah=ah>8");
r_vm_reg_add(vm,"ebx", R_VMREG_INT32, 0);
r_vm_reg_add(vm,"ecx", R_VMREG_INT32, 0);
r_vm_reg_add(vm,"edx", R_VMREG_INT32, 0);
r_vm_reg_add(vm,"esi", R_VMREG_INT32, 0);
r_vm_reg_add(vm,"edi", R_VMREG_INT32, 0);
r_vm_reg_add(vm,"eip", R_VMREG_INT32, 0);
r_vm_reg_add(vm,"esp", R_VMREG_INT32, 0);
r_vm_reg_add(vm,"ebp", R_VMREG_INT32, 0);
r_vm_reg_add(vm,"zf", R_VMREG_BIT, 0);
r_vm_reg_add(vm,"cf", R_VMREG_BIT, 0); // ...
r_vm_setup_cpu(vm, "eip", "esp", "ebp");
r_vm_setup_flags(vm, "zf");
//vm_setup_call("[ebp-4]", "[ebp-8]", "[ebp-12]", "edx");
r_vm_setup_fastcall(vm, "eax", "ebx", "ecx", "edx");
//vm_setup_loop("ecx");
//vm_setup_copy("esi", "edi");
r_vm_setup_ret(vm, "eax");
// TODO: do the same for fpregs and mmregs
#if 0
if (init) // XXX
r_vm_arch_x86_init(vm);
#endif
break;
#if 0
case ARCH_MIPS:
#if 0
vm_nregs = vm_arch_mips_nregs;
vm_regs = vm_arch_mips_regs;
vm_regs_str = vm_arch_mips_regs_str;
#endif
// TODO: do the same for fpregs and mmregs
if (init)
vm_arch_mips_init();
break;
//vm_regs = NULL;
#endif
}
//vm_setup_call("[ebp-4]", "[ebp-8]", "[ebp-12]", "edx");
r_vm_setup_fastcall (vm, "eax", "ebx", "ecx", "edx");
//vm_setup_loop("ecx");
//vm_setup_copy("esi", "edi");
// TODO: do the same for fpregs and mmregs
return 0;
}
@ -408,6 +381,7 @@ R_API int r_vm_eval_single(RVm *vm, const char *str) {
ptr = alloca(len);
memcpy(ptr, str, len);
//eprintf("EVAL(%s)\n", str);
/* TODO: sync with r1 */
eq = strchr(ptr, '=');
if (eq) {
@ -593,7 +567,8 @@ R_API int r_vm_cmd_op(RVm *vm, const char *op) {
ptr = strchr (cmd, ' ');
if (ptr) {
ptr[0]='\0';
eprintf ("vm: opcode '%s' added\n", cmd);
if(vm->log)
eprintf ("vm: opcode '%s' added\n", cmd);
r_vm_op_add (vm, cmd, ptr+1);
} else r_vm_cmd_op_help ();
return 0;