Make gdb_bfd_open able to open BFDs using target fileio
authorGary Benson <gbenson@redhat.com>
Thu, 2 Apr 2015 12:38:29 +0000 (13:38 +0100)
committerGary Benson <gbenson@redhat.com>
Thu, 2 Apr 2015 12:38:29 +0000 (13:38 +0100)
This commit updates gdb_bfd_open to access files using target
fileio functions if the supplied path starts with "target:"
and if the local and target filesystems are not the same.
This allows users to specify "set sysroot target:" and have
GDB access files locally or from the remote as appropriate.

The new functions in gdb_bfd.c are copies of functions from
remote.c. This duplication is intentional and will be removed
by the next commit in this series.

gdb/ChangeLog:

* gdb/gdb_bfd.h (TARGET_SYSROOT_PREFIX): New definition.
(is_target_filename): New declaration.
(gdb_bfd_has_target_filename): Likewise.
(gdb_bfd_open): Update documentation comment.
* gdb_bfd.c (target.h): New include.
(gdb/fileio.h): Likewise.
(is_target_filename): New function.
(gdb_bfd_has_target_filename): Likewise.
(fileio_errno_to_host): Likewise.
(gdb_bfd_iovec_fileio_open): Likewise.
(gdb_bfd_iovec_fileio_pread): Likewise.
(gdb_bfd_iovec_fileio_close): Likewise.
(gdb_bfd_iovec_fileio_fstat): Likewise.
(gdb_bfd_open): Use target fileio to access paths prefixed
with "target:" where necessary.

gdb/ChangeLog
gdb/gdb_bfd.c
gdb/gdb_bfd.h

index 697aeb1e4d0285bec02251c92b2f4db325340b46..35acfedfd932d133376409a56d9ce978b0c0ab08 100644 (file)
@@ -1,3 +1,21 @@
+2015-04-02  Gary Benson <gbenson@redhat.com>
+
+       * gdb/gdb_bfd.h (TARGET_SYSROOT_PREFIX): New definition.
+       (is_target_filename): New declaration.
+       (gdb_bfd_has_target_filename): Likewise.
+       (gdb_bfd_open): Update documentation comment.
+       * gdb_bfd.c (target.h): New include.
+       (gdb/fileio.h): Likewise.
+       (is_target_filename): New function.
+       (gdb_bfd_has_target_filename): Likewise.
+       (fileio_errno_to_host): Likewise.
+       (gdb_bfd_iovec_fileio_open): Likewise.
+       (gdb_bfd_iovec_fileio_pread): Likewise.
+       (gdb_bfd_iovec_fileio_close): Likewise.
+       (gdb_bfd_iovec_fileio_fstat): Likewise.
+       (gdb_bfd_open): Use target fileio to access paths prefixed
+       with "target:" where necessary.
+
 2015-04-02  Gary Benson <gbenson@redhat.com>
 
        * target.h (struct target_ops) <to_filesystem_is_local>:
index 40874a84d2d09e9000423f847db5c15111d3caa1..3d5d23f7cb81568291ddda7ccd8cf3247d878994 100644 (file)
@@ -30,6 +30,8 @@
 #define MAP_FAILED ((void *) -1)
 #endif
 #endif
+#include "target.h"
+#include "gdb/fileio.h"
 
 typedef bfd *bfdp;
 DEF_VEC_P (bfdp);
@@ -138,6 +140,177 @@ eq_bfd (const void *a, const void *b)
 
 /* See gdb_bfd.h.  */
 
