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