+static int
+cache_bflush (struct bfd *abfd)
+{
+ int sts;
+ FILE *f = bfd_cache_lookup (abfd, CACHE_NO_OPEN);
+
+ if (f == NULL)
+ return 0;
+ sts = fflush (f);
+ if (sts < 0)
+ bfd_set_error (bfd_error_system_call);
+ return sts;
+}
+
+static int
+cache_bstat (struct bfd *abfd, struct stat *sb)
+{
+ int sts;
+ FILE *f = bfd_cache_lookup (abfd, CACHE_NO_SEEK_ERROR);
+
+ if (f == NULL)
+ return -1;
+ sts = fstat (fileno (f), sb);
+ if (sts < 0)
+ bfd_set_error (bfd_error_system_call);
+ return sts;
+}
+
+static void *
+cache_bmmap (struct bfd *abfd ATTRIBUTE_UNUSED,
+ void *addr ATTRIBUTE_UNUSED,
+ bfd_size_type len ATTRIBUTE_UNUSED,
+ int prot ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ file_ptr offset ATTRIBUTE_UNUSED,
+ void **map_addr ATTRIBUTE_UNUSED,
+ bfd_size_type *map_len ATTRIBUTE_UNUSED)
+{
+ void *ret = (void *) -1;
+
+ if ((abfd->flags & BFD_IN_MEMORY) != 0)
+ abort ();
+#ifdef HAVE_MMAP
+ else
+ {
+ static uintptr_t pagesize_m1;
+ FILE *f;
+ file_ptr pg_offset;
+ bfd_size_type pg_len;
+
+ f = bfd_cache_lookup (abfd, CACHE_NO_SEEK_ERROR);
+ if (f == NULL)
+ return ret;
+
+ if (pagesize_m1 == 0)
+ pagesize_m1 = getpagesize () - 1;
+
+ /* Handle archive members. */
+ if (abfd->my_archive != NULL)
+ offset += abfd->origin;
+
+ /* Align. */
+ pg_offset = offset & ~pagesize_m1;
+ pg_len = (len + (offset - pg_offset) + pagesize_m1) & ~pagesize_m1;
+
+ ret = mmap (addr, pg_len, prot, flags, fileno (f), pg_offset);
+ if (ret == (void *) -1)
+ bfd_set_error (bfd_error_system_call);
+ else
+ {
+ *map_addr = ret;
+ *map_len = pg_len;
+ ret = (char *) ret + (offset & pagesize_m1);
+ }
+ }
+#endif
+
+ return ret;
+}
+
+static const struct bfd_iovec cache_iovec =
+{
+ &cache_bread, &cache_bwrite, &cache_btell, &cache_bseek,
+ &cache_bclose, &cache_bflush, &cache_bstat, &cache_bmmap
+};
+
+/*
+INTERNAL_FUNCTION
+ bfd_cache_init
+
+SYNOPSIS
+ bfd_boolean bfd_cache_init (bfd *abfd);
+
+DESCRIPTION
+ Add a newly opened BFD to the cache.
+*/
+
+bfd_boolean
+bfd_cache_init (bfd *abfd)
+{
+ BFD_ASSERT (abfd->iostream != NULL);
+ if (open_files >= bfd_cache_max_open ())
+ {
+ if (! close_one ())
+ return FALSE;
+ }
+ abfd->iovec = &cache_iovec;
+ insert (abfd);
+ ++open_files;
+ return TRUE;
+}
+
+/*
+INTERNAL_FUNCTION
+ bfd_cache_close
+
+SYNOPSIS
+ bfd_boolean bfd_cache_close (bfd *abfd);
+
+DESCRIPTION
+ Remove the BFD @var{abfd} from the cache. If the attached file is open,
+ then close it too.
+
+RETURNS
+ <<FALSE>> is returned if closing the file fails, <<TRUE>> is
+ returned if all is well.
+*/
+
+bfd_boolean
+bfd_cache_close (bfd *abfd)
+{
+ if (abfd->iovec != &cache_iovec)
+ return TRUE;
+
+ if (abfd->iostream == NULL)
+ /* Previously closed. */
+ return TRUE;
+
+ return bfd_cache_delete (abfd);
+}
+
+/*
+FUNCTION
+ bfd_cache_close_all
+
+SYNOPSIS
+ bfd_boolean bfd_cache_close_all (void);
+
+DESCRIPTION
+ Remove all BFDs from the cache. If the attached file is open,
+ then close it too.
+
+RETURNS
+ <<FALSE>> is returned if closing one of the file fails, <<TRUE>> is
+ returned if all is well.
+*/
+
+bfd_boolean
+bfd_cache_close_all ()
+{
+ bfd_boolean ret = TRUE;
+
+ while (bfd_last_cache != NULL)
+ ret &= bfd_cache_close (bfd_last_cache);
+
+ return ret;
+}
+
+/*
+INTERNAL_FUNCTION
+ bfd_open_file
+
+SYNOPSIS
+ FILE* bfd_open_file (bfd *abfd);
+
+DESCRIPTION
+ Call the OS to open a file for @var{abfd}. Return the <<FILE *>>
+ (possibly <<NULL>>) that results from this operation. Set up the
+ BFD so that future accesses know the file is open. If the <<FILE *>>
+ returned is <<NULL>>, then it won't have been put in the
+ cache, so it won't have to be removed from it.
+*/