| /* Remote target system call support. | 
 |    Copyright 1997-2022 Free Software Foundation, Inc. | 
 |    Contributed by Cygnus Solutions. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | /* This interface isn't intended to be specific to any particular kind | 
 |    of remote (hardware, simulator, whatever).  As such, support for it | 
 |    (e.g. sim/common/callback.c) should *not* live in the simulator source | 
 |    tree, nor should it live in the gdb source tree.  K&R C must be | 
 |    supported.  */ | 
 |  | 
 | /* This must come before any other includes.  */ | 
 | #include "defs.h" | 
 |  | 
 | #include <errno.h> | 
 | #include <fcntl.h> | 
 | #include <stdarg.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <time.h> | 
 | #ifdef HAVE_UNISTD_H | 
 | #include <unistd.h> | 
 | #endif | 
 | #include <sys/stat.h> | 
 | #include <sys/types.h> | 
 |  | 
 | #include "ansidecl.h" | 
 | #include "libiberty.h" | 
 |  | 
 | #include "sim/callback.h" | 
 |  | 
 | #ifndef ENOSYS | 
 | #define ENOSYS EINVAL | 
 | #endif | 
 | #ifndef ENAMETOOLONG | 
 | #define ENAMETOOLONG EINVAL | 
 | #endif | 
 |  | 
 | /* Maximum length of a path name.  */ | 
 | #ifndef MAX_PATH_LEN | 
 | #define MAX_PATH_LEN 1024 | 
 | #endif | 
 |  | 
 | /* When doing file read/writes, do this many bytes at a time.  */ | 
 | #define FILE_XFR_SIZE 4096 | 
 |  | 
 | /* FIXME: for now, need to consider target word size.  */ | 
 | #define TWORD long | 
 | #define TADDR unsigned long | 
 |  | 
 | /* Path to be prepended to syscalls with absolute paths, and to be | 
 |    chdir:ed at startup, if not empty.  */ | 
 | char *simulator_sysroot = ""; | 
 |  | 
 | /* Utility of cb_syscall to fetch a path name or other string from the target. | 
 |    The result is 0 for success or a host errno value.  */ | 
 |  | 
 | int | 
 | cb_get_string (host_callback *cb, CB_SYSCALL *sc, char *buf, int buflen, | 
 | 	       TADDR addr) | 
 | { | 
 |   char *p, *pend; | 
 |  | 
 |   for (p = buf, pend = buf + buflen; p < pend; ++p, ++addr) | 
 |     { | 
 |       /* No, it isn't expected that this would cause one transaction with | 
 | 	 the remote target for each byte.  The target could send the | 
 | 	 path name along with the syscall request, and cache the file | 
 | 	 name somewhere (or otherwise tweak this as desired).  */ | 
 |       unsigned int count = (*sc->read_mem) (cb, sc, addr, p, 1); | 
 |  | 
 |       if (count != 1) | 
 | 	return EINVAL; | 
 |       if (*p == 0) | 
 | 	break; | 
 |     } | 
 |   if (p == pend) | 
 |     return ENAMETOOLONG; | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Utility of cb_syscall to fetch a path name. | 
 |    The buffer is malloc'd and the address is stored in BUFP. | 
 |    The result is that of get_string, but prepended with | 
 |    simulator_sysroot if the string starts with '/'. | 
 |    If an error occurs, no buffer is left malloc'd.  */ | 
 |  | 
 | static int | 
 | get_path (host_callback *cb, CB_SYSCALL *sc, TADDR addr, char **bufp) | 
 | { | 
 |   char *buf = xmalloc (MAX_PATH_LEN); | 
 |   int result; | 
 |   int sysroot_len = strlen (simulator_sysroot); | 
 |  | 
 |   result = cb_get_string (cb, sc, buf, MAX_PATH_LEN - sysroot_len, addr); | 
 |   if (result == 0) | 
 |     { | 
 |       /* Prepend absolute paths with simulator_sysroot.  Relative paths | 
 | 	 are supposed to be relative to a chdir within that path, but at | 
 | 	 this point unknown where.  */ | 
 |       if (simulator_sysroot[0] != '\0' && *buf == '/') | 
 | 	{ | 
 | 	  /* Considering expected rareness of syscalls with absolute | 
 | 	     file paths (compared to relative file paths and insn | 
 | 	     execution), it does not seem worthwhile to rearrange things | 
 | 	     to get rid of the string moves here; we'd need at least an | 
 | 	     extra call to check the initial '/' in the path.  */ | 
 | 	  memmove (buf + sysroot_len, buf, sysroot_len); | 
 | 	  memcpy (buf, simulator_sysroot, sysroot_len); | 
 | 	} | 
 |  | 
 |       *bufp = buf; | 
 |     } | 
 |   else | 
 |     free (buf); | 
 |   return result; | 
 | } | 
 |  | 
 | /* Perform a system call on behalf of the target.  */ | 
 |  | 
 | CB_RC | 
 | cb_syscall (host_callback *cb, CB_SYSCALL *sc) | 
 | { | 
 |   TWORD result = 0, errcode = 0; | 
 |  | 
 |   if (sc->magic != CB_SYSCALL_MAGIC) | 
 |     abort (); | 
 |  | 
 |   switch (cb_target_to_host_syscall (cb, sc->func)) | 
 |     { | 
 |     case CB_SYS_argc: | 
 |       result = countargv (cb->argv); | 
 |       break; | 
 |  | 
 |     case CB_SYS_argnlen: | 
 |       { | 
 | 	if (sc->arg1 >= 0 && sc->arg1 < countargv (cb->argv)) | 
 | 	  result = strlen (cb->argv[sc->arg1]); | 
 | 	else | 
 | 	  { | 
 | 	    result = -1; | 
 | 	    errcode = EINVAL; | 
 | 	  } | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_argn: | 
 |       { | 
 | 	if (sc->arg1 >= 0 && sc->arg1 < countargv (cb->argv)) | 
 | 	  { | 
 | 	    const char *argn = cb->argv[sc->arg1]; | 
 | 	    int len = strlen (argn); | 
 | 	    int written = sc->write_mem (cb, sc, sc->arg2, argn, len + 1); | 
 |  | 
 | 	    if (written == len + 1) | 
 | 	      result = sc->arg2; | 
 | 	    else | 
 | 	      { | 
 | 		result = -1; | 
 | 		errcode = EINVAL; | 
 | 	      } | 
 | 	  } | 
 | 	else | 
 | 	  { | 
 | 	    result = -1; | 
 | 	    errcode = EINVAL; | 
 | 	  } | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_argvlen : | 
 |       { | 
 | 	/* Compute how much space is required to store the argv,envp | 
 | 	   strings so that the program can allocate the space and then | 
 | 	   call SYS_argv to fetch the values.  */ | 
 | 	int argc, envc, arglen, envlen; | 
 | 	char **argv = cb->argv; | 
 | 	char **envp = cb->envp; | 
 |  | 
 | 	argc = arglen = 0; | 
 | 	if (argv) | 
 | 	  { | 
 | 	    for ( ; argv[argc]; ++argc) | 
 | 	      arglen += strlen (argv[argc]) + 1; | 
 | 	  } | 
 | 	envc = envlen = 0; | 
 | 	if (envp) | 
 | 	  { | 
 | 	    for ( ; envp[envc]; ++envc) | 
 | 	      envlen += strlen (envp[envc]) + 1; | 
 | 	  } | 
 | 	result = arglen + 1 + envlen + 1; | 
 | 	break; | 
 |       } | 
 |  | 
 |     case CB_SYS_argv : | 
 |       { | 
 | 	/* Pointer to target's buffer.  */ | 
 | 	TADDR tbuf = sc->arg1; | 
 | 	/* Buffer size.  */ | 
 | 	int bufsize = sc->arg2; | 
 | 	int written = 0; | 
 | 	/* Q is the target address of where all the strings go.  */ | 
 | 	TADDR q; | 
 | 	int i, argc, envc, len, ret; | 
 | 	char **argv = cb->argv; | 
 | 	char **envp = cb->envp; | 
 |  | 
 | 	result = -1; | 
 |  | 
 | 	argc = 0; | 
 | 	if (argv) | 
 | 	  { | 
 | 	    for ( ; argv[argc]; ++argc) | 
 | 	      { | 
 | 		len = strlen (argv[argc]) + 1; | 
 | 		if (written + len > bufsize) | 
 | 		  goto efault; | 
 |  | 
 | 		ret = (*sc->write_mem) (cb, sc, tbuf + written, argv[argc], | 
 | 					len); | 
 | 		if (ret != len) | 
 | 		  goto einval; | 
 |  | 
 | 		written += ret; | 
 | 	      } | 
 | 	  } | 
 | 	/* Double NUL bytes indicates end of strings.  */ | 
 | 	if (written >= bufsize) | 
 | 	  goto efault; | 
 | 	if ((*sc->write_mem) (cb, sc, tbuf + written, "", 1) != 1) | 
 | 	  goto einval; | 
 | 	++written; | 
 |  | 
 | 	envc = 0; | 
 | 	if (envp) | 
 | 	  { | 
 | 	    for ( ; envp[envc]; ++envc) | 
 | 	      { | 
 | 		len = strlen (envp[envc]) + 1; | 
 | 		if (written + len > bufsize) | 
 | 		  goto efault; | 
 |  | 
 | 		ret = (*sc->write_mem) (cb, sc, tbuf + written, envp[envc], | 
 | 					len); | 
 | 		if (ret != len) | 
 | 		  goto einval; | 
 | 		written += ret; | 
 | 	      } | 
 | 	  } | 
 | 	/* Double NUL bytes indicates end of strings.  */ | 
 | 	if (written >= bufsize) | 
 | 	  goto efault; | 
 | 	if ((*sc->write_mem) (cb, sc, tbuf + written, "", 1) != 1) | 
 | 	  goto einval; | 
 |  | 
 | 	result = argc; | 
 | 	sc->result2 = envc; | 
 | 	break; | 
 |  | 
 |  efault: | 
 | 	errcode = EFAULT; | 
 | 	goto FinishSyscall; | 
 |  | 
 |  einval: | 
 | 	errcode = EINVAL; | 
 | 	goto FinishSyscall; | 
 |       } | 
 |  | 
 |     case CB_SYS_exit : | 
 |       /* Caller must catch and handle; see sim_syscall as an example.  */ | 
 |       break; | 
 |  | 
 |     case CB_SYS_open : | 
 |       { | 
 | 	char *path; | 
 |  | 
 | 	errcode = get_path (cb, sc, sc->arg1, &path); | 
 | 	if (errcode != 0) | 
 | 	  { | 
 | 	    result = -1; | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 | 	result = (*cb->open) (cb, path, sc->arg2 /*, sc->arg3*/); | 
 | 	free (path); | 
 | 	if (result < 0) | 
 | 	  goto ErrorFinish; | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_close : | 
 |       result = (*cb->close) (cb, sc->arg1); | 
 |       if (result < 0) | 
 | 	goto ErrorFinish; | 
 |       break; | 
 |  | 
 |     case CB_SYS_read : | 
 |       { | 
 | 	/* ??? Perfect handling of error conditions may require only one | 
 | 	   call to cb->read.  One can't assume all the data is | 
 | 	   contiguously stored in host memory so that would require | 
 | 	   malloc'ing/free'ing the space.  Maybe later.  */ | 
 | 	char buf[FILE_XFR_SIZE]; | 
 | 	int fd = sc->arg1; | 
 | 	TADDR addr = sc->arg2; | 
 | 	size_t count = sc->arg3; | 
 | 	size_t bytes_read = 0; | 
 | 	int bytes_written; | 
 |  | 
 | 	while (count > 0) | 
 | 	  { | 
 | 	    if (cb_is_stdin (cb, fd)) | 
 | 	      result = (int) (*cb->read_stdin) (cb, buf, | 
 | 						(count < FILE_XFR_SIZE | 
 | 						 ? count : FILE_XFR_SIZE)); | 
 | 	    else | 
 | 	      result = (int) (*cb->read) (cb, fd, buf, | 
 | 					  (count < FILE_XFR_SIZE | 
 | 					   ? count : FILE_XFR_SIZE)); | 
 | 	    if (result == -1) | 
 | 	      goto ErrorFinish; | 
 | 	    if (result == 0)	/* EOF */ | 
 | 	      break; | 
 | 	    bytes_written = (*sc->write_mem) (cb, sc, addr, buf, result); | 
 | 	    if (bytes_written != result) | 
 | 	      { | 
 | 		result = -1; | 
 | 		errcode = EINVAL; | 
 | 		goto FinishSyscall; | 
 | 	      } | 
 | 	    bytes_read += result; | 
 | 	    count -= result; | 
 | 	    addr += result; | 
 | 	    /* If this is a short read, don't go back for more */ | 
 | 	    if (result != FILE_XFR_SIZE) | 
 | 	      break; | 
 | 	  } | 
 | 	result = bytes_read; | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_write : | 
 |       { | 
 | 	/* ??? Perfect handling of error conditions may require only one | 
 | 	   call to cb->write.  One can't assume all the data is | 
 | 	   contiguously stored in host memory so that would require | 
 | 	   malloc'ing/free'ing the space.  Maybe later.  */ | 
 | 	char buf[FILE_XFR_SIZE]; | 
 | 	int fd = sc->arg1; | 
 | 	TADDR addr = sc->arg2; | 
 | 	size_t count = sc->arg3; | 
 | 	int bytes_read; | 
 | 	size_t bytes_written = 0; | 
 |  | 
 | 	while (count > 0) | 
 | 	  { | 
 | 	    int bytes_to_read = count < FILE_XFR_SIZE ? count : FILE_XFR_SIZE; | 
 | 	    bytes_read = (*sc->read_mem) (cb, sc, addr, buf, bytes_to_read); | 
 | 	    if (bytes_read != bytes_to_read) | 
 | 	      { | 
 | 		result = -1; | 
 | 		errcode = EINVAL; | 
 | 		goto FinishSyscall; | 
 | 	      } | 
 | 	    if (cb_is_stdout (cb, fd)) | 
 | 	      { | 
 | 		result = (int) (*cb->write_stdout) (cb, buf, bytes_read); | 
 | 		(*cb->flush_stdout) (cb); | 
 | 	      } | 
 | 	    else if (cb_is_stderr (cb, fd)) | 
 | 	      { | 
 | 		result = (int) (*cb->write_stderr) (cb, buf, bytes_read); | 
 | 		(*cb->flush_stderr) (cb); | 
 | 	      } | 
 | 	    else | 
 | 	      result = (int) (*cb->write) (cb, fd, buf, bytes_read); | 
 | 	    if (result == -1) | 
 | 	      goto ErrorFinish; | 
 | 	    bytes_written += result; | 
 | 	    count -= result; | 
 | 	    addr += result; | 
 | 	  } | 
 | 	result = bytes_written; | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_lseek : | 
 |       { | 
 | 	int fd = sc->arg1; | 
 | 	unsigned long offset = sc->arg2; | 
 | 	int whence = sc->arg3; | 
 |  | 
 | 	result = (*cb->lseek) (cb, fd, offset, whence); | 
 | 	if (result < 0) | 
 | 	  goto ErrorFinish; | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_unlink : | 
 |       { | 
 | 	char *path; | 
 |  | 
 | 	errcode = get_path (cb, sc, sc->arg1, &path); | 
 | 	if (errcode != 0) | 
 | 	  { | 
 | 	    result = -1; | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 | 	result = (*cb->unlink) (cb, path); | 
 | 	free (path); | 
 | 	if (result < 0) | 
 | 	  goto ErrorFinish; | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_truncate : | 
 |       { | 
 | 	char *path; | 
 | 	long len = sc->arg2; | 
 |  | 
 | 	errcode = get_path (cb, sc, sc->arg1, &path); | 
 | 	if (errcode != 0) | 
 | 	  { | 
 | 	    result = -1; | 
 | 	    errcode = EFAULT; | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 | 	result = (*cb->truncate) (cb, path, len); | 
 | 	free (path); | 
 | 	if (result < 0) | 
 | 	  goto ErrorFinish; | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_ftruncate : | 
 |       { | 
 | 	int fd = sc->arg1; | 
 | 	long len = sc->arg2; | 
 |  | 
 | 	result = (*cb->ftruncate) (cb, fd, len); | 
 | 	if (result < 0) | 
 | 	  goto ErrorFinish; | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_rename : | 
 |       { | 
 | 	char *path1, *path2; | 
 |  | 
 | 	errcode = get_path (cb, sc, sc->arg1, &path1); | 
 | 	if (errcode != 0) | 
 | 	  { | 
 | 	    result = -1; | 
 | 	    errcode = EFAULT; | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 | 	errcode = get_path (cb, sc, sc->arg2, &path2); | 
 | 	if (errcode != 0) | 
 | 	  { | 
 | 	    result = -1; | 
 | 	    errcode = EFAULT; | 
 | 	    free (path1); | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 | 	result = (*cb->rename) (cb, path1, path2); | 
 | 	free (path1); | 
 | 	free (path2); | 
 | 	if (result < 0) | 
 | 	  goto ErrorFinish; | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_stat : | 
 |       { | 
 | 	char *path,*buf; | 
 | 	int buflen; | 
 | 	struct stat statbuf; | 
 | 	TADDR addr = sc->arg2; | 
 |  | 
 | 	errcode = get_path (cb, sc, sc->arg1, &path); | 
 | 	if (errcode != 0) | 
 | 	  { | 
 | 	    result = -1; | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 | 	result = (*cb->to_stat) (cb, path, &statbuf); | 
 | 	free (path); | 
 | 	if (result < 0) | 
 | 	  goto ErrorFinish; | 
 | 	buflen = cb_host_to_target_stat (cb, NULL, NULL); | 
 | 	buf = xmalloc (buflen); | 
 | 	if (cb_host_to_target_stat (cb, &statbuf, buf) != buflen) | 
 | 	  { | 
 | 	    /* The translation failed.  This is due to an internal | 
 | 	       host program error, not the target's fault.  */ | 
 | 	    free (buf); | 
 | 	    errcode = ENOSYS; | 
 | 	    result = -1; | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 | 	if ((*sc->write_mem) (cb, sc, addr, buf, buflen) != buflen) | 
 | 	  { | 
 | 	    free (buf); | 
 | 	    errcode = EINVAL; | 
 | 	    result = -1; | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 | 	free (buf); | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_fstat : | 
 |       { | 
 | 	char *buf; | 
 | 	int buflen; | 
 | 	struct stat statbuf; | 
 | 	TADDR addr = sc->arg2; | 
 |  | 
 | 	result = (*cb->to_fstat) (cb, sc->arg1, &statbuf); | 
 | 	if (result < 0) | 
 | 	  goto ErrorFinish; | 
 | 	buflen = cb_host_to_target_stat (cb, NULL, NULL); | 
 | 	buf = xmalloc (buflen); | 
 | 	if (cb_host_to_target_stat (cb, &statbuf, buf) != buflen) | 
 | 	  { | 
 | 	    /* The translation failed.  This is due to an internal | 
 | 	       host program error, not the target's fault.  */ | 
 | 	    free (buf); | 
 | 	    errcode = ENOSYS; | 
 | 	    result = -1; | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 | 	if ((*sc->write_mem) (cb, sc, addr, buf, buflen) != buflen) | 
 | 	  { | 
 | 	    free (buf); | 
 | 	    errcode = EINVAL; | 
 | 	    result = -1; | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 | 	free (buf); | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_lstat : | 
 |       { | 
 | 	char *path, *buf; | 
 | 	int buflen; | 
 | 	struct stat statbuf; | 
 | 	TADDR addr = sc->arg2; | 
 |  | 
 | 	errcode = get_path (cb, sc, sc->arg1, &path); | 
 | 	if (errcode != 0) | 
 | 	  { | 
 | 	    result = -1; | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 | 	result = (*cb->to_lstat) (cb, path, &statbuf); | 
 | 	free (path); | 
 | 	if (result < 0) | 
 | 	  goto ErrorFinish; | 
 |  | 
 | 	buflen = cb_host_to_target_stat (cb, NULL, NULL); | 
 | 	buf = xmalloc (buflen); | 
 | 	if (cb_host_to_target_stat (cb, &statbuf, buf) != buflen) | 
 | 	  { | 
 | 	    /* The translation failed.  This is due to an internal | 
 | 	       host program error, not the target's fault. | 
 | 	       Unfortunately, it's hard to test this case, so there's no | 
 | 	       test-case for this execution path.  */ | 
 | 	    free (buf); | 
 | 	    errcode = ENOSYS; | 
 | 	    result = -1; | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 |  | 
 | 	if ((*sc->write_mem) (cb, sc, addr, buf, buflen) != buflen) | 
 | 	  { | 
 | 	    free (buf); | 
 | 	    errcode = EINVAL; | 
 | 	    result = -1; | 
 | 	    goto FinishSyscall; | 
 | 	  } | 
 |  | 
 | 	free (buf); | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_pipe : | 
 |       { | 
 | 	int p[2]; | 
 | 	char *target_p = xcalloc (1, cb->target_sizeof_int * 2); | 
 |  | 
 | 	result = (*cb->pipe) (cb, p); | 
 | 	if (result != 0) | 
 | 	  goto ErrorFinish; | 
 |  | 
 | 	cb_store_target_endian (cb, target_p, cb->target_sizeof_int, p[0]); | 
 | 	cb_store_target_endian (cb, target_p + cb->target_sizeof_int, | 
 | 				cb->target_sizeof_int, p[1]); | 
 | 	if ((*sc->write_mem) (cb, sc, sc->arg1, target_p, | 
 | 			      cb->target_sizeof_int * 2) | 
 | 	    != cb->target_sizeof_int * 2) | 
 | 	  { | 
 | 	    /* Close the pipe fd:s.  */ | 
 | 	    (*cb->close) (cb, p[0]); | 
 | 	    (*cb->close) (cb, p[1]); | 
 | 	    errcode = EFAULT; | 
 | 	    result = -1; | 
 | 	  } | 
 |  | 
 | 	free (target_p); | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_getpid: | 
 |       /* POSIX says getpid always succeeds.  */ | 
 |       result = (*cb->getpid) (cb); | 
 |       break; | 
 |  | 
 |     case CB_SYS_kill: | 
 |       /* If killing self, leave it to the caller to process so it can send the | 
 | 	 signal to the engine.  */ | 
 |       if (sc->arg1 == (*cb->getpid) (cb)) | 
 | 	{ | 
 | 	  result = -1; | 
 | 	  errcode = ENOSYS; | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  int signum = cb_target_to_host_signal (cb, sc->arg2); | 
 |  | 
 | 	  result = (*cb->kill) (cb, sc->arg1, signum); | 
 | 	  cb->last_errno = errno; | 
 | 	  goto ErrorFinish; | 
 | 	} | 
 |       break; | 
 |  | 
 |     case CB_SYS_time : | 
 |       { | 
 | 	/* FIXME: May wish to change CB_SYS_time to something else. | 
 | 	   We might also want gettimeofday or times, but if system calls | 
 | 	   can be built on others, we can keep the number we have to support | 
 | 	   here down.  */ | 
 | 	time_t t = (*cb->time) (cb); | 
 | 	result = t; | 
 | 	/* It is up to target code to process the argument to time().  */ | 
 |       } | 
 |       break; | 
 |  | 
 |     case CB_SYS_chdir : | 
 |     case CB_SYS_chmod : | 
 |     case CB_SYS_utime : | 
 |       /* fall through for now */ | 
 |  | 
 |     default : | 
 |       result = -1; | 
 |       errcode = ENOSYS; | 
 |       break; | 
 |     } | 
 |  | 
 |  FinishSyscall: | 
 |   sc->result = result; | 
 |   if (errcode == 0) | 
 |     sc->errcode = 0; | 
 |   else | 
 |     sc->errcode = cb_host_to_target_errno (cb, errcode); | 
 |   return CB_RC_OK; | 
 |  | 
 |  ErrorFinish: | 
 |   sc->result = result; | 
 |   sc->errcode = (*cb->get_errno) (cb); | 
 |   return CB_RC_OK; | 
 | } |