sparc: support %dN and %qN syntax for FP registers.
[deliverable/binutils-gdb.git] / bfd / opncls.c
index b2ed9beb9019460173996a82e6db57ddbe915ae7..f59786c3f48520c8647fa98ffd303959c868388a 100644 (file)
@@ -1,7 +1,5 @@
 /* opncls.c -- open and close a BFD.
 /* opncls.c -- open and close a BFD.
-   Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000,
-   2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012
-   Free Software Foundation, Inc.
+   Copyright (C) 1990-2015 Free Software Foundation, Inc.
 
    Written by Cygnus Support.
 
 
    Written by Cygnus Support.
 
@@ -82,31 +80,18 @@ _bfd_new_bfd (void)
 
   nbfd->arch_info = &bfd_default_arch_struct;
 
 
   nbfd->arch_info = &bfd_default_arch_struct;
 
-  nbfd->direction = no_direction;
-  nbfd->iostream = NULL;
-  nbfd->where = 0;
   if (!bfd_hash_table_init_n (& nbfd->section_htab, bfd_section_hash_newfunc,
   if (!bfd_hash_table_init_n (& nbfd->section_htab, bfd_section_hash_newfunc,
-                             sizeof (struct section_hash_entry), 251))
+                             sizeof (struct section_hash_entry), 13))
     {
       free (nbfd);
       return NULL;
     }
     {
       free (nbfd);
       return NULL;
     }
-  nbfd->sections = NULL;
-  nbfd->section_last = NULL;
-  nbfd->format = bfd_unknown;
-  nbfd->my_archive = NULL;
-  nbfd->origin = 0;
-  nbfd->opened_once = FALSE;
-  nbfd->output_has_begun = FALSE;
-  nbfd->section_count = 0;
-  nbfd->usrdata = NULL;
-  nbfd->cacheable = FALSE;
-  nbfd->flags = BFD_NO_FLAGS;
-  nbfd->mtime_set = FALSE;
 
   return nbfd;
 }
 
 
   return nbfd;
 }
 
+static const struct bfd_iovec opncls_iovec;
+
 /* Allocate a new BFD as a member of archive OBFD.  */
 
 bfd *
 /* Allocate a new BFD as a member of archive OBFD.  */
 
 bfd *
@@ -119,9 +104,13 @@ _bfd_new_bfd_contained_in (bfd *obfd)
     return NULL;
   nbfd->xvec = obfd->xvec;
   nbfd->iovec = obfd->iovec;
     return NULL;
   nbfd->xvec = obfd->xvec;
   nbfd->iovec = obfd->iovec;
+  if (obfd->iovec == &opncls_iovec)
+    nbfd->iostream = obfd->iostream;
   nbfd->my_archive = obfd;
   nbfd->direction = read_direction;
   nbfd->target_defaulted = obfd->target_defaulted;
   nbfd->my_archive = obfd;
   nbfd->direction = read_direction;
   nbfd->target_defaulted = obfd->target_defaulted;
+  nbfd->lto_output = obfd->lto_output;
+  nbfd->no_export = obfd->no_export;
   return nbfd;
 }
 
   return nbfd;
 }
 
@@ -136,6 +125,9 @@ _bfd_delete_bfd (bfd *abfd)
       objalloc_free ((struct objalloc *) abfd->memory);
     }
 
       objalloc_free ((struct objalloc *) abfd->memory);
     }
 
+  if (abfd->filename)
+    free ((char *) abfd->filename);
+  free (abfd->arelt_data);
   free (abfd);
 }
 
   free (abfd);
 }
 
@@ -181,7 +173,7 @@ DESCRIPTION
        Return a pointer to the created BFD.  If @var{fd} is not -1,
        then <<fdopen>> is used to open the file; otherwise, <<fopen>>
        is used.  @var{mode} is passed directly to <<fopen>> or
        Return a pointer to the created BFD.  If @var{fd} is not -1,
        then <<fdopen>> is used to open the file; otherwise, <<fopen>>
        is used.  @var{mode} is passed directly to <<fopen>> or
-       <<fdopen>>. 
+       <<fdopen>>.
 
        Calls <<bfd_find_target>>, so @var{target} is interpreted as by
        that function.
 
        Calls <<bfd_find_target>>, so @var{target} is interpreted as by
        that function.
