From 0bebac309c1e5772a22a4715b2367f565236f183 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Wed, 4 Nov 2020 00:24:56 +0000 Subject: [PATCH] Add libdep linker plugin Needs ld to be linked with -E to export its symbols; the plugin uses the libbfd that ld was linked with. --- ld/Makefile.am | 5 + ld/libdep_plugin.c | 284 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 289 insertions(+) create mode 100644 ld/libdep_plugin.c diff --git a/ld/Makefile.am b/ld/Makefile.am index 41db0c6016..1f5b73e0ce 100644 --- a/ld/Makefile.am +++ b/ld/Makefile.am @@ -947,6 +947,7 @@ ld_new_SOURCES = ldgram.y ldlex-wrapper.c lexsup.c ldlang.c mri.c ldctor.c ldmai ld_new_DEPENDENCIES = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) \ $(BFDLIB) $(LIBCTF) $(LIBIBERTY) $(LIBINTL_DEP) ld_new_LDADD = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) $(BFDLIB) $(LIBCTF) $(LIBIBERTY) $(LIBINTL) $(ZLIB) +ld_new_LDFLAGS = -Wl,-E # Dependency tracking for the generated emulation files. EXTRA_ld_new_SOURCES += $(ALL_EMULATION_SOURCES) $(ALL_64_EMULATION_SOURCES) @@ -998,6 +999,10 @@ libldtestplug4_la_SOURCES = testplug4.c libldtestplug4_la_CFLAGS= -g -O2 libldtestplug4_la_LDFLAGS = -no-undefined -rpath /nowhere +lib_LTLIBRARIES = libdep_plugin.la +libdep_plugin_la_SOURCES = libdep_plugin.c +libdep_plugin_la_LDFLAGS = -no-undefined -rpath /nowhere + # DOCUMENTATION TARGETS # Manual configuration file; not usually attached to normal configuration, # because almost all configs use "gen" version of manual. diff --git a/ld/libdep_plugin.c b/ld/libdep_plugin.c new file mode 100644 index 0000000000..2cf00b2b25 --- /dev/null +++ b/ld/libdep_plugin.c @@ -0,0 +1,284 @@ +/* libdeps plugin for the GNU linker. + Copyright (C) 2020 Free Software Foundation, Inc. + + This file is part of the GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "sysdep.h" +#include "bfd.h" +#if BFD_SUPPORTS_PLUGINS +#include "plugin-api.h" + +#include /* For isspace. */ + +/* Helper for calling plugin api message function. */ +#define TV_MESSAGE if (tv_message) (*tv_message) + +/* Function pointers to cache hooks passed at onload time. */ +static ld_plugin_register_claim_file tv_register_claim_file = 0; +static ld_plugin_register_cleanup tv_register_cleanup = 0; +static ld_plugin_message tv_message = 0; +static ld_plugin_add_input_library tv_add_input_library = 0; +static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0; + +/* Handle/record information received in a transfer vector entry. */ +static enum ld_plugin_status +parse_tv_tag (struct ld_plugin_tv *tv) +{ +#define SETVAR(x) x = tv->tv_u.x + switch (tv->tv_tag) + { + case LDPT_REGISTER_CLAIM_FILE_HOOK: + SETVAR(tv_register_claim_file); + break; + case LDPT_REGISTER_CLEANUP_HOOK: + SETVAR(tv_register_cleanup); + break; + case LDPT_MESSAGE: + SETVAR(tv_message); + break; + case LDPT_ADD_INPUT_LIBRARY: + SETVAR(tv_add_input_library); + break; + case LDPT_SET_EXTRA_LIBRARY_PATH: + SETVAR(tv_set_extra_library_path); + break; + default: + break; + } +#undef SETVAR + return LDPS_OK; +} + +/* Turn a string into an argvec. */ +static char ** +str2vec (char *in) +{ + char **res; + char *s, *first, *end; + char *sq, *dq; + int i, total; + + end = in + strlen(in); + s = in; + while (isspace (*s)) s++; + first = s; + + i = 1; + while (s = strchr (s, ' ')) { + s++; + i++; + } + res = (char **)malloc ((i+1) * sizeof (char *)); + if (!res) + return res; + + i = 0; + sq = NULL; + dq = NULL; + res[0] = first; + for (s = first; *s; s++) { + if (*s == '\\') { + memmove (s, s+1, end-s-1); + end--; + } + if (isspace (*s)) { + if (sq || dq) + continue; + *s++ = '\0'; + while(isspace (*s)) s++; + if (*s) + res[++i] = s; + } + if (*s == '\'') { + if (!dq) { + if (sq) { + memmove (sq, sq+1, s-sq-1); + memmove (s-2, s+1, end-s-1); + end -= 2; + s--; + sq = NULL; + } else { + sq = s; + } + } + } + if (*s == '"') { + if (!sq) { + if (dq) { + memmove (dq, dq+1, s-dq-1); + memmove (s-2, s+1, end-s-1); + end -= 2; + s--; + dq = NULL; + } else { + dq = s; + } + } + } + } + res[++i] = NULL; + return res; +} + +static char *prevfile; +#define LIBDEPS "__.LIBDEP" + +/* Standard plugin API registerable hook. */ +static enum ld_plugin_status +onclaim_file (const struct ld_plugin_input_file *file, int *claimed) +{ + bfd *arch, *next, *prev; + bfd_size_type len; + char *line = NULL, **vec; + enum ld_plugin_status rv; + + /* If we've already seen this file, ignore it. */ + if (prevfile && !strcmp (file->name, prevfile)) + return LDPS_OK; + + /* If it's not an archive member, ignore it. */ + if (!file->offset) + return LDPS_OK; + + if (prevfile) + free (prevfile); + + prevfile = strdup (file->name); + if (!prevfile) + return LDPS_ERR; + + /* This hook only gets called on actual object files. + * We have to examine the archive ourselves, to find + * our LIBDEPS member. */ + arch = bfd_openr (file->name, "plugin"); + if (!arch) + return LDPS_ERR; + + bfd_check_format (arch, bfd_archive); + + prev = NULL; + while ((next = bfd_openr_next_archived_file (arch, prev))) + { + prev = next; + if (!strcmp (bfd_get_filename (next), LIBDEPS)) + break; + } + if (!next) + { + bfd_close (arch); + return LDPS_OK; + } + { + struct stat st; + bfd_stat_arch_elt (next, &st); + len = st.st_size; + } + line = malloc (len); + if (!line) + { + rv = LDPS_ERR; + goto quit; + } + + if (bfd_bread (line, len, next) != len) + { + rv = LDPS_ERR; + goto quit; + } + + rv = LDPS_OK; + vec = str2vec (line); + if (vec) { + int i; + for (i=0; vec[i]; i++) { + if (vec[i][0] != '-') + continue; + rv = LDPS_ERR; + if (vec[i][1] == 'l') { + rv = tv_add_input_library (vec[i]+2); + } else if (vec[i][1] == 'L') { + rv = tv_set_extra_library_path (vec[i]+2); + } + if (rv != LDPS_OK) + break; + } + free (vec); + } + /* Inform the user/testsuite. */ + TV_MESSAGE (LDPL_INFO, "processed deps for library %s: %s", + file->name, line); + fflush (NULL); + +quit: + if (line) + free (line); + bfd_close (arch); + + return rv; +} + +/* Standard plugin API registerable hook. */ +static enum ld_plugin_status +oncleanup (void) +{ + if (prevfile) { + free (prevfile); + prevfile = NULL; + } + return LDPS_OK; +} + +/* Standard plugin API entry point. */ +enum ld_plugin_status +onload (struct ld_plugin_tv *tv) +{ + enum ld_plugin_status rv; + + /* This plugin requires a valid tv array. */ + if (!tv) + return LDPS_ERR; + + /* First entry should always be LDPT_MESSAGE, letting us get + hold of it easily so we can send output straight away. */ + if (tv[0].tv_tag == LDPT_MESSAGE) + tv_message = tv[0].tv_u.tv_message; + + do + if ((rv = parse_tv_tag (tv)) != LDPS_OK) + return rv; + while ((tv++)->tv_tag != LDPT_NULL); + + /* Register hooks. */ + if (!tv_register_claim_file) + { + TV_MESSAGE (LDPL_FATAL, "No register_claim_file hook"); + fflush (NULL); + return LDPS_ERR; + } + (*tv_register_claim_file) (onclaim_file); + if (!tv_register_cleanup) + { + TV_MESSAGE (LDPL_FATAL, "No register_cleanup hook"); + fflush (NULL); + return LDPS_ERR; + } + (*tv_register_cleanup) (oncleanup); + fflush (NULL); + return LDPS_OK; +} +#endif /* BFD_SUPPORTS_PLUGINS */ -- 2.20.1