+ size = bfd_section_size (sect);
+ /* FIXME: Should we support smaller build-id notes ? */
+ if (size < 0x24)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return NULL;
+ }
+
+ if (!bfd_malloc_and_get_section (abfd, sect, & contents))
+ {
+ free (contents);
+ return NULL;
+ }
+
+ /* FIXME: Paranoia - allow for compressed build-id sections.
+ Maybe we should complain if this size is different from
+ the one obtained above... */
+ size = bfd_section_size (sect);
+ if (size < sizeof (Elf_External_Note))
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ free (contents);
+ return NULL;
+ }
+
+ enote = (Elf_External_Note *) contents;
+ inote.type = H_GET_32 (abfd, enote->type);
+ inote.namesz = H_GET_32 (abfd, enote->namesz);
+ inote.namedata = enote->name;
+ inote.descsz = H_GET_32 (abfd, enote->descsz);
+ inote.descdata = inote.namedata + BFD_ALIGN (inote.namesz, 4);
+ /* FIXME: Should we check for extra notes in this section ? */
+
+ if (inote.descsz <= 0
+ || inote.type != NT_GNU_BUILD_ID
+ || inote.namesz != 4 /* sizeof "GNU" */
+ || !startswith (inote.namedata, "GNU")
+ || inote.descsz > 0x7ffffffe
+ || size < (12 + BFD_ALIGN (inote.namesz, 4) + inote.descsz))
+ {
+ free (contents);
+ bfd_set_error (bfd_error_invalid_operation);
+ return NULL;
+ }
+
+ build_id = bfd_alloc (abfd, sizeof (struct bfd_build_id) + inote.descsz);
+ if (build_id == NULL)
+ {
+ free (contents);
+ return NULL;
+ }
+
+ build_id->size = inote.descsz;
+ memcpy (build_id->data, inote.descdata, inote.descsz);
+ abfd->build_id = build_id;
+ free (contents);
+
+ return build_id;
+}
+
+/*
+INTERNAL_FUNCTION
+ get_build_id_name
+
+SYNOPSIS
+ char * get_build_id_name (bfd *abfd, void *build_id_out_p)
+
+DESCRIPTION
+ Searches @var{abfd} for a build-id, and then constructs a pathname
+ from it. The path is computed as .build-id/NN/NN+NN.debug where
+ NNNN+NN is the build-id value as a hexadecimal string.
+
+RETURNS
+ Returns the constructed filename or NULL upon error.
+ It is the caller's responsibility to free the memory used to hold the
+ filename.
+ If a filename is returned then the @var{build_id_out_p}
+ parameter (which points to a @code{struct bfd_build_id}
+ pointer) is set to a pointer to the build_id structure.
+*/
+
+static char *
+get_build_id_name (bfd *abfd, void *build_id_out_p)
+{
+ struct bfd_build_id **build_id_out = build_id_out_p;
+ struct bfd_build_id *build_id;
+ char *name;
+ char *n;
+ bfd_size_type s;
+ bfd_byte *d;
+
+ if (abfd == NULL || bfd_get_filename (abfd) == NULL || build_id_out == NULL)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return NULL;
+ }
+
+ build_id = get_build_id (abfd);
+ if (build_id == NULL)
+ return NULL;
+
+ /* Compute the debug pathname corresponding to the build-id. */
+ name = bfd_malloc (strlen (".build-id/") + build_id->size * 2 + 2 + strlen (".debug"));
+ if (name == NULL)
+ {
+ bfd_set_error (bfd_error_no_memory);
+ return NULL;
+ }
+ n = name;
+ d = build_id->data;
+ s = build_id->size;
+
+ n += sprintf (n, ".build-id/");
+ n += sprintf (n, "%02x", (unsigned) *d++); s--;
+ n += sprintf (n, "/");
+ while (s--)
+ n += sprintf (n, "%02x", (unsigned) *d++);
+ n += sprintf (n, ".debug");
+
+ *build_id_out = build_id;
+ return name;
+}
+
+/*
+INTERNAL_FUNCTION
+ check_build_id_file
+
+SYNOPSIS
+ bool check_build_id_file (char *name, void *buildid_p);
+
+DESCRIPTION
+ Checks to see if @var{name} is a readable file and if its build-id
+ matches @var{buildid}.
+
+RETURNS
+ Returns TRUE if the file exists, is readable, and contains a
+ build-id which matches the build-id pointed at by
+ @var{build_id_p} (which is really a @code{struct bfd_build_id **}).
+*/
+
+static bool
+check_build_id_file (const char *name, void *buildid_p)
+{
+ struct bfd_build_id *orig_build_id;
+ struct bfd_build_id *build_id;
+ bfd * file;
+ bool result;
+
+ BFD_ASSERT (name);
+ BFD_ASSERT (buildid_p);
+
+ file = bfd_openr (name, NULL);
+ if (file == NULL)
+ return false;
+
+ /* If the file is an archive, process all of its elements. */
+ if (! bfd_check_format (file, bfd_object))
+ {
+ bfd_close (file);
+ return false;
+ }
+
+ build_id = get_build_id (file);
+ if (build_id == NULL)
+ {
+ bfd_close (file);
+ return false;
+ }
+
+ orig_build_id = *(struct bfd_build_id **) buildid_p;
+
+ result = build_id->size == orig_build_id->size
+ && memcmp (build_id->data, orig_build_id->data, build_id->size) == 0;
+
+ (void) bfd_close (file);
+
+ return result;
+}
+
+/*
+FUNCTION
+ bfd_follow_build_id_debuglink
+
+SYNOPSIS
+ char *bfd_follow_build_id_debuglink (bfd *abfd, const char *dir);
+
+DESCRIPTION
+ Takes @var{abfd} and searches it for a .note.gnu.build-id section.
+ If this section is found, it extracts the value of the NT_GNU_BUILD_ID
+ note, which should be a hexadecimal value @var{NNNN+NN} (for
+ 32+ hex digits). It then searches the filesystem for a file named
+ @var{.build-id/NN/NN+NN.debug} in a set of standard locations,
+ including the directory tree rooted at @var{dir}. The filename
+ of the first matching file to be found is returned. A matching
+ file should contain a .note.gnu.build-id section with the same
+ @var{NNNN+NN} note as @var{abfd}, although this check is currently
+ not implemented.
+
+ If @var{dir} is NULL, the search will take place starting at
+ the current directory.
+
+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_build_id_debuglink (bfd *abfd, const char *dir)
+{
+ struct bfd_build_id *build_id;
+
+ return find_separate_debug_file (abfd, dir, false,
+ get_build_id_name,
+ check_build_id_file, &build_id);
+}
+
+/*
+FUNCTION
+ bfd_set_filename
+
+SYNOPSIS
+ const char *bfd_set_filename (bfd *abfd, const char *filename);
+
+DESCRIPTION
+ Set the filename of @var{abfd}, copying the FILENAME parameter to
+ bfd_alloc'd memory owned by @var{abfd}. Returns a pointer the
+ newly allocated name, or NULL if the allocation failed.
+*/
+
+const char *
+bfd_set_filename (bfd *abfd, const char *filename)
+{
+ size_t len = strlen (filename) + 1;
+ char *n = bfd_alloc (abfd, len);
+
+ if (n == NULL)
+ return NULL;
+
+ if (abfd->filename != NULL)
+ {
+ /* PR 29389. If we attempt to rename a file that has been closed due
+ to caching, then we will not be able to reopen it later on. */
+ if (abfd->iostream == NULL && (abfd->flags & BFD_CLOSED_BY_CACHE))
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return NULL;
+ }
+
+ /* Similarly if we attempt to close a renamed file because the
+ cache is now full, we will not be able to reopen it later on. */
+ if (abfd->iostream != NULL)
+ abfd->cacheable = 0;
+ }
+
+ memcpy (n, filename, len);
+ abfd->filename = n;
+
+ return n;