@@ -193,6 +185,9 @@ DESCRIPTION
        <<system_call>> error.
 
        On error, @var{fd} is always closed.
        <<system_call>> error.
 
        On error, @var{fd} is always closed.
+
+       A copy of the @var{filename} argument is stored in the newly created
+       BFD.  It can be accessed via the bfd_get_filename() macro.
 */
 
 bfd *
 */
 
 bfd *
@@ -217,7 +212,7 @@ bfd_fopen (const char *filename, const char *target, const char *mode, int fd)
       _bfd_delete_bfd (nbfd);
       return NULL;
     }
       _bfd_delete_bfd (nbfd);
       return NULL;
     }
-  
+
 #ifdef HAVE_FDOPEN
   if (fd != -1)
     nbfd->iostream = fdopen (fd, mode);
 #ifdef HAVE_FDOPEN
   if (fd != -1)
     nbfd->iostream = fdopen (fd, mode);
@@ -232,11 +227,14 @@ bfd_fopen (const char *filename, const char *target, const char *mode, int fd)
     }
 
   /* OK, put everything where it belongs.  */
     }
 
   /* OK, put everything where it belongs.  */
-  nbfd->filename = filename;
+
+  /* PR 11983: Do not cache the original filename, but
+     rather make a copy - the original might go away.  */
+  nbfd->filename = xstrdup (filename);
 
   /* Figure out whether the user is opening the file for reading,
      writing, or both, by looking at the MODE argument.  */
 
   /* Figure out whether the user is opening the file for reading,
      writing, or both, by looking at the MODE argument.  */
-  if ((mode[0] == 'r' || mode[0] == 'w' || mode[0] == 'a') 
+  if ((mode[0] == 'r' || mode[0] == 'w' || mode[0] == 'a')
       && mode[1] == '+')
     nbfd->direction = both_direction;
   else if (mode[0] == 'r')
       && mode[1] == '+')
     nbfd->direction = both_direction;
   else if (mode[0] == 'r')
@@ -250,12 +248,13 @@ bfd_fopen (const char *filename, const char *target, const char *mode, int fd)
       return NULL;
     }
   nbfd->opened_once = TRUE;
       return NULL;
     }
   nbfd->opened_once = TRUE;
+
   /* If we opened the file by name, mark it cacheable; we can close it
      and reopen it later.  However, if a file descriptor was provided,
      then it may have been opened with special flags that make it
      unsafe to close and reopen the file.  */
   if (fd == -1)
   /* If we opened the file by name, mark it cacheable; we can close it
      and reopen it later.  However, if a file descriptor was provided,
      then it may have been opened with special flags that make it
      unsafe to close and reopen the file.  */
   if (fd == -1)
-    bfd_set_cacheable (nbfd, TRUE);
+    (void) bfd_set_cacheable (nbfd, TRUE);
 
   return nbfd;
 }
 
   return nbfd;
 }
@@ -277,6 +276,9 @@ DESCRIPTION
        If <<NULL>> is returned then an error has occured.   Possible errors
        are <<bfd_error_no_memory>>, <<bfd_error_invalid_target>> or
        <<system_call>> error.
        If <<NULL>> is returned then an error has occured.   Possible errors
        are <<bfd_error_no_memory>>, <<bfd_error_invalid_target>> or
        <<system_call>> error.
+
+       A copy of the @var{filename} argument is stored in the newly created
+       BFD.  It can be accessed via the bfd_get_filename() macro.
 */
 
 bfd *
 */
 
 bfd *
@@ -318,6 +320,9 @@ DESCRIPTION
        <<bfd_error_invalid_target>> and <<bfd_error_system_call>>.
 
        On error, @var{fd} is closed.
        <<bfd_error_invalid_target>> and <<bfd_error_system_call>>.
 
        On error, @var{fd} is closed.
+
+       A copy of the @var{filename} argument is stored in the newly created
+       BFD.  It can be accessed via the bfd_get_filename() macro.
 */
 
 bfd *
 */
 
 bfd *
@@ -360,12 +365,15 @@ FUNCTION
        bfd_openstreamr
 
 SYNOPSIS
        bfd_openstreamr
 
 SYNOPSIS
