|  | /* libdeps plugin for the GNU linker. | 
|  | Copyright (C) 2020-2022 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; | 
|  | size_t amt; | 
|  | 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; | 
|  | } | 
|  | amt = mlen + sizeof (linerec); | 
|  | if (amt <= mlen) | 
|  | return LDPS_ERR; | 
|  | lr = malloc (amt); | 
|  | 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 ((unsigned char) *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 ((unsigned char) *s)) | 
|  | { | 
|  | if (sq || dq) | 
|  | continue; | 
|  | *s++ = '\0'; | 
|  | while (isspace ((unsigned char) *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_register_all_symbols_read | 
|  | && tv_register_cleanup) | 
|  | { | 
|  | (*tv_register_claim_file) (onclaim_file); | 
|  | (*tv_register_all_symbols_read) (onall_symbols_read); | 
|  | (*tv_register_cleanup) (oncleanup); | 
|  | } | 
|  | fflush (NULL); | 
|  | return LDPS_OK; | 
|  | } | 
|  | #endif /* BFD_SUPPORTS_PLUGINS */ |