1*c42dbd0eSchristos /* libdeps plugin for the GNU linker.
2*c42dbd0eSchristos Copyright (C) 2020-2022 Free Software Foundation, Inc.
3*c42dbd0eSchristos
4*c42dbd0eSchristos This file is part of the GNU Binutils.
5*c42dbd0eSchristos
6*c42dbd0eSchristos This program is free software; you can redistribute it and/or modify
7*c42dbd0eSchristos it under the terms of the GNU General Public License as published by
8*c42dbd0eSchristos the Free Software Foundation; either version 3 of the License, or
9*c42dbd0eSchristos (at your option) any later version.
10*c42dbd0eSchristos
11*c42dbd0eSchristos This program is distributed in the hope that it will be useful,
12*c42dbd0eSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of
13*c42dbd0eSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14*c42dbd0eSchristos GNU General Public License for more details.
15*c42dbd0eSchristos
16*c42dbd0eSchristos You should have received a copy of the GNU General Public License
17*c42dbd0eSchristos along with this program; if not, write to the Free Software
18*c42dbd0eSchristos Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
19*c42dbd0eSchristos MA 02110-1301, USA. */
20*c42dbd0eSchristos
21*c42dbd0eSchristos #include "sysdep.h"
22*c42dbd0eSchristos #include "bfd.h"
23*c42dbd0eSchristos #if BFD_SUPPORTS_PLUGINS
24*c42dbd0eSchristos #include "plugin-api.h"
25*c42dbd0eSchristos
26*c42dbd0eSchristos #include <ctype.h> /* For isspace. */
27*c42dbd0eSchristos
28*c42dbd0eSchristos extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
29*c42dbd0eSchristos
30*c42dbd0eSchristos /* Helper for calling plugin api message function. */
31*c42dbd0eSchristos #define TV_MESSAGE if (tv_message) (*tv_message)
32*c42dbd0eSchristos
33*c42dbd0eSchristos /* Function pointers to cache hooks passed at onload time. */
34*c42dbd0eSchristos static ld_plugin_register_claim_file tv_register_claim_file = 0;
35*c42dbd0eSchristos static ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0;
36*c42dbd0eSchristos static ld_plugin_register_cleanup tv_register_cleanup = 0;
37*c42dbd0eSchristos static ld_plugin_message tv_message = 0;
38*c42dbd0eSchristos static ld_plugin_add_input_library tv_add_input_library = 0;
39*c42dbd0eSchristos static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0;
40*c42dbd0eSchristos
41*c42dbd0eSchristos /* Handle/record information received in a transfer vector entry. */
42*c42dbd0eSchristos static enum ld_plugin_status
parse_tv_tag(struct ld_plugin_tv * tv)43*c42dbd0eSchristos parse_tv_tag (struct ld_plugin_tv *tv)
44*c42dbd0eSchristos {
45*c42dbd0eSchristos #define SETVAR(x) x = tv->tv_u.x
46*c42dbd0eSchristos switch (tv->tv_tag)
47*c42dbd0eSchristos {
48*c42dbd0eSchristos case LDPT_REGISTER_CLAIM_FILE_HOOK:
49*c42dbd0eSchristos SETVAR(tv_register_claim_file);
50*c42dbd0eSchristos break;
51*c42dbd0eSchristos case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
52*c42dbd0eSchristos SETVAR(tv_register_all_symbols_read);
53*c42dbd0eSchristos break;
54*c42dbd0eSchristos case LDPT_REGISTER_CLEANUP_HOOK:
55*c42dbd0eSchristos SETVAR(tv_register_cleanup);
56*c42dbd0eSchristos break;
57*c42dbd0eSchristos case LDPT_MESSAGE:
58*c42dbd0eSchristos SETVAR(tv_message);
59*c42dbd0eSchristos break;
60*c42dbd0eSchristos case LDPT_ADD_INPUT_LIBRARY:
61*c42dbd0eSchristos SETVAR(tv_add_input_library);
62*c42dbd0eSchristos break;
63*c42dbd0eSchristos case LDPT_SET_EXTRA_LIBRARY_PATH:
64*c42dbd0eSchristos SETVAR(tv_set_extra_library_path);
65*c42dbd0eSchristos break;
66*c42dbd0eSchristos default:
67*c42dbd0eSchristos break;
68*c42dbd0eSchristos }
69*c42dbd0eSchristos #undef SETVAR
70*c42dbd0eSchristos return LDPS_OK;
71*c42dbd0eSchristos }
72*c42dbd0eSchristos
73*c42dbd0eSchristos /* Defs for archive parsing. */
74*c42dbd0eSchristos #define ARMAGSIZE 8
75*c42dbd0eSchristos typedef struct arhdr
76*c42dbd0eSchristos {
77*c42dbd0eSchristos char ar_name[16];
78*c42dbd0eSchristos char ar_date[12];
79*c42dbd0eSchristos char ar_uid[6];
80*c42dbd0eSchristos char ar_gid[6];
81*c42dbd0eSchristos char ar_mode[8];
82*c42dbd0eSchristos char ar_size[10];
83*c42dbd0eSchristos char ar_fmag[2];
84*c42dbd0eSchristos } arhdr;
85*c42dbd0eSchristos
86*c42dbd0eSchristos typedef struct linerec
87*c42dbd0eSchristos {
88*c42dbd0eSchristos struct linerec *next;
89*c42dbd0eSchristos char line[];
90*c42dbd0eSchristos } linerec;
91*c42dbd0eSchristos
92*c42dbd0eSchristos #define LIBDEPS "__.LIBDEP/ "
93*c42dbd0eSchristos
94*c42dbd0eSchristos static linerec *line_head, **line_tail = &line_head;
95*c42dbd0eSchristos
96*c42dbd0eSchristos static enum ld_plugin_status
get_libdeps(int fd)97*c42dbd0eSchristos get_libdeps (int fd)
98*c42dbd0eSchristos {
99*c42dbd0eSchristos arhdr ah;
100*c42dbd0eSchristos int len;
101*c42dbd0eSchristos unsigned long mlen;
102*c42dbd0eSchristos size_t amt;
103*c42dbd0eSchristos linerec *lr;
104*c42dbd0eSchristos enum ld_plugin_status rc = LDPS_NO_SYMS;
105*c42dbd0eSchristos
106*c42dbd0eSchristos lseek (fd, ARMAGSIZE, SEEK_SET);
107*c42dbd0eSchristos for (;;)
108*c42dbd0eSchristos {
109*c42dbd0eSchristos len = read (fd, (void *) &ah, sizeof (ah));
110*c42dbd0eSchristos if (len != sizeof (ah))
111*c42dbd0eSchristos break;
112*c42dbd0eSchristos mlen = strtoul (ah.ar_size, NULL, 10);
113*c42dbd0eSchristos if (!mlen || strncmp (ah.ar_name, LIBDEPS, sizeof (LIBDEPS)-1))
114*c42dbd0eSchristos {
115*c42dbd0eSchristos lseek (fd, mlen, SEEK_CUR);
116*c42dbd0eSchristos continue;
117*c42dbd0eSchristos }
118*c42dbd0eSchristos amt = mlen + sizeof (linerec);
119*c42dbd0eSchristos if (amt <= mlen)
120*c42dbd0eSchristos return LDPS_ERR;
121*c42dbd0eSchristos lr = malloc (amt);
122*c42dbd0eSchristos if (!lr)
123*c42dbd0eSchristos return LDPS_ERR;
124*c42dbd0eSchristos lr->next = NULL;
125*c42dbd0eSchristos len = read (fd, lr->line, mlen);
126*c42dbd0eSchristos lr->line[mlen-1] = '\0';
127*c42dbd0eSchristos *line_tail = lr;
128*c42dbd0eSchristos line_tail = &lr->next;
129*c42dbd0eSchristos rc = LDPS_OK;
130*c42dbd0eSchristos break;
131*c42dbd0eSchristos }
132*c42dbd0eSchristos return rc;
133*c42dbd0eSchristos }
134*c42dbd0eSchristos
135*c42dbd0eSchristos /* Turn a string into an argvec. */
136*c42dbd0eSchristos static char **
str2vec(char * in)137*c42dbd0eSchristos str2vec (char *in)
138*c42dbd0eSchristos {
139*c42dbd0eSchristos char **res;
140*c42dbd0eSchristos char *s, *first, *end;
141*c42dbd0eSchristos char *sq, *dq;
142*c42dbd0eSchristos int i;
143*c42dbd0eSchristos
144*c42dbd0eSchristos end = in + strlen (in);
145*c42dbd0eSchristos s = in;
146*c42dbd0eSchristos while (isspace ((unsigned char) *s)) s++;
147*c42dbd0eSchristos first = s;
148*c42dbd0eSchristos
149*c42dbd0eSchristos i = 1;
150*c42dbd0eSchristos while ((s = strchr (s, ' ')))
151*c42dbd0eSchristos {
152*c42dbd0eSchristos s++;
153*c42dbd0eSchristos i++;
154*c42dbd0eSchristos }
155*c42dbd0eSchristos res = (char **)malloc ((i+1) * sizeof (char *));
156*c42dbd0eSchristos if (!res)
157*c42dbd0eSchristos return res;
158*c42dbd0eSchristos
159*c42dbd0eSchristos i = 0;
160*c42dbd0eSchristos sq = NULL;
161*c42dbd0eSchristos dq = NULL;
162*c42dbd0eSchristos res[0] = first;
163*c42dbd0eSchristos for (s = first; *s; s++)
164*c42dbd0eSchristos {
165*c42dbd0eSchristos if (*s == '\\')
166*c42dbd0eSchristos {
167*c42dbd0eSchristos memmove (s, s+1, end-s-1);
168*c42dbd0eSchristos end--;
169*c42dbd0eSchristos }
170*c42dbd0eSchristos if (isspace ((unsigned char) *s))
171*c42dbd0eSchristos {
172*c42dbd0eSchristos if (sq || dq)
173*c42dbd0eSchristos continue;
174*c42dbd0eSchristos *s++ = '\0';
175*c42dbd0eSchristos while (isspace ((unsigned char) *s)) s++;
176*c42dbd0eSchristos if (*s)
177*c42dbd0eSchristos res[++i] = s;
178*c42dbd0eSchristos }
179*c42dbd0eSchristos if (*s == '\'' && !dq)
180*c42dbd0eSchristos {
181*c42dbd0eSchristos if (sq)
182*c42dbd0eSchristos {
183*c42dbd0eSchristos memmove (sq, sq+1, s-sq-1);
184*c42dbd0eSchristos memmove (s-2, s+1, end-s-1);
185*c42dbd0eSchristos end -= 2;
186*c42dbd0eSchristos s--;
187*c42dbd0eSchristos sq = NULL;
188*c42dbd0eSchristos }
189*c42dbd0eSchristos else
190*c42dbd0eSchristos {
191*c42dbd0eSchristos sq = s;
192*c42dbd0eSchristos }
193*c42dbd0eSchristos }
194*c42dbd0eSchristos if (*s == '"' && !sq)
195*c42dbd0eSchristos {
196*c42dbd0eSchristos if (dq)
197*c42dbd0eSchristos {
198*c42dbd0eSchristos memmove (dq, dq+1, s-dq-1);
199*c42dbd0eSchristos memmove (s-2, s+1, end-s-1);
200*c42dbd0eSchristos end -= 2;
201*c42dbd0eSchristos s--;
202*c42dbd0eSchristos dq = NULL;
203*c42dbd0eSchristos }
204*c42dbd0eSchristos else
205*c42dbd0eSchristos {
206*c42dbd0eSchristos dq = s;
207*c42dbd0eSchristos }
208*c42dbd0eSchristos }
209*c42dbd0eSchristos }
210*c42dbd0eSchristos res[++i] = NULL;
211*c42dbd0eSchristos return res;
212*c42dbd0eSchristos }
213*c42dbd0eSchristos
214*c42dbd0eSchristos static char *prevfile;
215*c42dbd0eSchristos
216*c42dbd0eSchristos /* Standard plugin API registerable hook. */
217*c42dbd0eSchristos static enum ld_plugin_status
onclaim_file(const struct ld_plugin_input_file * file,int * claimed)218*c42dbd0eSchristos onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
219*c42dbd0eSchristos {
220*c42dbd0eSchristos enum ld_plugin_status rv;
221*c42dbd0eSchristos
222*c42dbd0eSchristos *claimed = 0;
223*c42dbd0eSchristos
224*c42dbd0eSchristos /* If we've already seen this file, ignore it. */
225*c42dbd0eSchristos if (prevfile && !strcmp (file->name, prevfile))
226*c42dbd0eSchristos return LDPS_OK;
227*c42dbd0eSchristos
228*c42dbd0eSchristos /* If it's not an archive member, ignore it. */
229*c42dbd0eSchristos if (!file->offset)
230*c42dbd0eSchristos return LDPS_OK;
231*c42dbd0eSchristos
232*c42dbd0eSchristos if (prevfile)
233*c42dbd0eSchristos free (prevfile);
234*c42dbd0eSchristos
235*c42dbd0eSchristos prevfile = strdup (file->name);
236*c42dbd0eSchristos if (!prevfile)
237*c42dbd0eSchristos return LDPS_ERR;
238*c42dbd0eSchristos
239*c42dbd0eSchristos /* This hook only gets called on actual object files.
240*c42dbd0eSchristos * We have to examine the archive ourselves, to find
241*c42dbd0eSchristos * our LIBDEPS member. */
242*c42dbd0eSchristos rv = get_libdeps (file->fd);
243*c42dbd0eSchristos if (rv == LDPS_ERR)
244*c42dbd0eSchristos return rv;
245*c42dbd0eSchristos
246*c42dbd0eSchristos if (rv == LDPS_OK)
247*c42dbd0eSchristos {
248*c42dbd0eSchristos linerec *lr = (linerec *)line_tail;
249*c42dbd0eSchristos /* Inform the user/testsuite. */
250*c42dbd0eSchristos TV_MESSAGE (LDPL_INFO, "got deps for library %s: %s",
251*c42dbd0eSchristos file->name, lr->line);
252*c42dbd0eSchristos fflush (NULL);
253*c42dbd0eSchristos }
254*c42dbd0eSchristos
255*c42dbd0eSchristos return LDPS_OK;
256*c42dbd0eSchristos }
257*c42dbd0eSchristos
258*c42dbd0eSchristos /* Standard plugin API registerable hook. */
259*c42dbd0eSchristos static enum ld_plugin_status
onall_symbols_read(void)260*c42dbd0eSchristos onall_symbols_read (void)
261*c42dbd0eSchristos {
262*c42dbd0eSchristos linerec *lr;
263*c42dbd0eSchristos char **vec;
264*c42dbd0eSchristos enum ld_plugin_status rv = LDPS_OK;
265*c42dbd0eSchristos
266*c42dbd0eSchristos while ((lr = line_head))
267*c42dbd0eSchristos {
268*c42dbd0eSchristos line_head = lr->next;
269*c42dbd0eSchristos vec = str2vec (lr->line);
270*c42dbd0eSchristos if (vec)
271*c42dbd0eSchristos {
272*c42dbd0eSchristos int i;
273*c42dbd0eSchristos for (i = 0; vec[i]; i++)
274*c42dbd0eSchristos {
275*c42dbd0eSchristos if (vec[i][0] != '-')
276*c42dbd0eSchristos {
277*c42dbd0eSchristos TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s",
278*c42dbd0eSchristos vec[i]);
279*c42dbd0eSchristos fflush (NULL);
280*c42dbd0eSchristos continue;
281*c42dbd0eSchristos }
282*c42dbd0eSchristos if (vec[i][1] == 'l')
283*c42dbd0eSchristos rv = tv_add_input_library (vec[i]+2);
284*c42dbd0eSchristos else if (vec[i][1] == 'L')
285*c42dbd0eSchristos rv = tv_set_extra_library_path (vec[i]+2);
286*c42dbd0eSchristos else
287*c42dbd0eSchristos {
288*c42dbd0eSchristos TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s",
289*c42dbd0eSchristos vec[i]);
290*c42dbd0eSchristos fflush (NULL);
291*c42dbd0eSchristos }
292*c42dbd0eSchristos if (rv != LDPS_OK)
293*c42dbd0eSchristos break;
294*c42dbd0eSchristos }
295*c42dbd0eSchristos free (vec);
296*c42dbd0eSchristos }
297*c42dbd0eSchristos free (lr);
298*c42dbd0eSchristos }
299*c42dbd0eSchristos line_tail = NULL;
300*c42dbd0eSchristos return rv;
301*c42dbd0eSchristos }
302*c42dbd0eSchristos
303*c42dbd0eSchristos /* Standard plugin API registerable hook. */
304*c42dbd0eSchristos static enum ld_plugin_status
oncleanup(void)305*c42dbd0eSchristos oncleanup (void)
306*c42dbd0eSchristos {
307*c42dbd0eSchristos if (prevfile)
308*c42dbd0eSchristos {
309*c42dbd0eSchristos free (prevfile);
310*c42dbd0eSchristos prevfile = NULL;
311*c42dbd0eSchristos }
312*c42dbd0eSchristos if (line_head)
313*c42dbd0eSchristos {
314*c42dbd0eSchristos linerec *lr;
315*c42dbd0eSchristos while ((lr = line_head))
316*c42dbd0eSchristos {
317*c42dbd0eSchristos line_head = lr->next;
318*c42dbd0eSchristos free (lr);
319*c42dbd0eSchristos }
320*c42dbd0eSchristos line_tail = NULL;
321*c42dbd0eSchristos }
322*c42dbd0eSchristos return LDPS_OK;
323*c42dbd0eSchristos }
324*c42dbd0eSchristos
325*c42dbd0eSchristos /* Standard plugin API entry point. */
326*c42dbd0eSchristos enum ld_plugin_status
onload(struct ld_plugin_tv * tv)327*c42dbd0eSchristos onload (struct ld_plugin_tv *tv)
328*c42dbd0eSchristos {
329*c42dbd0eSchristos enum ld_plugin_status rv;
330*c42dbd0eSchristos
331*c42dbd0eSchristos /* This plugin requires a valid tv array. */
332*c42dbd0eSchristos if (!tv)
333*c42dbd0eSchristos return LDPS_ERR;
334*c42dbd0eSchristos
335*c42dbd0eSchristos /* First entry should always be LDPT_MESSAGE, letting us get
336*c42dbd0eSchristos hold of it easily so we can send output straight away. */
337*c42dbd0eSchristos if (tv[0].tv_tag == LDPT_MESSAGE)
338*c42dbd0eSchristos tv_message = tv[0].tv_u.tv_message;
339*c42dbd0eSchristos
340*c42dbd0eSchristos do
341*c42dbd0eSchristos if ((rv = parse_tv_tag (tv)) != LDPS_OK)
342*c42dbd0eSchristos return rv;
343*c42dbd0eSchristos while ((tv++)->tv_tag != LDPT_NULL);
344*c42dbd0eSchristos
345*c42dbd0eSchristos /* Register hooks. */
346*c42dbd0eSchristos if (tv_register_claim_file
347*c42dbd0eSchristos && tv_register_all_symbols_read
348*c42dbd0eSchristos && tv_register_cleanup)
349*c42dbd0eSchristos {
350*c42dbd0eSchristos (*tv_register_claim_file) (onclaim_file);
351*c42dbd0eSchristos (*tv_register_all_symbols_read) (onall_symbols_read);
352*c42dbd0eSchristos (*tv_register_cleanup) (oncleanup);
353*c42dbd0eSchristos }
354*c42dbd0eSchristos fflush (NULL);
355*c42dbd0eSchristos return LDPS_OK;
356*c42dbd0eSchristos }
357*c42dbd0eSchristos #endif /* BFD_SUPPORTS_PLUGINS */
358