-       bfd *bfd_openstreamr (const char *, const char *, void *);
+       bfd *bfd_openstreamr (const char * filename, const char * target, void * stream);
 
 DESCRIPTION
 
        Open a BFD for read access on an existing stdio stream.  When
        the BFD is passed to <<bfd_close>>, the stream will be closed.
 
 DESCRIPTION
 
        Open a BFD for read access on an existing stdio stream.  When
        the BFD is passed to <<bfd_close>>, the stream will be closed.
+
+       A copy of the @var{filename} argument is stored in the newly created
+       BFD.  It can be accessed via the bfd_get_filename() macro.
 */
 
 bfd *
 */
 
 bfd *
@@ -387,7 +395,9 @@ bfd_openstreamr (const char *filename, const char *target, void *streamarg)
     }
 
   nbfd->iostream = stream;
     }
 
   nbfd->iostream = stream;
-  nbfd->filename = filename;
+  /* PR 11983: Do not cache the original filename, but
+     rather make a copy - the original might go away.  */
+  nbfd->filename = xstrdup (filename);
   nbfd->direction = read_direction;
 
   if (! bfd_cache_init (nbfd))
   nbfd->direction = read_direction;
 
   if (! bfd_cache_init (nbfd))
@@ -452,6 +462,8 @@ DESCRIPTION
        occurred.  Possible errors are <<bfd_error_no_memory>>,
        <<bfd_error_invalid_target>> and <<bfd_error_system_call>>.
 
        occurred.  Possible errors are <<bfd_error_no_memory>>,
        <<bfd_error_invalid_target>> and <<bfd_error_system_call>>.
 
+       A copy of the @var{filename} argument is stored in the newly created
+       BFD.  It can be accessed via the bfd_get_filename() macro.
 */
 
 struct opncls
 */
 
 struct opncls
@@ -577,7 +589,9 @@ bfd_openr_iovec (const char *filename, const char *target,
       return NULL;
     }
 
       return NULL;
     }
 
-  nbfd->filename = filename;
+  /* PR 11983: Do not cache the original filename, but
+     rather make a copy - the original might go away.  */
+  nbfd->filename = xstrdup (filename);
   nbfd->direction = read_direction;
 
   /* `open_p (...)' would get expanded by an the open(2) syscall macro.  */
   nbfd->direction = read_direction;
 
   /* `open_p (...)' would get expanded by an the open(2) syscall macro.  */
@@ -618,6 +632,9 @@ DESCRIPTION
 
        Possible errors are <<bfd_error_system_call>>, <<bfd_error_no_memory>>,
        <<bfd_error_invalid_target>>.
 
        Possible errors are <<bfd_error_system_call>>, <<bfd_error_no_memory>>,
        <<bfd_error_invalid_target>>.
+
+       A copy of the @var{filename} argument is stored in the newly created
+       BFD.  It can be accessed via the bfd_get_filename() macro.
 */
 
 bfd *
 */
 
 bfd *
@@ -639,7 +656,9 @@ bfd_openw (const char *filename, const char *target)
       return NULL;
     }
 
       return NULL;
     }
 
-  nbfd->filename = filename;
+  /* PR 11983: Do not cache the original filename, but
+     rather make a copy - the original might go away.  */
+  nbfd->filename = xstrdup (filename);
   nbfd->direction = write_direction;
 
   if (bfd_open_file (nbfd) == NULL)
   nbfd->direction = write_direction;
 
   if (bfd_open_file (nbfd) == NULL)
@@ -718,7 +737,7 @@ bfd_close (bfd *abfd)
   if (! BFD_SEND (abfd, _close_and_cleanup, (abfd)))
     return FALSE;
 
   if (! BFD_SEND (abfd, _close_and_cleanup, (abfd)))
     return FALSE;
 
-  ret = abfd->iovec->bclose (abfd);
+  ret = abfd->iovec->bclose (abfd) == 0;
 
   if (ret)
     _maybe_make_executable (abfd);
 
   if (ret)
     _maybe_make_executable (abfd);
@@ -776,6 +795,9 @@ DESCRIPTION
        Create a new BFD in the manner of <<bfd_openw>>, but without
        opening a file. The new BFD takes the target from the target
        used by @var{templ}. The format is always set to <<bfd_object>>.
        Create a new BFD in the manner of <<bfd_openw>>, but without
        opening a file. The new BFD takes the target from the target
        used by @var{templ}. The format is always set to <<bfd_object>>.
