summaryrefslogtreecommitdiff
path: root/sim/common/callback.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/common/callback.c')
-rw-r--r--sim/common/callback.c810
1 files changed, 810 insertions, 0 deletions
diff --git a/sim/common/callback.c b/sim/common/callback.c
new file mode 100644
index 00000000000..e8a28c43f3b
--- /dev/null
+++ b/sim/common/callback.c
@@ -0,0 +1,810 @@
+/* Remote target callback routines.
+ Copyright 1995, 1996, 1997 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 2 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 GAS; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* This file provides a standard way for targets to talk to the host OS
+ level. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "ansidecl.h"
+#ifdef ANSI_PROTOTYPES
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "callback.h"
+#include "targ-vals.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* ??? sim_cb_printf should be cb_printf, but until the callback support is
+ broken out of the simulator directory, these are here to not require
+ sim-utils.h. */
+void sim_cb_printf PARAMS ((host_callback *, const char *, ...));
+void sim_cb_eprintf PARAMS ((host_callback *, const char *, ...));
+
+extern CB_TARGET_DEFS_MAP cb_init_syscall_map[];
+extern CB_TARGET_DEFS_MAP cb_init_errno_map[];
+extern CB_TARGET_DEFS_MAP cb_init_open_map[];
+
+extern int system PARAMS ((const char *));
+
+static int os_init PARAMS ((host_callback *));
+static int os_shutdown PARAMS ((host_callback *));
+static int os_unlink PARAMS ((host_callback *, const char *));
+static long os_time PARAMS ((host_callback *, long *));
+static int os_system PARAMS ((host_callback *, const char *));
+static int os_rename PARAMS ((host_callback *, const char *, const char *));
+static int os_write_stdout PARAMS ((host_callback *, const char *, int));
+static void os_flush_stdout PARAMS ((host_callback *));
+static int os_write_stderr PARAMS ((host_callback *, const char *, int));
+static void os_flush_stderr PARAMS ((host_callback *));
+static int os_write PARAMS ((host_callback *, int, const char *, int));
+static int os_read_stdin PARAMS ((host_callback *, char *, int));
+static int os_read PARAMS ((host_callback *, int, char *, int));
+static int os_open PARAMS ((host_callback *, const char *, int));
+static int os_lseek PARAMS ((host_callback *, int, long, int));
+static int os_isatty PARAMS ((host_callback *, int));
+static int os_get_errno PARAMS ((host_callback *));
+static int os_close PARAMS ((host_callback *, int));
+static void os_vprintf_filtered PARAMS ((host_callback *, const char *, va_list));
+static void os_evprintf_filtered PARAMS ((host_callback *, const char *, va_list));
+static void os_error PARAMS ((host_callback *, const char *, ...));
+static int fdmap PARAMS ((host_callback *, int));
+static int fdbad PARAMS ((host_callback *, int));
+static int wrap PARAMS ((host_callback *, int));
+
+/* Set the callback copy of errno from what we see now. */
+
+static int
+wrap (p, val)
+ host_callback *p;
+ int val;
+{
+ p->last_errno = errno;
+ return val;
+}
+
+/* Make sure the FD provided is ok. If not, return non-zero
+ and set errno. */
+
+static int
+fdbad (p, fd)
+ host_callback *p;
+ int fd;
+{
+ if (fd < 0 || fd > MAX_CALLBACK_FDS || !p->fdopen[fd])
+ {
+ p->last_errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+static int
+fdmap (p, fd)
+ host_callback *p;
+ int fd;
+{
+ return p->fdmap[fd];
+}
+
+static int
+os_close (p, fd)
+ host_callback *p;
+ int fd;
+{
+ int result;
+
+ result = fdbad (p, fd);
+ if (result)
+ return result;
+ result = wrap (p, close (fdmap (p, fd)));
+ if (result == 0 && !p->alwaysopen[fd])
+ p->fdopen[fd] = 0;
+
+ return result;
+}
+
+
+/* taken from gdb/util.c:notice_quit() - should be in a library */
+
+
+#if defined(__GO32__) || defined (_MSC_VER)
+static int
+os_poll_quit (p)
+ host_callback *p;
+{
+#if defined(__GO32__)
+ int kbhit ();
+ int getkey ();
+ if (kbhit ())
+ {
+ int k = getkey ();
+ if (k == 1)
+ {
+ return 1;
+ }
+ else if (k == 2)
+ {
+ return 1;
+ }
+ else
+ {
+ sim_cb_eprintf (p, "CTRL-A to quit, CTRL-B to quit harder\n");
+ }
+ }
+#endif
+#if defined (_MSC_VER)
+ /* NB - this will not compile! */
+ int k = win32pollquit();
+ if (k == 1)
+ return 1;
+ else if (k == 2)
+ return 1;
+#endif
+ return 0;
+}
+#else
+#define os_poll_quit 0
+#endif /* defined(__GO32__) || defined(_MSC_VER) */
+
+static int
+os_get_errno (p)
+ host_callback *p;
+{
+ return cb_host_to_target_errno (p, p->last_errno);
+}
+
+
+static int
+os_isatty (p, fd)
+ host_callback *p;
+ int fd;
+{
+ int result;
+
+ result = fdbad (p, fd);
+ if (result)
+ return result;
+ result = wrap (p, isatty (fdmap (p, fd)));
+
+ return result;
+}
+
+static int
+os_lseek (p, fd, off, way)
+ host_callback *p;
+ int fd;
+ long off;
+ int way;
+{
+ int result;
+
+ result = fdbad (p, fd);
+ if (result)
+ return result;
+ result = lseek (fdmap (p, fd), off, way);
+ return result;
+}
+
+static int
+os_open (p, name, flags)
+ host_callback *p;
+ const char *name;
+ int flags;
+{
+ int i;
+ for (i = 0; i < MAX_CALLBACK_FDS; i++)
+ {
+ if (!p->fdopen[i])
+ {
+ int f = open (name, cb_target_to_host_open (p, flags), 0644);
+ if (f < 0)
+ {
+ p->last_errno = errno;
+ return f;
+ }
+ p->fdopen[i] = 1;
+ p->fdmap[i] = f;
+ return i;
+ }
+ }
+ p->last_errno = EMFILE;
+ return -1;
+}
+
+static int
+os_read (p, fd, buf, len)
+ host_callback *p;
+ int fd;
+ char *buf;
+ int len;
+{
+ int result;
+
+ result = fdbad (p, fd);
+ if (result)
+ return result;
+ result = wrap (p, read (fdmap (p, fd), buf, len));
+ return result;
+}
+
+static int
+os_read_stdin (p, buf, len)
+ host_callback *p;
+ char *buf;
+ int len;
+{
+ return wrap (p, read (0, buf, len));
+}
+
+static int
+os_write (p, fd, buf, len)
+ host_callback *p;
+ int fd;
+ const char *buf;
+ int len;
+{
+ int result;
+ int real_fd;
+
+ result = fdbad (p, fd);
+ if (result)
+ return result;
+ real_fd = fdmap (p, fd);
+ switch (real_fd)
+ {
+ default:
+ result = wrap (p, write (real_fd, buf, len));
+ break;
+ case 1:
+ result = p->write_stdout (p, buf, len);
+ break;
+ case 2:
+ result = p->write_stderr (p, buf, len);
+ break;
+ }
+ return result;
+}
+
+static int
+os_write_stdout (p, buf, len)
+ host_callback *p;
+ const char *buf;
+ int len;
+{
+ return fwrite (buf, 1, len, stdout);
+}
+
+static void
+os_flush_stdout (p)
+ host_callback *p;
+{
+ fflush (stdout);
+}
+
+static int
+os_write_stderr (p, buf, len)
+ host_callback *p;
+ const char *buf;
+ int len;
+{
+ return fwrite (buf, 1, len, stderr);
+}
+
+static void
+os_flush_stderr (p)
+ host_callback *p;
+{
+ fflush (stderr);
+}
+
+static int
+os_rename (p, f1, f2)
+ host_callback *p;
+ const char *f1;
+ const char *f2;
+{
+ return wrap (p, rename (f1, f2));
+}
+
+
+static int
+os_system (p, s)
+ host_callback *p;
+ const char *s;
+{
+ return wrap (p, system (s));
+}
+
+static long
+os_time (p, t)
+ host_callback *p;
+ long *t;
+{
+ return wrap (p, time (t));
+}
+
+
+static int
+os_unlink (p, f1)
+ host_callback *p;
+ const char *f1;
+{
+ return wrap (p, unlink (f1));
+}
+
+static int
+os_stat (p, file, buf)
+ host_callback *p;
+ const char *file;
+ struct stat *buf;
+{
+ /* ??? There is an issue of when to translate to the target layout.
+ One could do that inside this function, or one could have the
+ caller do it. It's more flexible to let the caller do it, though
+ I'm not sure the flexibility will ever be useful. */
+ return wrap (p, stat (file, buf));
+}
+
+static int
+os_fstat (p, fd, buf)
+ host_callback *p;
+ int fd;
+ struct stat *buf;
+{
+ if (fdbad (p, fd))
+ return -1;
+ /* ??? There is an issue of when to translate to the target layout.
+ One could do that inside this function, or one could have the
+ caller do it. It's more flexible to let the caller do it, though
+ I'm not sure the flexibility will ever be useful. */
+ return wrap (p, fstat (fdmap (p, fd), buf));
+}
+
+static int
+os_shutdown (p)
+ host_callback *p;
+{
+ int i;
+ for (i = 0; i < MAX_CALLBACK_FDS; i++)
+ {
+ if (p->fdopen[i] && !p->alwaysopen[i]) {
+ close (p->fdmap[i]);
+ p->fdopen[i] = 0;
+ }
+ }
+ return 1;
+}
+
+static int
+os_init (p)
+ host_callback *p;
+{
+ int i;
+
+ os_shutdown (p);
+ for (i = 0; i < 3; i++)
+ {
+ p->fdmap[i] = i;
+ p->fdopen[i] = 1;
+ p->alwaysopen[i] = 1;
+ }
+
+ p->syscall_map = cb_init_syscall_map;
+ p->errno_map = cb_init_errno_map;
+ p->open_map = cb_init_open_map;
+
+ return 1;
+}
+
+/* DEPRECIATED */
+
+/* VARARGS */
+static void
+#ifdef ANSI_PROTOTYPES
+os_printf_filtered (host_callback *p, const char *format, ...)
+#else
+os_printf_filtered (p, va_alist)
+ host_callback *p;
+ va_dcl
+#endif
+{
+ va_list args;
+#ifdef ANSI_PROTOTYPES
+ va_start (args, format);
+#else
+ char *format;
+
+ va_start (args);
+ format = va_arg (args, char *);
+#endif
+
+ vfprintf (stdout, format, args);
+ va_end (args);
+}
+
+/* VARARGS */
+static void
+#ifdef ANSI_PROTOTYPES
+os_vprintf_filtered (host_callback *p, const char *format, va_list args)
+#else
+os_vprintf_filtered (p, format, args)
+ host_callback *p;
+ const char *format;
+ va_list args;
+#endif
+{
+ vprintf (format, args);
+}
+
+/* VARARGS */
+static void
+#ifdef ANSI_PROTOTYPES
+os_evprintf_filtered (host_callback *p, const char *format, va_list args)
+#else
+os_evprintf_filtered (p, format, args)
+ host_callback *p;
+ const char *format;
+ va_list args;
+#endif
+{
+ vfprintf (stderr, format, args);
+}
+
+/* VARARGS */
+static void
+#ifdef ANSI_PROTOTYPES
+os_error (host_callback *p, const char *format, ...)
+#else
+os_error (p, va_alist)
+ host_callback *p;
+ va_dcl
+#endif
+{
+ va_list args;
+#ifdef ANSI_PROTOTYPES
+ va_start (args, format);
+#else
+ char *format;
+
+ va_start (args);
+ format = va_arg (args, char *);
+#endif
+
+ vfprintf (stderr, format, args);
+ fprintf (stderr, "\n");
+
+ va_end (args);
+ exit (1);
+}
+
+host_callback default_callback =
+{
+ os_close,
+ os_get_errno,
+ os_isatty,
+ os_lseek,
+ os_open,
+ os_read,
+ os_read_stdin,
+ os_rename,
+ os_system,
+ os_time,
+ os_unlink,
+ os_write,
+ os_write_stdout,
+ os_flush_stdout,
+ os_write_stderr,
+ os_flush_stderr,
+
+ os_stat,
+ os_fstat,
+
+ os_poll_quit,
+
+ os_shutdown,
+ os_init,
+
+ os_printf_filtered, /* deprecated */
+
+ os_vprintf_filtered,
+ os_evprintf_filtered,
+ os_error,
+
+ 0, /* last errno */
+
+ { 0, }, /* fdmap */
+ { 0, }, /* fdopen */
+ { 0, }, /* alwaysopen */
+
+ 0, /* syscall_map */
+ 0, /* errno_map */
+ 0, /* open_map */
+ 0, /* signal_map */
+ 0, /* stat_map */
+
+ HOST_CALLBACK_MAGIC,
+};
+
+/* Read in a file describing the target's system call values.
+ E.g. maybe someone will want to use something other than newlib.
+ This assumes that the basic system call recognition and value passing/
+ returning is supported. So maybe some coding/recompilation will be
+ necessary, but not as much.
+
+ If an error occurs, the existing mapping is not changed. */
+
+CB_RC
+cb_read_target_syscall_maps (cb, file)
+ host_callback *cb;
+ const char *file;
+{
+ CB_TARGET_DEFS_MAP *syscall_map, *errno_map, *open_map, *signal_map;
+ const char *stat_map;
+ FILE *f;
+
+ if ((f = fopen (file, "r")) == NULL)
+ return CB_RC_ACCESS;
+
+ /* ... read in and parse file ... */
+
+ fclose (f);
+ return CB_RC_NO_MEM; /* FIXME:wip */
+
+ /* Free storage allocated for any existing maps. */
+ if (cb->syscall_map)
+ free (cb->syscall_map);
+ if (cb->errno_map)
+ free (cb->errno_map);
+ if (cb->open_map)
+ free (cb->open_map);
+ if (cb->signal_map)
+ free (cb->signal_map);
+ if (cb->stat_map)
+ free ((PTR) cb->stat_map);
+
+ cb->syscall_map = syscall_map;
+ cb->errno_map = errno_map;
+ cb->open_map = open_map;
+ cb->signal_map = signal_map;
+ cb->stat_map = stat_map;
+
+ return CB_RC_OK;
+}
+
+/* Translate the target's version of a syscall number to the host's.
+ This isn't actually the host's version, rather a canonical form.
+ ??? Perhaps this should be renamed to ..._canon_syscall. */
+
+int
+cb_target_to_host_syscall (cb, target_val)
+ host_callback *cb;
+ int target_val;
+{
+ CB_TARGET_DEFS_MAP *m;
+
+ for (m = &cb->syscall_map[0]; m->target_val != -1; ++m)
+ if (m->target_val == target_val)
+ return m->host_val;
+
+ return -1;
+}
+
+/* FIXME: sort tables if large.
+ Alternatively, an obvious improvement for errno conversion is
+ to machine generate a function with a large switch(). */
+
+/* Translate the host's version of errno to the target's. */
+
+int
+cb_host_to_target_errno (cb, host_val)
+ host_callback *cb;
+ int host_val;
+{
+ CB_TARGET_DEFS_MAP *m;
+
+ for (m = &cb->errno_map[0]; m->host_val; ++m)
+ if (m->host_val == host_val)
+ return m->target_val;
+
+ /* ??? Which error to return in this case is up for grabs.
+ Note that some missing values may have standard alternatives.
+ For now return 0 and require caller to deal with it. */
+ return 0;
+}
+
+/* Given a set of target bitmasks for the open system call,
+ return the host equivalent.
+ Mapping open flag values is best done by looping so there's no need
+ to machine generate this function. */
+
+int
+cb_target_to_host_open (cb, target_val)
+ host_callback *cb;
+ int target_val;
+{
+ int host_val = 0;
+ CB_TARGET_DEFS_MAP *m;
+
+ for (m = &cb->open_map[0]; m->host_val != -1; ++m)
+ {
+ switch (m->target_val)
+ {
+ /* O_RDONLY can be (and usually is) 0 which needs to be treated
+ specially. */
+ case TARGET_O_RDONLY :
+ case TARGET_O_WRONLY :
+ case TARGET_O_RDWR :
+ if ((target_val & (TARGET_O_RDONLY | TARGET_O_WRONLY | TARGET_O_RDWR))
+ == m->target_val)
+ host_val |= m->host_val;
+ /* Handle the host/target differentiating between binary and
+ text mode. Only one case is of importance */
+#if ! defined (TARGET_O_BINARY) && defined (O_BINARY)
+ host_val |= O_BINARY;
+#endif
+ break;
+ default :
+ if ((m->target_val & target_val) == m->target_val)
+ host_val |= m->host_val;
+ break;
+ }
+ }
+
+ return host_val;
+}
+
+/* Utility for cb_host_to_target_stat to store values in the target's
+ stat struct. */
+
+static void
+store (p, size, val, big_p)
+ char *p;
+ int size;
+ long val; /* ??? must be as big as target word size */
+ int big_p;
+{
+ if (big_p)
+ {
+ p += size;
+ while (size-- > 0)
+ {
+ *--p = val;
+ val >>= 8;
+ }
+ }
+ else
+ {
+ while (size-- > 0)
+ {
+ *p++ = val;
+ val >>= 8;
+ }
+ }
+}
+
+/* Translate a host's stat struct into a target's.
+ If HS is NULL, just compute the length of the buffer required,
+ TS is ignored.
+
+ The result is the size of the target's stat struct,
+ or zero if an error occured during the translation. */
+
+int
+cb_host_to_target_stat (cb, hs, ts)
+ host_callback *cb;
+ const struct stat *hs;
+ PTR ts;
+{
+ const char *m = cb->stat_map;
+ char *p;
+ int big_p = 0;
+
+ if (hs == NULL)
+ ts = NULL;
+ p = ts;
+
+ while (m)
+ {
+ char *q = strchr (m, ',');
+ int size;
+
+ /* FIXME: Use sscanf? */
+ if (q == NULL)
+ {
+ /* FIXME: print error message */
+ return 0;
+ }
+ size = atoi (q + 1);
+ if (size == 0)
+ {
+ /* FIXME: print error message */
+ return 0;
+ }
+
+ if (hs != NULL)
+ {
+ if (strncmp (m, "st_dev", q - m) == 0)
+ store (p, size, hs->st_dev, big_p);
+ else if (strncmp (m, "st_ino", q - m) == 0)
+ store (p, size, hs->st_ino, big_p);
+ /* FIXME:wip */
+ else
+ store (p, size, 0, big_p); /* unsupported field, store 0 */
+ }
+
+ p += size;
+ m = strchr (q, ':');
+ if (m)
+ ++m;
+ }
+
+ return p - (char *) ts;
+}
+
+/* Cover functions to the vfprintf callbacks.
+
+ ??? If one thinks of the callbacks as a subsystem onto itself [or part of
+ a larger "remote target subsystem"] with a well defined interface, then
+ one would think that the subsystem would provide these. However, until
+ one is allowed to create such a subsystem (with its own source tree
+ independent of any particular user), such a critter can't exist. Thus
+ these functions are here for the time being. */
+
+void
+sim_cb_printf (host_callback *p, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ p->vprintf_filtered (p, fmt, ap);
+ va_end (ap);
+}
+
+void
+sim_cb_eprintf (host_callback *p, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ p->evprintf_filtered (p, fmt, ap);
+ va_end (ap);
+}