xref: /netbsd-src/external/gpl3/binutils.old/dist/ld/libdep_plugin.c (revision c42dbd0ed2e61fe6eda8590caa852ccf34719964)
1 /* libdeps plugin for the GNU linker.
2    Copyright (C) 2020-2022 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
parse_tv_tag(struct ld_plugin_tv * tv)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
get_libdeps(int fd)97 get_libdeps (int fd)
98 {
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;
105 
106   lseek (fd, ARMAGSIZE, SEEK_SET);
107   for (;;)
108     {
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))
114 	{
115 	  lseek (fd, mlen, SEEK_CUR);
116 	  continue;
117 	}
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;
131     }
132   return rc;
133 }
134 
135 /* Turn a string into an argvec.  */
136 static char **
str2vec(char * in)137 str2vec (char *in)
138 {
139   char **res;
140   char *s, *first, *end;
141   char *sq, *dq;
142   int i;
143 
144   end = in + strlen (in);
145   s = in;
146   while (isspace ((unsigned char) *s)) s++;
147   first = s;
148 
149   i = 1;
150   while ((s = strchr (s, ' ')))
151     {
152       s++;
153       i++;
154     }
155   res = (char **)malloc ((i+1) * sizeof (char *));
156   if (!res)
157     return res;
158 
159   i = 0;
160   sq = NULL;
161   dq = NULL;
162   res[0] = first;
163   for (s = first; *s; s++)
164     {
165       if (*s == '\\')
166 	{
167 	  memmove (s, s+1, end-s-1);
168 	  end--;
169 	}
170       if (isspace ((unsigned char) *s))
171 	{
172 	  if (sq || dq)
173 	    continue;
174 	  *s++ = '\0';
175 	  while (isspace ((unsigned char) *s)) s++;
176 	  if (*s)
177 	    res[++i] = s;
178 	}
179       if (*s == '\'' && !dq)
180 	{
181 	  if (sq)
182 	    {
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;
188 	    }
189 	  else
190 	    {
191 	      sq = s;
192 	    }
193 	}
194       if (*s == '"' && !sq)
195 	{
196 	  if (dq)
197 	    {
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;
203 	    }
204 	  else
205 	    {
206 	      dq = s;
207 	    }
208 	}
209     }
210   res[++i] = NULL;
211   return res;
212 }
213 
214 static char *prevfile;
215 
216 /* Standard plugin API registerable hook.  */
217 static enum ld_plugin_status
onclaim_file(const struct ld_plugin_input_file * file,int * claimed)218 onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
219 {
220   enum ld_plugin_status rv;
221 
222   *claimed = 0;
223 
224   /* If we've already seen this file, ignore it.  */
225   if (prevfile && !strcmp (file->name, prevfile))
226     return LDPS_OK;
227 
228   /* If it's not an archive member, ignore it.  */
229   if (!file->offset)
230     return LDPS_OK;
231 
232   if (prevfile)
233     free (prevfile);
234 
235   prevfile = strdup (file->name);
236   if (!prevfile)
237     return LDPS_ERR;
238 
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;
245 
246   if (rv == LDPS_OK)
247     {
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);
253     }
254 
255   return LDPS_OK;
256 }
257 
258 /* Standard plugin API registerable hook.  */
259 static enum ld_plugin_status
onall_symbols_read(void)260 onall_symbols_read (void)
261 {
262   linerec *lr;
263   char **vec;
264   enum ld_plugin_status rv = LDPS_OK;
265 
266   while ((lr = line_head))
267     {
268       line_head = lr->next;
269       vec = str2vec (lr->line);
270       if (vec)
271 	{
272 	  int i;
273 	  for (i = 0; vec[i]; i++)
274 	    {
275 	      if (vec[i][0] != '-')
276 		{
277 		  TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s",
278 			      vec[i]);
279 		  fflush (NULL);
280 		  continue;
281 		}
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
287 		{
288 		  TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s",
289 			      vec[i]);
290 		  fflush (NULL);
291 		}
292 	      if (rv != LDPS_OK)
293 		break;
294 	    }
295 	  free (vec);
296 	}
297       free (lr);
298     }
299   line_tail = NULL;
300   return rv;
301 }
302 
303 /* Standard plugin API registerable hook.  */
304 static enum ld_plugin_status
oncleanup(void)305 oncleanup (void)
306 {
307   if (prevfile)
308     {
309       free (prevfile);
310       prevfile = NULL;
311     }
312   if (line_head)
313     {
314       linerec *lr;
315       while ((lr = line_head))
316 	{
317 	  line_head = lr->next;
318 	  free (lr);
319 	}
320       line_tail = NULL;
321     }
322   return LDPS_OK;
323 }
324 
325 /* Standard plugin API entry point.  */
326 enum ld_plugin_status
onload(struct ld_plugin_tv * tv)327 onload (struct ld_plugin_tv *tv)
328 {
329   enum ld_plugin_status rv;
330 
331   /* This plugin requires a valid tv array.  */
332   if (!tv)
333     return LDPS_ERR;
334 
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;
339 
340   do
341     if ((rv = parse_tv_tag (tv)) != LDPS_OK)
342       return rv;
343   while ((tv++)->tv_tag != LDPT_NULL);
344 
345   /* Register hooks.  */
346   if (tv_register_claim_file
347       && tv_register_all_symbols_read
348       && tv_register_cleanup)
349     {
350       (*tv_register_claim_file) (onclaim_file);
351       (*tv_register_all_symbols_read) (onall_symbols_read);
352       (*tv_register_cleanup) (oncleanup);
353     }
354   fflush (NULL);
355   return LDPS_OK;
356 }
357 #endif /* BFD_SUPPORTS_PLUGINS */
358