+
+       A copy of the @var{filename} argument is stored in the newly created
+       BFD.  It can be accessed via the bfd_get_filename() macro.
 */
 
 bfd *
 */
 
 bfd *
@@ -786,7 +808,9 @@ bfd_create (const char *filename, bfd *templ)
   nbfd = _bfd_new_bfd ();
   if (nbfd == NULL)
     return NULL;
   nbfd = _bfd_new_bfd ();
   if (nbfd == NULL)
     return NULL;
-  nbfd->filename = filename;
+  /* PR 11983: Do not cache the original filename, but
+     rather make a copy - the original might go away.  */
+  nbfd->filename = xstrdup (filename);
   if (templ)
     nbfd->xvec = templ->xvec;
   nbfd->direction = no_direction;
   if (templ)
     nbfd->xvec = templ->xvec;
   nbfd->direction = no_direction;
@@ -915,14 +939,23 @@ void *
 bfd_alloc (bfd *abfd, bfd_size_type size)
 {
   void *ret;
 bfd_alloc (bfd *abfd, bfd_size_type size)
 {
   void *ret;
-
-  if (size != (unsigned long) size)
+  unsigned long ul_size = (unsigned long) size;
+
+  if (size != ul_size
+      /* Note - although objalloc_alloc takes an unsigned long as its
+        argument, internally the size is treated as a signed long.  This can
+        lead to problems where, for example, a request to allocate -1 bytes
+        can result in just 1 byte being allocated, rather than
+        ((unsigned long) -1) bytes.  Also memory checkers will often
+        complain about attempts to allocate a negative amount of memory.
+        So to stop these problems we fail if the size is negative.  */
+      || ((signed long) ul_size) < 0)
     {
       bfd_set_error (bfd_error_no_memory);
       return NULL;
     }
 
     {
       bfd_set_error (bfd_error_no_memory);
       return NULL;
     }
 
-  ret = objalloc_alloc ((struct objalloc *) abfd->memory, (unsigned long) size);
+  ret = objalloc_alloc ((struct objalloc *) abfd->memory, ul_size);
   if (ret == NULL)
     bfd_set_error (bfd_error_no_memory);
   return ret;
   if (ret == NULL)
     bfd_set_error (bfd_error_no_memory);
   return ret;
@@ -943,8 +976,6 @@ DESCRIPTION
 void *
 bfd_alloc2 (bfd *abfd, bfd_size_type nmemb, bfd_size_type size)
 {
 void *
 bfd_alloc2 (bfd *abfd, bfd_size_type nmemb, bfd_size_type size)
 {
-  void *ret;
-
   if ((nmemb | size) >= HALF_BFD_SIZE_TYPE
       && size != 0
       && nmemb > ~(bfd_size_type) 0 / size)
   if ((nmemb | size) >= HALF_BFD_SIZE_TYPE
       && size != 0
       && nmemb > ~(bfd_size_type) 0 / size)
@@ -953,18 +984,7 @@ bfd_alloc2 (bfd *abfd, bfd_size_type nmemb, bfd_size_type size)
       return NULL;
     }
 
       return NULL;
     }
 
-  size *= nmemb;
-
-  if (size != (unsigned long) size)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return NULL;
-    }
-
-  ret = objalloc_alloc ((struct objalloc *) abfd->memory, (unsigned long) size);
-  if (ret == NULL)
-    bfd_set_error (bfd_error_no_memory);
-  return ret;
+  return bfd_alloc (abfd, size * nmemb);
 }
 
 /*
 }
 
 /*
@@ -1044,10 +1064,11 @@ bfd_release (bfd *abfd, void *block)
 
    This facilitates "optional" provision of debugging information, without
    having to provide two complete copies of every binary object (with and
 
    This facilitates "optional" provision of debugging information, without
    having to provide two complete copies of every binary object (with and
-   without debug symbols).
-*/
+   without debug symbols).  */
+
+#define GNU_DEBUGLINK          ".gnu_debuglink"
+#define GNU_DEBUGALTLINK       ".gnu_debugaltlink"
 
 
-#define GNU_DEBUGLINK  ".gnu_debuglink"
 /*
 FUNCTION
        bfd_calc_gnu_debuglink_crc32
 /*
 FUNCTION
        bfd_calc_gnu_debuglink_crc32
@@ -1130,30 +1151,32 @@ bfd_calc_gnu_debuglink_crc32 (unsigned long crc,
   crc = ~crc & 0xffffffff;
   for (end = buf + len; buf < end; ++ buf)
     crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
   crc = ~crc & 0xffffffff;
   for (end = buf + len; buf < end; ++ buf)
     crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
-  return ~crc & 0xffffffff;;
+  return ~crc & 0xffffffff;
 }
 
 
 /*
 }
 
 
 /*
-INTERNAL_FUNCTION
-       get_debug_link_info
+FUNCTION
+       bfd_get_debug_link_info
 
 SYNOPSIS
 
 SYNOPSIS
-       char *get_debug_link_info (bfd *abfd, unsigned long *crc32_out);
+       char *bfd_get_debug_link_info (bfd *abfd, unsigned long *crc32_out);
 
 DESCRIPTION
 
 DESCRIPTION
-       fetch the filename and CRC32 value for any separate debuginfo
-       associated with @var{abfd}. Return NULL if no such info found,
-       otherwise return filename and update @var{crc32_out}.
+       Fetch the filename and CRC32 value for any separate debuginfo
+       associated with @var{abfd}.  Return NULL if no such info found,
+       otherwise return filename and update @var{crc32_out}.  The
+       returned filename is allocated with @code{malloc}; freeing it
+       is the responsibility of the caller.
 */
 
 */
 
