]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - ld/testplug3.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / ld / testplug3.c
1 /* Test plugin for the GNU linker. Check non-object IR file and calling
2 release_input_file from onclaim_file.
3 Copyright (C) 2015-2023 Free Software Foundation, Inc.
4
5 This file is part of the GNU Binutils.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20 MA 02110-1301, USA. */
21
22 #include "sysdep.h"
23 #include "bfd.h"
24 #if BFD_SUPPORTS_PLUGINS
25 #include "plugin-api.h"
26 #include "filenames.h"
27 /* For ARRAY_SIZE macro only - we don't link the library itself. */
28 #include "libiberty.h"
29
30 extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
31 static enum ld_plugin_status onclaim_file (const struct ld_plugin_input_file *file,
32 int *claimed);
33 static enum ld_plugin_status onall_symbols_read (void);
34 static enum ld_plugin_status oncleanup (void);
35
36 /* Helper for calling plugin api message function. */
37 #define TV_MESSAGE if (tv_message) (*tv_message)
38
39 /* Struct for recording files to claim / files claimed. */
40 typedef struct claim_file
41 {
42 struct claim_file *next;
43 struct ld_plugin_input_file file;
44 bool claimed;
45 struct ld_plugin_symbol *symbols;
46 int n_syms_allocated;
47 int n_syms_used;
48 } claim_file_t;
49
50 /* Types of things that can be added at all symbols read time. */
51 typedef enum addfile_enum
52 {
53 ADD_FILE,
54 ADD_LIB,
55 ADD_DIR
56 } addfile_enum_t;
57
58 /* Struct for recording files to add to final link. */
59 typedef struct add_file
60 {
61 struct add_file *next;
62 const char *name;
63 addfile_enum_t type;
64 } add_file_t;
65
66 /* Helper macro for defining array of transfer vector tags and names. */
67 #define ADDENTRY(tag) { tag, #tag }
68
69 /* Struct for looking up human-readable versions of tag names. */
70 typedef struct tag_name
71 {
72 enum ld_plugin_tag tag;
73 const char *name;
74 } tag_name_t;
75
76 /* Array of all known tags and their names. */
77 static const tag_name_t tag_names[] =
78 {
79 ADDENTRY(LDPT_NULL),
80 ADDENTRY(LDPT_API_VERSION),
81 ADDENTRY(LDPT_GOLD_VERSION),
82 ADDENTRY(LDPT_LINKER_OUTPUT),
83 ADDENTRY(LDPT_OPTION),
84 ADDENTRY(LDPT_REGISTER_CLAIM_FILE_HOOK),
85 ADDENTRY(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK),
86 ADDENTRY(LDPT_REGISTER_CLEANUP_HOOK),
87 ADDENTRY(LDPT_ADD_SYMBOLS),
88 ADDENTRY(LDPT_GET_SYMBOLS),
89 ADDENTRY(LDPT_GET_SYMBOLS_V2),
90 ADDENTRY(LDPT_ADD_INPUT_FILE),
91 ADDENTRY(LDPT_MESSAGE),
92 ADDENTRY(LDPT_GET_INPUT_FILE),
93 ADDENTRY(LDPT_GET_VIEW),
94 ADDENTRY(LDPT_RELEASE_INPUT_FILE),
95 ADDENTRY(LDPT_ADD_INPUT_LIBRARY),
96 ADDENTRY(LDPT_OUTPUT_NAME),
97 ADDENTRY(LDPT_SET_EXTRA_LIBRARY_PATH),
98 ADDENTRY(LDPT_GNU_LD_VERSION)
99 };
100
101 /* Function pointers to cache hooks passed at onload time. */
102 static ld_plugin_register_claim_file tv_register_claim_file = 0;
103 static ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0;
104 static ld_plugin_register_cleanup tv_register_cleanup = 0;
105 static ld_plugin_add_symbols tv_add_symbols = 0;
106 static ld_plugin_get_symbols tv_get_symbols = 0;
107 static ld_plugin_get_symbols tv_get_symbols_v2 = 0;
108 static ld_plugin_add_input_file tv_add_input_file = 0;
109 static ld_plugin_message tv_message = 0;
110 static ld_plugin_get_input_file tv_get_input_file = 0;
111 static ld_plugin_get_view tv_get_view = 0;
112 static ld_plugin_release_input_file tv_release_input_file = 0;
113 static ld_plugin_add_input_library tv_add_input_library = 0;
114 static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0;
115
116 /* Other cached info from the transfer vector. */
117 static enum ld_plugin_output_file_type linker_output;
118 static const char *output_name;
119
120 /* Behaviour control flags set by plugin options. */
121 static enum ld_plugin_status onload_ret = LDPS_OK;
122 static enum ld_plugin_status claim_file_ret = LDPS_OK;
123 static enum ld_plugin_status all_symbols_read_ret = LDPS_OK;
124 static enum ld_plugin_status cleanup_ret = LDPS_OK;
125 static bool register_claimfile_hook = true;
126 static bool register_allsymbolsread_hook = false;
127 static bool register_cleanup_hook = false;
128 static bool dumpresolutions = false;
129
130 /* The master list of all claimable/claimed files. */
131 static claim_file_t *claimfiles_list = NULL;
132
133 /* We keep a tail pointer for easy linking on the end. */
134 static claim_file_t **claimfiles_tail_chain_ptr = &claimfiles_list;
135
136 /* The last claimed file added to the list, for receiving syms. */
137 static claim_file_t *last_claimfile = NULL;
138
139 /* The master list of all files to add to the final link. */
140 static add_file_t *addfiles_list = NULL;
141
142 /* We keep a tail pointer for easy linking on the end. */
143 static add_file_t **addfiles_tail_chain_ptr = &addfiles_list;
144
145 /* Add a new claimfile on the end of the chain. */
146 static enum ld_plugin_status
147 record_claim_file (const char *file, off_t filesize)
148 {
149 claim_file_t *newfile;
150
151 newfile = malloc (sizeof *newfile);
152 if (!newfile)
153 return LDPS_ERR;
154 memset (newfile, 0, sizeof *newfile);
155 /* Only setup for now is remembering the name to look for. */
156 newfile->file.name = file;
157 newfile->file.filesize = filesize;
158 /* Chain it on the end of the list. */
159 *claimfiles_tail_chain_ptr = newfile;
160 claimfiles_tail_chain_ptr = &newfile->next;
161 /* Record it as active for receiving symbols to register. */
162 last_claimfile = newfile;
163 return LDPS_OK;
164 }
165
166 /* Add a new addfile on the end of the chain. */
167 static enum ld_plugin_status
168 record_add_file (const char *file, addfile_enum_t type)
169 {
170 add_file_t *newfile;
171
172 newfile = malloc (sizeof *newfile);
173 if (!newfile)
174 return LDPS_ERR;
175 newfile->next = NULL;
176 newfile->name = file;
177 newfile->type = type;
178 /* Chain it on the end of the list. */
179 *addfiles_tail_chain_ptr = newfile;
180 addfiles_tail_chain_ptr = &newfile->next;
181 return LDPS_OK;
182 }
183
184 /* Parse a command-line argument string into a symbol definition.
185 Symbol-strings follow the colon-separated format:
186 NAME:VERSION:def:vis:size:COMDATKEY
187 where the fields in capitals are strings and those in lower
188 case are integers. We don't allow to specify a resolution as
189 doing so is not meaningful when calling the add symbols hook. */
190 static enum ld_plugin_status
191 parse_symdefstr (const char *str, struct ld_plugin_symbol *sym)
192 {
193 int n;
194 long long size;
195 const char *colon1, *colon2, *colon5;
196
197 /* Locate the colons separating the first two strings. */
198 colon1 = strchr (str, ':');
199 if (!colon1)
200 return LDPS_ERR;
201 colon2 = strchr (colon1+1, ':');
202 if (!colon2)
203 return LDPS_ERR;
204 /* Name must not be empty (version may be). */
205 if (colon1 == str)
206 return LDPS_ERR;
207
208 /* The fifth colon and trailing comdat key string are optional,
209 but the intermediate ones must all be present. */
210 colon5 = strchr (colon2+1, ':'); /* Actually only third so far. */
211 if (!colon5)
212 return LDPS_ERR;
213 colon5 = strchr (colon5+1, ':'); /* Hopefully fourth now. */
214 if (!colon5)
215 return LDPS_ERR;
216 colon5 = strchr (colon5+1, ':'); /* Optional fifth now. */
217
218 /* Finally we'll use sscanf to parse the numeric fields, then
219 we'll split out the strings which we need to allocate separate
220 storage for anyway so that we can add nul termination. */
221 n = sscanf (colon2 + 1, "%hhi:%i:%lli", &sym->def, &sym->visibility, &size);
222 if (n != 3)
223 return LDPS_ERR;
224
225 /* Parsed successfully, so allocate strings and fill out fields. */
226 sym->size = size;
227 sym->unused = 0;
228 sym->section_kind = 0;
229 sym->symbol_type = 0;
230 sym->resolution = LDPR_UNKNOWN;
231 sym->name = malloc (colon1 - str + 1);
232 if (!sym->name)
233 return LDPS_ERR;
234 memcpy (sym->name, str, colon1 - str);
235 sym->name[colon1 - str] = '\0';
236 if (colon2 > (colon1 + 1))
237 {
238 sym->version = malloc (colon2 - colon1);
239 if (!sym->version)
240 return LDPS_ERR;
241 memcpy (sym->version, colon1 + 1, colon2 - (colon1 + 1));
242 sym->version[colon2 - (colon1 + 1)] = '\0';
243 }
244 else
245 sym->version = NULL;
246 if (colon5 && colon5[1])
247 {
248 sym->comdat_key = malloc (strlen (colon5 + 1) + 1);
249 if (!sym->comdat_key)
250 return LDPS_ERR;
251 strcpy (sym->comdat_key, colon5 + 1);
252 }
253 else
254 sym->comdat_key = 0;
255 return LDPS_OK;
256 }
257
258 /* Record a symbol to be added for the last-added claimfile. */
259 static enum ld_plugin_status
260 record_claimed_file_symbol (const char *symdefstr)
261 {
262 struct ld_plugin_symbol sym;
263
264 /* Can't add symbols except as belonging to claimed files. */
265 if (!last_claimfile)
266 return LDPS_ERR;
267
268 /* If string doesn't parse correctly, give an error. */
269 if (parse_symdefstr (symdefstr, &sym) != LDPS_OK)
270 return LDPS_ERR;
271
272 /* Check for enough space, resize array if needed, and add it. */
273 if (last_claimfile->n_syms_allocated == last_claimfile->n_syms_used)
274 {
275 int new_n_syms = last_claimfile->n_syms_allocated
276 ? 2 * last_claimfile->n_syms_allocated
277 : 10;
278 last_claimfile->symbols = realloc (last_claimfile->symbols,
279 new_n_syms * sizeof *last_claimfile->symbols);
280 if (!last_claimfile->symbols)
281 return LDPS_ERR;
282 last_claimfile->n_syms_allocated = new_n_syms;
283 }
284 last_claimfile->symbols[last_claimfile->n_syms_used++] = sym;
285
286 return LDPS_OK;
287 }
288
289 /* Records the status to return from one of the registered hooks. */
290 static enum ld_plugin_status
291 set_ret_val (const char *whichval, enum ld_plugin_status retval)
292 {
293 if (!strcmp ("onload", whichval))
294 onload_ret = retval;
295 else if (!strcmp ("claimfile", whichval))
296 claim_file_ret = retval;
297 else if (!strcmp ("allsymbolsread", whichval))
298 all_symbols_read_ret = retval;
299 else if (!strcmp ("cleanup", whichval))
300 cleanup_ret = retval;
301 else
302 return LDPS_ERR;
303 return LDPS_OK;
304 }
305
306 /* Records hooks which should be registered. */
307 static enum ld_plugin_status
308 set_register_hook (const char *whichhook, bool yesno)
309 {
310 if (!strcmp ("claimfile", whichhook))
311 register_claimfile_hook = yesno;
312 else if (!strcmp ("allsymbolsread", whichhook))
313 register_allsymbolsread_hook = yesno;
314 else if (!strcmp ("cleanup", whichhook))
315 register_cleanup_hook = yesno;
316 else
317 return LDPS_ERR;
318 return LDPS_OK;
319 }
320
321 /* Determine type of plugin option and pass to individual parsers. */
322 static enum ld_plugin_status
323 parse_option (const char *opt)
324 {
325 if (!strncmp ("fail", opt, 4))
326 return set_ret_val (opt + 4, LDPS_ERR);
327 else if (!strncmp ("pass", opt, 4))
328 return set_ret_val (opt + 4, LDPS_OK);
329 else if (!strncmp ("register", opt, 8))
330 return set_register_hook (opt + 8, true);
331 else if (!strncmp ("noregister", opt, 10))
332 return set_register_hook (opt + 10, false);
333 else if (!strncmp ("claim:", opt, 6))
334 return record_claim_file (opt + 6, 0);
335 else if (!strncmp ("sym:", opt, 4))
336 return record_claimed_file_symbol (opt + 4);
337 else if (!strncmp ("add:", opt, 4))
338 return record_add_file (opt + 4, ADD_FILE);
339 else if (!strncmp ("lib:", opt, 4))
340 return record_add_file (opt + 4, ADD_LIB);
341 else if (!strncmp ("dir:", opt, 4))
342 return record_add_file (opt + 4, ADD_DIR);
343 else if (!strcmp ("dumpresolutions", opt))
344 dumpresolutions = true;
345 else
346 return LDPS_ERR;
347 return LDPS_OK;
348 }
349
350 /* Handle/record information received in a transfer vector entry. */
351 static enum ld_plugin_status
352 parse_tv_tag (struct ld_plugin_tv *tv)
353 {
354 #define SETVAR(x) x = tv->tv_u.x
355 switch (tv->tv_tag)
356 {
357 case LDPT_OPTION:
358 return parse_option (tv->tv_u.tv_string);
359 case LDPT_NULL:
360 case LDPT_GOLD_VERSION:
361 case LDPT_GNU_LD_VERSION:
362 case LDPT_API_VERSION:
363 default:
364 break;
365 case LDPT_OUTPUT_NAME:
366 output_name = tv->tv_u.tv_string;
367 break;
368 case LDPT_LINKER_OUTPUT:
369 linker_output = tv->tv_u.tv_val;
370 break;
371 case LDPT_REGISTER_CLAIM_FILE_HOOK:
372 SETVAR(tv_register_claim_file);
373 break;
374 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
375 SETVAR(tv_register_all_symbols_read);
376 break;
377 case LDPT_REGISTER_CLEANUP_HOOK:
378 SETVAR(tv_register_cleanup);
379 break;
380 case LDPT_ADD_SYMBOLS:
381 SETVAR(tv_add_symbols);
382 break;
383 case LDPT_GET_SYMBOLS:
384 SETVAR(tv_get_symbols);
385 break;
386 case LDPT_GET_SYMBOLS_V2:
387 tv_get_symbols_v2 = tv->tv_u.tv_get_symbols;
388 break;
389 case LDPT_ADD_INPUT_FILE:
390 SETVAR(tv_add_input_file);
391 break;
392 case LDPT_MESSAGE:
393 SETVAR(tv_message);
394 break;
395 case LDPT_GET_INPUT_FILE:
396 SETVAR(tv_get_input_file);
397 break;
398 case LDPT_GET_VIEW:
399 SETVAR(tv_get_view);
400 break;
401 case LDPT_RELEASE_INPUT_FILE:
402 SETVAR(tv_release_input_file);
403 break;
404 case LDPT_ADD_INPUT_LIBRARY:
405 SETVAR(tv_add_input_library);
406 break;
407 case LDPT_SET_EXTRA_LIBRARY_PATH:
408 SETVAR(tv_set_extra_library_path);
409 break;
410 }
411 #undef SETVAR
412 return LDPS_OK;
413 }
414
415 /* Standard plugin API entry point. */
416 enum ld_plugin_status
417 onload (struct ld_plugin_tv *tv)
418 {
419 enum ld_plugin_status rv;
420
421 /* This plugin does nothing but dump the tv array. It would
422 be an error if this function was called without one. */
423 if (!tv)
424 return LDPS_ERR;
425
426 /* First entry should always be LDPT_MESSAGE, letting us get
427 hold of it easily so we can send output straight away. */
428 if (tv[0].tv_tag == LDPT_MESSAGE)
429 tv_message = tv[0].tv_u.tv_message;
430
431 do
432 if ((rv = parse_tv_tag (tv)) != LDPS_OK)
433 return rv;
434 while ((tv++)->tv_tag != LDPT_NULL);
435
436 /* Register hooks only if instructed by options. */
437 if (register_claimfile_hook)
438 {
439 if (!tv_register_claim_file)
440 {
441 TV_MESSAGE (LDPL_FATAL, "No register_claim_file hook");
442 fflush (NULL);
443 return LDPS_ERR;
444 }
445 (*tv_register_claim_file) (onclaim_file);
446 }
447 if (register_allsymbolsread_hook)
448 {
449 if (!tv_register_all_symbols_read)
450 {
451 TV_MESSAGE (LDPL_FATAL, "No register_all_symbols_read hook");
452 fflush (NULL);
453 return LDPS_ERR;
454 }
455 (*tv_register_all_symbols_read) (onall_symbols_read);
456 }
457 if (register_cleanup_hook)
458 {
459 if (!tv_register_cleanup)
460 {
461 TV_MESSAGE (LDPL_FATAL, "No register_cleanup hook");
462 fflush (NULL);
463 return LDPS_ERR;
464 }
465 (*tv_register_cleanup) (oncleanup);
466 }
467
468 /* Claim testsuite/ld-plugin/func.c, standalone or in a library. Its
469 size must be SIZE_OF_FUNC_C bytes. */
470 #define SIZE_OF_FUNC_C 248
471 if (onload_ret == LDPS_OK
472 && (record_claim_file ("func.c", SIZE_OF_FUNC_C) != LDPS_OK
473 || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
474 || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK
475 || record_claim_file ("libfunc.a", SIZE_OF_FUNC_C) != LDPS_OK
476 || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
477 || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK))
478 onload_ret = LDPS_ERR;
479
480 return onload_ret;
481 }
482
483 char *
484 xstrdup (const char *s)
485 {
486 size_t len = strlen (s) + 1;
487 char *ret = malloc (len + 1);
488 return (char *) memcpy (ret, s, len);
489 }
490
491 /* Standard plugin API registerable hook. */
492 static enum ld_plugin_status
493 onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
494 {
495 /* Let's see if we want to claim this file. */
496 claim_file_t *claimfile = claimfiles_list;
497 size_t len = strlen (file->name);
498 char *name = xstrdup (file->name);
499 char *p = name + len;
500 bool islib;
501
502 /* Only match the file name without the directory part. */
503 islib = *p == 'a' && *(p - 1) == '.';
504 for (; p != name; p--)
505 if (IS_DIR_SEPARATOR (*p))
506 {
507 p++;
508 break;
509 }
510
511 while (claimfile)
512 {
513 /* Claim the file only if the file name and size match and don't
514 match the whole library. */
515 if (!strcmp (p, claimfile->file.name)
516 && claimfile->file.filesize == file->filesize
517 && (!islib || file->offset != 0))
518 break;
519 claimfile = claimfile->next;
520 }
521
522 free (name);
523
524 /* If we decided to claim it, record that fact, and add any symbols
525 that were defined for it by plugin options. */
526 *claimed = (claimfile != 0);
527 if (claimfile)
528 {
529 char buffer[30];
530 int fd;
531
532 TV_MESSAGE (LDPL_INFO, "Claimed: %s [@%ld/%ld]", file->name,
533 (long)file->offset, (long)file->filesize);
534
535 claimfile->claimed = true;
536 claimfile->file = *file;
537 if (claimfile->n_syms_used && !tv_add_symbols)
538 claim_file_ret = LDPS_ERR;
539 else if (claimfile->n_syms_used)
540 claim_file_ret = (*tv_add_symbols) (claimfile->file.handle,
541 claimfile->n_syms_used,
542 claimfile->symbols);
543
544 fd = claimfile->file.fd;
545 name = xstrdup (claimfile->file.name);
546 claim_file_ret = tv_release_input_file (claimfile->file.handle);
547 if (claim_file_ret != LDPS_OK)
548 {
549 free (name);
550 return claim_file_ret;
551 }
552 if (read (fd, buffer, sizeof (buffer)) >= 0)
553 {
554 claim_file_ret = LDPS_ERR;
555 TV_MESSAGE (LDPL_FATAL, "Unreleased file descriptor on: %s", name);
556 }
557 free (name);
558 }
559
560 return claim_file_ret;
561 }
562
563 /* Standard plugin API registerable hook. */
564 static enum ld_plugin_status
565 onall_symbols_read (void)
566 {
567 static const char *resolutions[] =
568 {
569 "LDPR_UNKNOWN",
570 "LDPR_UNDEF",
571 "LDPR_PREVAILING_DEF",
572 "LDPR_PREVAILING_DEF_IRONLY",
573 "LDPR_PREEMPTED_REG",
574 "LDPR_PREEMPTED_IR",
575 "LDPR_RESOLVED_IR",
576 "LDPR_RESOLVED_EXEC",
577 "LDPR_RESOLVED_DYN",
578 "LDPR_PREVAILING_DEF_IRONLY_EXP",
579 };
580 claim_file_t *claimfile = dumpresolutions ? claimfiles_list : NULL;
581 add_file_t *addfile = addfiles_list;
582 TV_MESSAGE (LDPL_INFO, "hook called: all symbols read.");
583 for ( ; claimfile; claimfile = claimfile->next)
584 {
585 enum ld_plugin_status rv;
586 int n;
587 if (claimfile->n_syms_used && !tv_get_symbols_v2)
588 return LDPS_ERR;
589 else if (!claimfile->n_syms_used)
590 continue;
591 else if (!claimfile->file.handle)
592 continue;
593 rv = tv_get_symbols_v2 (claimfile->file.handle, claimfile->n_syms_used,
594 claimfile->symbols);
595 if (rv != LDPS_OK)
596 return rv;
597 for (n = 0; n < claimfile->n_syms_used; n++)
598 TV_MESSAGE (LDPL_INFO, "Sym: '%s%s%s' Resolution: %s",
599 claimfile->symbols[n].name,
600 claimfile->symbols[n].version ? "@" : "",
601 (claimfile->symbols[n].version
602 ? claimfile->symbols[n].version : ""),
603 resolutions[claimfile->symbols[n].resolution]);
604 }
605 for ( ; addfile ; addfile = addfile->next)
606 {
607 enum ld_plugin_status rv;
608 if (addfile->type == ADD_LIB && tv_add_input_library)
609 rv = (*tv_add_input_library) (addfile->name);
610 else if (addfile->type == ADD_FILE && tv_add_input_file)
611 rv = (*tv_add_input_file) (addfile->name);
612 else if (addfile->type == ADD_DIR && tv_set_extra_library_path)
613 rv = (*tv_set_extra_library_path) (addfile->name);
614 else
615 rv = LDPS_ERR;
616 if (rv != LDPS_OK)
617 return rv;
618 }
619 fflush (NULL);
620 return all_symbols_read_ret;
621 }
622
623 /* Standard plugin API registerable hook. */
624 static enum ld_plugin_status
625 oncleanup (void)
626 {
627 TV_MESSAGE (LDPL_INFO, "hook called: cleanup.");
628 fflush (NULL);
629 return cleanup_ret;
630 }
631 #endif /* BFD_SUPPORTS_PLUGINS */