aboutsummaryrefslogtreecommitdiff
path: root/ld/libdep_plugin.c
diff options
context:
space:
mode:
authorHoward Chu <hyc@symas.com>2020-12-14 14:26:11 +0000
committerNick Clifton <nickc@redhat.com>2020-12-14 14:26:11 +0000
commitbf6d80378274fa33001f2ca1cef084eabc904178 (patch)
tree165b359b2cc01df91fe2a090ddc3bbf5475c0f38 /ld/libdep_plugin.c
parenta86c6c19643e9ac795b17846e5b0db8b3e4c54de (diff)
downloadgdb-bf6d80378274fa33001f2ca1cef084eabc904178.zip
gdb-bf6d80378274fa33001f2ca1cef084eabc904178.tar.gz
gdb-bf6d80378274fa33001f2ca1cef084eabc904178.tar.bz2
Add a plugin for processing static library dependencies.
* libdep_plugin.c: New file: Processes archives that contain a special library dependencies element. * Makefile.am: Add build rules for libdep_plugin. * Makefile.in: Regenerate. * NEWS: Mention the new plugin. * ld.texi: Document the new plugin.
Diffstat (limited to 'ld/libdep_plugin.c')
-rw-r--r--ld/libdep_plugin.c366
1 files changed, 366 insertions, 0 deletions
diff --git a/ld/libdep_plugin.c b/ld/libdep_plugin.c
new file mode 100644
index 0000000..2a7fdc4
--- /dev/null
+++ b/ld/libdep_plugin.c
@@ -0,0 +1,366 @@
+/* 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 <ctype.h> /* For isspace. */
+
+extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
+
+/* 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_all_symbols_read tv_register_all_symbols_read = 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_ALL_SYMBOLS_READ_HOOK:
+ SETVAR(tv_register_all_symbols_read);
+ 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;
+}
+
+/* Defs for archive parsing. */
+#define ARMAGSIZE 8
+typedef struct arhdr
+{
+ char ar_name[16];
+ char ar_date[12];
+ char ar_uid[6];
+ char ar_gid[6];
+ char ar_mode[8];
+ char ar_size[10];
+ char ar_fmag[2];
+} arhdr;
+
+typedef struct linerec
+{
+ struct linerec *next;
+ char line[];
+} linerec;
+
+#define LIBDEPS "__.LIBDEP/ "
+
+static linerec *line_head, **line_tail = &line_head;
+
+static enum ld_plugin_status
+get_libdeps (int fd)
+{
+ arhdr ah;
+ int len;
+ unsigned long mlen;
+ linerec *lr;
+ enum ld_plugin_status rc = LDPS_NO_SYMS;
+
+ lseek (fd, ARMAGSIZE, SEEK_SET);
+ for (;;)
+ {
+ len = read (fd, (void *) &ah, sizeof (ah));
+ if (len != sizeof (ah))
+ break;
+ mlen = strtoul (ah.ar_size, NULL, 10);
+ if (!mlen || strncmp (ah.ar_name, LIBDEPS, sizeof (LIBDEPS)-1))
+ {
+ lseek (fd, mlen, SEEK_CUR);
+ continue;
+ }
+ lr = malloc (sizeof (linerec) + mlen);
+ if (!lr)
+ return LDPS_ERR;
+ lr->next = NULL;
+ len = read (fd, lr->line, mlen);
+ lr->line[mlen-1] = '\0';
+ *line_tail = lr;
+ line_tail = &lr->next;
+ rc = LDPS_OK;
+ break;
+ }
+ return rc;
+}
+
+/* Turn a string into an argvec. */
+static char **
+str2vec (char *in)
+{
+ char **res;
+ char *s, *first, *end;
+ char *sq, *dq;
+ int i;
+
+ 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 == '\'' && !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 == '"' && !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;
+
+/* Standard plugin API registerable hook. */
+static enum ld_plugin_status
+onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
+{
+ enum ld_plugin_status rv;
+
+ *claimed = 0;
+
+ /* 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. */
+ rv = get_libdeps (file->fd);
+ if (rv == LDPS_ERR)
+ return rv;
+
+ if (rv == LDPS_OK)
+ {
+ linerec *lr = (linerec *)line_tail;
+ /* Inform the user/testsuite. */
+ TV_MESSAGE (LDPL_INFO, "got deps for library %s: %s",
+ file->name, lr->line);
+ fflush (NULL);
+ }
+
+ return LDPS_OK;
+}
+
+/* Standard plugin API registerable hook. */
+static enum ld_plugin_status
+onall_symbols_read (void)
+{
+ linerec *lr;
+ char **vec;
+ enum ld_plugin_status rv = LDPS_OK;
+
+ while ((lr = line_head))
+ {
+ line_head = lr->next;
+ vec = str2vec (lr->line);
+ if (vec)
+ {
+ int i;
+ for (i = 0; vec[i]; i++)
+ {
+ if (vec[i][0] != '-')
+ {
+ TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s",
+ vec[i]);
+ fflush (NULL);
+ continue;
+ }
+ 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);
+ else
+ {
+ TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s",
+ vec[i]);
+ fflush (NULL);
+ }
+ if (rv != LDPS_OK)
+ break;
+ }
+ free (vec);
+ }
+ free (lr);
+ }
+ line_tail = NULL;
+ return rv;
+}
+
+/* Standard plugin API registerable hook. */
+static enum ld_plugin_status
+oncleanup (void)
+{
+ if (prevfile)
+ {
+ free (prevfile);
+ prevfile = NULL;
+ }
+ if (line_head)
+ {
+ linerec *lr;
+ while ((lr = line_head))
+ {
+ line_head = lr->next;
+ free (lr);
+ }
+ line_tail = 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_all_symbols_read)
+ {
+ TV_MESSAGE (LDPL_FATAL, "No register_all_symbols_read hook");
+ fflush (NULL);
+ return LDPS_ERR;
+ }
+ (*tv_register_all_symbols_read) (onall_symbols_read);
+ 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 */