-static char *
-get_debug_link_info (bfd *abfd, unsigned long *crc32_out)
+char *
+bfd_get_debug_link_info (bfd *abfd, unsigned long *crc32_out)
 {
   asection *sect;
   unsigned long crc32;
   bfd_byte *contents;
 {
   asection *sect;
   unsigned long crc32;
   bfd_byte *contents;
-  int crc_offset;
+  unsigned int crc_offset;
   char *name;
 
   BFD_ASSERT (abfd);
   char *name;
 
   BFD_ASSERT (abfd);
@@ -1171,10 +1194,13 @@ get_debug_link_info (bfd *abfd, unsigned long *crc32_out)
       return NULL;
     }
 
       return NULL;
     }
 
-  /* Crc value is stored after the filename, aligned up to 4 bytes.  */
+  /* CRC value is stored after the filename, aligned up to 4 bytes.  */
   name = (char *) contents;
   name = (char *) contents;
-  crc_offset = strlen (name) + 1;
+  /* PR 17597: avoid reading off the end of the buffer.  */
+  crc_offset = strnlen (name, bfd_get_section_size (sect)) + 1;
   crc_offset = (crc_offset + 3) & ~3;
   crc_offset = (crc_offset + 3) & ~3;
+  if (crc_offset >= bfd_get_section_size (sect))
+    return NULL;
 
   crc32 = bfd_get_32 (abfd, contents + crc_offset);
 
 
   crc32 = bfd_get_32 (abfd, contents + crc_offset);
 
@@ -1182,6 +1208,62 @@ get_debug_link_info (bfd *abfd, unsigned long *crc32_out)
   return name;
 }
 
   return name;
 }
 
