1 /* Test plugin for the GNU linker. Check non-object IR file as well as
2 get_input_file, get_view and release_input_file interfaces.
3 Copyright (C) 2015-2023 Free Software Foundation, Inc.
5 This file is part of the GNU Binutils.
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.
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.
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. */
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"
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
,
33 static enum ld_plugin_status
onall_symbols_read (void);
34 static enum ld_plugin_status
oncleanup (void);
36 /* Helper for calling plugin api message function. */
37 #define TV_MESSAGE if (tv_message) (*tv_message)
39 /* Struct for recording files to claim / files claimed. */
40 typedef struct claim_file
42 struct claim_file
*next
;
43 struct ld_plugin_input_file file
;
45 struct ld_plugin_symbol
*symbols
;
50 /* Types of things that can be added at all symbols read time. */
51 typedef enum addfile_enum
58 /* Struct for recording files to add to final link. */
59 typedef struct add_file
61 struct add_file
*next
;
66 /* Helper macro for defining array of transfer vector tags and names. */
67 #define ADDENTRY(tag) { tag, #tag }
69 /* Struct for looking up human-readable versions of tag names. */
70 typedef struct tag_name
72 enum ld_plugin_tag tag
;
76 /* Array of all known tags and their names. */
77 static const tag_name_t tag_names
[] =
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_CLAIM_FILE_HOOK_V2
),
86 ADDENTRY(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
),
87 ADDENTRY(LDPT_REGISTER_CLEANUP_HOOK
),
88 ADDENTRY(LDPT_ADD_SYMBOLS
),
89 ADDENTRY(LDPT_GET_SYMBOLS
),
90 ADDENTRY(LDPT_GET_SYMBOLS_V2
),
91 ADDENTRY(LDPT_ADD_INPUT_FILE
),
92 ADDENTRY(LDPT_MESSAGE
),
93 ADDENTRY(LDPT_GET_INPUT_FILE
),
94 ADDENTRY(LDPT_GET_VIEW
),
95 ADDENTRY(LDPT_RELEASE_INPUT_FILE
),
96 ADDENTRY(LDPT_ADD_INPUT_LIBRARY
),
97 ADDENTRY(LDPT_OUTPUT_NAME
),
98 ADDENTRY(LDPT_SET_EXTRA_LIBRARY_PATH
),
99 ADDENTRY(LDPT_GNU_LD_VERSION
)
102 /* Function pointers to cache hooks passed at onload time. */
103 static ld_plugin_register_claim_file tv_register_claim_file
= 0;
104 static ld_plugin_register_claim_file_v2 tv_register_claim_file_v2
= 0;
105 static ld_plugin_register_all_symbols_read tv_register_all_symbols_read
= 0;
106 static ld_plugin_register_cleanup tv_register_cleanup
= 0;
107 static ld_plugin_add_symbols tv_add_symbols
= 0;
108 static ld_plugin_get_symbols tv_get_symbols
= 0;
109 static ld_plugin_get_symbols tv_get_symbols_v2
= 0;
110 static ld_plugin_add_input_file tv_add_input_file
= 0;
111 static ld_plugin_message tv_message
= 0;
112 static ld_plugin_get_input_file tv_get_input_file
= 0;
113 static ld_plugin_get_view tv_get_view
= 0;
114 static ld_plugin_release_input_file tv_release_input_file
= 0;
115 static ld_plugin_add_input_library tv_add_input_library
= 0;
116 static ld_plugin_set_extra_library_path tv_set_extra_library_path
= 0;
118 /* Other cached info from the transfer vector. */
119 static enum ld_plugin_output_file_type linker_output
;
120 static const char *output_name
;
122 /* Behaviour control flags set by plugin options. */
123 static enum ld_plugin_status onload_ret
= LDPS_OK
;
124 static enum ld_plugin_status claim_file_ret
= LDPS_OK
;
125 static enum ld_plugin_status all_symbols_read_ret
= LDPS_OK
;
126 static enum ld_plugin_status cleanup_ret
= LDPS_OK
;
127 static bool register_claimfile_hook
= true;
128 static bool register_allsymbolsread_hook
= false;
129 static bool register_cleanup_hook
= false;
130 static bool dumpresolutions
= false;
131 static bool allsymbolsread_silent
= false;
133 /* The master list of all claimable/claimed files. */
134 static claim_file_t
*claimfiles_list
= NULL
;
136 /* We keep a tail pointer for easy linking on the end. */
137 static claim_file_t
**claimfiles_tail_chain_ptr
= &claimfiles_list
;
139 /* The last claimed file added to the list, for receiving syms. */
140 static claim_file_t
*last_claimfile
= NULL
;
142 /* The master list of all files to add to the final link. */
143 static add_file_t
*addfiles_list
= NULL
;
145 /* We keep a tail pointer for easy linking on the end. */
146 static add_file_t
**addfiles_tail_chain_ptr
= &addfiles_list
;
148 /* Add a new claimfile on the end of the chain. */
149 static enum ld_plugin_status
150 record_claim_file (const char *file
, off_t filesize
)
152 claim_file_t
*newfile
;
154 newfile
= malloc (sizeof *newfile
);
157 memset (newfile
, 0, sizeof *newfile
);
158 /* Only setup for now is remembering the name to look for. */
159 newfile
->file
.name
= file
;
160 newfile
->file
.filesize
= filesize
;
161 /* Chain it on the end of the list. */
162 *claimfiles_tail_chain_ptr
= newfile
;
163 claimfiles_tail_chain_ptr
= &newfile
->next
;
164 /* Record it as active for receiving symbols to register. */
165 last_claimfile
= newfile
;
169 /* Add a new addfile on the end of the chain. */
170 static enum ld_plugin_status
171 record_add_file (const char *file
, addfile_enum_t type
)
175 newfile
= malloc (sizeof *newfile
);
178 newfile
->next
= NULL
;
179 newfile
->name
= file
;
180 newfile
->type
= type
;
181 /* Chain it on the end of the list. */
182 *addfiles_tail_chain_ptr
= newfile
;
183 addfiles_tail_chain_ptr
= &newfile
->next
;
187 /* Parse a command-line argument string into a symbol definition.
188 Symbol-strings follow the colon-separated format:
189 NAME:VERSION:def:vis:size:COMDATKEY
190 where the fields in capitals are strings and those in lower
191 case are integers. We don't allow to specify a resolution as
192 doing so is not meaningful when calling the add symbols hook. */
193 static enum ld_plugin_status
194 parse_symdefstr (const char *str
, struct ld_plugin_symbol
*sym
)
198 const char *colon1
, *colon2
, *colon5
;
200 /* Locate the colons separating the first two strings. */
201 colon1
= strchr (str
, ':');
204 colon2
= strchr (colon1
+1, ':');
207 /* Name must not be empty (version may be). */
211 /* The fifth colon and trailing comdat key string are optional,
212 but the intermediate ones must all be present. */
213 colon5
= strchr (colon2
+1, ':'); /* Actually only third so far. */
216 colon5
= strchr (colon5
+1, ':'); /* Hopefully fourth now. */
219 colon5
= strchr (colon5
+1, ':'); /* Optional fifth now. */
221 /* Finally we'll use sscanf to parse the numeric fields, then
222 we'll split out the strings which we need to allocate separate
223 storage for anyway so that we can add nul termination. */
224 n
= sscanf (colon2
+ 1, "%hhi:%i:%lli", &sym
->def
, &sym
->visibility
, &size
);
228 /* Parsed successfully, so allocate strings and fill out fields. */
231 sym
->section_kind
= 0;
232 sym
->symbol_type
= 0;
233 sym
->resolution
= LDPR_UNKNOWN
;
234 sym
->name
= malloc (colon1
- str
+ 1);
237 memcpy (sym
->name
, str
, colon1
- str
);
238 sym
->name
[colon1
- str
] = '\0';
239 if (colon2
> (colon1
+ 1))
241 sym
->version
= malloc (colon2
- colon1
);
244 memcpy (sym
->version
, colon1
+ 1, colon2
- (colon1
+ 1));
245 sym
->version
[colon2
- (colon1
+ 1)] = '\0';
249 if (colon5
&& colon5
[1])
251 sym
->comdat_key
= malloc (strlen (colon5
+ 1) + 1);
252 if (!sym
->comdat_key
)
254 strcpy (sym
->comdat_key
, colon5
+ 1);
261 /* Record a symbol to be added for the last-added claimfile. */
262 static enum ld_plugin_status
263 record_claimed_file_symbol (const char *symdefstr
)
265 struct ld_plugin_symbol sym
;
267 /* Can't add symbols except as belonging to claimed files. */
271 /* If string doesn't parse correctly, give an error. */
272 if (parse_symdefstr (symdefstr
, &sym
) != LDPS_OK
)
275 /* Check for enough space, resize array if needed, and add it. */
276 if (last_claimfile
->n_syms_allocated
== last_claimfile
->n_syms_used
)
278 int new_n_syms
= last_claimfile
->n_syms_allocated
279 ? 2 * last_claimfile
->n_syms_allocated
281 last_claimfile
->symbols
= realloc (last_claimfile
->symbols
,
282 new_n_syms
* sizeof *last_claimfile
->symbols
);
283 if (!last_claimfile
->symbols
)
285 last_claimfile
->n_syms_allocated
= new_n_syms
;
287 last_claimfile
->symbols
[last_claimfile
->n_syms_used
++] = sym
;
292 /* Records the status to return from one of the registered hooks. */
293 static enum ld_plugin_status
294 set_ret_val (const char *whichval
, enum ld_plugin_status retval
)
296 if (!strcmp ("onload", whichval
))
298 else if (!strcmp ("claimfile", whichval
))
299 claim_file_ret
= retval
;
300 else if (!strcmp ("allsymbolsread", whichval
))
301 all_symbols_read_ret
= retval
;
302 else if (!strcmp ("cleanup", whichval
))
303 cleanup_ret
= retval
;
309 /* Records hooks which should be registered. */
310 static enum ld_plugin_status
311 set_register_hook (const char *whichhook
, bool yesno
)
313 if (!strcmp ("claimfile", whichhook
))
314 register_claimfile_hook
= yesno
;
315 else if (!strcmp ("allsymbolsread", whichhook
))
316 register_allsymbolsread_hook
= yesno
;
317 else if (!strcmp ("allsymbolsreadsilent", whichhook
))
319 register_allsymbolsread_hook
= yesno
;
320 allsymbolsread_silent
= true;
322 else if (!strcmp ("cleanup", whichhook
))
323 register_cleanup_hook
= yesno
;
329 /* Determine type of plugin option and pass to individual parsers. */
330 static enum ld_plugin_status
331 parse_option (const char *opt
)
333 if (!strncmp ("fatal", opt
, 5))
335 TV_MESSAGE (LDPL_FATAL
, "Fatal error");
338 else if (!strncmp ("error", opt
, 5))
340 TV_MESSAGE (LDPL_ERROR
, "Error");
343 else if (!strncmp ("warning", opt
, 7))
345 TV_MESSAGE (LDPL_WARNING
, "Warning");
348 else if (!strncmp ("fail", opt
, 4))
349 return set_ret_val (opt
+ 4, LDPS_ERR
);
350 else if (!strncmp ("pass", opt
, 4))
351 return set_ret_val (opt
+ 4, LDPS_OK
);
352 else if (!strncmp ("register", opt
, 8))
353 return set_register_hook (opt
+ 8, true);
354 else if (!strncmp ("noregister", opt
, 10))
355 return set_register_hook (opt
+ 10, false);
356 else if (!strncmp ("claim:", opt
, 6))
357 return record_claim_file (opt
+ 6, 0);
358 else if (!strncmp ("sym:", opt
, 4))
359 return record_claimed_file_symbol (opt
+ 4);
360 else if (!strncmp ("add:", opt
, 4))
361 return record_add_file (opt
+ 4, ADD_FILE
);
362 else if (!strncmp ("lib:", opt
, 4))
363 return record_add_file (opt
+ 4, ADD_LIB
);
364 else if (!strncmp ("dir:", opt
, 4))
365 return record_add_file (opt
+ 4, ADD_DIR
);
366 else if (!strcmp ("dumpresolutions", opt
))
367 dumpresolutions
= true;
373 /* Handle/record information received in a transfer vector entry. */
374 static enum ld_plugin_status
375 parse_tv_tag (struct ld_plugin_tv
*tv
)
377 #define SETVAR(x) x = tv->tv_u.x
381 return parse_option (tv
->tv_u
.tv_string
);
383 case LDPT_GOLD_VERSION
:
384 case LDPT_GNU_LD_VERSION
:
385 case LDPT_API_VERSION
:
388 case LDPT_OUTPUT_NAME
:
389 output_name
= tv
->tv_u
.tv_string
;
391 case LDPT_LINKER_OUTPUT
:
392 linker_output
= tv
->tv_u
.tv_val
;
394 case LDPT_REGISTER_CLAIM_FILE_HOOK
:
395 SETVAR(tv_register_claim_file
);
397 case LDPT_REGISTER_CLAIM_FILE_HOOK_V2
:
398 SETVAR(tv_register_claim_file_v2
);
400 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
:
401 SETVAR(tv_register_all_symbols_read
);
403 case LDPT_REGISTER_CLEANUP_HOOK
:
404 SETVAR(tv_register_cleanup
);
406 case LDPT_ADD_SYMBOLS
:
407 SETVAR(tv_add_symbols
);
409 case LDPT_GET_SYMBOLS
:
410 SETVAR(tv_get_symbols
);
412 case LDPT_GET_SYMBOLS_V2
:
413 tv_get_symbols_v2
= tv
->tv_u
.tv_get_symbols
;
415 case LDPT_ADD_INPUT_FILE
:
416 SETVAR(tv_add_input_file
);
421 case LDPT_GET_INPUT_FILE
:
422 SETVAR(tv_get_input_file
);
427 case LDPT_RELEASE_INPUT_FILE
:
428 SETVAR(tv_release_input_file
);
430 case LDPT_ADD_INPUT_LIBRARY
:
431 SETVAR(tv_add_input_library
);
433 case LDPT_SET_EXTRA_LIBRARY_PATH
:
434 SETVAR(tv_set_extra_library_path
);
441 /* Standard plugin API entry point. */
442 enum ld_plugin_status
443 onload (struct ld_plugin_tv
*tv
)
445 enum ld_plugin_status rv
;
447 /* This plugin does nothing but dump the tv array. It would
448 be an error if this function was called without one. */
452 /* First entry should always be LDPT_MESSAGE, letting us get
453 hold of it easily so we can send output straight away. */
454 if (tv
[0].tv_tag
== LDPT_MESSAGE
)
455 tv_message
= tv
[0].tv_u
.tv_message
;
458 if ((rv
= parse_tv_tag (tv
)) != LDPS_OK
)
460 while ((tv
++)->tv_tag
!= LDPT_NULL
);
462 /* Register hooks only if instructed by options. */
463 if (register_claimfile_hook
)
465 if (!tv_register_claim_file
)
467 TV_MESSAGE (LDPL_FATAL
, "No register_claim_file hook");
471 (*tv_register_claim_file
) (onclaim_file
);
473 if (register_allsymbolsread_hook
)
475 if (!tv_register_all_symbols_read
)
477 TV_MESSAGE (LDPL_FATAL
, "No register_all_symbols_read hook");
481 (*tv_register_all_symbols_read
) (onall_symbols_read
);
483 if (register_cleanup_hook
)
485 if (!tv_register_cleanup
)
487 TV_MESSAGE (LDPL_FATAL
, "No register_cleanup hook");
491 (*tv_register_cleanup
) (oncleanup
);
494 /* Claim testsuite/ld-plugin/func.c, standalone or in a library. Its
495 size must be SIZE_OF_FUNC_C bytes. */
496 #define SIZE_OF_FUNC_C 248
497 if (onload_ret
== LDPS_OK
498 && (record_claim_file ("func.c", SIZE_OF_FUNC_C
) != LDPS_OK
499 || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
500 || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK
501 || record_claim_file ("libfunc.a", SIZE_OF_FUNC_C
) != LDPS_OK
502 || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
503 || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK
))
504 onload_ret
= LDPS_ERR
;
510 xstrdup (const char *s
)
512 size_t len
= strlen (s
) + 1;
513 char *ret
= malloc (len
+ 1);
514 return (char *) memcpy (ret
, s
, len
);
517 /* Standard plugin API registerable hook. */
518 static enum ld_plugin_status
519 onclaim_file (const struct ld_plugin_input_file
*file
, int *claimed
)
521 /* Let's see if we want to claim this file. */
522 claim_file_t
*claimfile
= claimfiles_list
;
523 size_t len
= strlen (file
->name
);
524 char *name
= xstrdup (file
->name
);
525 char *p
= name
+ len
;
528 /* Only match the file name without the directory part. */
529 islib
= *p
== 'a' && *(p
- 1) == '.';
530 for (; p
!= name
; p
--)
531 if (IS_DIR_SEPARATOR (*p
))
539 /* Claim the file only if the file name and size match and don't
540 match the whole library. */
541 if (!strcmp (p
, claimfile
->file
.name
)
542 && claimfile
->file
.filesize
== file
->filesize
543 && (!islib
|| file
->offset
!= 0))
545 claimfile
= claimfile
->next
;
550 /* If we decided to claim it, record that fact, and add any symbols
551 that were defined for it by plugin options. */
552 *claimed
= (claimfile
!= 0);
555 claimfile
->claimed
= true;
556 claimfile
->file
= *file
;
557 if (claimfile
->n_syms_used
&& !tv_add_symbols
)
559 else if (claimfile
->n_syms_used
)
560 return (*tv_add_symbols
) (claimfile
->file
.handle
,
561 claimfile
->n_syms_used
, claimfile
->symbols
);
564 return claim_file_ret
;
567 /* Standard plugin API registerable hook. */
568 static enum ld_plugin_status
569 onall_symbols_read (void)
571 static const char *resolutions
[] =
575 "LDPR_PREVAILING_DEF",
576 "LDPR_PREVAILING_DEF_IRONLY",
577 "LDPR_PREEMPTED_REG",
580 "LDPR_RESOLVED_EXEC",
582 "LDPR_PREVAILING_DEF_IRONLY_EXP",
584 claim_file_t
*claimfile
= dumpresolutions
? claimfiles_list
: NULL
;
585 add_file_t
*addfile
= addfiles_list
;
586 struct ld_plugin_input_file file
;
591 if (! allsymbolsread_silent
)
592 TV_MESSAGE (LDPL_INFO
, "hook called: all symbols read.");
593 for ( ; claimfile
; claimfile
= claimfile
->next
)
595 enum ld_plugin_status rv
;
597 if (claimfile
->n_syms_used
&& !tv_get_symbols_v2
)
599 else if (!claimfile
->n_syms_used
)
601 else if (!claimfile
->file
.handle
)
603 rv
= tv_get_input_file (claimfile
->file
.handle
, &file
);
606 TV_MESSAGE (LDPL_INFO
, "Input: %s (%s)", file
.name
,
607 claimfile
->file
.name
);
608 rv
= tv_get_view (claimfile
->file
.handle
, &view
);
611 #define EXPECTED_VIEW "/* The first line of this file must match the expectation of"
612 #define EXPECTED_VIEW_LENGTH (sizeof (EXPECTED_VIEW) - 1)
613 if (file
.filesize
!= SIZE_OF_FUNC_C
614 || SIZE_OF_FUNC_C
< EXPECTED_VIEW_LENGTH
615 || memcmp (view
, EXPECTED_VIEW
, EXPECTED_VIEW_LENGTH
) != 0)
617 char result
[EXPECTED_VIEW_LENGTH
+ 1];
618 memcpy (result
, view
, sizeof (result
));
619 result
[EXPECTED_VIEW_LENGTH
] = '\0';
620 TV_MESSAGE (LDPL_INFO
, "Incorrect view:");
621 TV_MESSAGE (LDPL_INFO
, " Expect: " EXPECTED_VIEW
);
622 TV_MESSAGE (LDPL_INFO
, " Result: %s", result
);
624 rv
= tv_get_symbols_v2 (claimfile
->file
.handle
, claimfile
->n_syms_used
,
628 for (n
= 0; n
< claimfile
->n_syms_used
; n
++)
629 TV_MESSAGE (LDPL_INFO
, "Sym: '%s%s%s' Resolution: %s",
630 claimfile
->symbols
[n
].name
,
631 claimfile
->symbols
[n
].version
? "@" : "",
632 (claimfile
->symbols
[n
].version
633 ? claimfile
->symbols
[n
].version
: ""),
634 resolutions
[claimfile
->symbols
[n
].resolution
]);
635 fd
= claimfile
->file
.fd
;
636 filename
= xstrdup (claimfile
->file
.name
);
637 rv
= tv_release_input_file (claimfile
->file
.handle
);
643 if (read (fd
, buffer
, sizeof (buffer
)) >= 0)
645 TV_MESSAGE (LDPL_FATAL
, "Unreleased file descriptor on: %s",
646 claimfile
->file
.name
);
652 for ( ; addfile
; addfile
= addfile
->next
)
654 enum ld_plugin_status rv
;
655 if (addfile
->type
== ADD_LIB
&& tv_add_input_library
)
656 rv
= (*tv_add_input_library
) (addfile
->name
);
657 else if (addfile
->type
== ADD_FILE
&& tv_add_input_file
)
658 rv
= (*tv_add_input_file
) (addfile
->name
);
659 else if (addfile
->type
== ADD_DIR
&& tv_set_extra_library_path
)
660 rv
= (*tv_set_extra_library_path
) (addfile
->name
);
667 return all_symbols_read_ret
;
670 /* Standard plugin API registerable hook. */
671 static enum ld_plugin_status
674 TV_MESSAGE (LDPL_INFO
, "hook called: cleanup.");
678 #endif /* BFD_SUPPORTS_PLUGINS */