2004-04-21 Andrew Cagney <cagney@redhat.com>
authorAndrew Cagney <cagney@redhat.com>
Wed, 21 Apr 2004 17:05:12 +0000 (17:05 +0000)
committerAndrew Cagney <cagney@redhat.com>
Wed, 21 Apr 2004 17:05:12 +0000 (17:05 +0000)
* opncls.c (_bfd_new_bfd_contained_in): Copy "iovec".
(struct opncls, opncls_btell, opncls_bseek, opncls_bread)
(opncls_bwrite, opncls_bclose, opncls_bflush)
(opncls_bstat, opncls_iovec, bfd_openr_iovec): Implement a
bfd iovec that uses function callbacks.
(bfd_close): Use the iovec's bclose.
* cache.c (cache_btell, cache_bseek, cache_bread, cache_bwrite)
(cache_bclose, cache_bflush, cache_bstat)
(cache_iovec): New functions and global variable, implement a
cache "iovec", where applicable set bfd_error.
(bfd_cache_init, bfd_cache_close): Set/test the bfd's iovec.
* bfdio.c (struct bfd_iovec): Define.
(real_read): Delete function.
(bfd_bread, bfd_bread, bfd_bwrite, bfd_tell, bfd_flush, bfd_stat)
(bfd_seek, bfd_get_mtime, bfd_get_size): Use the bfd's "iovec",
assume that bread and bwrite set bfd_error.
* bfd.c (struct bfd): Add "iovec", update comments.
* bfd-in2.h, libbfd.h: Re-generate.

bfd/ChangeLog
bfd/bfd-in2.h
bfd/bfd.c
bfd/bfdio.c
bfd/cache.c
bfd/libbfd.h
bfd/opncls.c

index 279a97c9b5a992e02d1e505469d667ad8b4e9a93..23cc010dd7c5b78ba1b5ab75a98ca835a79264c4 100644 (file)
@@ -1,3 +1,24 @@
+2004-04-21  Andrew Cagney  <cagney@redhat.com>
+
+       * opncls.c (_bfd_new_bfd_contained_in): Copy "iovec".
+       (struct opncls, opncls_btell, opncls_bseek, opncls_bread)
+       (opncls_bwrite, opncls_bclose, opncls_bflush)
+       (opncls_bstat, opncls_iovec, bfd_openr_iovec): Implement a
+       bfd iovec that uses function callbacks.
+       (bfd_close): Use the iovec's bclose.
+       * cache.c (cache_btell, cache_bseek, cache_bread, cache_bwrite)
+       (cache_bclose, cache_bflush, cache_bstat)
+       (cache_iovec): New functions and global variable, implement a
+       cache "iovec", where applicable set bfd_error.
+       (bfd_cache_init, bfd_cache_close): Set/test the bfd's iovec.
+       * bfdio.c (struct bfd_iovec): Define.
+       (real_read): Delete function.
+       (bfd_bread, bfd_bread, bfd_bwrite, bfd_tell, bfd_flush, bfd_stat)
+       (bfd_seek, bfd_get_mtime, bfd_get_size): Use the bfd's "iovec",
+       assume that bread and bwrite set bfd_error.
+       * bfd.c (struct bfd): Add "iovec", update comments.
+       * bfd-in2.h, libbfd.h: Re-generate.
+
 2004-04-21  Andrew Cagney  <cagney@redhat.com>
 
        * libaout.h (enum machine_type): Add M_POWERPC_NETBSD.
index 07af231fbaacaa86e35ca81404ba2182803f6c15..49e22b4d40fbb4e929f590556888215d1dda1e7a 100644 (file)
@@ -839,6 +839,18 @@ bfd *bfd_fdopenr (const char *filename, const char *target, int fd);
 
 bfd *bfd_openstreamr (const char *, const char *, void *);
 
