/* Plugin support for BFD.
- Copyright (C) 2009-2018 Free Software Foundation, Inc.
+ Copyright (C) 2009-2020 Free Software Foundation, Inc.
This file is part of BFD, the Binary File Descriptor library.
#define bfd_plugin_bfd_lookup_section_flags bfd_generic_lookup_section_flags
#define bfd_plugin_bfd_merge_sections bfd_generic_merge_sections
#define bfd_plugin_bfd_is_group_section bfd_generic_is_group_section
+#define bfd_plugin_bfd_group_name bfd_generic_group_name
#define bfd_plugin_bfd_discard_group bfd_generic_discard_group
#define bfd_plugin_section_already_linked _bfd_generic_section_already_linked
#define bfd_plugin_bfd_define_common_symbol bfd_generic_define_common_symbol
return LDPS_OK;
}
+struct plugin_list_entry
+{
+ /* These must be initialized for each IR object with LTO wrapper. */
+ ld_plugin_claim_file_handler claim_file;
+ ld_plugin_all_symbols_read_handler all_symbols_read;
+ ld_plugin_all_symbols_read_handler cleanup_handler;
+ bfd_boolean has_symbol_type;
+
+ struct plugin_list_entry *next;
+
+ /* These can be reused for all IR objects. */
+ const char *plugin_name;
+};
+
+static const char *plugin_program_name;
+
+void
+bfd_plugin_set_program_name (const char *program_name)
+{
+ plugin_program_name = program_name;
+}
+
+static struct plugin_list_entry *plugin_list = NULL;
+static struct plugin_list_entry *current_plugin = NULL;
+
/* Register a claim-file handler. */
-static ld_plugin_claim_file_handler claim_file = NULL;
static enum ld_plugin_status
register_claim_file (ld_plugin_claim_file_handler handler)
{
- claim_file = handler;
+ current_plugin->claim_file = handler;
return LDPS_OK;
}
struct plugin_data_struct *plugin_data =
bfd_alloc (abfd, sizeof (plugin_data_struct));
+ if (!plugin_data)
+ return LDPS_ERR;
+
plugin_data->nsyms = nsyms;
plugin_data->syms = syms;
return LDPS_OK;
}
-static const char *plugin_program_name;
-
-void
-bfd_plugin_set_program_name (const char *program_name)
+static enum ld_plugin_status
+add_symbols_v2 (void *handle, int nsyms,
+ const struct ld_plugin_symbol *syms)
{
- plugin_program_name = program_name;
+ current_plugin->has_symbol_type = TRUE;
+ return add_symbols (handle, nsyms, syms);
}
int
struct ld_plugin_input_file file;
file.handle = abfd;
- if (!bfd_plugin_open_input (abfd, &file))
- return 0;
- if (claim_file)
- claim_file (&file, &claimed);
- close (file.fd);
+ if (bfd_plugin_open_input (abfd, &file)
+ && current_plugin->claim_file)
+ {
+ current_plugin->claim_file (&file, &claimed);
+ close (file.fd);
+ }
+
return claimed;
}
static int
-try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
+try_load_plugin (const char *pname,
+ struct plugin_list_entry *plugin_list_iter,
+ bfd *abfd, bfd_boolean build_list_p)
{
- void *plugin_handle = NULL;
- struct ld_plugin_tv tv[4];
+ void *plugin_handle;
+ struct ld_plugin_tv tv[5];
int i;
ld_plugin_onload onload;
enum ld_plugin_status status;
+ int result = 0;
- if (claim_file)
- goto have_claim_file;
+ /* NB: Each object is independent. Reuse the previous plugin from
+ the last run will lead to wrong result. */
+ if (current_plugin)
+ memset (current_plugin, 0,
+ offsetof (struct plugin_list_entry, next));
- *has_plugin_p = 0;
+ if (plugin_list_iter)
+ pname = plugin_list_iter->plugin_name;
plugin_handle = dlopen (pname, RTLD_NOW);
if (!plugin_handle)
return 0;
}
+ if (plugin_list_iter == NULL)
+ {
+ size_t length_plugin_name = strlen (pname) + 1;
+ char *plugin_name = bfd_malloc (length_plugin_name);
+ if (plugin_name == NULL)
+ goto short_circuit;
+ plugin_list_iter = bfd_malloc (sizeof *plugin_list_iter);
+ if (plugin_list_iter == NULL)
+ {
+ free (plugin_name);
+ goto short_circuit;
+ }
+ /* Make a copy of PNAME since PNAME from load_plugin () will be
+ freed. */
+ memcpy (plugin_name, pname, length_plugin_name);
+ memset (plugin_list_iter, 0, sizeof (*plugin_list_iter));
+ plugin_list_iter->plugin_name = plugin_name;
+ plugin_list_iter->next = plugin_list;
+ plugin_list = plugin_list_iter;
+ }
+
+ current_plugin = plugin_list_iter;
+ if (build_list_p)
+ goto short_circuit;
+
onload = dlsym (plugin_handle, "onload");
if (!onload)
- goto err;
+ goto short_circuit;
i = 0;
tv[i].tv_tag = LDPT_MESSAGE;
tv[i].tv_tag = LDPT_ADD_SYMBOLS;
tv[i].tv_u.tv_add_symbols = add_symbols;
+ ++i;
+ tv[i].tv_tag = LDPT_ADD_SYMBOLS_V2;
+ tv[i].tv_u.tv_add_symbols = add_symbols_v2;
+
++i;
tv[i].tv_tag = LDPT_NULL;
tv[i].tv_u.tv_val = 0;
+ /* LTO plugin will call handler hooks to set up plugin handlers. */
status = (*onload)(tv);
if (status != LDPS_OK)
- goto err;
-
-have_claim_file:
- *has_plugin_p = 1;
+ goto short_circuit;
abfd->plugin_format = bfd_plugin_no;
- if (!claim_file)
- goto err;
+ if (!current_plugin->claim_file)
+ goto short_circuit;
if (!try_claim (abfd))
- goto err;
+ goto short_circuit;
abfd->plugin_format = bfd_plugin_yes;
+ result = 1;
- return 1;
-
- err:
- if (plugin_handle)
- dlclose (plugin_handle);
- register_claim_file (NULL);
- return 0;
+ short_circuit:
+ dlclose (plugin_handle);
+ return result;
}
/* There may be plugin libraries in lib/bfd-plugins. */
+static int has_plugin_list = -1;
-static int has_plugin = -1;
-
-static const bfd_target *(*ld_plugin_object_p) (bfd *);
+static bfd_cleanup (*ld_plugin_object_p) (bfd *);
static const char *plugin_name;
bfd_plugin_set_plugin (const char *p)
{
plugin_name = p;
- has_plugin = p != NULL;
}
/* Return TRUE if a plugin library is used. */
bfd_boolean
bfd_plugin_specified_p (void)
{
- return has_plugin > 0;
+ return plugin_list != NULL;
}
/* Return TRUE if ABFD can be claimed by linker LTO plugin. */
/* Register OBJECT_P to be used by bfd_plugin_object_p. */
void
-register_ld_plugin_object_p (const bfd_target *(*object_p) (bfd *))
+register_ld_plugin_object_p (bfd_cleanup (*object_p) (bfd *))
{
ld_plugin_object_p = object_p;
}
+static void
+build_plugin_list (bfd *abfd)
+{
+ /* The intent was to search ${libdir}/bfd-plugins for plugins, but
+ unfortunately the original implementation wasn't precisely that
+ when configuring binutils using --libdir. Search in the proper
+ path first, then the old one for backwards compatibility. */
+ static const char *path[]
+ = { LIBDIR "/bfd-plugins", BINDIR "/../lib/bfd-plugins" };
+ struct stat last_st;
+ unsigned int i;
+
+ if (has_plugin_list >= 0)
+ return;
+
+ /* Try not to search the same dir twice, by looking at st_dev and
+ st_ino for the dir. If we are on a file system that always sets
+ st_ino to zero or the actual st_ino is zero we might waste some
+ time, but that doesn't matter too much. */
+ last_st.st_dev = 0;
+ last_st.st_ino = 0;
+ for (i = 0; i < sizeof (path) / sizeof (path[0]); i++)
+ {
+ char *plugin_dir = make_relative_prefix (plugin_program_name,
+ BINDIR,
+ path[i]);
+ if (plugin_dir)
+ {
+ struct stat st;
+ DIR *d;
+
+ if (stat (plugin_dir, &st) == 0
+ && S_ISDIR (st.st_mode)
+ && !(last_st.st_dev == st.st_dev
+ && last_st.st_ino == st.st_ino
+ && st.st_ino != 0)
+ && (d = opendir (plugin_dir)) != NULL)
+ {
+ struct dirent *ent;
+
+ last_st.st_dev = st.st_dev;
+ last_st.st_ino = st.st_ino;
+ while ((ent = readdir (d)) != NULL)
+ {
+ char *full_name;
+
+ full_name = concat (plugin_dir, "/", ent->d_name, NULL);
+ if (stat (full_name, &st) == 0 && S_ISREG (st.st_mode))
+ try_load_plugin (full_name, NULL, abfd, TRUE);
+ free (full_name);
+ }
+ closedir (d);
+ }
+ free (plugin_dir);
+ }
+ }
+
+ has_plugin_list = plugin_list != NULL;
+}
+
static int
load_plugin (bfd *abfd)
{
- char *plugin_dir;
- char *p;
- DIR *d;
- struct dirent *ent;
- int found = 0;
-
- if (!has_plugin)
- return found;
+ struct plugin_list_entry *plugin_list_iter;
if (plugin_name)
- return try_load_plugin (plugin_name, abfd, &has_plugin);
+ return try_load_plugin (plugin_name, plugin_list, abfd, FALSE);
if (plugin_program_name == NULL)
- return found;
-
- plugin_dir = concat (BINDIR, "/../lib/bfd-plugins", NULL);
- p = make_relative_prefix (plugin_program_name,
- BINDIR,
- plugin_dir);
- free (plugin_dir);
- plugin_dir = NULL;
-
- d = opendir (p);
- if (!d)
- goto out;
+ return 0;
- while ((ent = readdir (d)))
- {
- char *full_name;
- struct stat s;
- int valid_plugin;
-
- full_name = concat (p, "/", ent->d_name, NULL);
- if (stat (full_name, &s) == 0 && S_ISREG (s.st_mode))
- found = try_load_plugin (full_name, abfd, &valid_plugin);
- if (has_plugin <= 0)
- has_plugin = valid_plugin;
- free (full_name);
- if (found)
- break;
- }
+ build_plugin_list (abfd);
- out:
- free (p);
- if (d)
- closedir (d);
+ for (plugin_list_iter = plugin_list;
+ plugin_list_iter;
+ plugin_list_iter = plugin_list_iter->next)
+ if (try_load_plugin (NULL, plugin_list_iter, abfd, FALSE))
+ return 1;
- return found;
+ return 0;
}
-static const bfd_target *
+static bfd_cleanup
bfd_plugin_object_p (bfd *abfd)
{
if (ld_plugin_object_p)
if (abfd->plugin_format == bfd_plugin_unknown && !load_plugin (abfd))
return NULL;
- return abfd->plugin_format == bfd_plugin_yes ? abfd->xvec : NULL;
+ return abfd->plugin_format == bfd_plugin_yes ? _bfd_no_cleanup : NULL;
}
/* Copy any private info we understand from the input bfd
struct plugin_data_struct *plugin_data = abfd->tdata.plugin_data;
long nsyms = plugin_data->nsyms;
const struct ld_plugin_symbol *syms = plugin_data->syms;
- static asection fake_section;
- static asection fake_common_section;
+ static asection fake_text_section
+ = BFD_FAKE_SECTION (fake_text_section, NULL, "plug", 0,
+ SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS);
+ static asection fake_data_section
+ = BFD_FAKE_SECTION (fake_data_section, NULL, "plug", 0,
+ SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_HAS_CONTENTS);
+ static asection fake_bss_section
+ = BFD_FAKE_SECTION (fake_bss_section, NULL, "plug", 0,
+ SEC_ALLOC);
+ static asection fake_common_section
+ = BFD_FAKE_SECTION (fake_common_section, NULL, "plug", 0, SEC_IS_COMMON);
int i;
- fake_section.name = ".text";
- fake_common_section.flags = SEC_IS_COMMON;
-
for (i = 0; i < nsyms; i++)
{
asymbol *s = bfd_alloc (abfd, sizeof (asymbol));
break;
case LDPK_DEF:
case LDPK_WEAKDEF:
- s->section = &fake_section;
+ if (current_plugin->has_symbol_type)
+ switch (syms[i].symbol_type)
+ {
+ case LDST_UNKNOWN:
+ /* What is the best fake section for LDST_UNKNOWN? */
+ case LDST_FUNCTION:
+ s->section = &fake_text_section;
+ break;
+ case LDST_VARIABLE:
+ if (syms[i].section_kind == LDSSK_BSS)
+ s->section = &fake_bss_section;
+ else
+ s->section = &fake_data_section;
+ break;
+ }
+ else
+ s->section = &fake_text_section;
break;
default:
BFD_ASSERT (0);