/* Plugin control for the GNU linker.
- Copyright (C) 2010-2019 Free Software Foundation, Inc.
+ Copyright (C) 2010-2024 Free Software Foundation, Inc.
This file is part of the GNU Binutils.
#include "sysdep.h"
#include "libiberty.h"
#include "bfd.h"
+#if BFD_SUPPORTS_PLUGINS
#include "bfdlink.h"
#include "bfdver.h"
+#include "ctf-api.h"
#include "ld.h"
#include "ldmain.h"
#include "ldmisc.h"
#if !(defined(errno) || defined(_MSC_VER) && defined(_INC_ERRNO))
extern int errno;
#endif
-#if !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H)
+#if defined (HAVE_DLFCN_H)
+#include <dlfcn.h>
+#elif defined (HAVE_WINDOWS_H)
#include <windows.h>
#endif
/* Report plugin symbols. */
-bfd_boolean report_plugin_symbols;
+bool report_plugin_symbols;
/* The suffix to append to the name of the real (claimed) object file
when generating a dummy BFD to hold the IR symbols sent from the
size_t n_args;
/* The plugin's event handlers. */
ld_plugin_claim_file_handler claim_file_handler;
+ ld_plugin_claim_file_handler_v2 claim_file_handler_v2;
ld_plugin_all_symbols_read_handler all_symbols_read_handler;
ld_plugin_cleanup_handler cleanup_handler;
/* TRUE if the cleanup handlers have been called. */
- bfd_boolean cleanup_done;
+ bool cleanup_done;
} plugin_t;
typedef struct view_buffer
pointer. */
typedef struct plugin_input_file
{
+ /* The dummy BFD. */
bfd *abfd;
+ /* The original input BFD. Non-NULL if it is an archive member. */
+ bfd *ibfd;
view_buffer_t view_buffer;
char *name;
int fd;
- bfd_boolean use_mmap;
+ bool use_mmap;
off_t offset;
off_t filesize;
} plugin_input_file_t;
static const char *error_plugin = NULL;
/* State of linker "notice" interface before we poked at it. */
-static bfd_boolean orig_notice_all;
+static bool orig_notice_all;
/* Original linker callbacks, and the plugin version. */
static const struct bfd_link_callbacks *orig_callbacks;
/* Set at all symbols read time, to avoid recursively offering the plugin
its own newly-added input files and libs to claim. */
-bfd_boolean no_more_claiming = FALSE;
+bool no_more_claiming = false;
#if HAVE_MMAP && HAVE_GETPAGESIZE
/* Page size used by mmap. */
LDPT_LINKER_OUTPUT,
LDPT_OUTPUT_NAME,
LDPT_REGISTER_CLAIM_FILE_HOOK,
+ LDPT_REGISTER_CLAIM_FILE_HOOK_V2,
LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
LDPT_REGISTER_CLEANUP_HOOK,
LDPT_ADD_SYMBOLS,
static const size_t tv_header_size = ARRAY_SIZE (tv_header_tags);
/* Forward references. */
-static bfd_boolean plugin_notice (struct bfd_link_info *,
- struct bfd_link_hash_entry *,
- struct bfd_link_hash_entry *,
- bfd *, asection *, bfd_vma, flagword);
+static bool plugin_notice (struct bfd_link_info *,
+ struct bfd_link_hash_entry *,
+ struct bfd_link_hash_entry *,
+ bfd *, asection *, bfd_vma, flagword);
-static const bfd_target * plugin_object_p (bfd *);
+static bfd_cleanup plugin_object_p (bfd *, bool);
#if !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H)
}
/* Test if an error occurred. */
-static bfd_boolean
+static bool
plugin_error_p (void)
{
return error_plugin != NULL;
plugin_get_ir_dummy_bfd (const char *name, bfd *srctemplate)
{
bfd *abfd;
- bfd_boolean bfd_plugin_target;
+ bool bfd_plugin_target;
bfd_use_reserved_id = 1;
bfd_plugin_target = bfd_plugin_target_p (srctemplate->xvec);
return abfd;
}
}
-report_error:
+ report_error:
einfo (_("%F%P: could not create dummy IR bfd: %E\n"));
return NULL;
}
/* Check if the BFD passed in is an IR dummy object file. */
-static inline bfd_boolean
+static inline bool
is_ir_dummy_bfd (const bfd *abfd)
{
/* ABFD can sometimes legitimately be NULL, e.g. when called from one
flags = BSF_GLOBAL;
section = bfd_com_section_ptr;
asym->value = ldsym->size;
- /* For ELF targets, set alignment of common symbol to 1. */
- if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
- {
- ((elf_symbol_type *) asym)->internal_elf_sym.st_shndx = SHN_COMMON;
- ((elf_symbol_type *) asym)->internal_elf_sym.st_value = 1;
- }
break;
default:
asym->flags = flags;
asym->section = section;
- /* Visibility only applies on ELF targets. */
if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
{
- elf_symbol_type *elfsym = elf_symbol_from (abfd, asym);
+ elf_symbol_type *elfsym = elf_symbol_from (asym);
unsigned char visibility;
if (!elfsym)
einfo (_("%F%P: %s: non-ELF symbol in ELF BFD!\n"), asym->name);
+
+ if (ldsym->def == LDPK_COMMON)
+ {
+ elfsym->internal_elf_sym.st_shndx = SHN_COMMON;
+ elfsym->internal_elf_sym.st_value = 1;
+ }
+
switch (ldsym->visibility)
{
default:
visibility = STV_HIDDEN;
break;
}
- elfsym->internal_elf_sym.st_other
- = (visibility | (elfsym->internal_elf_sym.st_other
- & ~ELF_ST_VISIBILITY (-1)));
+ elfsym->internal_elf_sym.st_other |= visibility;
}
return LDPS_OK;
return LDPS_OK;
}
+/* Register a claim-file version 2 handler. */
+static enum ld_plugin_status
+register_claim_file_v2 (ld_plugin_claim_file_handler_v2 handler)
+{
+ ASSERT (called_plugin);
+ called_plugin->claim_file_handler_v2 = handler;
+ return LDPS_OK;
+}
+
/* Register an all-symbols-read handler. */
static enum ld_plugin_status
register_all_symbols_read (ld_plugin_all_symbols_read_handler handler)
int n;
ASSERT (called_plugin);
- symptrs = xmalloc (nsyms * sizeof *symptrs);
+ symptrs = bfd_alloc (abfd, nsyms * sizeof *symptrs);
+ if (symptrs == NULL)
+ return LDPS_ERR;
for (n = 0; n < nsyms; n++)
{
enum ld_plugin_status rv;
bfdsym = bfd_make_empty_symbol (abfd);
symptrs[n] = bfdsym;
+ if (bfdsym == NULL)
+ return LDPS_ERR;
rv = asymbol_from_plugin_symbol (abfd, bfdsym, syms + n);
if (rv != LDPS_OK)
return rv;
buffer = mmap (NULL, size, PROT_READ, MAP_PRIVATE, input->fd, offset);
if (buffer != MAP_FAILED)
{
- input->use_mmap = TRUE;
+ input->use_mmap = true;
# if HAVE_GETPAGESIZE
buffer += bias;
# endif
{
char *p;
- input->use_mmap = FALSE;
+ input->use_mmap = false;
if (lseek (input->fd, offset, SEEK_SET) < 0)
return LDPS_ERR;
return LDPS_OK;
}
+/* Release plugin file descriptor. */
+
+static void
+release_plugin_file_descriptor (plugin_input_file_t *input)
+{
+ if (input->fd != -1)
+ {
+ bfd_plugin_close_file_descriptor (input->ibfd, input->fd);
+ input->fd = -1;
+ }
+}
+
/* Release the input file. */
static enum ld_plugin_status
release_input_file (const void *handle)
{
plugin_input_file_t *input = (plugin_input_file_t *) handle;
ASSERT (called_plugin);
- if (input->fd != -1)
- {
- close (input->fd);
- input->fd = -1;
- }
+ release_plugin_file_descriptor (input);
return LDPS_OK;
}
/* Return TRUE if a defined symbol might be reachable from outside the
universe of claimed objects. */
-static inline bfd_boolean
+static inline bool
is_visible_from_outside (struct ld_plugin_symbol *lsym,
struct bfd_link_hash_entry *blhe)
{
if (bfd_link_relocatable (&link_info))
- return TRUE;
+ return true;
if (blhe->non_ir_ref_dynamic
|| link_info.export_dynamic
|| bfd_link_dll (&link_info))
/* Check if symbol is hidden by version script. */
if (bfd_hide_sym_by_version (link_info.version_info,
blhe->root.string))
- return FALSE;
+ return false;
/* Only ELF symbols really have visibility. */
- if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour)
+ if (is_elf_hash_table (link_info.hash))
{
struct elf_link_hash_entry *el = (struct elf_link_hash_entry *)blhe;
int vis = ELF_ST_VISIBILITY (el->other);
|| lsym->visibility == LDPV_PROTECTED);
}
- return FALSE;
+ return false;
+}
+
+/* Return LTO kind string name that corresponds to IDX enum value. */
+static const char *
+get_lto_kind (unsigned int idx)
+{
+ static char buffer[64];
+ const char *lto_kind_str[5] =
+ {
+ "DEF",
+ "WEAKDEF",
+ "UNDEF",
+ "WEAKUNDEF",
+ "COMMON"
+ };
+
+ if (idx < ARRAY_SIZE (lto_kind_str))
+ return lto_kind_str [idx];
+
+ sprintf (buffer, _("unknown LTO kind value %x"), idx);
+ return buffer;
+}
+
+/* Return LTO resolution string name that corresponds to IDX enum value. */
+static const char *
+get_lto_resolution (unsigned int idx)
+{
+ static char buffer[64];
+ static const char *lto_resolution_str[10] =
+ {
+ "UNKNOWN",
+ "UNDEF",
+ "PREVAILING_DEF",
+ "PREVAILING_DEF_IRONLY",
+ "PREEMPTED_REG",
+ "PREEMPTED_IR",
+ "RESOLVED_IR",
+ "RESOLVED_EXEC",
+ "RESOLVED_DYN",
+ "PREVAILING_DEF_IRONLY_EXP",
+ };
+
+ if (idx < ARRAY_SIZE (lto_resolution_str))
+ return lto_resolution_str [idx];
+
+ sprintf (buffer, _("unknown LTO resolution value %x"), idx);
+ return buffer;
+}
+
+/* Return LTO visibility string name that corresponds to IDX enum value. */
+static const char *
+get_lto_visibility (unsigned int idx)
+{
+ static char buffer[64];
+ const char *lto_visibility_str[4] =
+ {
+ "DEFAULT",
+ "PROTECTED",
+ "INTERNAL",
+ "HIDDEN"
+ };
+
+ if (idx < ARRAY_SIZE (lto_visibility_str))
+ return lto_visibility_str [idx];
+
+ sprintf (buffer, _("unknown LTO visibility value %x"), idx);
+ return buffer;
}
/* Get the symbol resolution info for a plugin-claimed input file. */
struct bfd_link_hash_entry *blhe;
asection *owner_sec;
int res;
+ struct bfd_link_hash_entry *h
+ = bfd_link_hash_lookup (link_info.hash, syms[n].name,
+ false, false, true);
+ enum { wrap_none, wrapper, wrapped } wrap_status = wrap_none;
- if (syms[n].def != LDPK_UNDEF)
- blhe = bfd_link_hash_lookup (link_info.hash, syms[n].name,
- FALSE, FALSE, TRUE);
+ if (syms[n].def != LDPK_UNDEF && syms[n].def != LDPK_WEAKUNDEF)
+ {
+ blhe = h;
+ if (blhe && link_info.wrap_hash != NULL)
+ {
+ /* Check if a symbol is a wrapper symbol. */
+ struct bfd_link_hash_entry *unwrap
+ = unwrap_hash_lookup (&link_info, (bfd *) abfd, blhe);
+ if (unwrap && unwrap != h)
+ wrap_status = wrapper;
+ }
+ }
else
- blhe = bfd_wrapped_link_hash_lookup (link_info.output_bfd, &link_info,
- syms[n].name, FALSE, FALSE, TRUE);
+ {
+ blhe = bfd_wrapped_link_hash_lookup (link_info.output_bfd,
+ &link_info, syms[n].name,
+ false, false, true);
+ /* Check if a symbol is a wrapped symbol. */
+ if (blhe && blhe != h)
+ wrap_status = wrapped;
+ }
if (!blhe)
{
/* The plugin is called to claim symbols in an archive element
/* We need to know if the sym is referenced from non-IR files. Or
even potentially-referenced, perhaps in a future final link if
this is a partial one, perhaps dynamically at load-time if the
- symbol is externally visible. */
- if (blhe->non_ir_ref_regular)
+ symbol is externally visible. Also check for __real_SYM
+ reference and wrapper symbol. */
+ if (blhe->non_ir_ref_regular
+ || blhe->ref_real
+ || wrap_status == wrapper)
res = LDPR_PREVAILING_DEF;
+ else if (wrap_status == wrapped)
+ res = LDPR_RESOLVED_IR;
else if (is_visible_from_outside (&syms[n], blhe))
res = def_ironly_exp;
}
syms[n].resolution = res;
if (report_plugin_symbols)
einfo (_("%P: %pB: symbol `%s' "
- "definition: %d, visibility: %d, resolution: %d\n"),
+ "definition: %s, visibility: %s, resolution: %s\n"),
abfd, syms[n].name,
- syms[n].def, syms[n].visibility, res);
+ get_lto_kind (syms[n].def),
+ get_lto_visibility (syms[n].visibility),
+ get_lto_resolution (res));
}
return LDPS_OK;
}
set_extra_library_path (const char *path)
{
ASSERT (called_plugin);
- ldfile_add_library_path (xstrdup (path), FALSE);
+ ldfile_add_library_path (xstrdup (path), false);
return LDPS_OK;
}
switch (level)
{
case LDPL_INFO:
- vfinfo (stdout, format, args, FALSE);
+ vfinfo (stdout, format, args, false);
putchar ('\n');
break;
case LDPL_WARNING:
{
char *newfmt = concat (_("%P: warning: "), format, "\n",
(const char *) NULL);
- vfinfo (stdout, newfmt, args, TRUE);
+ vfinfo (stdout, newfmt, args, true);
free (newfmt);
}
break;
_("%P: error: "), format, "\n",
(const char *) NULL);
fflush (stdout);
- vfinfo (stderr, newfmt, args, TRUE);
+ vfinfo (stderr, newfmt, args, true);
fflush (stderr);
free (newfmt);
}
case LDPT_REGISTER_CLAIM_FILE_HOOK:
TVU(register_claim_file) = register_claim_file;
break;
+ case LDPT_REGISTER_CLAIM_FILE_HOOK_V2:
+ TVU(register_claim_file_v2) = register_claim_file_v2;
+ break;
case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
TVU(register_all_symbols_read) = register_all_symbols_read;
break;
orig_callbacks = link_info.callbacks;
plugin_callbacks = *orig_callbacks;
plugin_callbacks.notice = &plugin_notice;
- link_info.notice_all = TRUE;
- link_info.lto_plugin_active = TRUE;
+ link_info.notice_all = true;
+ link_info.lto_plugin_active = true;
link_info.callbacks = &plugin_callbacks;
register_ld_plugin_object_p (plugin_object_p);
/* Call 'claim file' hook for all plugins. */
static int
-plugin_call_claim_file (const struct ld_plugin_input_file *file, int *claimed)
+plugin_call_claim_file (const struct ld_plugin_input_file *file, int *claimed,
+ bool known_used)
{
plugin_t *curplug = plugins_list;
- *claimed = FALSE;
+ *claimed = false;
while (curplug && !*claimed)
{
if (curplug->claim_file_handler)
enum ld_plugin_status rv;
called_plugin = curplug;
- rv = (*curplug->claim_file_handler) (file, claimed);
+ if (curplug->claim_file_handler_v2)
+ rv = (*curplug->claim_file_handler_v2) (file, claimed, known_used);
+ else
+ rv = (*curplug->claim_file_handler) (file, claimed);
called_plugin = NULL;
if (rv != LDPS_OK)
set_plugin_error (curplug->name);
return copy;
}
-static const bfd_target *
-plugin_object_p (bfd *ibfd)
+static void
+plugin_cleanup (bfd *abfd ATTRIBUTE_UNUSED)
+{
+}
+
+static bfd_cleanup
+plugin_object_p (bfd *ibfd, bool known_used)
{
int claimed;
plugin_input_file_t *input;
if (ibfd->plugin_format != bfd_plugin_unknown)
{
if (ibfd->plugin_format == bfd_plugin_yes)
- return ibfd->plugin_dummy_bfd->xvec;
+ return plugin_cleanup;
else
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);
+ abfd = plugin_get_ir_dummy_bfd (bfd_get_filename (ibfd), ibfd);
input = bfd_alloc (abfd, sizeof (*input));
if (input == NULL)
if (!bfd_plugin_open_input (ibfd, &file))
return NULL;
- if (file.name == ibfd->filename)
+ if (file.name == bfd_get_filename (ibfd))
{
/* We must copy filename attached to ibfd if it is not an archive
member since it may be freed by bfd_close below. */
file.handle = input;
input->abfd = abfd;
+ input->ibfd = ibfd->my_archive != NULL ? ibfd : NULL;
input->view_buffer.addr = NULL;
input->view_buffer.filesize = 0;
input->view_buffer.offset = 0;
input->fd = file.fd;
- input->use_mmap = FALSE;
+ input->use_mmap = false;
input->offset = file.offset;
input->filesize = file.filesize;
- input->name = plugin_strdup (abfd, ibfd->filename);
+ input->name = plugin_strdup (abfd, bfd_get_filename (ibfd));
claimed = 0;
- if (plugin_call_claim_file (&file, &claimed))
+ if (plugin_call_claim_file (&file, &claimed, known_used))
einfo (_("%F%P: %s: plugin reported error claiming file\n"),
plugin_error_plugin ());
- if (input->fd != -1 && !bfd_plugin_target_p (ibfd->xvec))
+ if (input->fd != -1
+ && (!claimed || !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
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 (input->fd);
- input->fd = -1;
+ release_plugin_file_descriptor (input);
}
if (claimed)
ibfd->plugin_format = bfd_plugin_yes;
ibfd->plugin_dummy_bfd = abfd;
bfd_make_readable (abfd);
- return abfd->xvec;
+ abfd->no_export = ibfd->no_export;
+ return plugin_cleanup;
}
else
{
plugin_maybe_claim (lang_input_statement_type *entry)
{
ASSERT (entry->header.type == lang_input_statement_enum);
- if (plugin_object_p (entry->the_bfd))
+ if (plugin_object_p (entry->the_bfd, true))
{
bfd *abfd = entry->the_bfd->plugin_dummy_bfd;
plugin_t *curplug = plugins_list;
/* Disable any further file-claiming. */
- no_more_claiming = TRUE;
+ no_more_claiming = true;
while (curplug)
{
{
if (curplug->cleanup_handler && !curplug->cleanup_done)
{
- enum ld_plugin_status rv;
- curplug->cleanup_done = TRUE;
- called_plugin = curplug;
- rv = (*curplug->cleanup_handler) ();
- called_plugin = NULL;
- if (rv != LDPS_OK)
- info_msg (_("%P: %s: error in plugin cleanup: %d (ignored)\n"),
- curplug->name, rv);
+ if (!config.plugin_save_temps)
+ {
+ enum ld_plugin_status rv;
+ curplug->cleanup_done = true;
+ called_plugin = curplug;
+ rv = (*curplug->cleanup_handler) ();
+ called_plugin = NULL;
+ if (rv != LDPS_OK)
+ info_msg (_("%P: %s: error in plugin cleanup: %d (ignored)\n"),
+ curplug->name, rv);
+ }
dlclose (curplug->dlhandle);
}
curplug = curplug->next;
non_ir_ref_dynamic as appropriate. We have to notice_all symbols,
because we won't necessarily know until later which ones will be
contributed by IR files. */
-static bfd_boolean
+static bool
plugin_notice (struct bfd_link_info *info,
struct bfd_link_hash_entry *h,
struct bfd_link_hash_entry *inh,
if (h != NULL)
{
bfd *sym_bfd;
- bfd_boolean ref = FALSE;
+ bool ref = false;
if (h->type == bfd_link_hash_warning)
h = h->u.i.link;
|| inh->type == bfd_link_hash_new)
{
if ((abfd->flags & DYNAMIC) == 0)
- inh->non_ir_ref_regular = TRUE;
+ inh->non_ir_ref_regular = true;
else
- inh->non_ir_ref_dynamic = TRUE;
+ inh->non_ir_ref_dynamic = true;
}
if (h->type != bfd_link_hash_new)
- ref = TRUE;
+ ref = true;
}
/* Nothing to do here for warning symbols. */
&& (h->u.undef.abfd == NULL
|| (h->u.undef.abfd->flags & BFD_PLUGIN) != 0))
h->u.undef.abfd = abfd;
- ref = TRUE;
+ ref = true;
}
- /* Otherwise, it must be a new def. */
- else
+
+ /* A common symbol should be merged with other commons or
+ defs with the same name. In particular, a common ought
+ to be overridden by a def in a -flto object. In that
+ sense a common is also a ref. */
+ else if (bfd_is_com_section (section))
{
- /* Ensure any symbol defined in an IR dummy BFD takes on a
- new value from a real BFD. Weak symbols are not normally
- overridden by a new weak definition, and strong symbols
- will normally cause multiple definition errors. Avoid
- this by making the symbol appear to be undefined. */
- if (((h->type == bfd_link_hash_defweak
- || h->type == bfd_link_hash_defined)
- && is_ir_dummy_bfd (sym_bfd = h->u.def.section->owner))
- || (h->type == bfd_link_hash_common
- && is_ir_dummy_bfd (sym_bfd = h->u.c.p->section->owner)))
+ if (h->type == bfd_link_hash_common
+ && is_ir_dummy_bfd (sym_bfd = h->u.c.p->section->owner))
{
h->type = bfd_link_hash_undefweak;
h->u.undef.abfd = sym_bfd;
}
+ ref = true;
+ }
- /* A common symbol should be merged with other commons or
- defs with the same name. In particular, a common ought
- to be overridden by a def in a -flto object. In that
- sense a common is also a ref. */
- if (bfd_is_com_section (section))
- ref = TRUE;
+ /* Otherwise, it must be a new def.
+ Ensure any symbol defined in an IR dummy BFD takes on a
+ new value from a real BFD. Weak symbols are not normally
+ overridden by a new weak definition, and strong symbols
+ will normally cause multiple definition errors. Avoid
+ this by making the symbol appear to be undefined.
+
+ NB: We change the previous definition in the IR object to
+ undefweak only after all LTO symbols have been read or for
+ non-ELF targets. */
+ else if ((info->lto_all_symbols_read
+ || bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+ && (((h->type == bfd_link_hash_defweak
+ || h->type == bfd_link_hash_defined)
+ && is_ir_dummy_bfd (sym_bfd = h->u.def.section->owner))
+ || (h->type == bfd_link_hash_common
+ && is_ir_dummy_bfd (sym_bfd = h->u.c.p->section->owner))))
+ {
+ h->type = bfd_link_hash_undefweak;
+ h->u.undef.abfd = sym_bfd;
}
if (ref)
{
if ((abfd->flags & DYNAMIC) == 0)
- h->non_ir_ref_regular = TRUE;
+ h->non_ir_ref_regular = true;
else
- h->non_ir_ref_dynamic = TRUE;
+ h->non_ir_ref_dynamic = true;
}
}
|| orig_notice_all
|| (info->notice_hash != NULL
&& bfd_hash_lookup (info->notice_hash, orig_h->root.string,
- FALSE, FALSE) != NULL))
+ false, false) != NULL))
return (*orig_callbacks->notice) (info, orig_h, inh,
abfd, section, value, flags);
- return TRUE;
+ return true;
}
+#endif /* BFD_SUPPORTS_PLUGINS */