]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - ld/libdep_plugin.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / ld / libdep_plugin.c
1 /* libdeps plugin for the GNU linker.
2 Copyright (C) 2020-2021 Free Software Foundation, Inc.
3
4 This file is part of the GNU Binutils.
5
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.
10
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.
15
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. */
20
21 #include "sysdep.h"
22 #include "bfd.h"
23 #if BFD_SUPPORTS_PLUGINS
24 #include "plugin-api.h"
25
26 #include <ctype.h> /* For isspace. */
27
28 extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
29
30 /* Helper for calling plugin api message function. */
31 #define TV_MESSAGE if (tv_message) (*tv_message)
32
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;
40
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)
44 {
45 #define SETVAR(x) x = tv->tv_u.x
46 switch (tv->tv_tag)
47 {
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;
68 }
69 #undef SETVAR
70 return LDPS_OK;
71 }
72
73 /* Defs for archive parsing. */
74 #define ARMAGSIZE 8
75 typedef struct arhdr
76 {
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;
85
86 typedef struct linerec
87 {
88 struct linerec *next;
89 char line[];
90 } linerec;
91
92 #define LIBDEPS "__.LIBDEP/ "
93
94 static linerec *line_head, **line_tail = &line_head;
95
96 static enum ld_plugin_status
97 get_libdeps (int fd)
98 {
99 arhdr ah;
100 int len;
101 unsigned long mlen;
102 linerec *lr;
103 enum ld_plugin_status rc = LDPS_NO_SYMS;
104
105 lseek (fd, ARMAGSIZE, SEEK_SET);
106 for (;;)
107 {
108 len = read (fd, (void *) &ah, sizeof (ah));
109 if (len != sizeof (ah))
110 break;
111 mlen = strtoul (ah.ar_size, NULL, 10);
112 if (!mlen || strncmp (ah.ar_name, LIBDEPS, sizeof (LIBDEPS)-1))
113 {
114 lseek (fd, mlen, SEEK_CUR);
115 continue;
116 }
117 lr = malloc (sizeof (linerec) + mlen);
118 if (!lr)
119 return LDPS_ERR;
120 lr->next = NULL;
121 len = read (fd, lr->line, mlen);
122 lr->line[mlen-1] = '\0';
123 *line_tail = lr;
124 line_tail = &lr->next;
125 rc = LDPS_OK;
126 break;
127 }
128 return rc;
129 }
130
131 /* Turn a string into an argvec. */
132 static char **
133 str2vec (char *in)
134 {
135 char **res;
136 char *s, *first, *end;
137 char *sq, *dq;
138 int i;
139
140 end = in + strlen (in);
141 s = in;
142 while (isspace (*s)) s++;
143 first = s;
144
145 i = 1;
146 while ((s = strchr (s, ' ')))
147 {
148 s++;
149 i++;
150 }
151 res = (char **)malloc ((i+1) * sizeof (char *));
152 if (!res)
153 return res;
154
155 i = 0;
156 sq = NULL;
157 dq = NULL;
158 res[0] = first;
159 for (s = first; *s; s++)
160 {
161 if (*s == '\\')
162 {
163 memmove (s, s+1, end-s-1);
164 end--;
165 }
166 if (isspace (*s))
167 {
168 if (sq || dq)
169 continue;
170 *s++ = '\0';
171 while (isspace (*s)) s++;
172 if (*s)
173 res[++i] = s;
174 }
175 if (*s == '\'' && !dq)
176 {
177 if (sq)
178 {
179 memmove (sq, sq+1, s-sq-1);
180 memmove (s-2, s+1, end-s-1);
181 end -= 2;
182 s--;
183 sq = NULL;
184 }
185 else
186 {
187 sq = s;
188 }
189 }
190 if (*s == '"' && !sq)
191 {
192 if (dq)
193 {
194 memmove (dq, dq+1, s-dq-1);
195 memmove (s-2, s+1, end-s-1);
196 end -= 2;
197 s--;
198 dq = NULL;
199 }
200 else
201 {
202 dq = s;
203 }
204 }
205 }
206 res[++i] = NULL;
207 return res;
208 }
209
210 static char *prevfile;
211
212 /* Standard plugin API registerable hook. */
213 static enum ld_plugin_status
214 onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
215 {
216 enum ld_plugin_status rv;
217
218 *claimed = 0;
219
220 /* If we've already seen this file, ignore it. */
221 if (prevfile && !strcmp (file->name, prevfile))
222 return LDPS_OK;
223
224 /* If it's not an archive member, ignore it. */
225 if (!file->offset)
226 return LDPS_OK;
227
228 if (prevfile)
229 free (prevfile);
230
231 prevfile = strdup (file->name);
232 if (!prevfile)
233 return LDPS_ERR;
234
235 /* This hook only gets called on actual object files.
236 * We have to examine the archive ourselves, to find
237 * our LIBDEPS member. */
238 rv = get_libdeps (file->fd);
239 if (rv == LDPS_ERR)
240 return rv;
241
242 if (rv == LDPS_OK)
243 {
244 linerec *lr = (linerec *)line_tail;
245 /* Inform the user/testsuite. */
246 TV_MESSAGE (LDPL_INFO, "got deps for library %s: %s",
247 file->name, lr->line);
248 fflush (NULL);
249 }
250
251 return LDPS_OK;
252 }
253
254 /* Standard plugin API registerable hook. */
255 static enum ld_plugin_status
256 onall_symbols_read (void)
257 {
258 linerec *lr;
259 char **vec;
260 enum ld_plugin_status rv = LDPS_OK;
261
262 while ((lr = line_head))
263 {
264 line_head = lr->next;
265 vec = str2vec (lr->line);
266 if (vec)
267 {
268 int i;
269 for (i = 0; vec[i]; i++)
270 {
271 if (vec[i][0] != '-')
272 {
273 TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s",
274 vec[i]);
275 fflush (NULL);
276 continue;
277 }
278 if (vec[i][1] == 'l')
279 rv = tv_add_input_library (vec[i]+2);
280 else if (vec[i][1] == 'L')
281 rv = tv_set_extra_library_path (vec[i]+2);
282 else
283 {
284 TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s",
285 vec[i]);
286 fflush (NULL);
287 }
288 if (rv != LDPS_OK)
289 break;
290 }
291 free (vec);
292 }
293 free (lr);
294 }
295 line_tail = NULL;
296 return rv;
297 }
298
299 /* Standard plugin API registerable hook. */
300 static enum ld_plugin_status
301 oncleanup (void)
302 {
303 if (prevfile)
304 {
305 free (prevfile);
306 prevfile = NULL;
307 }
308 if (line_head)
309 {
310 linerec *lr;
311 while ((lr = line_head))
312 {
313 line_head = lr->next;
314 free (lr);
315 }
316 line_tail = NULL;
317 }
318 return LDPS_OK;
319 }
320
321 /* Standard plugin API entry point. */
322 enum ld_plugin_status
323 onload (struct ld_plugin_tv *tv)
324 {
325 enum ld_plugin_status rv;
326
327 /* This plugin requires a valid tv array. */
328 if (!tv)
329 return LDPS_ERR;
330
331 /* First entry should always be LDPT_MESSAGE, letting us get
332 hold of it easily so we can send output straight away. */
333 if (tv[0].tv_tag == LDPT_MESSAGE)
334 tv_message = tv[0].tv_u.tv_message;
335
336 do
337 if ((rv = parse_tv_tag (tv)) != LDPS_OK)
338 return rv;
339 while ((tv++)->tv_tag != LDPT_NULL);
340
341 /* Register hooks. */
342 if (tv_register_claim_file
343 && tv_register_all_symbols_read
344 && tv_register_cleanup)
345 {
346 (*tv_register_claim_file) (onclaim_file);
347 (*tv_register_all_symbols_read) (onall_symbols_read);
348 (*tv_register_cleanup) (oncleanup);
349 }
350 fflush (NULL);
351 return LDPS_OK;
352 }
353 #endif /* BFD_SUPPORTS_PLUGINS */