2008-11-20 Tristan Gingold <gingold@adacore.com>
[binutils-gdb.git] / bfd / opncls.c
index c5cc252c66bdc4dc2cf8f9f44934374b5500f1b1..7be82b21948c8e2d39f6f014b354fe211cbbb2bc 100644 (file)
@@ -1,6 +1,6 @@
 /* opncls.c -- open and close a BFD.
    Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000,
 /* opncls.c -- open and close a BFD.
    Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000,
-   2001, 2002, 2003, 2004, 2005
+   2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
    Free Software Foundation, Inc.
 
    Written by Cygnus Support.
    Free Software Foundation, Inc.
 
    Written by Cygnus Support.
@@ -9,7 +9,7 @@
 
    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
 
    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 2 of the License, or
+   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,
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
 
-#include "bfd.h"
 #include "sysdep.h"
 #include "sysdep.h"
+#include "bfd.h"
 #include "objalloc.h"
 #include "libbfd.h"
 #include "libiberty.h"
 #include "objalloc.h"
 #include "libbfd.h"
 #include "libiberty.h"
@@ -71,7 +72,7 @@ _bfd_new_bfd (void)
   nbfd->iostream = NULL;
   nbfd->where = 0;
   if (!bfd_hash_table_init_n (& nbfd->section_htab, bfd_section_hash_newfunc,
   nbfd->iostream = NULL;
   nbfd->where = 0;
   if (!bfd_hash_table_init_n (& nbfd->section_htab, bfd_section_hash_newfunc,
-                             251))
+                             sizeof (struct section_hash_entry), 251))
     {
       free (nbfd);
       return NULL;
     {
       free (nbfd);
       return NULL;
@@ -115,15 +116,41 @@ _bfd_new_bfd_contained_in (bfd *obfd)
 void
 _bfd_delete_bfd (bfd *abfd)
 {
 void
 _bfd_delete_bfd (bfd *abfd)
 {
-  bfd_hash_table_free (&abfd->section_htab);
-  objalloc_free ((struct objalloc *) abfd->memory);
+  if (abfd->memory)
+    {
+      bfd_hash_table_free (&abfd->section_htab);
+      objalloc_free ((struct objalloc *) abfd->memory);
+    }
   free (abfd);
 }
 
   free (abfd);
 }
 
+/* Free objalloc memory.  */
+
+bfd_boolean
+_bfd_free_cached_info (bfd *abfd)
+{
+  if (abfd->memory)
+    {
+      bfd_hash_table_free (&abfd->section_htab);
+      objalloc_free ((struct objalloc *) abfd->memory);
+
+      abfd->sections = NULL;
+      abfd->section_last = NULL;
+      abfd->outsymbols = NULL;
+      abfd->tdata.any = NULL;
+      abfd->usrdata = NULL;
+      abfd->memory = NULL;
+    }
+
+  return TRUE;
+}
+
 /*
 SECTION
        Opening and closing BFDs
 
 /*
 SECTION
        Opening and closing BFDs
 
+SUBSECTION
+       Functions for opening and closing
 */
 
 /*
 */
 
 /*
@@ -173,7 +200,7 @@ bfd_fopen (const char *filename, const char *target, const char *mode, int fd)
     nbfd->iostream = fdopen (fd, mode);
   else
 #endif
     nbfd->iostream = fdopen (fd, mode);
   else
 #endif
-    nbfd->iostream = fopen (filename, mode);
+    nbfd->iostream = real_fopen (filename, mode);
   if (nbfd->iostream == NULL)
     {
       bfd_set_error (bfd_error_system_call);
   if (nbfd->iostream == NULL)
     {
       bfd_set_error (bfd_error_system_call);
@@ -358,7 +385,10 @@ SYNOPSIS
                                                  file_ptr nbytes,
                                                  file_ptr offset),
                               int (*close) (struct bfd *nbfd,
                                                  file_ptr nbytes,
                                                  file_ptr offset),
                               int (*close) (struct bfd *nbfd,
-                                            void *stream));
+                                            void *stream),
+                             int (*stat) (struct bfd *abfd,
+                                          void *stream,
+                                          struct stat *sb));
 
 DESCRIPTION
 
 
 DESCRIPTION
 
@@ -385,6 +415,10 @@ DESCRIPTION
        <<bfd_close>>.  @var{close} either succeeds returning 0, or
        fails returning -1 (setting <<bfd_error>>).
 
        <<bfd_close>>.  @var{close} either succeeds returning 0, or
        fails returning -1 (setting <<bfd_error>>).
 
+       Calls @var{stat} to fill in a stat structure for bfd_stat,
+       bfd_get_size, and bfd_get_mtime calls.  @var{stat} returns 0
+       on success, or returns -1 on failure (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>>.
        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>>.
@@ -397,6 +431,7 @@ struct opncls
   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 (*pread) (struct bfd *abfd, void *stream, void *buf,
                     file_ptr nbytes, file_ptr offset);
   int (*close) (struct bfd *abfd, void *stream);
+  int (*stat) (struct bfd *abfd, void *stream, struct stat *sb);
   file_ptr where;
 };
 
   file_ptr where;
 };
 
@@ -459,10 +494,15 @@ opncls_bflush (struct bfd *abfd ATTRIBUTE_UNUSED)
 }
 
 static int
 }
 
 static int
-opncls_bstat (struct bfd *abfd ATTRIBUTE_UNUSED, struct stat *sb)
+opncls_bstat (struct bfd *abfd, struct stat *sb)
 {
 {
+  struct opncls *vec = abfd->iostream;
+
   memset (sb, 0, sizeof (*sb));
   memset (sb, 0, sizeof (*sb));
-  return 0;
+  if (vec->stat == NULL)
+    return 0;
+
+  return (vec->stat) (abfd, vec->stream, sb);
 }
 
 static const struct bfd_iovec opncls_iovec = {
 }
 
 static const struct bfd_iovec opncls_iovec = {
@@ -481,7 +521,10 @@ bfd_openr_iovec (const char *filename, const char *target,
                                    file_ptr nbytes,
                                    file_ptr offset),
                 int (*close) (struct bfd *nbfd,
                                    file_ptr nbytes,
                                    file_ptr offset),
                 int (*close) (struct bfd *nbfd,
-                              void *stream))
+                              void *stream),
+                int (*stat) (struct bfd *abfd,
+                             void *stream,
+                             struct stat *sb))
 {
   bfd *nbfd;
   const bfd_target *target_vec;
 {
   bfd *nbfd;
   const bfd_target *target_vec;
@@ -502,7 +545,8 @@ bfd_openr_iovec (const char *filename, const char *target,
   nbfd->filename = filename;
   nbfd->direction = read_direction;
 
   nbfd->filename = filename;
   nbfd->direction = read_direction;
 
-  stream = open (nbfd, open_closure);
+  /* `open (...)' would get expanded by an the open(2) syscall macro.  */
+  stream = (*open) (nbfd, open_closure);
   if (stream == NULL)
     {
       _bfd_delete_bfd (nbfd);
   if (stream == NULL)
     {
       _bfd_delete_bfd (nbfd);
@@ -513,6 +557,7 @@ bfd_openr_iovec (const char *filename, const char *target,
   vec->stream = stream;
   vec->pread = pread;
   vec->close = close;
   vec->stream = stream;
   vec->pread = pread;
   vec->close = close;
+  vec->stat = stat;
 
   nbfd->iovec = &opncls_iovec;
   nbfd->iostream = vec;
 
   nbfd->iovec = &opncls_iovec;
   nbfd->iostream = vec;
@@ -602,6 +647,8 @@ bfd_boolean
 bfd_close (bfd *abfd)
 {
   bfd_boolean ret;
 bfd_close (bfd *abfd)
 {
   bfd_boolean ret;
+  bfd *nbfd;
+  bfd *next;
 
   if (bfd_write_p (abfd))
     {
 
   if (bfd_write_p (abfd))
     {
@@ -609,6 +656,13 @@ bfd_close (bfd *abfd)
        return FALSE;
     }
 
        return FALSE;
     }
 
+  /* Close nested archives (if this bfd is a thin archive).  */
+  for (nbfd = abfd->nested_archives; nbfd; nbfd = next)
+    {
+      next = nbfd->archive_next;
+      bfd_close (nbfd);
+    }
+
   if (! BFD_SEND (abfd, _close_and_cleanup, (abfd)))
     return FALSE;
 
   if (! BFD_SEND (abfd, _close_and_cleanup, (abfd)))
     return FALSE;
 
@@ -755,6 +809,8 @@ bfd_make_writable (bfd *abfd)
     }
 
   bim = bfd_malloc (sizeof (struct bfd_in_memory));
     }
 
   bim = bfd_malloc (sizeof (struct bfd_in_memory));
+  if (bim == NULL)
+    return FALSE;      /* bfd_error already set.  */
   abfd->iostream = bim;
   /* bfd_bwrite will grow these as needed.  */
   bim->size = 0;
   abfd->iostream = bim;
   /* bfd_bwrite will grow these as needed.  */
   bim->size = 0;
@@ -1128,19 +1184,19 @@ separate_debug_file_exists (const char *name, const unsigned long crc)
 {
   static unsigned char buffer [8 * 1024];
   unsigned long file_crc = 0;
 {
   static unsigned char buffer [8 * 1024];
   unsigned long file_crc = 0;
-  int fd;
+  FILE *f;
   bfd_size_type count;
 
   BFD_ASSERT (name);
 
   bfd_size_type count;
 
   BFD_ASSERT (name);
 
-  fd = open (name, O_RDONLY);
-  if (fd < 0)
+  f = real_fopen (name, FOPEN_RB);
+  if (f == NULL)
     return FALSE;
 
     return FALSE;
 
-  while ((count = read (fd, buffer, sizeof (buffer))) > 0)
+  while ((count = fread (buffer, 1, sizeof (buffer), f)) > 0)
     file_crc = bfd_calc_gnu_debuglink_crc32 (file_crc, buffer, count);
 
     file_crc = bfd_calc_gnu_debuglink_crc32 (file_crc, buffer, count);
 
-  close (fd);
+  fclose (f);
 
   return crc == file_crc;
 }
 
   return crc == file_crc;
 }
@@ -1168,52 +1224,64 @@ find_separate_debug_file (bfd *abfd, const char *debug_file_directory)
   char *basename;
   char *dir;
   char *debugfile;
   char *basename;
   char *dir;
   char *debugfile;
+  char *canon_dir;
   unsigned long crc32;
   unsigned long crc32;
-  int i;
+  size_t dirlen;
+  size_t canon_dirlen;
 
   BFD_ASSERT (abfd);
   if (debug_file_directory == NULL)
     debug_file_directory = ".";
 
   /* BFD may have been opened from a stream.  */
 
   BFD_ASSERT (abfd);
   if (debug_file_directory == NULL)
     debug_file_directory = ".";
 
   /* BFD may have been opened from a stream.  */
-  if (! abfd->filename)
-    return NULL;
+  if (abfd->filename == NULL)
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return NULL;
+    }
 
   basename = get_debug_link_info (abfd, & crc32);
   if (basename == NULL)
     return NULL;
 
 
   basename = get_debug_link_info (abfd, & crc32);
   if (basename == NULL)
     return NULL;
 
-  if (strlen (basename) < 1)
+  if (basename[0] == '\0')
     {
       free (basename);
     {
       free (basename);
+      bfd_set_error (bfd_error_no_debug_section);
       return NULL;
     }
 
       return NULL;
     }
 
-  dir = strdup (abfd->filename);
+  for (dirlen = strlen (abfd->filename); dirlen > 0; dirlen--)
+    if (IS_DIR_SEPARATOR (abfd->filename[dirlen - 1]))
+      break;
+
+  dir = bfd_malloc (dirlen + 1);
   if (dir == NULL)
     {
       free (basename);
       return NULL;
     }
   if (dir == NULL)
     {
       free (basename);
       return NULL;
     }
-  BFD_ASSERT (strlen (dir) != 0);
-
-  /* Strip off filename part.  */
-  for (i = strlen (dir) - 1; i >= 0; i--)
-    if (IS_DIR_SEPARATOR (dir[i]))
+  memcpy (dir, abfd->filename, dirlen);
+  dir[dirlen] = '\0';
+
+  /* Compute the canonical name of the bfd object with all symbolic links
+     resolved, for use in the global debugfile directory.  */
+  canon_dir = lrealpath (abfd->filename);
+  for (canon_dirlen = strlen (canon_dir); canon_dirlen > 0; canon_dirlen--)
+    if (IS_DIR_SEPARATOR (canon_dir[canon_dirlen - 1]))
       break;
       break;
+  canon_dir[canon_dirlen] = '\0';
 
 
-  dir[i + 1] = '\0';
-  BFD_ASSERT (dir[i] == '/' || dir[0] == '\0');
-
-  debugfile = malloc (strlen (debug_file_directory) + 1
-                     + strlen (dir)
-                     + strlen (".debug/")
-                     + strlen (basename)
-                     + 1);
+  debugfile = bfd_malloc (strlen (debug_file_directory) + 1
+                         + (canon_dirlen > dirlen ? canon_dirlen : dirlen)
+                         + strlen (".debug/")
+                         + strlen (basename)
+                         + 1);
   if (debugfile == NULL)
     {
       free (basename);
       free (dir);
   if (debugfile == NULL)
     {
       free (basename);
       free (dir);
+      free (canon_dir);
       return NULL;
     }
 
       return NULL;
     }
 
@@ -1225,6 +1293,7 @@ find_separate_debug_file (bfd *abfd, const char *debug_file_directory)
     {
       free (basename);
       free (dir);
     {
       free (basename);
       free (dir);
+      free (canon_dir);
       return debugfile;
     }
 
       return debugfile;
     }
 
@@ -1237,29 +1306,32 @@ find_separate_debug_file (bfd *abfd, const char *debug_file_directory)
     {
       free (basename);
       free (dir);
     {
       free (basename);
       free (dir);
+      free (canon_dir);
       return debugfile;
     }
 
   /* Then try in the global debugfile directory.  */
   strcpy (debugfile, debug_file_directory);
       return debugfile;
     }
 
   /* Then try in the global debugfile directory.  */
   strcpy (debugfile, debug_file_directory);
-  i = strlen (debug_file_directory) - 1;
-  if (i > 0
-      && debug_file_directory[i] != '/'
-      && dir[0] != '/')
+  dirlen = strlen (debug_file_directory) - 1;
+  if (dirlen > 0
+      && debug_file_directory[dirlen] != '/'
+      && canon_dir[0] != '/')
     strcat (debugfile, "/");
     strcat (debugfile, "/");
-  strcat (debugfile, dir);
+  strcat (debugfile, canon_dir);
   strcat (debugfile, basename);
 
   if (separate_debug_file_exists (debugfile, crc32))
     {
       free (basename);
       free (dir);
   strcat (debugfile, basename);
 
   if (separate_debug_file_exists (debugfile, crc32))
     {
       free (basename);
       free (dir);
+      free (canon_dir);
       return debugfile;
     }
 
   free (debugfile);
   free (basename);
   free (dir);
       return debugfile;
     }
 
   free (debugfile);
   free (basename);
   free (dir);
+  free (canon_dir);
   return NULL;
 }
 
   return NULL;
 }
 
