* gas/nios2/nios2.exp: Add copyright.
[deliverable/binutils-gdb.git] / gdb / gdb_bfd.c
index 5ad987887a841689b188a01dafa1d1226f21b43f..d4540b9e45ebfbf8493b5e8026c7b82af0dcdac1 100644 (file)
@@ -1,7 +1,6 @@
 /* Definitions for BFD wrappers used by GDB.
 
-   Copyright (C) 2011, 2012
-   Free Software Foundation, Inc.
+   Copyright (C) 2011-2013 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 #include "gdb_bfd.h"
 #include "gdb_assert.h"
 #include "gdb_string.h"
+#include "ui-out.h"
+#include "gdbcmd.h"
 #include "hashtab.h"
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *) -1)
+#endif
+#endif
+
+/* An object of this type is stored in the section's user data when
+   mapping a section.  */
+
+struct gdb_bfd_section_data
+{
+  /* Size of the data.  */
+  bfd_size_type size;
+  /* If the data was mmapped, this is the length of the map.  */
+  bfd_size_type map_len;
+  /* The data.  If NULL, the section data has not been read.  */
+  void *data;
+  /* If the data was mmapped, this is the map address.  */
+  void *map_addr;
+};
+
+/* A hash table holding every BFD that gdb knows about.  This is not
+   to be confused with 'gdb_bfd_cache', which is used for sharing
+   BFDs; in contrast, this hash is used just to implement
+   "maint info bfd".  */
+
+static htab_t all_bfds;
 
 /* See gdb_bfd.h.  */
 
@@ -48,8 +80,20 @@ struct gdb_bfd_data
 
   /* The mtime of the BFD at the point the cache entry was made.  */
   time_t mtime;
+
+  /* If the BFD comes from an archive, this points to the archive's
+     BFD.  Otherwise, this is NULL.  */
+  bfd *archive_bfd;
+
+  /* The registry.  */
+  REGISTRY_FIELDS;
 };
 
+#define GDB_BFD_DATA_ACCESSOR(ABFD) \
+  ((struct gdb_bfd_data *) bfd_usrdata (ABFD))
+
+DEFINE_REGISTRY (bfd, GDB_BFD_DATA_ACCESSOR)
+
 /* A hash table storing all the BFDs maintained in the cache.  */
 
 static htab_t gdb_bfd_cache;
@@ -134,7 +178,8 @@ gdb_bfd_open (const char *name, const char *target, int fd)
   if (abfd != NULL)
     {
       close (fd);
-      return gdb_bfd_ref (abfd);
+      gdb_bfd_ref (abfd);
+      return abfd;
     }
 
   abfd = bfd_fopen (name, target, FOPEN_RB, fd);
@@ -146,7 +191,32 @@ gdb_bfd_open (const char *name, const char *target, int fd)
   *slot = abfd;
 
   gdb_bfd_stash_filename (abfd);
-  return gdb_bfd_ref (abfd);
+  gdb_bfd_ref (abfd);
+  return abfd;
+}
+
+/* A helper function that releases any section data attached to the
+   BFD.  */
+
+static void
+free_one_bfd_section (bfd *abfd, asection *sectp, void *ignore)
+{
+  struct gdb_bfd_section_data *sect = bfd_get_section_userdata (abfd, sectp);
+
+  if (sect != NULL && sect->data != NULL)
+    {
+#ifdef HAVE_MMAP
+      if (sect->map_addr != NULL)
+       {
+         int res;
+
+         res = munmap (sect->map_addr, sect->map_len);
+         gdb_assert (res == 0);
+       }
+      else
+#endif
+       xfree (sect->data);
+    }
 }
 
 /* Close ABFD, and warn if that fails.  */
@@ -157,6 +227,8 @@ gdb_bfd_close_or_warn (struct bfd *abfd)
   int ret;
   char *name = bfd_get_filename (abfd);
 
+  bfd_map_over_sections (abfd, free_one_bfd_section, NULL);
+
   ret = bfd_close (abfd);
 
   if (!ret)
@@ -166,39 +238,50 @@ gdb_bfd_close_or_warn (struct bfd *abfd)
   return ret;
 }
 
-/* Add reference to ABFD.  Returns ABFD.  */
+/* See gdb_bfd.h.  */
 
-struct bfd *
+void
 gdb_bfd_ref (struct bfd *abfd)
 {
   struct gdb_bfd_data *gdata;
+  void **slot;
 
   if (abfd == NULL)
-    return NULL;
+    return;
 
   gdata = bfd_usrdata (abfd);
 
   if (gdata != NULL)
     {
       gdata->refc += 1;
-      return abfd;
+      return;
     }
 
+  /* Ask BFD to decompress sections in bfd_get_full_section_contents.  */
+  abfd->flags |= BFD_DECOMPRESS;
+
   gdata = bfd_zalloc (abfd, sizeof (struct gdb_bfd_data));
   gdata->refc = 1;
   gdata->mtime = bfd_get_mtime (abfd);
+  gdata->archive_bfd = NULL;
   bfd_usrdata (abfd) = gdata;
 
-  return abfd;
+  bfd_alloc_data (abfd);
+
+  /* This is the first we've seen it, so add it to the hash table.  */
+  slot = htab_find_slot (all_bfds, abfd, INSERT);
+  gdb_assert (slot && !*slot);
+  *slot = abfd;
 }
 