+/*
+FUNCTION
+       bfd_get_alt_debug_link_info
+
+SYNOPSIS
+       char *bfd_get_alt_debug_link_info (bfd * abfd,
+                                          bfd_size_type *buildid_len,
+                                          bfd_byte **buildid_out);
+
+DESCRIPTION
+       Fetch the filename and BuildID value for any alternate debuginfo
+       associated with @var{abfd}.  Return NULL if no such info found,
+       otherwise return filename and update @var{buildid_len} and
+       @var{buildid_out}.  The returned filename and build_id are
+       allocated with @code{malloc}; freeing them is the
+       responsibility of the caller.
+*/
+
+char *
+bfd_get_alt_debug_link_info (bfd * abfd, bfd_size_type *buildid_len,
+                            bfd_byte **buildid_out)
+{
+  asection *sect;
+  bfd_byte *contents;
+  unsigned int buildid_offset;
+  char *name;
+
+  BFD_ASSERT (abfd);
+  BFD_ASSERT (buildid_len);
+  BFD_ASSERT (buildid_out);
+
+  sect = bfd_get_section_by_name (abfd, GNU_DEBUGALTLINK);
+
+  if (sect == NULL)
+    return NULL;
+
+  if (!bfd_malloc_and_get_section (abfd, sect, & contents))
+    {
+      if (contents != NULL)
+       free (contents);
+      return NULL;
+    }
+
+  /* BuildID value is stored after the filename.  */
+  name = (char *) contents;
+  buildid_offset = strnlen (name, bfd_get_section_size (sect)) + 1;
+  if (buildid_offset >= bfd_get_section_size (sect))
+    return NULL;
+
+  *buildid_len = bfd_get_section_size (sect) - buildid_offset;
+  *buildid_out = bfd_malloc (*buildid_len);
+  memcpy (*buildid_out, contents + buildid_offset, *buildid_len);
+
+  return name;
+}
+
 /*
 INTERNAL_FUNCTION
        separate_debug_file_exists
 /*
 INTERNAL_FUNCTION
        separate_debug_file_exists
@@ -1217,6 +1299,37 @@ separate_debug_file_exists (const char *name, const unsigned long crc)
   return crc == file_crc;
 }
 
   return crc == file_crc;
 }
 
+/*
+INTERNAL_FUNCTION
+       separate_alt_debug_file_exists
+
+SYNOPSIS
+       bfd_boolean separate_alt_debug_file_exists
+         (char *name, unsigned long crc32);
+
+DESCRIPTION
+       Checks to see if @var{name} is a file and if its BuildID
+       matches @var{buildid}.
+*/
+
+static bfd_boolean
+separate_alt_debug_file_exists (const char *name,
+                               const unsigned long buildid ATTRIBUTE_UNUSED)
+{
+  FILE *f;
+
+  BFD_ASSERT (name);
+
+  f = real_fopen (name, FOPEN_RB);
+  if (f == NULL)
+    return FALSE;
+
+  /* FIXME: Add code to check buildid.  */
+
+  fclose (f);
+
+  return TRUE;
+}
 
 /*
 INTERNAL_FUNCTION
 
 /*
 INTERNAL_FUNCTION
@@ -1226,16 +1339,24 @@ SYNOPSIS
        char *find_separate_debug_file (bfd *abfd);
 
 DESCRIPTION
        char *find_separate_debug_file (bfd *abfd);
 
 DESCRIPTION
-       Searches @var{abfd} for a reference to separate debugging
-       information, scans various locations in the filesystem, including
-       the file tree rooted at @var{debug_file_directory}, and returns a
-       filename of such debugging information if the file is found and has
-       matching CRC32.  Returns NULL if no reference to debugging file
-       exists, or file cannot be found.
+       Searches @var{abfd} for a section called @var{section_name} which
+       is expected to contain a reference to a file containing separate
+       debugging information.  The function scans various locations in
+       the filesystem, including the file tree rooted at
+       @var{debug_file_directory}, and returns the first matching
+       filename that it finds.  If @var{check_crc} is TRUE then the
+       contents of the file must also match the CRC value contained in
+       @var{section_name}.  Returns NULL if no valid file could be found.
 */
 
 */
 
+typedef char *      (* get_func_type) (bfd *, unsigned long *);
+typedef bfd_boolean (* check_func_type) (const char *, const unsigned long);
+
 static char *
 static char *
-find_separate_debug_file (bfd *abfd, const char *debug_file_directory)
+find_separate_debug_file (bfd *           abfd,
+                         const char *    debug_file_directory,
+                         get_func_type   get_func,
+                         check_func_type check_func)
 {
   char *base;
   char *dir;
 {
   char *base;
   char *dir;
@@ -1256,7 +1377,8 @@ find_separate_debug_file (bfd *abfd, const char *debug_file_directory)
       return NULL;
     }
 
       return NULL;
     }
 
-  base = get_debug_link_info (abfd, & crc32);
+  base = get_func (abfd, & crc32);
+
   if (base == NULL)
     return NULL;
 
   if (base == NULL)
     return NULL;
 
@@ -1295,37 +1417,22 @@ find_separate_debug_file (bfd *abfd, const char *debug_file_directory)
                   + strlen (base)
                   + 1);
   if (debugfile == NULL)
                   + strlen (base)
                   + 1);
   if (debugfile == NULL)
-    {
-      free (base);
-      free (dir);
-      free (canon_dir);
-      return NULL;
-    }
+    goto found; /* Actually this returns NULL.  */
 
   /* First try in the same directory as the original file:  */
   strcpy (debugfile, dir);
   strcat (debugfile, base);
 
 
   /* First try in the same directory as the original file:  */
   strcpy (debugfile, dir);
   strcat (debugfile, base);
 
