+/* Duplicates a character string with memory attached to ABFD. */
+
+static char *
+plugin_strdup (bfd *abfd, const char *str)
+{
+ size_t strlength;
+ char *copy;
+ strlength = strlen (str) + 1;
+ copy = bfd_alloc (abfd, strlength);
+ if (copy == NULL)
+ einfo (_("%P%F: plugin_strdup failed to allocate memory: %s\n"),
+ bfd_get_error ());
+ memcpy (copy, str, strlength);
+ return copy;
+}
+
+static const bfd_target *
+plugin_object_p (bfd *ibfd)
+{
+ int claimed;
+ plugin_input_file_t *input;
+ off_t offset, filesize;
+ struct ld_plugin_input_file file;
+ bfd *abfd;
+ bfd_boolean inarchive;
+ const char *name;
+ int fd;
+
+ /* Don't try the dummy object file. */
+ if ((ibfd->flags & BFD_PLUGIN) != 0)
+ return NULL;
+
+ if (ibfd->plugin_format != bfd_plugin_uknown)
+ {
+ if (ibfd->plugin_format == bfd_plugin_yes)
+ return ibfd->plugin_dummy_bfd->xvec;
+ else
+ return NULL;
+ }
+
+ inarchive = bfd_my_archive (ibfd) != NULL;
+ name = inarchive ? bfd_my_archive (ibfd)->filename : ibfd->filename;
+ fd = open (name, O_RDONLY | O_BINARY);
+
+ if (fd < 0)
+ return NULL;
+
+ /* We create a dummy BFD, initially empty, to house whatever symbols
+ the plugin may want to add. */
+ abfd = plugin_get_ir_dummy_bfd (ibfd->filename, ibfd);
+
+ input = bfd_alloc (abfd, sizeof (*input));
+ if (input == NULL)
+ einfo (_("%P%F: plugin failed to allocate memory for input: %s\n"),
+ bfd_get_error ());
+
+ if (inarchive)
+ {
+ /* Offset and filesize must refer to the individual archive
+ member, not the whole file, and must exclude the header.
+ Fortunately for us, that is how the data is stored in the
+ origin field of the bfd and in the arelt_data. */
+ offset = ibfd->origin;
+ filesize = arelt_size (ibfd);
+ }
+ else
+ {
+ offset = 0;
+ filesize = lseek (fd, 0, SEEK_END);
+
+ /* We must copy filename attached to ibfd if it is not an archive
+ member since it may be freed by bfd_close below. */
+ name = plugin_strdup (abfd, name);
+ }
+
+ file.name = name;
+ file.offset = offset;
+ file.filesize = filesize;
+ file.fd = fd;
+ file.handle = input;
+
+ input->abfd = abfd;
+ input->view_buffer.addr = NULL;
+ input->view_buffer.filesize = 0;
+ input->view_buffer.offset = 0;
+ input->fd = fd;
+ input->use_mmap = FALSE;
+ input->offset = offset;
+ input->filesize = filesize;
+ input->name = plugin_strdup (abfd, ibfd->filename);
+
+ claimed = 0;
+
+ if (plugin_call_claim_file (&file, &claimed))
+ einfo (_("%P%F: %s: plugin reported error claiming file\n"),
+ plugin_error_plugin ());
+
+ if (input->fd != -1 && ! bfd_plugin_target_p (ibfd->xvec))
+ {
+ /* FIXME: fd belongs to us, not the plugin. GCC plugin, which
+ doesn't need fd after plugin_call_claim_file, doesn't use
+ BFD plugin target vector. Since GCC plugin doesn't call
+ release_input_file, we close it here. LLVM plugin, which
+ needs fd after plugin_call_claim_file and calls
+ release_input_file after it is done, uses BFD plugin target
+ vector. This scheme doesn't work when a plugin needs fd and
+ doesn't use BFD plugin target vector neither. */
+ close (fd);
+ input->fd = -1;
+ }
+
+ if (claimed)
+ {
+ ibfd->plugin_format = bfd_plugin_yes;
+ ibfd->plugin_dummy_bfd = abfd;
+ bfd_make_readable (abfd);
+ return abfd->xvec;
+ }
+ else
+ {
+#if HAVE_MMAP
+ if (input->use_mmap)
+ {
+ /* If plugin didn't claim the file, unmap the buffer. */
+ char *addr = input->view_buffer.addr;
+ off_t size = input->view_buffer.filesize;
+# if HAVE_GETPAGESIZE
+ off_t bias = input->view_buffer.offset % plugin_pagesize;
+ size += bias;
+ addr -= bias;
+# endif
+ munmap (addr, size);
+ }
+#endif
+
+ /* If plugin didn't claim the file, we don't need the dummy bfd.
+ Can't avoid speculatively creating it, alas. */
+ ibfd->plugin_format = bfd_plugin_no;
+ bfd_close_all_done (abfd);
+ return NULL;
+ }
+}
+
+void
+plugin_maybe_claim (lang_input_statement_type *entry)
+{
+ if (plugin_object_p (entry->the_bfd))
+ {
+ bfd *abfd = entry->the_bfd->plugin_dummy_bfd;
+
+ /* Discard the real file's BFD and substitute the dummy one. */
+
+ /* BFD archive handling caches elements so we can't call
+ bfd_close for archives. */
+ if (entry->the_bfd->my_archive == NULL)
+ bfd_close (entry->the_bfd);
+ entry->the_bfd = abfd;
+ entry->flags.claimed = 1;
+ }
+}
+