-/* Unreference and possibly close ABFD.  */
+/* See gdb_bfd.h.  */
 
 void
 gdb_bfd_unref (struct bfd *abfd)
 {
   struct gdb_bfd_data *gdata;
   struct gdb_bfd_cache_search search;
+  bfd *archive_bfd;
 
   if (abfd == NULL)
     return;
@@ -210,6 +293,7 @@ gdb_bfd_unref (struct bfd *abfd)
   if (gdata->refc > 0)
     return;
 
+  archive_bfd = gdata->archive_bfd;
   search.filename = bfd_get_filename (abfd);
 
   if (gdb_bfd_cache && search.filename)
@@ -225,7 +309,293 @@ gdb_bfd_unref (struct bfd *abfd)
        htab_clear_slot (gdb_bfd_cache, slot);
     }
 
+  bfd_free_data (abfd);
   bfd_usrdata (abfd) = NULL;  /* Paranoia.  */
 
+  htab_remove_elt (all_bfds, abfd);
+
   gdb_bfd_close_or_warn (abfd);
+
+  gdb_bfd_unref (archive_bfd);
+}
+
+/* A helper function that returns the section data descriptor
+   associated with SECTION.  If no such descriptor exists, a new one
+   is allocated and cleared.  */
+
+static struct gdb_bfd_section_data *
+get_section_descriptor (asection *section)
+{
+  struct gdb_bfd_section_data *result;
+
+  result = bfd_get_section_userdata (section->owner, section);
+
+  if (result == NULL)
+    {
+      result = bfd_zalloc (section->owner, sizeof (*result));
+      bfd_set_section_userdata (section->owner, section, result);
+    }
+
+  return result;
+}
+
+/* See gdb_bfd.h.  */
+
+const gdb_byte *
+gdb_bfd_map_section (asection *sectp, bfd_size_type *size)
+{
+  bfd *abfd;
+  struct gdb_bfd_section_data *descriptor;
+  bfd_byte *data;
+
+  gdb_assert ((sectp->flags & SEC_RELOC) == 0);
+  gdb_assert (size != NULL);
+
+  abfd = sectp->owner;
+
+  descriptor = get_section_descriptor (sectp);
+
+  /* If the data was already read for this BFD, just reuse it.  */
+  if (descriptor->data != NULL)
+    goto done;
+
+#ifdef HAVE_MMAP
+  if (!bfd_is_section_compressed (abfd, sectp))
+    {
+      /* The page size, used when mmapping.  */
+      static int pagesize;
+
+      if (pagesize == 0)
+       pagesize = getpagesize ();
+
+      /* Only try to mmap sections which are large enough: we don't want
+        to waste space due to fragmentation.  */
+
+      if (bfd_get_section_size (sectp) > 4 * pagesize)
+       {
+         descriptor->size = bfd_get_section_size (sectp);
+         descriptor->data = bfd_mmap (abfd, 0, descriptor->size, PROT_READ,
+                                      MAP_PRIVATE, sectp->filepos,
+                                      &descriptor->map_addr,
+                                      &descriptor->map_len);
+
+         if ((caddr_t)descriptor->data != MAP_FAILED)
+           {
+#if HAVE_POSIX_MADVISE
+             posix_madvise (descriptor->map_addr, descriptor->map_len,
+                            POSIX_MADV_WILLNEED);
+#endif
+             goto done;
+           }
+
+         /* On failure, clear out the section data and try again.  */
+         memset (descriptor, 0, sizeof (*descriptor));
+       }
+    }
+#endif /* HAVE_MMAP */
+
+  /* Handle compressed sections, or ordinary uncompressed sections in
+     the no-mmap case.  */
+
+  descriptor->size = bfd_get_section_size (sectp);
+  descriptor->data = NULL;
+
+  data = NULL;
+  if (!bfd_get_full_section_contents (abfd, sectp, &data))
+    error (_("Can't read data for section '%s' in file '%s'"),
+          bfd_get_section_name (abfd, sectp),
+          bfd_get_filename (abfd));
+  descriptor->data = data;
+
+ done:
+  gdb_assert (descriptor->data != NULL);
+  *size = descriptor->size;
+  return descriptor->data;
+}
+
+\f
+
+/* See gdb_bfd.h.  */
+
+bfd *
+gdb_bfd_fopen (const char *filename, const char *target, const char *mode,
+              int fd)
+{
+  bfd *result = bfd_fopen (filename, target, mode, fd);
+
+  if (result)
+    {
+      gdb_bfd_stash_filename (result);
+      gdb_bfd_ref (result);
+    }
+
+  return result;
+}
+
+/* See gdb_bfd.h.  */
+
+bfd *
+gdb_bfd_openr (const char *filename, const char *target)
+{
+  bfd *result = bfd_openr (filename, target);
+
+  if (result)
+    {
+      gdb_bfd_stash_filename (result);
+      gdb_bfd_ref (result);
+    }
+
+  return result;
+}
+
+/* See gdb_bfd.h.  */
+
+bfd *
+gdb_bfd_openw (const char *filename, const char *target)
+{
+  bfd *result = bfd_openw (filename, target);
+
+  if (result)
+    {
+      gdb_bfd_stash_filename (result);
+      gdb_bfd_ref (result);
+    }
+
+  return result;
+}
+
+/* See gdb_bfd.h.  */
+
+bfd *
+gdb_bfd_openr_iovec (const char *filename, const char *target,
+                    void *(*open_func) (struct bfd *nbfd,
+                                        void *open_closure),
+                    void *open_closure,
+                    file_ptr (*pread_func) (struct bfd *nbfd,
+                                            void *stream,
+                                            void *buf,
+                                            file_ptr nbytes,
+                                            file_ptr offset),
+                    int (*close_func) (struct bfd *nbfd,
+                                       void *stream),
+                    int (*stat_func) (struct bfd *abfd,
+                                      void *stream,
+                                      struct stat *sb))
+{
+  bfd *result = bfd_openr_iovec (filename, target,
+                                open_func, open_closure,
+                                pread_func, close_func, stat_func);
+
+  if (result)
+    {
+      gdb_bfd_ref (result);
+      gdb_bfd_stash_filename (result);
+    }
+
+  return result;
+}
+
+/* See gdb_bfd.h.  */
+
+void
+gdb_bfd_mark_parent (bfd *child, bfd *parent)
+{
+  struct gdb_bfd_data *gdata;
+
+  gdb_bfd_ref (child);
+  /* No need to stash the filename here, because we also keep a
+     reference on the parent archive.  */
+
+  gdata = bfd_usrdata (child);
+  if (gdata->archive_bfd == NULL)
+    {
+      gdata->archive_bfd = parent;
+      gdb_bfd_ref (parent);
+    }
+  else
+    gdb_assert (gdata->archive_bfd == parent);
+}
+
+/* See gdb_bfd.h.  */
+
+bfd *
+gdb_bfd_openr_next_archived_file (bfd *archive, bfd *previous)
+{
+  bfd *result = bfd_openr_next_archived_file (archive, previous);
+
+  if (result)
+    gdb_bfd_mark_parent (result, archive);
+
+  return result;
+}
+
+/* See gdb_bfd.h.  */
+
+bfd *
+gdb_bfd_fdopenr (const char *filename, const char *target, int fd)
+{
+  bfd *result = bfd_fdopenr (filename, target, fd);
+
+  if (result)
+    {
+      gdb_bfd_ref (result);
+      gdb_bfd_stash_filename (result);
+    }
+
+  return result;
+}
+
+\f
+
+/* A callback for htab_traverse that prints a single BFD.  */
+
+static int
+print_one_bfd (void **slot, void *data)
+{
+  bfd *abfd = *slot;
+  struct gdb_bfd_data *gdata = bfd_usrdata (abfd);
+  struct ui_out *uiout = data;
+  struct cleanup *inner;
+
+  inner = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+  ui_out_field_int (uiout, "refcount", gdata->refc);
+  ui_out_field_string (uiout, "addr", host_address_to_string (abfd));
+  ui_out_field_string (uiout, "filename", bfd_get_filename (abfd));
+  ui_out_text (uiout, "\n");
+  do_cleanups (inner);
+
+  return 1;
+}
+
+/* Implement the 'maint info bfd' command.  */
+
+static void
+maintenance_info_bfds (char *arg, int from_tty)
+{
+  struct cleanup *cleanup;
+  struct ui_out *uiout = current_uiout;
+
+  cleanup = make_cleanup_ui_out_table_begin_end (uiout, 3, -1, "bfds");
+  ui_out_table_header (uiout, 10, ui_left, "refcount", "Refcount");
+  ui_out_table_header (uiout, 18, ui_left, "addr", "Address");
+  ui_out_table_header (uiout, 40, ui_left, "filename", "Filename");
+
+  ui_out_table_body (uiout);
+  htab_traverse (all_bfds, print_one_bfd, uiout);
+
+  do_cleanups (cleanup);
+}
+
+/* -Wmissing-prototypes */
+extern initialize_file_ftype _initialize_gdb_bfd;
+
+void
+_initialize_gdb_bfd (void)
+{
+  all_bfds = htab_create_alloc (10, htab_hash_pointer, htab_eq_pointer,
+                               NULL, xcalloc, xfree);
+
+  add_cmd ("bfds", class_maintenance, maintenance_info_bfds, _("\
+List the BFDs that are currently open."),
+          &maintenanceinfolist);
 }
This page took 0.035993 seconds and 4 git commands to generate.