This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH] Add plugin interface to LD [3/4] Get symbols and resolutions.
- From: Dave Korn <dave dot korn dot cygwin at gmail dot com>
- To: "binutils at sourceware dot org" <binutils at sourceware dot org>
- Date: Thu, 23 Sep 2010 06:31:58 +0100
- Subject: [PATCH] Add plugin interface to LD [3/4] Get symbols and resolutions.
- References: <4C9AE5CA.80707@gmail.com>
This one works by using the bfd linker's "notice symbol" callback to track
all symbols that are referenced by non-IR files, so that we can later
determine for prevailing defs from IR files whether or not they are IR-only.
It's pretty simple and the only minor trickiness is turing on notice_all
without inadvertently behaving like we've invoked --trace-sym on the whole world.
- As with the asymbol conversion in the previous patch, I'd appreciate extra
eyeballs looking at the logic here in get_symbols by which I determine
LDPR_xxx resolution types from the info in the linker hash table, just to
double-check my working.
ld/ChangeLog:
* ldmain.c (notice)[ENABLE_PLUGINS]: Call plugin_notice.
* plugin.c (non_ironly_hash): Add new bfd hash table.
(plugin_load_plugins): Exit early if no plugins to load. If plugins
do load successfully, set notice_all flag in link info.
(add_symbols): Clean up for-loop slightly.
(get_symbols): Implement.
(init_non_ironly_hash): Lazily init non_ironly_hash table.
(plugin_notice): Record symbols referenced from non-IR files in the
non_ironly_hash. Suppress tracing, cref generation and nocrossrefs
tracking for symbols from dummy IR bfds.
* plugin.h: Fix formatting.
(plugin_notice): Add prototype.
* testplug.c (dumpresolutions): New global var.
(parse_options): Accept "dumpresolutions".
(onall_symbols_read): Get syms and dump resolutions if it was given.
ld/testsuite/ChangeLog:
* ld-plugin/plugin-8.d: New testcase.
* ld-plugin/plugin.exp: Invoke it.
cheers,
DaveK
>From f09bc8ba9165c81d82a8a027129601867b93edd9 Mon Sep 17 00:00:00 2001
From: Dave Korn <dave.korn.cygwin@gmail.com>
Date: Wed, 22 Sep 2010 04:37:28 +0100
Subject: [PATCH] Implement get symbols callback.
ld/ChangeLog:
* ldmain.c (notice)[ENABLE_PLUGINS]: Call plugin_notice.
* plugin.c (IRONLY_SUFFIX_LEN): Correct off-by-one in definition.
(non_ironly_hash): Add new bfd hash table.
(plugin_load_plugins): Exit early if no plugins to load. If plugins
do load successfully, set notice_all flag in link info.
(add_symbols): Clean up for-loop slightly.
(get_symbols): Implement.
(init_non_ironly_hash): Lazily init non_ironly_hash table.
(plugin_notice): Record symbols referenced from non-IR files in the
non_ironly_hash. Suppress tracing, cref generation and nocrossrefs
tracking for symbols from dummy IR bfds.
* plugin.h: Fix formatting.
(plugin_notice): Add prototype.
* testplug.c (dumpresolutions): New global var.
(parse_options): Accept "dumpresolutions".
(onall_symbols_read): Get syms and dump resolutions if it was given.
ld/testsuite/ChangeLog:
* ld-plugin/plugin-8.d: New testcase.
* ld-plugin/plugin.exp: Invoke it.
---
ld/ldmain.c | 19 ++++-
ld/plugin.c | 149 +++++++++++++++++++++++++++++++++++--
ld/plugin.h | 7 ++-
ld/testplug.c | 36 +++++++++
ld/testsuite/ld-plugin/plugin-8.d | 34 +++++++++
ld/testsuite/ld-plugin/plugin.exp | 6 ++
6 files changed, 238 insertions(+), 13 deletions(-)
diff --git a/ld/ldmain.c b/ld/ldmain.c
index 85236ed..ed1738e 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -1380,7 +1380,10 @@ unattached_reloc (struct bfd_link_info *info ATTRIBUTE_UNUSED,
/* This is called if link_info.notice_all is set, or when a symbol in
link_info.notice_hash is found. Symbols are put in notice_hash
- using the -y option. */
+ using the -y option, while notice_all is set if the --cref option
+ has been supplied, or if there are any NOCROSSREFS sections in the
+ linker script; and if plugins are active, since they need to monitor
+ all references from non-IR files. */
static bfd_boolean
notice (struct bfd_link_info *info,
@@ -1396,9 +1399,17 @@ notice (struct bfd_link_info *info,
return TRUE;
}
- if (! info->notice_all
- || (info->notice_hash != NULL
- && bfd_hash_lookup (info->notice_hash, name, FALSE, FALSE) != NULL))
+#ifdef ENABLE_PLUGINS
+ /* We should hide symbols in the dummy IR BFDs from the nocrossrefs list
+ and let the real object files that are generated and added later trip
+ the error instead. Similarly would be better to trace the real symbol
+ from the real file than the temporary dummy. */
+ if (!plugin_notice (info, name, abfd, section, value))
+ return TRUE;
+#endif /* ENABLE_PLUGINS */
+
+ if (info->notice_hash != NULL
+ && bfd_hash_lookup (info->notice_hash, name, FALSE, FALSE) != NULL)
{
if (bfd_is_und_section (section))
einfo ("%B: reference to %s\n", abfd, name);
diff --git a/ld/plugin.c b/ld/plugin.c
index 6c71978..4e8505d 100644
--- a/ld/plugin.c
+++ b/ld/plugin.c
@@ -55,8 +55,10 @@ PLUGAPIFUNC set_extra_library_path (const char *path);
when generating a dummy BFD to hold the IR symbols sent from the
plugin. */
#define IRONLY_SUFFIX ".ironly\004"
-/* This is sizeof an array of chars, not sizeof a const char *. */
-#define IRONLY_SUFFIX_LEN (sizeof (IRONLY_SUFFIX))
+
+/* This is sizeof an array of chars, not sizeof a const char *. We
+ also have to avoid inadvertently counting the trailing NUL. */
+#define IRONLY_SUFFIX_LEN (sizeof (IRONLY_SUFFIX) - 1)
/* Always use this macro when invoking a plugin function. */
#define INVOKE_PLUGIN_FN(plugin, retval, fn, args) \
@@ -110,6 +112,13 @@ static plugin_t *called_plugin = NULL;
/* Last plugin to cause an error, if any. */
static const char *error_plugin = NULL;
+/* A hash table that records symbols referenced by non-IR files. Used
+ at get_symbols time to determine whether any prevailing defs from
+ IR files are referenced only from other IR files, so tthat we can
+ we can distinguish the LDPR_PREVAILING_DEF and LDPR_PREVAILING_DEF_IRONLY
+ cases when establishing symbol resolutions. */
+static struct bfd_hash_table *non_ironly_hash = NULL;
+
/* Helper to size leading part of tv array and set it up. */
static size_t
set_tv_header (struct ld_plugin_tv *tv)
@@ -292,6 +301,10 @@ int plugin_load_plugins (void)
unsigned int max_args = 0;
plugin_t *curplug = plugins_list;
+ /* If there are no plugins, we need do nothing this run. */
+ if (!curplug)
+ return 0;
+
/* First pass over plugins to find max # args needed so that we
can size and allocate the tv array. */
while (curplug)
@@ -319,6 +332,12 @@ int plugin_load_plugins (void)
return set_plugin_error (curplug->name);
curplug = curplug->next;
}
+
+ /* Since plugin(s) inited ok, assume they're going to want symbol
+ resolutions, which needs us to track which symbols are referenced
+ by non-IR files using the linker's notice callback. */
+ link_info.notice_all = TRUE;
+
return 0;
}
@@ -489,12 +508,12 @@ add_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms)
int n;
ASSERT (called_plugin);
symptrs = xmalloc (nsyms * sizeof *symptrs);
- for (n = 0; n < nsyms; n++)
+ for (n = 0; n < nsyms; n++, syms++)
{
enum ld_plugin_status rv;
asymbol *bfdsym = bfd_make_empty_symbol (abfd);
symptrs[n] = bfdsym;
- rv = asymbol_from_plugin_symbol (abfd, bfdsym, syms++);
+ rv = asymbol_from_plugin_symbol (abfd, bfdsym, syms);
if (rv != LDPS_OK)
return rv;
}
@@ -526,11 +545,71 @@ release_input_file (const void *handle)
static enum ld_plugin_status
get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
{
+ const bfd *abfd = handle;
+ int n;
ASSERT (called_plugin);
- handle = handle;
- nsyms = nsyms;
- syms = syms;
- return LDPS_ERR;
+ for (n = 0; n < nsyms; n++)
+ {
+ struct bfd_link_hash_entry *blhe;
+ blhe = bfd_link_hash_lookup (link_info.hash, syms[n].name,
+ FALSE, FALSE, TRUE);
+ if (!blhe)
+ {
+ syms[n].resolution = LDPR_UNKNOWN;
+ continue;
+ }
+ /* Determine resolution from blhe type and symbol's original type. */
+ if (blhe->type == bfd_link_hash_undefined
+ || blhe->type == bfd_link_hash_undefweak)
+ {
+ syms[n].resolution = LDPR_UNDEF;
+ continue;
+ }
+ if (blhe->type == bfd_link_hash_common)
+ {
+ /* Is this right? The COMMON section only exists in the
+ output file, so it seems reasonable. */
+ syms[n].resolution = LDPR_RESOLVED_EXEC;
+ continue;
+ }
+ if (blhe->type != bfd_link_hash_defined
+ && blhe->type != bfd_link_hash_defweak)
+ {
+ /* We should not have a new, indirect or warning symbol here. */
+ einfo ("%P%F: %s: plugin symbol table corrupt (sym type %d)",
+ called_plugin->name, blhe->type);
+ }
+ /* Defined or defweak. If it was originally undefined, then it
+ has been resolved; determine how. */
+ if (syms[n].def == LDPK_UNDEF || syms[n].def == LDPK_WEAKUNDEF)
+ {
+ if (is_ir_dummy_bfd (blhe->u.def.section->owner))
+ syms[n].resolution = LDPR_RESOLVED_IR;
+ else if (blhe->u.def.section->owner == link_info.output_bfd)
+ syms[n].resolution = LDPR_RESOLVED_EXEC;
+ else
+ syms[n].resolution = blhe->u.def.section->owner->flags
+ & DYNAMIC ? LDPR_RESOLVED_DYN : LDPR_RESOLVED_EXEC;
+ continue;
+ }
+ /* Was originally def, weakdef, or common. Does it prevail? If
+ the owner is the original dummy bfd that supplied it, then this
+ is the definition that has prevailed. */
+ if (blhe->u.def.section->owner == abfd)
+ {
+ bfd_boolean ironly = !bfd_hash_lookup (non_ironly_hash,
+ syms[n].name, FALSE, FALSE);
+ syms[n].resolution = (ironly)
+ ? LDPR_PREVAILING_DEF_IRONLY
+ : LDPR_PREVAILING_DEF;
+ continue;
+ }
+ /* Was originally def, weakdef, or common, but has been pre-empted. */
+ syms[n].resolution = is_ir_dummy_bfd (blhe->u.def.section->owner)
+ ? LDPR_PREEMPTED_IR
+ : LDPR_PREEMPTED_REG;
+ }
+ return LDPS_OK;
}
/* Add a new (real) input file generated by a plugin. */
@@ -593,3 +672,57 @@ message (int level, const char *format, ...)
return LDPS_OK;
}
+/* Lazily init the non_ironly hash table. */
+static void
+init_non_ironly_hash (void)
+{
+ if (non_ironly_hash == NULL)
+ {
+ non_ironly_hash =
+ (struct bfd_hash_table *) xmalloc (sizeof (struct bfd_hash_table));
+ if (!bfd_hash_table_init_n (non_ironly_hash,
+ bfd_hash_newfunc,
+ sizeof (struct bfd_hash_entry),
+ 61))
+ einfo (_("%P%F: bfd_hash_table_init failed: %E\n"));
+ }
+}
+
+/* To determine which symbols should be resolved LDPR_PREVAILING_DEF
+ and which LDPR_PREVAILING_DEF_IRONLY, we notice all the symbols as
+ the linker adds them to the linker hash table. If we see a symbol
+ being referenced from a non-IR file, we add it to the non_ironly hash
+ table. If we can't find it there at get_symbols time, we know that
+ it was referenced only by IR files. We have to notice_all symbols,
+ because we won't necessarily know until later which ones will be
+ contributed by IR files. */
+bfd_boolean
+plugin_notice (struct bfd_link_info *info, const char *name, bfd *abfd,
+ asection *section, bfd_vma value)
+{
+ bfd_boolean is_ref = bfd_is_und_section (section);
+ bfd_boolean is_dummy = is_ir_dummy_bfd (abfd);
+ init_non_ironly_hash ();
+ /* We only care about refs, not defs, indicated by section pointing
+ to the undefined section (according to the bfd linker notice callback
+ interface definition). */
+ if (is_ref && !is_dummy)
+ {
+ /* This is a ref from a non-IR file, so note the ref'd symbol
+ in the non-IR-only hash. */
+ if (!bfd_hash_lookup (non_ironly_hash, name, TRUE, TRUE))
+ einfo (_("%P%X: %s: hash table failure adding symbol %s"),
+ abfd->filename, name);
+ }
+ else if (!is_ref && is_dummy)
+ {
+ /* No further processing since this is a def from an IR dummy BFD. */
+ return FALSE;
+ }
+
+ /* Suppresses "unused" warnings without relying on GCC attribute. */
+ info = info;
+ value = value;
+ /* Continue with cref/nocrossref/trace-sym processing. */
+ return TRUE;
+}
diff --git a/ld/plugin.h b/ld/plugin.h
index 8839b8b..5bbe93d 100644
--- a/ld/plugin.h
+++ b/ld/plugin.h
@@ -40,7 +40,8 @@ extern int plugin_load_plugins (void);
extern const char *plugin_error_plugin (void);
/* Call 'claim file' hook for all plugins. */
-extern int plugin_call_claim_file (const struct ld_plugin_input_file *file, int *claimed);
+extern int plugin_call_claim_file (const struct ld_plugin_input_file *file,
+ int *claimed);
/* Call 'all symbols read' hook for all plugins. */
extern int plugin_call_all_symbols_read (void);
@@ -58,6 +59,10 @@ extern bfd *plugin_get_ir_dummy_bfd (const char *name, bfd *template);
/* Check if the BFD passed in is an IR dummy object file. */
extern bfd_boolean is_ir_dummy_bfd (const bfd *abfd);
+/* Notice-symbol bfd linker callback hook. */
+extern bfd_boolean plugin_notice (struct bfd_link_info *info,
+ const char *name, bfd *abfd, asection *section,
+ bfd_vma value);
#endif /* !def GLD_PLUGIN_H */
diff --git a/ld/testplug.c b/ld/testplug.c
index 9746465..ad1bdf6 100644
--- a/ld/testplug.c
+++ b/ld/testplug.c
@@ -106,6 +106,7 @@ static enum ld_plugin_status cleanup_ret = LDPS_OK;
static bfd_boolean register_claimfile_hook = FALSE;
static bfd_boolean register_allsymbolsread_hook = FALSE;
static bfd_boolean register_cleanup_hook = FALSE;
+static bfd_boolean dumpresolutions = FALSE;
/* The master list of all claimable/claimed files. */
static claim_file_t *claimfiles_list = NULL;
@@ -276,6 +277,8 @@ parse_option (const char *opt)
return record_claim_file (opt + 6);
else if (!strncmp ("sym:", opt, 4))
return record_claimed_file_symbol (opt + 4);
+ else if (!strcmp ("dumpresolutions", opt))
+ dumpresolutions = TRUE;
else
return LDPS_ERR;
return LDPS_OK;
@@ -498,7 +501,40 @@ onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
static enum ld_plugin_status
onall_symbols_read (void)
{
+ static const char *resolutions[] =
+ {
+ "LDPR_UNKNOWN",
+ "LDPR_UNDEF",
+ "LDPR_PREVAILING_DEF",
+ "LDPR_PREVAILING_DEF_IRONLY",
+ "LDPR_PREEMPTED_REG",
+ "LDPR_PREEMPTED_IR",
+ "LDPR_RESOLVED_IR",
+ "LDPR_RESOLVED_EXEC",
+ "LDPR_RESOLVED_DYN",
+ };
+ claim_file_t *claimfile = dumpresolutions ? claimfiles_list : NULL;
TV_MESSAGE (LDPL_INFO, "hook called: all symbols read.\n");
+ for ( ; claimfile; claimfile = claimfile->next)
+ {
+ enum ld_plugin_status rv;
+ int n;
+ if (claimfile->n_syms_used && !tv_get_symbols)
+ return LDPS_ERR;
+ else if (!claimfile->n_syms_used)
+ continue;
+ rv = tv_get_symbols (claimfile->file.handle, claimfile->n_syms_used,
+ claimfile->symbols);
+ if (rv != LDPS_OK)
+ return rv;
+ for (n = 0; n < claimfile->n_syms_used; n++)
+ TV_MESSAGE (LDPL_INFO, "Sym: '%s%s%s' Resolution: %s\n",
+ claimfile->symbols[n].name,
+ claimfile->symbols[n].version ? "@" : "",
+ claimfile->symbols[n].version ? claimfile->symbols[n].version
+ : "",
+ resolutions[claimfile->symbols[n].resolution]);
+ }
fflush (NULL);
return all_symbols_read_ret;
}
diff --git a/ld/testsuite/ld-plugin/plugin-8.d b/ld/testsuite/ld-plugin/plugin-8.d
new file mode 100644
index 0000000..e72b039
--- /dev/null
+++ b/ld/testsuite/ld-plugin/plugin-8.d
@@ -0,0 +1,34 @@
+Hello from testplugin.
+tv\[0\]: LDPT_MESSAGE func@0x.*
+tv\[1\]: LDPT_API_VERSION value 0x1 \(1\)
+tv\[2\]: LDPT_GNU_LD_VERSION value 0x.*
+tv\[3\]: LDPT_LINKER_OUTPUT value 0x1 \(1\)
+tv\[4\]: LDPT_OUTPUT_NAME 'tmpdir/main.x'
+tv\[5\]: LDPT_REGISTER_CLAIM_FILE_HOOK func@0x.*
+tv\[6\]: LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK func@0x.*
+tv\[7\]: LDPT_REGISTER_CLEANUP_HOOK func@0x.*
+tv\[8\]: LDPT_ADD_SYMBOLS func@0x.*
+tv\[9\]: LDPT_GET_INPUT_FILE func@0x.*
+tv\[10\]: LDPT_RELEASE_INPUT_FILE func@0x.*
+tv\[11\]: LDPT_GET_SYMBOLS func@0x.*
+tv\[12\]: LDPT_ADD_INPUT_FILE func@0x.*
+tv\[13\]: LDPT_ADD_INPUT_LIBRARY func@0x.*
+tv\[14\]: LDPT_SET_EXTRA_LIBRARY_PATH func@0x.*
+tv\[15\]: LDPT_OPTION 'registerclaimfile'
+tv\[16\]: LDPT_OPTION 'registerallsymbolsread'
+tv\[17\]: LDPT_OPTION 'registercleanup'
+tv\[18\]: LDPT_OPTION 'claim:tmpdir/func.o'
+tv\[19\]: LDPT_OPTION 'sym:_?func::0:0:0'
+tv\[20\]: LDPT_OPTION 'sym:_?func2::0:0:0'
+tv\[21\]: LDPT_OPTION 'dumpresolutions'
+tv\[22\]: LDPT_NULL value 0x0 \(0\)
+#...
+hook called: claim_file tmpdir/main.o \[@0/.* not claimed
+hook called: claim_file tmpdir/func.o \[@0/.* CLAIMED
+hook called: claim_file tmpdir/text.o \[@0/.* not claimed
+#...
+hook called: all symbols read.
+Sym: '_?func' Resolution: LDPR_PREVAILING_DEF
+Sym: '_?func2' Resolution: LDPR_PREVAILING_DEF_IRONLY
+hook called: cleanup.
+#...
diff --git a/ld/testsuite/ld-plugin/plugin.exp b/ld/testsuite/ld-plugin/plugin.exp
index b983ce4..6ed2787 100644
--- a/ld/testsuite/ld-plugin/plugin.exp
+++ b/ld/testsuite/ld-plugin/plugin.exp
@@ -93,6 +93,12 @@ set plugin_tests [list \
$regas $regcln -plugin-arg claim:tmpdir/func.o \
-plugin-arg sym:${_}func::0:0:0 \
$testobjfiles $libs" "" "" {{ld plugin-7.d}} "main.x" ] \
+ [list "plugin claimfile resolve symbol" "-plugin $plugin_path $regclm \
+ $regas $regcln -plugin-arg claim:tmpdir/func.o \
+ -plugin-arg sym:${_}func::0:0:0 \
+ -plugin-arg sym:${_}func2::0:0:0 \
+ -plugin-arg dumpresolutions \
+ $testobjfiles $libs" "" "" {{ld plugin-8.d}} "main.x" ] \
]
if { $failed_compile != 0 } {