+bfd *bfd_openr_iovec (const char *filename, const char *target,
+    void *(*open) (struct bfd *nbfd,
+    void *open_closure),
+    void *open_closure,
+    file_ptr (*pread) (struct bfd *nbfd,
+    void *stream,
+    void *buf,
+    file_ptr nbytes,
+    file_ptr offset),
+    int (*close) (struct bfd *nbfd,
+    void *stream));
+
 bfd *bfd_openw (const char *filename, const char *target);
 
 bfd_boolean bfd_close (bfd *abfd);
@@ -3708,14 +3720,10 @@ struct bfd
   /* A pointer to the target jump table.  */
   const struct bfd_target *xvec;
 
-  /* To avoid dragging too many header files into every file that
-     includes `<<bfd.h>>', IOSTREAM has been declared as a "char *",
-     and MTIME as a "long".  Their correct types, to which they
-     are cast when used, are "FILE *" and "time_t".    The iostream
-     is the result of an fopen on the filename.  However, if the
-     BFD_IN_MEMORY flag is set, then iostream is actually a pointer
-     to a bfd_in_memory struct.  */
+  /* The IOSTREAM, and corresponding IO vector that provide access
+     to the file backing the BFD.  */
   void *iostream;
+  const struct bfd_iovec *iovec;
 
   /* Is the file descriptor being cached?  That is, can it be closed as
      needed, and re-opened when accessed later?  */
index 59e22aaed8dd3aa1c0fd6ec2228118bdea411b64..524e7d2af33054ecf5e17a965da64fe6839ab365 100644 (file)
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -45,14 +45,10 @@ CODE_FRAGMENT
 .  {* A pointer to the target jump table.  *}
 .  const struct bfd_target *xvec;
 .