-  if (separate_debug_file_exists (debugfile, crc32))
-    {
-      free (base);
-      free (dir);
-      free (canon_dir);
-      return debugfile;
-    }
+  if (check_func (debugfile, crc32))
+    goto found;
 
   /* Then try in a subdirectory called .debug.  */
   strcpy (debugfile, dir);
   strcat (debugfile, ".debug/");
   strcat (debugfile, base);
 
 
   /* Then try in a subdirectory called .debug.  */
   strcpy (debugfile, dir);
   strcat (debugfile, ".debug/");
   strcat (debugfile, base);
 
-  if (separate_debug_file_exists (debugfile, crc32))
-    {
-      free (base);
-      free (dir);
-      free (canon_dir);
-      return debugfile;
-    }
+  if (check_func (debugfile, crc32))
+    goto found;
 
   /* Then try in the global debugfile directory.  */
   strcpy (debugfile, debug_file_directory);
 
   /* Then try in the global debugfile directory.  */
   strcpy (debugfile, debug_file_directory);
@@ -1337,19 +1444,18 @@ find_separate_debug_file (bfd *abfd, const char *debug_file_directory)
   strcat (debugfile, canon_dir);
   strcat (debugfile, base);
 
   strcat (debugfile, canon_dir);
   strcat (debugfile, base);
 
-  if (separate_debug_file_exists (debugfile, crc32))
-    {
-      free (base);
-      free (dir);
-      free (canon_dir);
-      return debugfile;
-    }
+  if (check_func (debugfile, crc32))
+    goto found;
 
 
+  /* Failed to find the file.  */
   free (debugfile);
   free (debugfile);
+  debugfile = NULL;
+
+ found:
   free (base);
   free (dir);
   free (canon_dir);
   free (base);
   free (dir);
   free (canon_dir);
-  return NULL;
+  return debugfile;
 }
 
 
 }
 
 
@@ -1382,7 +1488,61 @@ RETURNS
 char *
 bfd_follow_gnu_debuglink (bfd *abfd, const char *dir)
 {
 char *
 bfd_follow_gnu_debuglink (bfd *abfd, const char *dir)
 {
-  return find_separate_debug_file (abfd, dir);
+  return find_separate_debug_file (abfd, dir,
+                                  bfd_get_debug_link_info,
+                                  separate_debug_file_exists);
+}
+
+/* Helper for bfd_follow_gnu_debugaltlink.  It just pretends to return
+   a CRC.  .gnu_debugaltlink supplies a build-id, which is different,
+   but this is ok because separate_alt_debug_file_exists ignores the
+   CRC anyway.  */
+
+static char *
+get_alt_debug_link_info_shim (bfd * abfd, unsigned long *crc32_out)
+{
+  bfd_size_type len;
+  bfd_byte *buildid = NULL;
+  char *result = bfd_get_alt_debug_link_info (abfd, &len, &buildid);
+
+  *crc32_out = 0;
+  free (buildid);
+
+  return result;
+}
+
+/*
+FUNCTION
+       bfd_follow_gnu_debugaltlink
+
+SYNOPSIS
+       char *bfd_follow_gnu_debugaltlink (bfd *abfd, const char *dir);
+
+DESCRIPTION
+
+       Takes a BFD and searches it for a .gnu_debugaltlink section.  If this
+       section is found, it examines the section for the name of a file
+       containing auxiliary debugging information.  It then searches the
+       filesystem for this file in a set of standard locations, including
+       the directory tree rooted at @var{dir}, and if found returns the
+       full filename.
+
+       If @var{dir} is NULL, it will search a default path configured into
+       libbfd at build time.  [FIXME: This feature is not currently
+       implemented].
+
+RETURNS
+       <<NULL>> on any errors or failure to locate the debug file,
+       otherwise a pointer to a heap-allocated string containing the
+       filename.  The caller is responsible for freeing this string.
+*/
+
+char *
+bfd_follow_gnu_debugaltlink (bfd *abfd, const char *dir)
+{
+  return find_separate_debug_file (abfd, dir,
+                                  get_alt_debug_link_info_shim,
+                                  separate_alt_debug_file_exists);
 }
 
 /*
 }
 
 /*
This page took 0.030934 seconds and 4 git commands to generate.