+ static unsigned char buffer [8 * 1024];
+ unsigned long file_crc = 0;
+ FILE *f;
+ bfd_size_type count;
+
+ BFD_ASSERT (name);
+
+ f = real_fopen (name, FOPEN_RB);
+ if (f == NULL)
+ return FALSE;
+
+ while ((count = fread (buffer, 1, sizeof (buffer), f)) > 0)
+ file_crc = bfd_calc_gnu_debuglink_crc32 (file_crc, buffer, count);
+
+ fclose (f);
+
+ 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
+ find_separate_debug_file
+
+SYNOPSIS
+ char *find_separate_debug_file (bfd *abfd);
+
+DESCRIPTION
+ 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 *
+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 *debugfile;
+ char *canon_dir;
+ unsigned long crc32;
+ 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. */
+ if (abfd->filename == NULL)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return NULL;
+ }
+
+ base = get_func (abfd, & crc32);
+
+ if (base == NULL)
+ return NULL;
+
+ if (base[0] == '\0')
+ {
+ free (base);
+ bfd_set_error (bfd_error_no_debug_section);
+ return NULL;
+ }
+
+ for (dirlen = strlen (abfd->filename); dirlen > 0; dirlen--)
+ if (IS_DIR_SEPARATOR (abfd->filename[dirlen - 1]))
+ break;
+
+ dir = (char *) bfd_malloc (dirlen + 1);
+ if (dir == NULL)
+ {
+ free (base);
+ return NULL;
+ }
+ 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;
+ canon_dir[canon_dirlen] = '\0';
+
+ debugfile = (char *)
+ bfd_malloc (strlen (debug_file_directory) + 1
+ + (canon_dirlen > dirlen ? canon_dirlen : dirlen)
+ + strlen (".debug/")
+ + strlen (base)
+ + 1);
+ if (debugfile == NULL)
+ goto found; /* Actually this returns NULL. */
+
+ /* First try in the same directory as the original file: */
+ strcpy (debugfile, dir);
+ strcat (debugfile, base);
+
+ if (check_func (debugfile, crc32))
+ goto found;
+
+ /* Then try in a subdirectory called .debug. */
+ strcpy (debugfile, dir);
+ strcat (debugfile, ".debug/");
+ strcat (debugfile, base);
+
+ if (check_func (debugfile, crc32))
+ goto found;
+
+ /* Then try in the global debugfile directory. */
+ strcpy (debugfile, debug_file_directory);
+ dirlen = strlen (debug_file_directory) - 1;
+ if (dirlen > 0
+ && debug_file_directory[dirlen] != '/'
+ && canon_dir[0] != '/')
+ strcat (debugfile, "/");
+ strcat (debugfile, canon_dir);
+ strcat (debugfile, base);
+
+ if (check_func (debugfile, crc32))
+ goto found;
+
+ /* Failed to find the file. */
+ free (debugfile);
+ debugfile = NULL;
+
+ found:
+ free (base);
+ free (dir);
+ free (canon_dir);
+ return debugfile;
+}
+
+
+/*
+FUNCTION
+ bfd_follow_gnu_debuglink
+
+SYNOPSIS
+ char *bfd_follow_gnu_debuglink (bfd *abfd, const char *dir);
+
+DESCRIPTION
+
+ Takes a BFD and searches it for a .gnu_debuglink section. If this
+ section is found, it examines the section for the name and checksum
+ of a '.debug' file containing auxiliary debugging information. It
+ then searches the filesystem for this .debug file in some 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. [XXX 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_debuglink (bfd *abfd, const char *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);
+}
+
+/*
+FUNCTION
+ bfd_create_gnu_debuglink_section
+
+SYNOPSIS
+ struct bfd_section *bfd_create_gnu_debuglink_section
+ (bfd *abfd, const char *filename);
+
+DESCRIPTION
+
+ Takes a @var{BFD} and adds a .gnu_debuglink section to it. The section is sized
+ to be big enough to contain a link to the specified @var{filename}.
+
+RETURNS
+ A pointer to the new section is returned if all is ok. Otherwise <<NULL>> is
+ returned and bfd_error is set.
+*/
+
+asection *
+bfd_create_gnu_debuglink_section (bfd *abfd, const char *filename)
+{
+ asection *sect;
+ bfd_size_type debuglink_size;
+ flagword flags;
+
+ if (abfd == NULL || filename == NULL)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return NULL;
+ }
+
+ /* Strip off any path components in filename. */
+ filename = lbasename (filename);
+
+ sect = bfd_get_section_by_name (abfd, GNU_DEBUGLINK);
+ if (sect)
+ {
+ /* Section already exists. */
+ bfd_set_error (bfd_error_invalid_operation);
+ return NULL;
+ }
+
+ flags = SEC_HAS_CONTENTS | SEC_READONLY | SEC_DEBUGGING;
+ sect = bfd_make_section_with_flags (abfd, GNU_DEBUGLINK, flags);
+ if (sect == NULL)
+ return NULL;
+
+ debuglink_size = strlen (filename) + 1;
+ debuglink_size += 3;
+ debuglink_size &= ~3;
+ debuglink_size += 4;
+
+ if (! bfd_set_section_size (abfd, sect, debuglink_size))
+ /* XXX Should we delete the section from the bfd ? */
+ return NULL;
+
+ return sect;
+}
+
+
+/*
+FUNCTION
+ bfd_fill_in_gnu_debuglink_section
+
+SYNOPSIS
+ bfd_boolean bfd_fill_in_gnu_debuglink_section
+ (bfd *abfd, struct bfd_section *sect, const char *filename);
+
+DESCRIPTION
+
+ Takes a @var{BFD} and containing a .gnu_debuglink section @var{SECT}
+ and fills in the contents of the section to contain a link to the
+ specified @var{filename}. The filename should be relative to the
+ current directory.
+
+RETURNS
+ <<TRUE>> is returned if all is ok. Otherwise <<FALSE>> is returned
+ and bfd_error is set.
+*/
+
+bfd_boolean
+bfd_fill_in_gnu_debuglink_section (bfd *abfd,
+ struct bfd_section *sect,
+ const char *filename)
+{
+ bfd_size_type debuglink_size;
+ unsigned long crc32;
+ char * contents;
+ bfd_size_type crc_offset;
+ FILE * handle;
+ static unsigned char buffer[8 * 1024];
+ size_t count;
+ size_t filelen;
+
+ if (abfd == NULL || sect == NULL || filename == NULL)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return FALSE;
+ }
+
+ /* Make sure that we can read the file.
+ XXX - Should we attempt to locate the debug info file using the same
+ algorithm as gdb ? At the moment, since we are creating the
+ .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 = real_fopen (filename, FOPEN_RB);
+ if (handle == NULL)
+ {
+ bfd_set_error (bfd_error_system_call);
+ return FALSE;
+ }
+
+ crc32 = 0;
+ while ((count = fread (buffer, 1, sizeof buffer, handle)) > 0)
+ crc32 = bfd_calc_gnu_debuglink_crc32 (crc32, buffer, count);
+ fclose (handle);
+
+ /* Strip off any path components in filename,
+ now that we no longer need them. */
+ filename = lbasename (filename);
+
+ filelen = strlen (filename);
+ debuglink_size = filelen + 1;
+ debuglink_size += 3;
+ debuglink_size &= ~3;
+ debuglink_size += 4;
+
+ contents = (char *) bfd_malloc (debuglink_size);
+ if (contents == NULL)
+ {
+ /* XXX Should we delete the section from the bfd ? */
+ return FALSE;
+ }
+
+ crc_offset = debuglink_size - 4;
+ memcpy (contents, filename, filelen);
+ memset (contents + filelen, 0, crc_offset - filelen);
+
+ bfd_put_32 (abfd, crc32, contents + crc_offset);
+
+ if (! bfd_set_section_contents (abfd, sect, contents, 0, debuglink_size))
+ {
+ /* XXX Should we delete the section from the bfd ? */
+ free (contents);
+ return FALSE;
+ }
+
+ return TRUE;