14f645668Schristos /* libdeps plugin for the GNU linker.
2*cb63e24eSchristos Copyright (C) 2020-2024 Free Software Foundation, Inc.
34f645668Schristos
44f645668Schristos This file is part of the GNU Binutils.
54f645668Schristos
64f645668Schristos This program is free software; you can redistribute it and/or modify
74f645668Schristos it under the terms of the GNU General Public License as published by
84f645668Schristos the Free Software Foundation; either version 3 of the License, or
94f645668Schristos (at your option) any later version.
104f645668Schristos
114f645668Schristos This program is distributed in the hope that it will be useful,
124f645668Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
134f645668Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
144f645668Schristos GNU General Public License for more details.
154f645668Schristos
164f645668Schristos You should have received a copy of the GNU General Public License
174f645668Schristos along with this program; if not, write to the Free Software
184f645668Schristos Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
194f645668Schristos MA 02110-1301, USA. */
204f645668Schristos
214f645668Schristos #include "sysdep.h"
224f645668Schristos #include "bfd.h"
234f645668Schristos #if BFD_SUPPORTS_PLUGINS
244f645668Schristos #include "plugin-api.h"
254f645668Schristos
264f645668Schristos #include <ctype.h> /* For isspace. */
274f645668Schristos
284f645668Schristos extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
294f645668Schristos
304f645668Schristos /* Helper for calling plugin api message function. */
314f645668Schristos #define TV_MESSAGE if (tv_message) (*tv_message)
324f645668Schristos
334f645668Schristos /* Function pointers to cache hooks passed at onload time. */
344f645668Schristos static ld_plugin_register_claim_file tv_register_claim_file = 0;
354f645668Schristos static ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0;
364f645668Schristos static ld_plugin_register_cleanup tv_register_cleanup = 0;
374f645668Schristos static ld_plugin_message tv_message = 0;
384f645668Schristos static ld_plugin_add_input_library tv_add_input_library = 0;
394f645668Schristos static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0;
404f645668Schristos
414f645668Schristos /* Handle/record information received in a transfer vector entry. */
424f645668Schristos static enum ld_plugin_status
parse_tv_tag(struct ld_plugin_tv * tv)434f645668Schristos parse_tv_tag (struct ld_plugin_tv *tv)
444f645668Schristos {
454f645668Schristos #define SETVAR(x) x = tv->tv_u.x
464f645668Schristos switch (tv->tv_tag)
474f645668Schristos {
484f645668Schristos case LDPT_REGISTER_CLAIM_FILE_HOOK:
494f645668Schristos SETVAR(tv_register_claim_file);
504f645668Schristos break;
514f645668Schristos case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
524f645668Schristos SETVAR(tv_register_all_symbols_read);
534f645668Schristos break;
544f645668Schristos case LDPT_REGISTER_CLEANUP_HOOK:
554f645668Schristos SETVAR(tv_register_cleanup);
564f645668Schristos break;
574f645668Schristos case LDPT_MESSAGE:
584f645668Schristos SETVAR(tv_message);
594f645668Schristos break;
604f645668Schristos case LDPT_ADD_INPUT_LIBRARY:
614f645668Schristos SETVAR(tv_add_input_library);
624f645668Schristos break;
634f645668Schristos case LDPT_SET_EXTRA_LIBRARY_PATH:
644f645668Schristos SETVAR(tv_set_extra_library_path);
654f645668Schristos break;
664f645668Schristos default:
674f645668Schristos break;
684f645668Schristos }
694f645668Schristos #undef SETVAR
704f645668Schristos return LDPS_OK;
714f645668Schristos }
724f645668Schristos
734f645668Schristos /* Defs for archive parsing. */
744f645668Schristos #define ARMAGSIZE 8
754f645668Schristos typedef struct arhdr
764f645668Schristos {
774f645668Schristos char ar_name[16];
784f645668Schristos char ar_date[12];
794f645668Schristos char ar_uid[6];
804f645668Schristos char ar_gid[6];
814f645668Schristos char ar_mode[8];
824f645668Schristos char ar_size[10];
834f645668Schristos char ar_fmag[2];
844f645668Schristos } arhdr;
854f645668Schristos
864f645668Schristos typedef struct linerec
874f645668Schristos {
884f645668Schristos struct linerec *next;
894f645668Schristos char line[];
904f645668Schristos } linerec;
914f645668Schristos
924f645668Schristos #define LIBDEPS "__.LIBDEP/ "
934f645668Schristos
944f645668Schristos static linerec *line_head, **line_tail = &line_head;
954f645668Schristos
964f645668Schristos static enum ld_plugin_status
get_libdeps(int fd)974f645668Schristos get_libdeps (int fd)
984f645668Schristos {
994f645668Schristos arhdr ah;
1004f645668Schristos int len;
1014f645668Schristos unsigned long mlen;
1024f645668Schristos size_t amt;
1034f645668Schristos linerec *lr;
1044f645668Schristos enum ld_plugin_status rc = LDPS_NO_SYMS;
1054f645668Schristos
1064f645668Schristos lseek (fd, ARMAGSIZE, SEEK_SET);
1074f645668Schristos for (;;)
1084f645668Schristos {
1094f645668Schristos len = read (fd, (void *) &ah, sizeof (ah));
1104f645668Schristos if (len != sizeof (ah))
1114f645668Schristos break;
1124f645668Schristos mlen = strtoul (ah.ar_size, NULL, 10);
1134f645668Schristos if (!mlen || strncmp (ah.ar_name, LIBDEPS, sizeof (LIBDEPS)-1))
1144f645668Schristos {
1154f645668Schristos lseek (fd, mlen, SEEK_CUR);
1164f645668Schristos continue;
1174f645668Schristos }
1184f645668Schristos amt = mlen + sizeof (linerec);
1194f645668Schristos if (amt <= mlen)
1204f645668Schristos return LDPS_ERR;
1214f645668Schristos lr = malloc (amt);
1224f645668Schristos if (!lr)
1234f645668Schristos return LDPS_ERR;
1244f645668Schristos lr->next = NULL;
1254f645668Schristos len = read (fd, lr->line, mlen);
1264f645668Schristos lr->line[mlen-1] = '\0';
1274f645668Schristos *line_tail = lr;
1284f645668Schristos line_tail = &lr->next;
1294f645668Schristos rc = LDPS_OK;
1304f645668Schristos break;
1314f645668Schristos }
1324f645668Schristos return rc;
1334f645668Schristos }
1344f645668Schristos
1354f645668Schristos /* Turn a string into an argvec. */
1364f645668Schristos static char **
str2vec(char * in)1374f645668Schristos str2vec (char *in)
1384f645668Schristos {
1394f645668Schristos char **res;
1404f645668Schristos char *s, *first, *end;
1414f645668Schristos char *sq, *dq;
1424f645668Schristos int i;
1434f645668Schristos
1444f645668Schristos end = in + strlen (in);
1454f645668Schristos s = in;
1464f645668Schristos while (isspace ((unsigned char) *s)) s++;
1474f645668Schristos first = s;
1484f645668Schristos
1494f645668Schristos i = 1;
1504f645668Schristos while ((s = strchr (s, ' ')))
1514f645668Schristos {
1524f645668Schristos s++;
1534f645668Schristos i++;
1544f645668Schristos }
1554f645668Schristos res = (char **)malloc ((i+1) * sizeof (char *));
1564f645668Schristos if (!res)
1574f645668Schristos return res;
1584f645668Schristos
1594f645668Schristos i = 0;
1604f645668Schristos sq = NULL;
1614f645668Schristos dq = NULL;
1624f645668Schristos res[0] = first;
1634f645668Schristos for (s = first; *s; s++)
1644f645668Schristos {
1654f645668Schristos if (*s == '\\')
1664f645668Schristos {
1674f645668Schristos memmove (s, s+1, end-s-1);
1684f645668Schristos end--;
1694f645668Schristos }
1704f645668Schristos if (isspace ((unsigned char) *s))
1714f645668Schristos {
1724f645668Schristos if (sq || dq)
1734f645668Schristos continue;
1744f645668Schristos *s++ = '\0';
1754f645668Schristos while (isspace ((unsigned char) *s)) s++;
1764f645668Schristos if (*s)
1774f645668Schristos res[++i] = s;
1784f645668Schristos }
1794f645668Schristos if (*s == '\'' && !dq)
1804f645668Schristos {
1814f645668Schristos if (sq)
1824f645668Schristos {
1834f645668Schristos memmove (sq, sq+1, s-sq-1);
1844f645668Schristos memmove (s-2, s+1, end-s-1);
1854f645668Schristos end -= 2;
1864f645668Schristos s--;
1874f645668Schristos sq = NULL;
1884f645668Schristos }
1894f645668Schristos else
1904f645668Schristos {
1914f645668Schristos sq = s;
1924f645668Schristos }
1934f645668Schristos }
1944f645668Schristos if (*s == '"' && !sq)
1954f645668Schristos {
1964f645668Schristos if (dq)
1974f645668Schristos {
1984f645668Schristos memmove (dq, dq+1, s-dq-1);
1994f645668Schristos memmove (s-2, s+1, end-s-1);
2004f645668Schristos end -= 2;
2014f645668Schristos s--;
2024f645668Schristos dq = NULL;
2034f645668Schristos }
2044f645668Schristos else
2054f645668Schristos {
2064f645668Schristos dq = s;
2074f645668Schristos }
2084f645668Schristos }
2094f645668Schristos }
2104f645668Schristos res[++i] = NULL;
2114f645668Schristos return res;
2124f645668Schristos }
2134f645668Schristos
2144f645668Schristos static char *prevfile;
2154f645668Schristos
2164f645668Schristos /* Standard plugin API registerable hook. */
2174f645668Schristos static enum ld_plugin_status
onclaim_file(const struct ld_plugin_input_file * file,int * claimed)2184f645668Schristos onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
2194f645668Schristos {
2204f645668Schristos enum ld_plugin_status rv;
2214f645668Schristos
2224f645668Schristos *claimed = 0;
2234f645668Schristos
2244f645668Schristos /* If we've already seen this file, ignore it. */
2254f645668Schristos if (prevfile && !strcmp (file->name, prevfile))
2264f645668Schristos return LDPS_OK;
2274f645668Schristos
2284f645668Schristos /* If it's not an archive member, ignore it. */
2294f645668Schristos if (!file->offset)
2304f645668Schristos return LDPS_OK;
2314f645668Schristos
2324f645668Schristos if (prevfile)
2334f645668Schristos free (prevfile);
2344f645668Schristos
2354f645668Schristos prevfile = strdup (file->name);
2364f645668Schristos if (!prevfile)
2374f645668Schristos return LDPS_ERR;
2384f645668Schristos
2394f645668Schristos /* This hook only gets called on actual object files.
2404f645668Schristos * We have to examine the archive ourselves, to find
2414f645668Schristos * our LIBDEPS member. */
2424f645668Schristos rv = get_libdeps (file->fd);
2434f645668Schristos if (rv == LDPS_ERR)
2444f645668Schristos return rv;
2454f645668Schristos
2464f645668Schristos if (rv == LDPS_OK)
2474f645668Schristos {
2484f645668Schristos linerec *lr = (linerec *)line_tail;
2494f645668Schristos /* Inform the user/testsuite. */
2504f645668Schristos TV_MESSAGE (LDPL_INFO, "got deps for library %s: %s",
2514f645668Schristos file->name, lr->line);
2524f645668Schristos fflush (NULL);
2534f645668Schristos }
2544f645668Schristos
2554f645668Schristos return LDPS_OK;
2564f645668Schristos }
2574f645668Schristos
2584f645668Schristos /* Standard plugin API registerable hook. */
2594f645668Schristos static enum ld_plugin_status
onall_symbols_read(void)2604f645668Schristos onall_symbols_read (void)
2614f645668Schristos {
2624f645668Schristos linerec *lr;
2634f645668Schristos char **vec;
2644f645668Schristos enum ld_plugin_status rv = LDPS_OK;
2654f645668Schristos
2664f645668Schristos while ((lr = line_head))
2674f645668Schristos {
2684f645668Schristos line_head = lr->next;
2694f645668Schristos vec = str2vec (lr->line);
2704f645668Schristos if (vec)
2714f645668Schristos {
2724f645668Schristos int i;
2734f645668Schristos for (i = 0; vec[i]; i++)
2744f645668Schristos {
2754f645668Schristos if (vec[i][0] != '-')
2764f645668Schristos {
2774f645668Schristos TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s",
2784f645668Schristos vec[i]);
2794f645668Schristos fflush (NULL);
2804f645668Schristos continue;
2814f645668Schristos }
2824f645668Schristos if (vec[i][1] == 'l')
2834f645668Schristos rv = tv_add_input_library (vec[i]+2);
2844f645668Schristos else if (vec[i][1] == 'L')
2854f645668Schristos rv = tv_set_extra_library_path (vec[i]+2);
2864f645668Schristos else
2874f645668Schristos {
2884f645668Schristos TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s",
2894f645668Schristos vec[i]);
2904f645668Schristos fflush (NULL);
2914f645668Schristos }
2924f645668Schristos if (rv != LDPS_OK)
2934f645668Schristos break;
2944f645668Schristos }
2954f645668Schristos free (vec);
2964f645668Schristos }
2974f645668Schristos free (lr);
2984f645668Schristos }
2994f645668Schristos line_tail = NULL;
3004f645668Schristos return rv;
3014f645668Schristos }
3024f645668Schristos
3034f645668Schristos /* Standard plugin API registerable hook. */
3044f645668Schristos static enum ld_plugin_status
oncleanup(void)3054f645668Schristos oncleanup (void)
3064f645668Schristos {
3074f645668Schristos if (prevfile)
3084f645668Schristos {
3094f645668Schristos free (prevfile);
3104f645668Schristos prevfile = NULL;
3114f645668Schristos }
3124f645668Schristos if (line_head)
3134f645668Schristos {
3144f645668Schristos linerec *lr;
3154f645668Schristos while ((lr = line_head))
3164f645668Schristos {
3174f645668Schristos line_head = lr->next;
3184f645668Schristos free (lr);
3194f645668Schristos }
3204f645668Schristos line_tail = NULL;
3214f645668Schristos }
3224f645668Schristos return LDPS_OK;
3234f645668Schristos }
3244f645668Schristos
3254f645668Schristos /* Standard plugin API entry point. */
3264f645668Schristos enum ld_plugin_status
onload(struct ld_plugin_tv * tv)3274f645668Schristos onload (struct ld_plugin_tv *tv)
3284f645668Schristos {
3294f645668Schristos enum ld_plugin_status rv;
3304f645668Schristos
3314f645668Schristos /* This plugin requires a valid tv array. */
3324f645668Schristos if (!tv)
3334f645668Schristos return LDPS_ERR;
3344f645668Schristos
3354f645668Schristos /* First entry should always be LDPT_MESSAGE, letting us get
3364f645668Schristos hold of it easily so we can send output straight away. */
3374f645668Schristos if (tv[0].tv_tag == LDPT_MESSAGE)
3384f645668Schristos tv_message = tv[0].tv_u.tv_message;
3394f645668Schristos
3404f645668Schristos do
3414f645668Schristos if ((rv = parse_tv_tag (tv)) != LDPS_OK)
3424f645668Schristos return rv;
3434f645668Schristos while ((tv++)->tv_tag != LDPT_NULL);
3444f645668Schristos
3454f645668Schristos /* Register hooks. */
3464f645668Schristos if (tv_register_claim_file
3474f645668Schristos && tv_register_all_symbols_read
3484f645668Schristos && tv_register_cleanup)
3494f645668Schristos {
3504f645668Schristos (*tv_register_claim_file) (onclaim_file);
3514f645668Schristos (*tv_register_all_symbols_read) (onall_symbols_read);
3524f645668Schristos (*tv_register_cleanup) (oncleanup);
3534f645668Schristos }
3544f645668Schristos fflush (NULL);
3554f645668Schristos return LDPS_OK;
3564f645668Schristos }
3574f645668Schristos #endif /* BFD_SUPPORTS_PLUGINS */
358