-.  {* To avoid dragging too many header files into every file that
-.     includes `<<bfd.h>>', IOSTREAM has been declared as a "char *",
-.     and MTIME as a "long".  Their correct types, to which they
-.     are cast when used, are "FILE *" and "time_t".    The iostream
-.     is the result of an fopen on the filename.  However, if the
-.     BFD_IN_MEMORY flag is set, then iostream is actually a pointer
-.     to a bfd_in_memory struct.  *}
+.  {* The IOSTREAM, and corresponding IO vector that provide access
+.     to the file backing the BFD.  *}
 .  void *iostream;
+.  const struct bfd_iovec *iovec;
 .
 .  {* Is the file descriptor being cached?  That is, can it be closed as
 .     needed, and re-opened when accessed later?  *}
index a90cb33ea5f87b3641093bd2433b545fedf5377a..53534f4f66f6f02f40019bd484396e64a51b9413 100644 (file)
@@ -62,37 +62,41 @@ real_fseek (FILE *file, file_ptr offset, int whence)
 #endif
 }
 
-/* Note that archive entries don't have streams; they share their parent's.
-   This allows someone to play with the iostream behind BFD's back.
+/*
+INTERNAL_DEFINITION
+       struct bfd_iovec
 
-   Also, note that the origin pointer points to the beginning of a file's
-   contents (0 for non-archive elements).  For archive entries this is the
-   first octet in the file, NOT the beginning of the archive header.  */
+DESCRIPTION
 
-static size_t
-real_read (void *where, size_t a, size_t b, FILE *file)
-{
-  /* FIXME - this looks like an optimization, but it's really to cover
-     up for a feature of some OSs (not solaris - sigh) that
-     ld/pe-dll.c takes advantage of (apparently) when it creates BFDs
-     internally and tries to link against them.  BFD seems to be smart
-     enough to realize there are no symbol records in the "file" that
-     doesn't exist but attempts to read them anyway.  On Solaris,
-     attempting to read zero bytes from a NULL file results in a core
-     dump, but on other platforms it just returns zero bytes read.
-     This makes it to something reasonable. - DJ */
-  if (a == 0 || b == 0)
-    return 0;
+       The <<struct bfd_iovec>> contains the internal file I/O class.
+       Each <<BFD>> has an instance of this class and all file I/O is
+       routed through it (it is assumed that the instance implements
+       all methods listed below).
+
+.struct bfd_iovec
+.{
+.  {* To avoid problems with macros, a "b" rather than "f"
+.     prefix is prepended to each method name.  *}
+.  {* Attempt to read/write NBYTES on ABFD's IOSTREAM storing/fetching
+.     bytes starting at PTR.  Return the number of bytes actually
+.     transfered (a read past end-of-file returns less than NBYTES),
+.     or -1 (setting <<bfd_error>>) if an error occurs.  *}
+.  file_ptr (*bread) (struct bfd *abfd, void *ptr, file_ptr nbytes);
+.  file_ptr (*bwrite) (struct bfd *abfd, const void *ptr,
+.                      file_ptr nbytes);
+.  {* Return the current IOSTREAM file offset, or -1 (setting <<bfd_error>>
+.     if an error occurs.  *}
+.  file_ptr (*btell) (struct bfd *abfd);
+.  {* For the following, on successful completion a value of 0 is returned.
+.     Otherwise, a value of -1 is returned (and  <<bfd_error>> is set).  *}
+.  int (*bseek) (struct bfd *abfd, file_ptr offset, int whence);
+.  int (*bclose) (struct bfd *abfd);
+.  int (*bflush) (struct bfd *abfd);
+.  int (*bstat) (struct bfd *abfd, struct stat *sb);
+.};
 
+*/
 
-#if defined (__VAX) && defined (VMS)
-  /* Apparently fread on Vax VMS does not keep the record length
-     information.  */
-  return read (fileno (file), where, a * b);
-#else
-  return fread (where, a, b, file);
-#endif
-}
 
 /* Return value is amount read.  */
 
@@ -121,25 +125,10 @@ bfd_bread (void *ptr, bfd_size_type size, bfd *abfd)
       return get;
     }
 
-  nread = real_read (ptr, 1, (size_t) size, bfd_cache_lookup (abfd));
+  nread = abfd->iovec->bread (abfd, ptr, size);
   if (nread != (size_t) -1)
     abfd->where += nread;
 
-  /* Set bfd_error if we did not read as much data as we expected.
-
-     If the read failed due to an error set the bfd_error_system_call,
-     else set bfd_error_file_truncated.
-
-     A BFD backend may wish to override bfd_error_file_truncated to
-     provide something more useful (eg. no_symbols or wrong_format).  */
-  if (nread != size)
-    {
-      if (ferror (bfd_cache_lookup (abfd)))
-       bfd_set_error (bfd_error_system_call);
-      else
-       bfd_set_error (bfd_error_file_truncated);
-    }
-
   return nread;
 }
 
@@ -175,7 +164,7 @@ bfd_bwrite (const void *ptr, bfd_size_type size, bfd *abfd)
       return size;
     }
 
-  nwrote = fwrite (ptr, 1, (size_t) size, bfd_cache_lookup (abfd));
+  nwrote = abfd->iovec->bwrite (abfd, ptr, size);
   if (nwrote != (size_t) -1)
     abfd->where += nwrote;
   if (nwrote != size)
@@ -196,7 +185,7 @@ bfd_tell (bfd *abfd)
   if ((abfd->flags & BFD_IN_MEMORY) != 0)
     return abfd->where;
 
-  ptr = real_ftell (bfd_cache_lookup (abfd));
+  ptr = abfd->iovec->btell (abfd);
 
   if (abfd->my_archive)
     ptr -= abfd->origin;
@@ -209,7 +198,7 @@ bfd_flush (bfd *abfd)
 {
   if ((abfd->flags & BFD_IN_MEMORY) != 0)
     return 0;
-  return fflush (bfd_cache_lookup(abfd));
+  return abfd->iovec->bflush (abfd);
 }
 
 /* Returns 0 for success, negative value for failure (in which case
@@ -217,19 +206,12 @@ bfd_flush (bfd *abfd)
 int
 bfd_stat (bfd *abfd, struct stat *statbuf)
 {
-  FILE *f;
   int result;
 
   if ((abfd->flags & BFD_IN_MEMORY) != 0)
     abort ();
 
-  f = bfd_cache_lookup (abfd);
-  if (f == NULL)
-    {
-      bfd_set_error (bfd_error_system_call);
-      return -1;
-    }
-  result = fstat (fileno (f), statbuf);
+  result = abfd->iovec->bstat (abfd, statbuf);
   if (result < 0)
     bfd_set_error (bfd_error_system_call);
   return result;
@@ -242,7 +224,6 @@ int
 bfd_seek (bfd *abfd, file_ptr position, int direction)
 {
   int result;
-  FILE *f;
   file_ptr file_position;
   /* For the time being, a BFD may not seek to it's end.  The problem
      is that we don't easily have a way to recognize the end of an
@@ -328,12 +309,11 @@ bfd_seek (bfd *abfd, file_ptr position, int direction)
         In the meantime, no optimization for archives.  */
     }
 
-  f = bfd_cache_lookup (abfd);
   file_position = position;
   if (direction == SEEK_SET && abfd->my_archive != NULL)
     file_position += abfd->origin;
 
-  result = real_fseek (f, file_position, direction);
+  result = abfd->iovec->bseek (abfd, file_position, direction);
   if (result != 0)
     {
       int hold_errno = errno;
@@ -378,14 +358,12 @@ DESCRIPTION
 long
 bfd_get_mtime (bfd *abfd)
 {
-  FILE *fp;
   struct stat buf;
 
   if (abfd->mtime_set)
     return abfd->mtime;
 
-  fp = bfd_cache_lookup (abfd);
-  if (0 != fstat (fileno (fp), &buf))
+  if (abfd->iovec->bstat (abfd, &buf) != 0)
     return 0;
 
   abfd->mtime = buf.st_mtime;          /* Save value in case anyone wants it */
@@ -428,14 +406,12 @@ DESCRIPTION
 long
 bfd_get_size (bfd *abfd)
 {
-  FILE *fp;
   struct stat buf;
 
   if ((abfd->flags & BFD_IN_MEMORY) != 0)
     return ((struct bfd_in_memory *) abfd->iostream)->size;
 
-  fp = bfd_cache_lookup (abfd);
-  if (0 != fstat (fileno (fp), & buf))
+  if (abfd->iovec->bstat (abfd, &buf) != 0)
     return 0;
 
   return buf.st_size;
index 7d056ea0cfa1b748d651f018d20b179cd367d627..9146c05caca698febe0159986ad3665abcdc96d8 100644 (file)
@@ -44,6 +44,109 @@ SECTION
 
 static bfd_boolean bfd_cache_delete (bfd *);
 
+
+static file_ptr
+cache_btell (struct bfd *abfd)
+{
+  return real_ftell (bfd_cache_lookup (abfd));
+}
+
+static int
+cache_bseek (struct bfd *abfd, file_ptr offset, int whence)
+{
+  return real_fseek (bfd_cache_lookup (abfd), offset, whence);
+}
+
+/* Note that archive entries don't have streams; they share their parent's.
+   This allows someone to play with the iostream behind BFD's back.
+
+   Also, note that the origin pointer points to the beginning of a file's
+   contents (0 for non-archive elements).  For archive entries this is the
+   first octet in the file, NOT the beginning of the archive header.  */
+
+static file_ptr
+cache_bread (struct bfd *abfd, void *buf, file_ptr nbytes)
+{
+  file_ptr nread;
+  /* FIXME - this looks like an optimization, but it's really to cover
+     up for a feature of some OSs (not solaris - sigh) that
+     ld/pe-dll.c takes advantage of (apparently) when it creates BFDs
+     internally and tries to link against them.  BFD seems to be smart
+     enough to realize there are no symbol records in the "file" that
+     doesn't exist but attempts to read them anyway.  On Solaris,
+     attempting to read zero bytes from a NULL file results in a core
+     dump, but on other platforms it just returns zero bytes read.
+     This makes it to something reasonable. - DJ */
+  if (nbytes == 0)
+    return 0;
+
+#if defined (__VAX) && defined (VMS)
+  /* Apparently fread on Vax VMS does not keep the record length
+     information.  */
+  nread = read (fileno (bfd_cache_lookup (abfd)), buf, nbytes);
+  /* Set bfd_error if we did not read as much data as we expected.  If
+     the read failed due to an error set the bfd_error_system_call,
+     else set bfd_error_file_truncated.  */
+  if (nread == (file_ptr)-1)
+    {
+      bfd_set_error (bfd_error_system_call);
+      return -1;
+    }
+#else
+  nread = fread (buf, 1, nbytes, bfd_cache_lookup (abfd));
+  /* Set bfd_error if we did not read as much data as we expected.  If
+     the read failed due to an error set the bfd_error_system_call,
+     else set bfd_error_file_truncated.  */
+  if (nread < nbytes && ferror (bfd_cache_lookup (abfd)))
+    {
+      bfd_set_error (bfd_error_system_call);
+      return -1;
+    }
+#endif
+  return nread;
+}
+
+static file_ptr
+cache_bwrite (struct bfd *abfd, const void *where, file_ptr nbytes)
+{
+  file_ptr nwrite = fwrite (where, 1, nbytes, bfd_cache_lookup (abfd));
+  if (nwrite < nbytes && ferror (bfd_cache_lookup (abfd)))
+    {
+      bfd_set_error (bfd_error_system_call);
+      return -1;
+    }
+  return nwrite;
+}
+
+static int
+cache_bclose (struct bfd *abfd)
+{
+  return bfd_cache_close (abfd);
+}
+
+static int
+cache_bflush (struct bfd *abfd)
+{
+  int sts = fflush (bfd_cache_lookup (abfd));
+  if (sts < 0)
+    bfd_set_error (bfd_error_system_call);
+  return sts;
+}
+
+static int
+cache_bstat (struct bfd *abfd, struct stat *sb)
+{
+  int sts = fstat (fileno (bfd_cache_lookup (abfd)), sb);
+  if (sts < 0)
+    bfd_set_error (bfd_error_system_call);
+  return sts;
+}
+
+static const struct bfd_iovec cache_iovec = {
+  &cache_bread, &cache_bwrite, &cache_btell, &cache_bseek,
+  &cache_bclose, &cache_bflush, &cache_bstat
+};
+
 /*
 INTERNAL_FUNCTION
        BFD_CACHE_MAX_OPEN macro
@@ -205,6 +308,7 @@ bfd_cache_init (bfd *abfd)
       if (! close_one ())
        return FALSE;
     }
+  abfd->iovec = &cache_iovec;
   insert (abfd);
   ++open_files;
   return TRUE;
@@ -229,8 +333,7 @@ RETURNS
 bfd_boolean
 bfd_cache_close (bfd *abfd)
 {
-  if (abfd->iostream == NULL
-      || (abfd->flags & BFD_IN_MEMORY) != 0)
+  if (abfd->iovec != &cache_iovec)
     return TRUE;
 
   return bfd_cache_delete (abfd);
index 6afe58d4635133a92dbd904a198cc1e9473462c1..b4bc9f88f6ba4ae145ded69b941e6aedfc56aa6c 100644 (file)
@@ -657,6 +657,27 @@ bfd_boolean bfd_write_bigendian_4byte_int (bfd *, unsigned int);
 unsigned int bfd_log2 (bfd_vma x);
 
 /* Extracted from bfdio.c.  */
+struct bfd_iovec
+{
+  /* To avoid problems with macros, a "b" rather than "f"
+     prefix is prepended to each method name.  */
+  /* Attempt to read/write NBYTES on ABFD's IOSTREAM storing/fetching
+     bytes starting at PTR.  Return the number of bytes actually
+     transfered (a read past end-of-file returns less than NBYTES),
+     or -1 (setting <<bfd_error>>) if an error occurs.  */
+  file_ptr (*bread) (struct bfd *abfd, void *ptr, file_ptr nbytes);
+  file_ptr (*bwrite) (struct bfd *abfd, const void *ptr,
+                      file_ptr nbytes);
+  /* Return the current IOSTREAM file offset, or -1 (setting <<bfd_error>>
+     if an error occurs.  */
+  file_ptr (*btell) (struct bfd *abfd);
+  /* For the following, on successful completion a value of 0 is returned.
+     Otherwise, a value of -1 is returned (and  <<bfd_error>> is set).  */
+  int (*bseek) (struct bfd *abfd, file_ptr offset, int whence);
+  int (*bclose) (struct bfd *abfd);
+  int (*bflush) (struct bfd *abfd);
+  int (*bstat) (struct bfd *abfd, struct stat *sb);
+};
 /* Extracted from bfdwin.c.  */
 struct _bfd_window_internal {
   struct _bfd_window_internal *next;
index 6abd40509ca2af4e0791ac2f693cc4b95e76bc91..3291cbabaeaa49bbef5f236e428b766cd541cc4e 100644 (file)
@@ -103,6 +103,7 @@ _bfd_new_bfd_contained_in (bfd *obfd)
   if (nbfd == NULL)
     return NULL;
   nbfd->xvec = obfd->xvec;
+  nbfd->iovec = obfd->iovec;
   nbfd->my_archive = obfd;
   nbfd->direction = read_direction;
   nbfd->target_defaulted = obfd->target_defaulted;
@@ -322,6 +323,183 @@ bfd_openstreamr (const char *filename, const char *target, void *streamarg)
 
   return nbfd;
 }
+
+/*
+FUNCTION
+       bfd_openr_iovec
+
+SYNOPSIS
+        bfd *bfd_openr_iovec (const char *filename, const char *target,
+                              void *(*open) (struct bfd *nbfd,
+                                             void *open_closure),
+                              void *open_closure,
+                              file_ptr (*pread) (struct bfd *nbfd,
+                                                 void *stream,
+                                                 void *buf,
+                                                 file_ptr nbytes,
+                                                 file_ptr offset),
+                              int (*close) (struct bfd *nbfd,
+                                            void *stream));
+
+DESCRIPTION
+
+        Create and return a BFD backed by a read-only @var{stream}.
+        The @var{stream} is created using @var{open}, accessed using
+        @var{pread} and destroyed using @var{close}.
+
+       Calls <<bfd_find_target>>, so @var{target} is interpreted as by
+       that function.
+
+       Calls @var{open} (which can call <<bfd_zalloc>> and
+       <<bfd_get_filename>>) to obtain the read-only stream backing
+       the BFD.  @var{open} either succeeds returning the
+       non-<<NULL>> @var{stream}, or fails returning <<NULL>>
+       (setting <<bfd_error>>).
+
+       Calls @var{pread} to request @var{nbytes} of data from
+       @var{stream} starting at @var{offset} (e.g., via a call to
+       <<bfd_read>>).  @var{pread} either succeeds returning the
+       number of bytes read (which can be less than @var{nbytes} when
+       end-of-file), or fails returning -1 (setting <<bfd_error>>).
+
+       Calls @var{close} when the BFD is later closed using
+       <<bfd_close>>.  @var{close} either succeeds returning 0, or
+       fails returning -1 (setting <<bfd_error>>).
+
+       If <<bfd_openr_iovec>> returns <<NULL>> then an error has
+       occurred.  Possible errors are <<bfd_error_no_memory>>,
+       <<bfd_error_invalid_target>> and <<bfd_error_system_call>>.
+
+*/
+
+struct opncls
+{
+  void *stream;
+  file_ptr (*pread) (struct bfd *abfd, void *stream, void *buf,
+                    file_ptr nbytes, file_ptr offset);
+  int (*close) (struct bfd *abfd, void *stream);
+  file_ptr where;
+};
+
+static file_ptr
+opncls_btell (struct bfd *abfd)
+{
+  struct opncls *vec = abfd->iostream;
+  return vec->where;
+}
+
+static int
+opncls_bseek (struct bfd *abfd, file_ptr offset, int whence)
+{
+  struct opncls *vec = abfd->iostream;
+  switch (whence)
+    {
+    case SEEK_SET: vec->where = offset; break;
+    case SEEK_CUR: vec->where += offset; break;
+    case SEEK_END: return -1;
+    }
+  return 0;
+}
+
+static file_ptr
+opncls_bread (struct bfd *abfd, void *buf, file_ptr nbytes)
+{
+  struct opncls *vec = abfd->iostream;
+  file_ptr nread = vec->pread (abfd, vec->stream, buf, nbytes, vec->where);
+  if (nread < 0)
+    return nread;
+  vec->where += nread;
+  return nread;
+}
+
+static file_ptr
+opncls_bwrite (struct bfd *abfd ATTRIBUTE_UNUSED,
+             const void *where ATTRIBUTE_UNUSED,
+             file_ptr nbytes ATTRIBUTE_UNUSED)
+{
+  return -1;
+}
+
+static int
+opncls_bclose (struct bfd *abfd)
+{
+  struct opncls *vec = abfd->iostream;
+  /* Since the VEC's memory is bound to the bfd deleting the bfd will
+     free it.  */
+  int status = 0;
+  if (vec->close != NULL)
+    status = vec->close (abfd, vec->stream);
+  abfd->iostream = NULL;
+  return status;
+}
+
+static int
+opncls_bflush (struct bfd *abfd ATTRIBUTE_UNUSED)
+{
+  return 0;
+}
+
+static int
+opncls_bstat (struct bfd *abfd ATTRIBUTE_UNUSED, struct stat *sb)
+{
+  memset (sb, 0, sizeof (*sb));
+  return 0;
+}
+
+static const struct bfd_iovec opncls_iovec = {
+  &opncls_bread, &opncls_bwrite, &opncls_btell, &opncls_bseek,
+  &opncls_bclose, &opncls_bflush, &opncls_bstat
+};
+
+bfd *
+bfd_openr_iovec (const char *filename, const char *target,
+                void *(*open) (struct bfd *nbfd,
+                               void *open_closure),
+                void *open_closure,
+                file_ptr (*pread) (struct bfd *abfd,
+                                   void *stream,
+                                   void *buf,
+                                   file_ptr nbytes,
+                                   file_ptr offset),
+                int (*close) (struct bfd *nbfd,
+                              void *stream))
+{
+  bfd *nbfd;
+  const bfd_target *target_vec;
+  struct opncls *vec;
+  void *stream;
+
+  nbfd = _bfd_new_bfd ();
+  if (nbfd == NULL)
+    return NULL;
+
+  target_vec = bfd_find_target (target, nbfd);
+  if (target_vec == NULL)
+    {
+      _bfd_delete_bfd (nbfd);
+      return NULL;
+    }
+
+  nbfd->filename = filename;
+  nbfd->direction = read_direction;
+
+  stream = open (nbfd, open_closure);
+  if (stream == NULL)
+    {
+      _bfd_delete_bfd (nbfd);
+      return NULL;
+    }
+
+  vec = bfd_zalloc (nbfd, sizeof (struct opncls));
+  vec->stream = stream;
+  vec->pread = pread;
+  vec->close = close;
+
+  nbfd->iovec = &opncls_iovec;
+  nbfd->iostream = vec;
+
+  return nbfd;
+}
 \f
 /* bfd_openw -- open for writing.
    Returns a pointer to a freshly-allocated BFD on success, or NULL.
@@ -415,7 +593,12 @@ bfd_close (bfd *abfd)
   if (! BFD_SEND (abfd, _close_and_cleanup, (abfd)))
     return FALSE;
 
-  ret = bfd_cache_close (abfd);
+  /* FIXME: cagney/2004-02-15: Need to implement a BFD_IN_MEMORY io
+     vector.  */
+  if (!(abfd->flags & BFD_IN_MEMORY))
+    ret = abfd->iovec->bclose (abfd);
+  else
+    ret = 0;
 
   /* If the file was open for writing and is now executable,
      make it so.  */