+int
+is_target_filename (const char *name)
+{
+  return startswith (name, TARGET_SYSROOT_PREFIX);
+}
+
+/* See gdb_bfd.h.  */
+
+int
+gdb_bfd_has_target_filename (struct bfd *abfd)
+{
+  return is_target_filename (bfd_get_filename (abfd));
+}
+
+
+/* Return the system error number corresponding to ERRNUM.  */
+
+static int
+fileio_errno_to_host (int errnum)
+{
+  switch (errnum)
+    {
+      case FILEIO_EPERM:
+        return EPERM;
+      case FILEIO_ENOENT:
+        return ENOENT;
+      case FILEIO_EINTR:
+        return EINTR;
+      case FILEIO_EIO:
+        return EIO;
+      case FILEIO_EBADF:
+        return EBADF;
+      case FILEIO_EACCES:
+        return EACCES;
+      case FILEIO_EFAULT:
+        return EFAULT;
+      case FILEIO_EBUSY:
+        return EBUSY;
+      case FILEIO_EEXIST:
+        return EEXIST;
+      case FILEIO_ENODEV:
+        return ENODEV;
+      case FILEIO_ENOTDIR:
+        return ENOTDIR;
+      case FILEIO_EISDIR:
+        return EISDIR;
+      case FILEIO_EINVAL:
+        return EINVAL;
+      case FILEIO_ENFILE:
+        return ENFILE;
+      case FILEIO_EMFILE:
+        return EMFILE;
+      case FILEIO_EFBIG:
+        return EFBIG;
+      case FILEIO_ENOSPC:
+        return ENOSPC;
+      case FILEIO_ESPIPE:
+        return ESPIPE;
+      case FILEIO_EROFS:
+        return EROFS;
+      case FILEIO_ENOSYS:
+        return ENOSYS;
+      case FILEIO_ENAMETOOLONG:
+        return ENAMETOOLONG;
+    }
+  return -1;
+}
+
+/* Wrapper for target_fileio_open suitable for passing as the
+   OPEN_FUNC argument to gdb_bfd_openr_iovec.  The supplied
+   OPEN_CLOSURE is unused.  */
+
+static void *
+gdb_bfd_iovec_fileio_open (struct bfd *abfd, void *open_closure)
+{
+  const char *filename = bfd_get_filename (abfd);
+  int fd, target_errno;
+  int *stream;
+
+  gdb_assert (is_target_filename (filename));
+
+  fd = target_fileio_open (filename + strlen (TARGET_SYSROOT_PREFIX),
+                          FILEIO_O_RDONLY, 0,
+                          &target_errno);
+  if (fd == -1)
+    {
+      errno = fileio_errno_to_host (target_errno);
+      bfd_set_error (bfd_error_system_call);
+      return NULL;
+    }
+
+  stream = XCNEW (int);
+  *stream = fd;
+  return stream;
+}
+
+/* Wrapper for target_fileio_pread suitable for passing as the
+   PREAD_FUNC argument to gdb_bfd_openr_iovec.  */
+
+static file_ptr
+gdb_bfd_iovec_fileio_pread (struct bfd *abfd, void *stream, void *buf,
+                           file_ptr nbytes, file_ptr offset)
+{
+  int fd = *(int *) stream;
+  int target_errno;
+  file_ptr pos, bytes;
+
+  pos = 0;
+  while (nbytes > pos)
+    {
+      bytes = target_fileio_pread (fd, (gdb_byte *) buf + pos,
+                                  nbytes - pos, offset + pos,
+                                  &target_errno);
+      if (bytes == 0)
+        /* Success, but no bytes, means end-of-file.  */
+        break;
+      if (bytes == -1)
+       {
+         errno = fileio_errno_to_host (target_errno);
+         bfd_set_error (bfd_error_system_call);
+         return -1;
+       }
+
+      pos += bytes;
+    }
+
+  return pos;
+}
+
+/* Wrapper for target_fileio_close suitable for passing as the
+   CLOSE_FUNC argument to gdb_bfd_openr_iovec.  */
+
+static int
+gdb_bfd_iovec_fileio_close (struct bfd *abfd, void *stream)
+{
+  int fd = *(int *) stream;
+  int target_errno;
+
+  xfree (stream);
+
+  /* Ignore errors on close.  These may happen with remote
+     targets if the connection has already been torn down.  */
+  target_fileio_close (fd, &target_errno);
+
+  /* Zero means success.  */
+  return 0;
+}
+
+/* Wrapper for target_fileio_fstat suitable for passing as the
+   STAT_FUNC argument to gdb_bfd_openr_iovec.  */
+
+static int
+gdb_bfd_iovec_fileio_fstat (struct bfd *abfd, void *stream,
+                           struct stat *sb)
+{
+  int fd = *(int *) stream;
+  int target_errno;
+  int result;
+
+  result = target_fileio_fstat (fd, sb, &target_errno);
+  if (result == -1)
+    {
+      errno = fileio_errno_to_host (target_errno);
+      bfd_set_error (bfd_error_system_call);
+    }
+
+  return result;
+}
+
+/* See gdb_bfd.h.  */
+
 struct bfd *
 gdb_bfd_open (const char *name, const char *target, int fd)
 {
@@ -147,6 +320,36 @@ gdb_bfd_open (const char *name, const char *target, int fd)
   struct gdb_bfd_cache_search search;
   struct stat st;
 
+  if (is_target_filename (name))
+    {
+      if (!target_filesystem_is_local ())
+       {
+         gdb_assert (fd == -1);
+
+         abfd = gdb_bfd_openr_iovec (name, target,
+                                     gdb_bfd_iovec_fileio_open, NULL,
+                                     gdb_bfd_iovec_fileio_pread,
+                                     gdb_bfd_iovec_fileio_close,
+                                     gdb_bfd_iovec_fileio_fstat);
+
+         if (abfd != NULL || errno != ENOSYS)
+           return abfd;
+
+         /* gdb_bfd_iovec_fileio_open failed with ENOSYS.  This can
+            happen, for example, with vgdb (Valgrind GDB), which
+            presents itself as a remote target but works on the local
+            filesystem: it does not implement remote get and users
+            are not expected to set gdb_sysroot.  To handle this case
+            we fall back to trying the local filesystem iff
+            gdb_sysroot is exactly TARGET_SYSROOT_PREFIX.  */
+         if (gdb_sysroot == NULL
+             || strcmp (gdb_sysroot, TARGET_SYSROOT_PREFIX) != 0)
+           return NULL;
+       }
+
+      name += strlen (TARGET_SYSROOT_PREFIX);
+    }
+
   if (gdb_bfd_cache == NULL)
     gdb_bfd_cache = htab_create_alloc (1, hash_bfd, eq_bfd, NULL,
                                       xcalloc, xfree);
index 05b68703cf86ef058c0a514c8ee61be97ebe3fc7..8fbdf3652a99b4b9fe9bfc132ad4e316e1d1654e 100644 (file)
 
 DECLARE_REGISTRY (bfd);
 
+/* If supplied a path starting with this sequence, gdb_bfd_open will
+   open BFDs using target fileio operations.  */
+
+#define TARGET_SYSROOT_PREFIX "target:"
+
+/* Returns nonzero if NAME starts with TARGET_SYSROOT_PREFIX, zero
+   otherwise.  */
+
+int is_target_filename (const char *name);
+
+/* Returns nonzero if the filename associated with ABFD starts with
+   TARGET_SYSROOT_PREFIX, zero otherwise.  */
+
+int gdb_bfd_has_target_filename (struct bfd *abfd);
+
 /* Open a read-only (FOPEN_RB) BFD given arguments like bfd_fopen.
-   Returns NULL on error.  On success, returns a new reference to the
-   BFD, which must be freed with gdb_bfd_unref.  BFDs returned by this
-   call are shared among all callers opening the same file.  If FD is
-   not -1, then after this call it is owned by BFD.  */
+   If NAME starts with TARGET_SYSROOT_PREFIX then the BFD will be
+   opened using target fileio operations if necessary.  Returns NULL
+   on error.  On success, returns a new reference to the BFD, which
+   must be freed with gdb_bfd_unref.  BFDs returned by this call are
+   shared among all callers opening the same file.  If FD is not -1,
+   then after this call it is owned by BFD.  If the BFD was not
+   accessed using target fileio operations then the filename
+   associated with the BFD and accessible with bfd_get_filename will
+   not be exactly NAME but rather NAME with TARGET_SYSROOT_PREFIX
+   stripped.  */
 
 struct bfd *gdb_bfd_open (const char *name, const char *target, int fd);