mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-23 18:14:13 +08:00
1d506c26d9
This commit is the result of the following actions: - Running gdb/copyright.py to update all of the copyright headers to include 2024, - Manually updating a few files the copyright.py script told me to update, these files had copyright headers embedded within the file, - Regenerating gdbsupport/Makefile.in to refresh it's copyright date, - Using grep to find other files that still mentioned 2023. If these files were updated last year from 2022 to 2023 then I've updated them this year to 2024. I'm sure I've probably missed some dates. Feel free to fix them up as you spot them.
1142 lines
24 KiB
C
1142 lines
24 KiB
C
/* Remote target callback routines.
|
||
Copyright 1995-2024 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 file provides a standard way for targets to talk to the host OS
|
||
level. */
|
||
|
||
/* This must come before any other includes. */
|
||
#include "defs.h"
|
||
|
||
#include <errno.h>
|
||
#include <fcntl.h>
|
||
/* For PIPE_BUF. */
|
||
#include <limits.h>
|
||
#include <signal.h>
|
||
#include <stdarg.h>
|
||
#include <stdint.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <time.h>
|
||
#include <unistd.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/types.h>
|
||
|
||
#include "ansidecl.h"
|
||
/* For xmalloc. */
|
||
#include "libiberty.h"
|
||
|
||
#include "sim/callback.h"
|
||
|
||
#ifndef PIPE_BUF
|
||
#define PIPE_BUF 512
|
||
#endif
|
||
|
||
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_signal_map[];
|
||
extern CB_TARGET_DEFS_MAP cb_init_open_map[];
|
||
|
||
/* Make sure the FD provided is ok. If not, return non-zero
|
||
and set errno. */
|
||
|
||
static int
|
||
fdbad (host_callback *p, int fd)
|
||
{
|
||
if (fd < 0 || fd > MAX_CALLBACK_FDS || p->fd_buddy[fd] < 0)
|
||
{
|
||
p->last_errno = EBADF;
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
fdmap (host_callback *p, int fd)
|
||
{
|
||
return p->fdmap[fd];
|
||
}
|
||
|
||
static int
|
||
os_close (host_callback *p, int fd)
|
||
{
|
||
int result;
|
||
int i, next;
|
||
|
||
result = fdbad (p, fd);
|
||
if (result)
|
||
return result;
|
||
/* If this file descripter has one or more buddies (originals /
|
||
duplicates from a dup), just remove it from the circular list. */
|
||
for (i = fd; (next = p->fd_buddy[i]) != fd; )
|
||
i = next;
|
||
if (fd != i)
|
||
p->fd_buddy[i] = p->fd_buddy[fd];
|
||
else
|
||
{
|
||
if (p->ispipe[fd])
|
||
{
|
||
int other = p->ispipe[fd];
|
||
int reader, writer;
|
||
|
||
if (other > 0)
|
||
{
|
||
/* Closing the read side. */
|
||
reader = fd;
|
||
writer = other;
|
||
}
|
||
else
|
||
{
|
||
/* Closing the write side. */
|
||
writer = fd;
|
||
reader = -other;
|
||
}
|
||
|
||
/* If there was data in the buffer, make a last "now empty"
|
||
call, then deallocate data. */
|
||
if (p->pipe_buffer[writer].buffer != NULL)
|
||
{
|
||
(*p->pipe_empty) (p, reader, writer);
|
||
free (p->pipe_buffer[writer].buffer);
|
||
p->pipe_buffer[writer].buffer = NULL;
|
||
}
|
||
|
||
/* Clear pipe data for this side. */
|
||
p->pipe_buffer[fd].size = 0;
|
||
p->ispipe[fd] = 0;
|
||
|
||
/* If this was the first close, mark the other side as the
|
||
only remaining side. */
|
||
if (fd != abs (other))
|
||
p->ispipe[abs (other)] = -other;
|
||
p->fd_buddy[fd] = -1;
|
||
return 0;
|
||
}
|
||
|
||
result = close (fdmap (p, fd));
|
||
p->last_errno = errno;
|
||
}
|
||
p->fd_buddy[fd] = -1;
|
||
|
||
return result;
|
||
}
|
||
|
||
|
||
/* taken from gdb/util.c:notice_quit() - should be in a library */
|
||
|
||
|
||
#if defined(_MSC_VER)
|
||
static int
|
||
os_poll_quit (host_callback *p)
|
||
{
|
||
/* NB - this will not compile! */
|
||
int k = win32pollquit ();
|
||
if (k == 1)
|
||
return 1;
|
||
else if (k == 2)
|
||
return 1;
|
||
return 0;
|
||
}
|
||
#else
|
||
#define os_poll_quit 0
|
||
#endif /* defined(_MSC_VER) */
|
||
|
||
static int
|
||
os_get_errno (host_callback *p)
|
||
{
|
||
return cb_host_to_target_errno (p, p->last_errno);
|
||
}
|
||
|
||
|
||
static int
|
||
os_isatty (host_callback *p, int fd)
|
||
{
|
||
int result;
|
||
|
||
result = fdbad (p, fd);
|
||
if (result)
|
||
return result;
|
||
|
||
result = isatty (fdmap (p, fd));
|
||
p->last_errno = errno;
|
||
return result;
|
||
}
|
||
|
||
static int64_t
|
||
os_lseek (host_callback *p, int fd, int64_t off, int way)
|
||
{
|
||
int64_t result;
|
||
|
||
result = fdbad (p, fd);
|
||
if (result)
|
||
return result;
|
||
|
||
result = lseek (fdmap (p, fd), off, way);
|
||
p->last_errno = errno;
|
||
return result;
|
||
}
|
||
|
||
static int
|
||
os_open (host_callback *p, const char *name, int flags)
|
||
{
|
||
int i;
|
||
for (i = 0; i < MAX_CALLBACK_FDS; i++)
|
||
{
|
||
if (p->fd_buddy[i] < 0)
|
||
{
|
||
int f = open (name, cb_target_to_host_open (p, flags), 0644);
|
||
if (f < 0)
|
||
{
|
||
p->last_errno = errno;
|
||
return f;
|
||
}
|
||
p->fd_buddy[i] = i;
|
||
p->fdmap[i] = f;
|
||
return i;
|
||
}
|
||
}
|
||
p->last_errno = EMFILE;
|
||
return -1;
|
||
}
|
||
|
||
static int
|
||
os_read (host_callback *p, int fd, char *buf, int len)
|
||
{
|
||
int result;
|
||
|
||
result = fdbad (p, fd);
|
||
if (result)
|
||
return result;
|
||
if (p->ispipe[fd])
|
||
{
|
||
int writer = p->ispipe[fd];
|
||
|
||
/* Can't read from the write-end. */
|
||
if (writer < 0)
|
||
{
|
||
p->last_errno = EBADF;
|
||
return -1;
|
||
}
|
||
|
||
/* Nothing to read if nothing is written. */
|
||
if (p->pipe_buffer[writer].size == 0)
|
||
return 0;
|
||
|
||
/* Truncate read request size to buffer size minus what's already
|
||
read. */
|
||
if (len > p->pipe_buffer[writer].size - p->pipe_buffer[fd].size)
|
||
len = p->pipe_buffer[writer].size - p->pipe_buffer[fd].size;
|
||
|
||
memcpy (buf, p->pipe_buffer[writer].buffer + p->pipe_buffer[fd].size,
|
||
len);
|
||
|
||
/* Account for what we just read. */
|
||
p->pipe_buffer[fd].size += len;
|
||
|
||
/* If we've read everything, empty and deallocate the buffer and
|
||
signal buffer-empty to client. (This isn't expected to be a
|
||
hot path in the simulator, so we don't hold on to the buffer.) */
|
||
if (p->pipe_buffer[fd].size == p->pipe_buffer[writer].size)
|
||
{
|
||
free (p->pipe_buffer[writer].buffer);
|
||
p->pipe_buffer[writer].buffer = NULL;
|
||
p->pipe_buffer[fd].size = 0;
|
||
p->pipe_buffer[writer].size = 0;
|
||
(*p->pipe_empty) (p, fd, writer);
|
||
}
|
||
|
||
return len;
|
||
}
|
||
|
||
result = read (fdmap (p, fd), buf, len);
|
||
p->last_errno = errno;
|
||
return result;
|
||
}
|
||
|
||
static int
|
||
os_read_stdin (host_callback *p, char *buf, int len)
|
||
{
|
||
int result;
|
||
|
||
result = read (0, buf, len);
|
||
p->last_errno = errno;
|
||
return result;
|
||
}
|
||
|
||
static int
|
||
os_write (host_callback *p, int fd, const char *buf, int len)
|
||
{
|
||
int result;
|
||
int real_fd;
|
||
|
||
result = fdbad (p, fd);
|
||
if (result)
|
||
return result;
|
||
|
||
if (p->ispipe[fd])
|
||
{
|
||
int reader = -p->ispipe[fd];
|
||
|
||
/* Can't write to the read-end. */
|
||
if (reader < 0)
|
||
{
|
||
p->last_errno = EBADF;
|
||
return -1;
|
||
}
|
||
|
||
/* Can't write to pipe with closed read end.
|
||
FIXME: We should send a SIGPIPE. */
|
||
if (reader == fd)
|
||
{
|
||
p->last_errno = EPIPE;
|
||
return -1;
|
||
}
|
||
|
||
/* As a sanity-check, we bail out it the buffered contents is much
|
||
larger than the size of the buffer on the host. We don't want
|
||
to run out of memory in the simulator due to a target program
|
||
bug if we can help it. Unfortunately, regarding the value that
|
||
reaches the simulated program, it's no use returning *less*
|
||
than the requested amount, because cb_syscall loops calling
|
||
this function until the whole amount is done. */
|
||
if (p->pipe_buffer[fd].size + len > 10 * PIPE_BUF)
|
||
{
|
||
p->last_errno = EFBIG;
|
||
return -1;
|
||
}
|
||
|
||
p->pipe_buffer[fd].buffer
|
||
= xrealloc (p->pipe_buffer[fd].buffer, p->pipe_buffer[fd].size + len);
|
||
memcpy (p->pipe_buffer[fd].buffer + p->pipe_buffer[fd].size,
|
||
buf, len);
|
||
p->pipe_buffer[fd].size += len;
|
||
|
||
(*p->pipe_nonempty) (p, reader, fd);
|
||
return len;
|
||
}
|
||
|
||
real_fd = fdmap (p, fd);
|
||
switch (real_fd)
|
||
{
|
||
default:
|
||
result = write (real_fd, buf, len);
|
||
p->last_errno = errno;
|
||
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 (host_callback *p ATTRIBUTE_UNUSED, const char *buf, int len)
|
||
{
|
||
return fwrite (buf, 1, len, stdout);
|
||
}
|
||
|
||
static void
|
||
os_flush_stdout (host_callback *p ATTRIBUTE_UNUSED)
|
||
{
|
||
fflush (stdout);
|
||
}
|
||
|
||
static int
|
||
os_write_stderr (host_callback *p ATTRIBUTE_UNUSED, const char *buf, int len)
|
||
{
|
||
return fwrite (buf, 1, len, stderr);
|
||
}
|
||
|
||
static void
|
||
os_flush_stderr (host_callback *p ATTRIBUTE_UNUSED)
|
||
{
|
||
fflush (stderr);
|
||
}
|
||
|
||
static int
|
||
os_rename (host_callback *p, const char *f1, const char *f2)
|
||
{
|
||
int result;
|
||
|
||
result = rename (f1, f2);
|
||
p->last_errno = errno;
|
||
return result;
|
||
}
|
||
|
||
|
||
static int
|
||
os_system (host_callback *p, const char *s)
|
||
{
|
||
int result;
|
||
|
||
result = system (s);
|
||
p->last_errno = errno;
|
||
return result;
|
||
}
|
||
|
||
static int64_t
|
||
os_time (host_callback *p)
|
||
{
|
||
int64_t result;
|
||
|
||
result = time (NULL);
|
||
p->last_errno = errno;
|
||
return result;
|
||
}
|
||
|
||
|
||
static int
|
||
os_unlink (host_callback *p, const char *f1)
|
||
{
|
||
int result;
|
||
|
||
result = unlink (f1);
|
||
p->last_errno = errno;
|
||
return result;
|
||
}
|
||
|
||
static int
|
||
os_stat (host_callback *p, const char *file, struct stat *buf)
|
||
{
|
||
int result;
|
||
|
||
/* ??? 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. */
|
||
result = stat (file, buf);
|
||
p->last_errno = errno;
|
||
return result;
|
||
}
|
||
|
||
static int
|
||
os_fstat (host_callback *p, int fd, struct stat *buf)
|
||
{
|
||
int result;
|
||
|
||
if (fdbad (p, fd))
|
||
return -1;
|
||
|
||
if (p->ispipe[fd])
|
||
{
|
||
#if defined (HAVE_STRUCT_STAT_ST_ATIME) || defined (HAVE_STRUCT_STAT_ST_CTIME) || defined (HAVE_STRUCT_STAT_ST_MTIME)
|
||
time_t t = (*p->time) (p);
|
||
#endif
|
||
|
||
/* We have to fake the struct stat contents, since the pipe is
|
||
made up in the simulator. */
|
||
memset (buf, 0, sizeof (*buf));
|
||
|
||
#ifdef HAVE_STRUCT_STAT_ST_MODE
|
||
buf->st_mode = S_IFIFO;
|
||
#endif
|
||
|
||
/* If more accurate tracking than current-time is needed (for
|
||
example, on GNU/Linux we get accurate numbers), the p->time
|
||
callback (which may be something other than os_time) should
|
||
happen for each read and write, and we'd need to keep track of
|
||
atime, ctime and mtime. */
|
||
#ifdef HAVE_STRUCT_STAT_ST_ATIME
|
||
buf->st_atime = t;
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_CTIME
|
||
buf->st_ctime = t;
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_MTIME
|
||
buf->st_mtime = t;
|
||
#endif
|
||
return 0;
|
||
}
|
||
|
||
/* ??? 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. */
|
||
result = fstat (fdmap (p, fd), buf);
|
||
p->last_errno = errno;
|
||
return result;
|
||
}
|
||
|
||
static int
|
||
os_lstat (host_callback *p, const char *file, struct stat *buf)
|
||
{
|
||
int result;
|
||
|
||
/* NOTE: hpn/2004-12-12: Same issue here as with os_fstat. */
|
||
#ifdef HAVE_LSTAT
|
||
result = lstat (file, buf);
|
||
#else
|
||
result = stat (file, buf);
|
||
#endif
|
||
p->last_errno = errno;
|
||
return result;
|
||
}
|
||
|
||
static int
|
||
os_ftruncate (host_callback *p, int fd, int64_t len)
|
||
{
|
||
int result;
|
||
|
||
result = fdbad (p, fd);
|
||
if (p->ispipe[fd])
|
||
{
|
||
p->last_errno = EINVAL;
|
||
return -1;
|
||
}
|
||
if (result)
|
||
return result;
|
||
#ifdef HAVE_FTRUNCATE
|
||
result = ftruncate (fdmap (p, fd), len);
|
||
p->last_errno = errno;
|
||
#else
|
||
p->last_errno = EINVAL;
|
||
result = -1;
|
||
#endif
|
||
return result;
|
||
}
|
||
|
||
static int
|
||
os_truncate (host_callback *p, const char *file, int64_t len)
|
||
{
|
||
#ifdef HAVE_TRUNCATE
|
||
int result;
|
||
|
||
result = truncate (file, len);
|
||
p->last_errno = errno;
|
||
return result;
|
||
#else
|
||
p->last_errno = EINVAL;
|
||
return -1;
|
||
#endif
|
||
}
|
||
|
||
static int
|
||
os_getpid (host_callback *p)
|
||
{
|
||
int result;
|
||
|
||
result = getpid ();
|
||
/* POSIX says getpid always succeeds. */
|
||
p->last_errno = 0;
|
||
return result;
|
||
}
|
||
|
||
static int
|
||
os_kill (host_callback *p, int pid, int signum)
|
||
{
|
||
#ifdef HAVE_KILL
|
||
int result;
|
||
|
||
result = kill (pid, signum);
|
||
p->last_errno = errno;
|
||
return result;
|
||
#else
|
||
p->last_errno = ENOSYS;
|
||
return -1;
|
||
#endif
|
||
}
|
||
|
||
static int
|
||
os_pipe (host_callback *p, int *filedes)
|
||
{
|
||
int i;
|
||
|
||
/* We deliberately don't use fd 0. It's probably stdin anyway. */
|
||
for (i = 1; i < MAX_CALLBACK_FDS; i++)
|
||
{
|
||
int j;
|
||
|
||
if (p->fd_buddy[i] < 0)
|
||
for (j = i + 1; j < MAX_CALLBACK_FDS; j++)
|
||
if (p->fd_buddy[j] < 0)
|
||
{
|
||
/* Found two free fd:s. Set stat to allocated and mark
|
||
pipeness. */
|
||
p->fd_buddy[i] = i;
|
||
p->fd_buddy[j] = j;
|
||
p->ispipe[i] = j;
|
||
p->ispipe[j] = -i;
|
||
filedes[0] = i;
|
||
filedes[1] = j;
|
||
|
||
/* Poison the FD map to make bugs apparent. */
|
||
p->fdmap[i] = -1;
|
||
p->fdmap[j] = -1;
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
p->last_errno = EMFILE;
|
||
return -1;
|
||
}
|
||
|
||
/* Stub functions for pipe support. They should always be overridden in
|
||
targets using the pipe support, but that's up to the target. */
|
||
|
||
/* Called when the simulator says that the pipe at (reader, writer) is
|
||
now empty (so the writer should leave its waiting state). */
|
||
|
||
static void
|
||
os_pipe_empty (host_callback *p, int reader, int writer)
|
||
{
|
||
}
|
||
|
||
/* Called when the simulator says the pipe at (reader, writer) is now
|
||
non-empty (so the writer should wait). */
|
||
|
||
static void
|
||
os_pipe_nonempty (host_callback *p, int reader, int writer)
|
||
{
|
||
}
|
||
|
||
static int
|
||
os_shutdown (host_callback *p)
|
||
{
|
||
int i, next, j;
|
||
for (i = 0; i < MAX_CALLBACK_FDS; i++)
|
||
{
|
||
int do_close = 1;
|
||
|
||
/* Zero out all pipe state. Don't call callbacks for non-empty
|
||
pipes; the target program has likely terminated at this point
|
||
or we're called at initialization time. */
|
||
p->ispipe[i] = 0;
|
||
p->pipe_buffer[i].size = 0;
|
||
p->pipe_buffer[i].buffer = NULL;
|
||
|
||
next = p->fd_buddy[i];
|
||
if (next < 0)
|
||
continue;
|
||
do
|
||
{
|
||
j = next;
|
||
if (j == MAX_CALLBACK_FDS)
|
||
do_close = 0;
|
||
next = p->fd_buddy[j];
|
||
p->fd_buddy[j] = -1;
|
||
/* At the initial call of os_init, we got -1, 0, 0, 0, ... */
|
||
if (next < 0)
|
||
{
|
||
p->fd_buddy[i] = -1;
|
||
do_close = 0;
|
||
break;
|
||
}
|
||
}
|
||
while (j != i);
|
||
if (do_close)
|
||
close (p->fdmap[i]);
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
os_init (host_callback *p)
|
||
{
|
||
int i;
|
||
|
||
os_shutdown (p);
|
||
for (i = 0; i < 3; i++)
|
||
{
|
||
p->fdmap[i] = i;
|
||
p->fd_buddy[i] = i - 1;
|
||
}
|
||
p->fd_buddy[0] = MAX_CALLBACK_FDS;
|
||
p->fd_buddy[MAX_CALLBACK_FDS] = 2;
|
||
|
||
p->syscall_map = cb_init_syscall_map;
|
||
p->errno_map = cb_init_errno_map;
|
||
p->signal_map = cb_init_signal_map;
|
||
p->open_map = cb_init_open_map;
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* DEPRECATED */
|
||
|
||
/* VARARGS */
|
||
static void ATTRIBUTE_PRINTF (2, 3)
|
||
os_printf_filtered (host_callback *p ATTRIBUTE_UNUSED, const char *format, ...)
|
||
{
|
||
va_list args;
|
||
va_start (args, format);
|
||
|
||
vfprintf (stdout, format, args);
|
||
va_end (args);
|
||
}
|
||
|
||
/* VARARGS */
|
||
static void ATTRIBUTE_PRINTF (2, 0)
|
||
os_vprintf_filtered (host_callback *p ATTRIBUTE_UNUSED, const char *format, va_list args)
|
||
{
|
||
vprintf (format, args);
|
||
}
|
||
|
||
/* VARARGS */
|
||
static void ATTRIBUTE_PRINTF (2, 0)
|
||
os_evprintf_filtered (host_callback *p ATTRIBUTE_UNUSED, const char *format, va_list args)
|
||
{
|
||
vfprintf (stderr, format, args);
|
||
}
|
||
|
||
/* VARARGS */
|
||
static void ATTRIBUTE_PRINTF (2, 3) ATTRIBUTE_NORETURN
|
||
os_error (host_callback *p ATTRIBUTE_UNUSED, const char *format, ...)
|
||
{
|
||
va_list args;
|
||
va_start (args, format);
|
||
|
||
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_lstat,
|
||
|
||
os_ftruncate,
|
||
os_truncate,
|
||
|
||
os_getpid,
|
||
os_kill,
|
||
|
||
os_pipe,
|
||
os_pipe_empty,
|
||
os_pipe_nonempty,
|
||
|
||
os_poll_quit,
|
||
|
||
os_shutdown,
|
||
os_init,
|
||
|
||
os_printf_filtered, /* deprecated */
|
||
|
||
os_vprintf_filtered,
|
||
os_evprintf_filtered,
|
||
os_error,
|
||
|
||
0, /* last errno */
|
||
|
||
{ 0, }, /* fdmap */
|
||
{ -1, }, /* fd_buddy */
|
||
{ 0, }, /* ispipe */
|
||
{ { 0, 0 }, }, /* pipe_buffer */
|
||
|
||
0, /* syscall_map */
|
||
0, /* errno_map */
|
||
0, /* open_map */
|
||
0, /* signal_map */
|
||
0, /* stat_map */
|
||
|
||
/* Defaults expected to be overridden at initialization, where needed. */
|
||
BFD_ENDIAN_UNKNOWN, /* target_endian */
|
||
NULL, /* argv */
|
||
NULL, /* envp */
|
||
4, /* target_sizeof_int */
|
||
|
||
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 (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 ((void *) 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;
|
||
}
|
||
|
||
/* General utility functions to search a map for a value. */
|
||
|
||
static const CB_TARGET_DEFS_MAP *
|
||
cb_target_map_entry (const CB_TARGET_DEFS_MAP map[], int target_val)
|
||
{
|
||
const CB_TARGET_DEFS_MAP *m;
|
||
|
||
for (m = &map[0]; m->target_val != -1; ++m)
|
||
if (m->target_val == target_val)
|
||
return m;
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static const CB_TARGET_DEFS_MAP *
|
||
cb_host_map_entry (const CB_TARGET_DEFS_MAP map[], int host_val)
|
||
{
|
||
const CB_TARGET_DEFS_MAP *m;
|
||
|
||
for (m = &map[0]; m->host_val != -1; ++m)
|
||
if (m->host_val == host_val)
|
||
return m;
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/* 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 (host_callback *cb, int target_val)
|
||
{
|
||
const CB_TARGET_DEFS_MAP *m =
|
||
cb_target_map_entry (cb->syscall_map, target_val);
|
||
|
||
return m ? m->host_val : -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 (host_callback *cb, int host_val)
|
||
{
|
||
const CB_TARGET_DEFS_MAP *m = cb_host_map_entry (cb->errno_map, host_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 m ? m->target_val : 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 (host_callback *cb, int target_val)
|
||
{
|
||
int host_val = 0;
|
||
CB_TARGET_DEFS_MAP *m;
|
||
int o_rdonly = 0;
|
||
int o_wronly = 0;
|
||
int o_rdwr = 0;
|
||
int o_binary = 0;
|
||
int o_rdwrmask;
|
||
|
||
/* O_RDONLY can be (and usually is) 0 which needs to be treated specially. */
|
||
for (m = &cb->open_map[0]; m->host_val != -1; ++m)
|
||
{
|
||
if (!strcmp (m->name, "O_RDONLY"))
|
||
o_rdonly = m->target_val;
|
||
else if (!strcmp (m->name, "O_WRONLY"))
|
||
o_wronly = m->target_val;
|
||
else if (!strcmp (m->name, "O_RDWR"))
|
||
o_rdwr = m->target_val;
|
||
else if (!strcmp (m->name, "O_BINARY"))
|
||
o_binary = m->target_val;
|
||
}
|
||
o_rdwrmask = o_rdonly | o_wronly | o_rdwr;
|
||
|
||
for (m = &cb->open_map[0]; m->host_val != -1; ++m)
|
||
{
|
||
if (m->target_val == o_rdonly || m->target_val == o_wronly
|
||
|| m->target_val == o_rdwr)
|
||
{
|
||
if ((target_val & o_rdwrmask) == m->target_val)
|
||
host_val |= m->host_val;
|
||
/* Handle the host/target differentiating between binary and
|
||
text mode. Only one case is of importance */
|
||
#ifdef O_BINARY
|
||
if (o_binary == 0)
|
||
host_val |= O_BINARY;
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
if ((m->target_val & target_val) == m->target_val)
|
||
host_val |= m->host_val;
|
||
}
|
||
}
|
||
|
||
return host_val;
|
||
}
|
||
|
||
/* Translate the target's version of a signal number to the host's.
|
||
This isn't actually the host's version, rather a canonical form.
|
||
??? Perhaps this should be renamed to ..._canon_signal. */
|
||
|
||
int
|
||
cb_target_to_host_signal (host_callback *cb, int target_val)
|
||
{
|
||
const CB_TARGET_DEFS_MAP *m =
|
||
cb_target_map_entry (cb->signal_map, target_val);
|
||
|
||
return m ? m->host_val : -1;
|
||
}
|
||
|
||
/* Utility for e.g. cb_host_to_target_stat to store values in the target's
|
||
stat struct.
|
||
|
||
??? The "val" must be as big as target word size. */
|
||
|
||
void
|
||
cb_store_target_endian (host_callback *cb, char *p, int size, long val)
|
||
{
|
||
if (cb->target_endian == BFD_ENDIAN_BIG)
|
||
{
|
||
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 occurred during the translation. */
|
||
|
||
int
|
||
cb_host_to_target_stat (host_callback *cb, const struct stat *hs, void *ts)
|
||
{
|
||
const char *m = cb->stat_map;
|
||
char *p;
|
||
|
||
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 (0)
|
||
;
|
||
/* Defined here to avoid emacs indigestion on a lone "else". */
|
||
#undef ST_x
|
||
#define ST_x(FLD) \
|
||
else if (strncmp (m, #FLD, q - m) == 0) \
|
||
cb_store_target_endian (cb, p, size, hs->FLD)
|
||
|
||
#ifdef HAVE_STRUCT_STAT_ST_DEV
|
||
ST_x (st_dev);
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_INO
|
||
ST_x (st_ino);
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_MODE
|
||
ST_x (st_mode);
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_NLINK
|
||
ST_x (st_nlink);
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_UID
|
||
ST_x (st_uid);
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_GID
|
||
ST_x (st_gid);
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_RDEV
|
||
ST_x (st_rdev);
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_SIZE
|
||
ST_x (st_size);
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
|
||
ST_x (st_blksize);
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
|
||
ST_x (st_blocks);
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_ATIME
|
||
ST_x (st_atime);
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_MTIME
|
||
ST_x (st_mtime);
|
||
#endif
|
||
#ifdef HAVE_STRUCT_STAT_ST_CTIME
|
||
ST_x (st_ctime);
|
||
#endif
|
||
#undef ST_x
|
||
/* FIXME:wip */
|
||
else
|
||
/* Unsupported field, store 0. */
|
||
cb_store_target_endian (cb, p, size, 0);
|
||
}
|
||
|
||
p += size;
|
||
m = strchr (q, ':');
|
||
if (m)
|
||
++m;
|
||
}
|
||
|
||
return p - (char *) ts;
|
||
}
|
||
|
||
int
|
||
cb_is_stdin (host_callback *cb, int fd)
|
||
{
|
||
return fdbad (cb, fd) ? 0 : fdmap (cb, fd) == 0;
|
||
}
|
||
|
||
int
|
||
cb_is_stdout (host_callback *cb, int fd)
|
||
{
|
||
return fdbad (cb, fd) ? 0 : fdmap (cb, fd) == 1;
|
||
}
|
||
|
||
int
|
||
cb_is_stderr (host_callback *cb, int fd)
|
||
{
|
||
return fdbad (cb, fd) ? 0 : fdmap (cb, fd) == 2;
|
||
}
|
||
|
||
const char *
|
||
cb_host_str_syscall (host_callback *cb, int host_val)
|
||
{
|
||
const CB_TARGET_DEFS_MAP *m = cb_host_map_entry (cb->syscall_map, host_val);
|
||
|
||
return m ? m->name : NULL;
|
||
}
|
||
|
||
const char *
|
||
cb_host_str_errno (host_callback *cb, int host_val)
|
||
{
|
||
const CB_TARGET_DEFS_MAP *m = cb_host_map_entry (cb->errno_map, host_val);
|
||
|
||
return m ? m->name : NULL;
|
||
}
|
||
|
||
const char *
|
||
cb_host_str_signal (host_callback *cb, int host_val)
|
||
{
|
||
const CB_TARGET_DEFS_MAP *m = cb_host_map_entry (cb->signal_map, host_val);
|
||
|
||
return m ? m->name : NULL;
|
||
}
|
||
|
||
const char *
|
||
cb_target_str_syscall (host_callback *cb, int target_val)
|
||
{
|
||
const CB_TARGET_DEFS_MAP *m =
|
||
cb_target_map_entry (cb->syscall_map, target_val);
|
||
|
||
return m ? m->name : NULL;
|
||
}
|
||
|
||
const char *
|
||
cb_target_str_errno (host_callback *cb, int target_val)
|
||
{
|
||
const CB_TARGET_DEFS_MAP *m =
|
||
cb_target_map_entry (cb->errno_map, target_val);
|
||
|
||
return m ? m->name : NULL;
|
||
}
|
||
|
||
const char *
|
||
cb_target_str_signal (host_callback *cb, int target_val)
|
||
{
|
||
const CB_TARGET_DEFS_MAP *m =
|
||
cb_target_map_entry (cb->signal_map, target_val);
|
||
|
||
return m ? m->name : NULL;
|
||
}
|