@@ -1319,6 +1391,7 @@ bfd_create_gnu_debuglink_section (bfd *abfd, const char *filename)
 {
   asection *sect;
   bfd_size_type debuglink_size;
 {
   asection *sect;
   bfd_size_type debuglink_size;
+  flagword flags;
 
   if (abfd == NULL || filename == NULL)
     {
 
   if (abfd == NULL || filename == NULL)
     {
@@ -1337,16 +1410,11 @@ bfd_create_gnu_debuglink_section (bfd *abfd, const char *filename)
       return NULL;
     }
 
       return NULL;
     }
 
-  sect = bfd_make_section (abfd, GNU_DEBUGLINK);
+  flags = SEC_HAS_CONTENTS | SEC_READONLY | SEC_DEBUGGING;
+  sect = bfd_make_section_with_flags (abfd, GNU_DEBUGLINK, flags);
   if (sect == NULL)
     return NULL;
 
   if (sect == NULL)
     return NULL;
 
-  if (! bfd_set_section_flags (abfd, sect,
-                              SEC_HAS_CONTENTS | SEC_READONLY | SEC_DEBUGGING))
-    /* XXX Should we delete the section from the bfd ?  */
-    return NULL;
-
-
   debuglink_size = strlen (filename) + 1;
   debuglink_size += 3;
   debuglink_size &= ~3;
   debuglink_size = strlen (filename) + 1;
   debuglink_size += 3;
   debuglink_size &= ~3;
@@ -1392,6 +1460,7 @@ bfd_fill_in_gnu_debuglink_section (bfd *abfd,
   FILE * handle;
   static unsigned char buffer[8 * 1024];
   size_t count;
   FILE * handle;
   static unsigned char buffer[8 * 1024];
   size_t count;
+  size_t filelen;
 
   if (abfd == NULL || sect == NULL || filename == NULL)
     {
 
   if (abfd == NULL || sect == NULL || filename == NULL)
     {
@@ -1405,7 +1474,7 @@ bfd_fill_in_gnu_debuglink_section (bfd *abfd,
      .gnu_debuglink section, we insist upon the user providing us with a
      correct-for-section-creation-time path, but this need not conform to
      the gdb location algorithm.  */
      .gnu_debuglink section, we insist upon the user providing us with a
      correct-for-section-creation-time path, but this need not conform to
      the gdb location algorithm.  */
-  handle = fopen (filename, FOPEN_RB);
+  handle = real_fopen (filename, FOPEN_RB);
   if (handle == NULL)
     {
       bfd_set_error (bfd_error_system_call);
   if (handle == NULL)
     {
       bfd_set_error (bfd_error_system_call);
@@ -1421,21 +1490,22 @@ bfd_fill_in_gnu_debuglink_section (bfd *abfd,
      now that we no longer need them.  */
   filename = lbasename (filename);
 
      now that we no longer need them.  */
   filename = lbasename (filename);
 
-  debuglink_size = strlen (filename) + 1;
+  filelen = strlen (filename);
+  debuglink_size = filelen + 1;
   debuglink_size += 3;
   debuglink_size &= ~3;
   debuglink_size += 4;
 
   debuglink_size += 3;
   debuglink_size &= ~3;
   debuglink_size += 4;
 
-  contents = malloc (debuglink_size);
+  contents = bfd_malloc (debuglink_size);
   if (contents == NULL)
     {
       /* XXX Should we delete the section from the bfd ?  */
   if (contents == NULL)
     {
       /* XXX Should we delete the section from the bfd ?  */
-      bfd_set_error (bfd_error_no_memory);
       return FALSE;
     }
 
       return FALSE;
     }
 
-  strcpy (contents, filename);
   crc_offset = debuglink_size - 4;
   crc_offset = debuglink_size - 4;
+  memcpy (contents, filename, filelen);
+  memset (contents + filelen, 0, crc_offset - filelen);
 
   bfd_put_32 (abfd, crc32, contents + crc_offset);
 
 
   bfd_put_32 (abfd, crc32, contents + crc_offset);