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-2020 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 #include "plugin-api.h"
25 #include "filenames.h"
26 /* For ARRAY_SIZE macro only - we don't link the library itself. */
27 #include "libiberty.h"
29 extern enum ld_plugin_status
onload (struct ld_plugin_tv
*tv
);
30 static enum ld_plugin_status
onclaim_file (const struct ld_plugin_input_file
*file
,
32 static enum ld_plugin_status
onall_symbols_read (void);
33 static enum ld_plugin_status
oncleanup (void);
35 /* Helper for calling plugin api message function. */
36 #define TV_MESSAGE if (tv_message) (*tv_message)
38 /* Struct for recording files to claim / files claimed. */
39 typedef struct claim_file
41 struct claim_file
*next
;
42 struct ld_plugin_input_file file
;
44 struct ld_plugin_symbol
*symbols
;
49 /* Types of things that can be added at all symbols read time. */
50 typedef enum addfile_enum
57 /* Struct for recording files to add to final link. */
58 typedef struct add_file
60 struct add_file
*next
;
65 /* Helper macro for defining array of transfer vector tags and names. */
66 #define ADDENTRY(tag) { tag, #tag }
68 /* Struct for looking up human-readable versions of tag names. */
69 typedef struct tag_name
71 enum ld_plugin_tag tag
;
75 /* Array of all known tags and their names. */
76 static const tag_name_t tag_names
[] =
79 ADDENTRY(LDPT_API_VERSION
),
80 ADDENTRY(LDPT_GOLD_VERSION
),
81 ADDENTRY(LDPT_LINKER_OUTPUT
),
82 ADDENTRY(LDPT_OPTION
),
83 ADDENTRY(LDPT_REGISTER_CLAIM_FILE_HOOK
),
84 ADDENTRY(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
),
85 ADDENTRY(LDPT_REGISTER_CLEANUP_HOOK
),
86 ADDENTRY(LDPT_ADD_SYMBOLS
),
87 ADDENTRY(LDPT_GET_SYMBOLS
),
88 ADDENTRY(LDPT_GET_SYMBOLS_V2
),
89 ADDENTRY(LDPT_ADD_INPUT_FILE
),
90 ADDENTRY(LDPT_MESSAGE
),
91 ADDENTRY(LDPT_GET_INPUT_FILE
),
92 ADDENTRY(LDPT_GET_VIEW
),
93 ADDENTRY(LDPT_RELEASE_INPUT_FILE
),
94 ADDENTRY(LDPT_ADD_INPUT_LIBRARY
),
95 ADDENTRY(LDPT_OUTPUT_NAME
),
96 ADDENTRY(LDPT_SET_EXTRA_LIBRARY_PATH
),
97 ADDENTRY(LDPT_GNU_LD_VERSION
)
100 /* Function pointers to cache hooks passed at onload time. */
101 static ld_plugin_register_claim_file tv_register_claim_file
= 0;
102 static ld_plugin_register_all_symbols_read tv_register_all_symbols_read
= 0;
103 static ld_plugin_register_cleanup tv_register_cleanup
= 0;
104 static ld_plugin_add_symbols tv_add_symbols
= 0;
105 static ld_plugin_get_symbols tv_get_symbols
= 0;
106 static ld_plugin_get_symbols tv_get_symbols_v2
= 0;
107 static ld_plugin_add_input_file tv_add_input_file
= 0;
108 static ld_plugin_message tv_message
= 0;
109 static ld_plugin_get_input_file tv_get_input_file
= 0;
110 static ld_plugin_get_view tv_get_view
= 0;
111 static ld_plugin_release_input_file tv_release_input_file
= 0;
112 static ld_plugin_add_input_library tv_add_input_library
= 0;
113 static ld_plugin_set_extra_library_path tv_set_extra_library_path
= 0;
115 /* Other cached info from the transfer vector. */
116 static enum ld_plugin_output_file_type linker_output
;
117 static const char *output_name
;
119 /* Behaviour control flags set by plugin options. */
120 static enum ld_plugin_status onload_ret
= LDPS_OK
;
121 static enum ld_plugin_status claim_file_ret
= LDPS_OK
;
122 static enum ld_plugin_status all_symbols_read_ret
= LDPS_OK
;
123 static enum ld_plugin_status cleanup_ret
= LDPS_OK
;
124 static bfd_boolean register_claimfile_hook
= TRUE
;
125 static bfd_boolean register_allsymbolsread_hook
= FALSE
;
126 static bfd_boolean register_cleanup_hook
= FALSE
;
127 static bfd_boolean dumpresolutions
= FALSE
;
128 static bfd_boolean allsymbolsread_silent
= FALSE
;
130 /* The master list of all claimable/claimed files. */
131 static claim_file_t
*claimfiles_list
= NULL
;
133 /* We keep a tail pointer for easy linking on the end. */
134 static claim_file_t
**claimfiles_tail_chain_ptr
= &claimfiles_list
;
136 /* The last claimed file added to the list, for receiving syms. */
137 static claim_file_t
*last_claimfile
= NULL
;
139 /* The master list of all files to add to the final link. */
140 static add_file_t
*addfiles_list
= NULL
;
142 /* We keep a tail pointer for easy linking on the end. */
143 static add_file_t
**addfiles_tail_chain_ptr
= &addfiles_list
;
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
)
149 claim_file_t
*newfile
;
151 newfile
= malloc (sizeof *newfile
);
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
;
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
)
172 newfile
= malloc (sizeof *newfile
);
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
;
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
)
195 const char *colon1
, *colon2
, *colon5
;
197 /* Locate the colons separating the first two strings. */
198 colon1
= strchr (str
, ':');
201 colon2
= strchr (colon1
+1, ':');
204 /* Name must not be empty (version may be). */
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. */
213 colon5
= strchr (colon5
+1, ':'); /* Hopefully fourth now. */
216 colon5
= strchr (colon5
+1, ':'); /* Optional fifth now. */
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
);
225 /* Parsed successfully, so allocate strings and fill out fields. */
228 sym
->section_kind
= 0;
229 sym
->symbol_type
= 0;
230 sym
->resolution
= LDPR_UNKNOWN
;
231 sym
->name
= malloc (colon1
- str
+ 1);
234 memcpy (sym
->name
, str
, colon1
- str
);
235 sym
->name
[colon1
- str
] = '\0';
236 if (colon2
> (colon1
+ 1))
238 sym
->version
= malloc (colon2
- colon1
);
241 memcpy (sym
->version
, colon1
+ 1, colon2
- (colon1
+ 1));
242 sym
->version
[colon2
- (colon1
+ 1)] = '\0';
246 if (colon5
&& colon5
[1])
248 sym
->comdat_key
= malloc (strlen (colon5
+ 1) + 1);
249 if (!sym
->comdat_key
)
251 strcpy (sym
->comdat_key
, colon5
+ 1);
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
)
262 struct ld_plugin_symbol sym
;
264 /* Can't add symbols except as belonging to claimed files. */
268 /* If string doesn't parse correctly, give an error. */
269 if (parse_symdefstr (symdefstr
, &sym
) != LDPS_OK
)
272 /* Check for enough space, resize array if needed, and add it. */
273 if (last_claimfile
->n_syms_allocated
== last_claimfile
->n_syms_used
)
275 int new_n_syms
= last_claimfile
->n_syms_allocated
276 ? 2 * last_claimfile
->n_syms_allocated
278 last_claimfile
->symbols
= realloc (last_claimfile
->symbols
,
279 new_n_syms
* sizeof *last_claimfile
->symbols
);
280 if (!last_claimfile
->symbols
)
282 last_claimfile
->n_syms_allocated
= new_n_syms
;
284 last_claimfile
->symbols
[last_claimfile
->n_syms_used
++] = sym
;
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
)
293 if (!strcmp ("onload", whichval
))
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
;
306 /* Records hooks which should be registered. */
307 static enum ld_plugin_status
308 set_register_hook (const char *whichhook
, bfd_boolean yesno
)
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 ("allsymbolsreadsilent", whichhook
))
316 register_allsymbolsread_hook
= yesno
;
317 allsymbolsread_silent
= TRUE
;
319 else if (!strcmp ("cleanup", whichhook
))
320 register_cleanup_hook
= yesno
;
326 /* Determine type of plugin option and pass to individual parsers. */
327 static enum ld_plugin_status
328 parse_option (const char *opt
)
330 if (!strncmp ("fatal", opt
, 5))
332 TV_MESSAGE (LDPL_FATAL
, "Fatal error");
335 else if (!strncmp ("error", opt
, 5))
337 TV_MESSAGE (LDPL_ERROR
, "Error");
340 else if (!strncmp ("warning", opt
, 7))
342 TV_MESSAGE (LDPL_WARNING
, "Warning");
345 else if (!strncmp ("fail", opt
, 4))
346 return set_ret_val (opt
+ 4, LDPS_ERR
);
347 else if (!strncmp ("pass", opt
, 4))
348 return set_ret_val (opt
+ 4, LDPS_OK
);
349 else if (!strncmp ("register", opt
, 8))
350 return set_register_hook (opt
+ 8, TRUE
);
351 else if (!strncmp ("noregister", opt
, 10))
352 return set_register_hook (opt
+ 10, FALSE
);
353 else if (!strncmp ("claim:", opt
, 6))
354 return record_claim_file (opt
+ 6, 0);
355 else if (!strncmp ("sym:", opt
, 4))
356 return record_claimed_file_symbol (opt
+ 4);
357 else if (!strncmp ("add:", opt
, 4))
358 return record_add_file (opt
+ 4, ADD_FILE
);
359 else if (!strncmp ("lib:", opt
, 4))
360 return record_add_file (opt
+ 4, ADD_LIB
);
361 else if (!strncmp ("dir:", opt
, 4))
362 return record_add_file (opt
+ 4, ADD_DIR
);
363 else if (!strcmp ("dumpresolutions", opt
))
364 dumpresolutions
= TRUE
;
370 /* Handle/record information received in a transfer vector entry. */
371 static enum ld_plugin_status
372 parse_tv_tag (struct ld_plugin_tv
*tv
)
374 #define SETVAR(x) x = tv->tv_u.x
378 return parse_option (tv
->tv_u
.tv_string
);
380 case LDPT_GOLD_VERSION
:
381 case LDPT_GNU_LD_VERSION
:
382 case LDPT_API_VERSION
:
385 case LDPT_OUTPUT_NAME
:
386 output_name
= tv
->tv_u
.tv_string
;
388 case LDPT_LINKER_OUTPUT
:
389 linker_output
= tv
->tv_u
.tv_val
;
391 case LDPT_REGISTER_CLAIM_FILE_HOOK
:
392 SETVAR(tv_register_claim_file
);
394 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
:
395 SETVAR(tv_register_all_symbols_read
);
397 case LDPT_REGISTER_CLEANUP_HOOK
:
398 SETVAR(tv_register_cleanup
);
400 case LDPT_ADD_SYMBOLS
:
401 SETVAR(tv_add_symbols
);
403 case LDPT_GET_SYMBOLS
:
404 SETVAR(tv_get_symbols
);
406 case LDPT_GET_SYMBOLS_V2
:
407 tv_get_symbols_v2
= tv
->tv_u
.tv_get_symbols
;
409 case LDPT_ADD_INPUT_FILE
:
410 SETVAR(tv_add_input_file
);
415 case LDPT_GET_INPUT_FILE
:
416 SETVAR(tv_get_input_file
);
421 case LDPT_RELEASE_INPUT_FILE
:
422 SETVAR(tv_release_input_file
);
424 case LDPT_ADD_INPUT_LIBRARY
:
425 SETVAR(tv_add_input_library
);
427 case LDPT_SET_EXTRA_LIBRARY_PATH
:
428 SETVAR(tv_set_extra_library_path
);
435 /* Standard plugin API entry point. */
436 enum ld_plugin_status
437 onload (struct ld_plugin_tv
*tv
)
439 enum ld_plugin_status rv
;
441 /* This plugin does nothing but dump the tv array. It would
442 be an error if this function was called without one. */
446 /* First entry should always be LDPT_MESSAGE, letting us get
447 hold of it easily so we can send output straight away. */
448 if (tv
[0].tv_tag
== LDPT_MESSAGE
)
449 tv_message
= tv
[0].tv_u
.tv_message
;
452 if ((rv
= parse_tv_tag (tv
)) != LDPS_OK
)
454 while ((tv
++)->tv_tag
!= LDPT_NULL
);
456 /* Register hooks only if instructed by options. */
457 if (register_claimfile_hook
)
459 if (!tv_register_claim_file
)
461 TV_MESSAGE (LDPL_FATAL
, "No register_claim_file hook");
465 (*tv_register_claim_file
) (onclaim_file
);
467 if (register_allsymbolsread_hook
)
469 if (!tv_register_all_symbols_read
)
471 TV_MESSAGE (LDPL_FATAL
, "No register_all_symbols_read hook");
475 (*tv_register_all_symbols_read
) (onall_symbols_read
);
477 if (register_cleanup_hook
)
479 if (!tv_register_cleanup
)
481 TV_MESSAGE (LDPL_FATAL
, "No register_cleanup hook");
485 (*tv_register_cleanup
) (oncleanup
);
488 /* Claim testsuite/ld-plugin/func.c, standalone or in a library. Its
489 size must be SIZE_OF_FUNC_C bytes. */
490 #define SIZE_OF_FUNC_C 248
491 if (onload_ret
== LDPS_OK
492 && (record_claim_file ("func.c", SIZE_OF_FUNC_C
) != LDPS_OK
493 || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
494 || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK
495 || record_claim_file ("libfunc.a", SIZE_OF_FUNC_C
) != LDPS_OK
496 || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
497 || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK
))
498 onload_ret
= LDPS_ERR
;
504 xstrdup (const char *s
)
506 size_t len
= strlen (s
) + 1;
507 char *ret
= malloc (len
+ 1);
508 return (char *) memcpy (ret
, s
, len
);
511 /* Standard plugin API registerable hook. */
512 static enum ld_plugin_status
513 onclaim_file (const struct ld_plugin_input_file
*file
, int *claimed
)
515 /* Let's see if we want to claim this file. */
516 claim_file_t
*claimfile
= claimfiles_list
;
517 size_t len
= strlen (file
->name
);
518 char *name
= xstrdup (file
->name
);
519 char *p
= name
+ len
;
522 /* Only match the file name without the directory part. */
523 islib
= *p
== 'a' && *(p
- 1) == '.';
524 for (; p
!= name
; p
--)
525 if (IS_DIR_SEPARATOR (*p
))
533 /* Claim the file only if the file name and size match and don't
534 match the whole library. */
535 if (!strcmp (p
, claimfile
->file
.name
)
536 && claimfile
->file
.filesize
== file
->filesize
537 && (!islib
|| file
->offset
!= 0))
539 claimfile
= claimfile
->next
;
544 /* If we decided to claim it, record that fact, and add any symbols
545 that were defined for it by plugin options. */
546 *claimed
= (claimfile
!= 0);
549 claimfile
->claimed
= TRUE
;
550 claimfile
->file
= *file
;
551 if (claimfile
->n_syms_used
&& !tv_add_symbols
)
553 else if (claimfile
->n_syms_used
)
554 return (*tv_add_symbols
) (claimfile
->file
.handle
,
555 claimfile
->n_syms_used
, claimfile
->symbols
);
558 return claim_file_ret
;
561 /* Standard plugin API registerable hook. */
562 static enum ld_plugin_status
563 onall_symbols_read (void)
565 static const char *resolutions
[] =
569 "LDPR_PREVAILING_DEF",
570 "LDPR_PREVAILING_DEF_IRONLY",
571 "LDPR_PREEMPTED_REG",
574 "LDPR_RESOLVED_EXEC",
576 "LDPR_PREVAILING_DEF_IRONLY_EXP",
578 claim_file_t
*claimfile
= dumpresolutions
? claimfiles_list
: NULL
;
579 add_file_t
*addfile
= addfiles_list
;
580 struct ld_plugin_input_file file
;
585 if (! allsymbolsread_silent
)
586 TV_MESSAGE (LDPL_INFO
, "hook called: all symbols read.");
587 for ( ; claimfile
; claimfile
= claimfile
->next
)
589 enum ld_plugin_status rv
;
591 if (claimfile
->n_syms_used
&& !tv_get_symbols_v2
)
593 else if (!claimfile
->n_syms_used
)
595 else if (!claimfile
->file
.handle
)
597 rv
= tv_get_input_file (claimfile
->file
.handle
, &file
);
600 TV_MESSAGE (LDPL_INFO
, "Input: %s (%s)", file
.name
,
601 claimfile
->file
.name
);
602 rv
= tv_get_view (claimfile
->file
.handle
, &view
);
605 #define EXPECTED_VIEW "/* The first line of this file must match the expectation of"
606 #define EXPECTED_VIEW_LENGTH (sizeof (EXPECTED_VIEW) - 1)
607 if (file
.filesize
!= SIZE_OF_FUNC_C
608 || SIZE_OF_FUNC_C
< EXPECTED_VIEW_LENGTH
609 || memcmp (view
, EXPECTED_VIEW
, EXPECTED_VIEW_LENGTH
) != 0)
611 char result
[EXPECTED_VIEW_LENGTH
+ 1];
612 memcpy (result
, view
, sizeof (result
));
613 result
[EXPECTED_VIEW_LENGTH
] = '\0';
614 TV_MESSAGE (LDPL_INFO
, "Incorrect view:");
615 TV_MESSAGE (LDPL_INFO
, " Expect: " EXPECTED_VIEW
);
616 TV_MESSAGE (LDPL_INFO
, " Result: %s", result
);
618 rv
= tv_get_symbols_v2 (claimfile
->file
.handle
, claimfile
->n_syms_used
,
622 for (n
= 0; n
< claimfile
->n_syms_used
; n
++)
623 TV_MESSAGE (LDPL_INFO
, "Sym: '%s%s%s' Resolution: %s",
624 claimfile
->symbols
[n
].name
,
625 claimfile
->symbols
[n
].version
? "@" : "",
626 (claimfile
->symbols
[n
].version
627 ? claimfile
->symbols
[n
].version
: ""),
628 resolutions
[claimfile
->symbols
[n
].resolution
]);
629 fd
= claimfile
->file
.fd
;
630 filename
= xstrdup (claimfile
->file
.name
);
631 rv
= tv_release_input_file (claimfile
->file
.handle
);
637 if (read (fd
, buffer
, sizeof (buffer
)) >= 0)
639 TV_MESSAGE (LDPL_FATAL
, "Unreleased file descriptor on: %s",
640 claimfile
->file
.name
);
646 for ( ; addfile
; addfile
= addfile
->next
)
648 enum ld_plugin_status rv
;
649 if (addfile
->type
== ADD_LIB
&& tv_add_input_library
)
650 rv
= (*tv_add_input_library
) (addfile
->name
);
651 else if (addfile
->type
== ADD_FILE
&& tv_add_input_file
)
652 rv
= (*tv_add_input_file
) (addfile
->name
);
653 else if (addfile
->type
== ADD_DIR
&& tv_set_extra_library_path
)
654 rv
= (*tv_set_extra_library_path
) (addfile
->name
);
661 return all_symbols_read_ret
;
664 /* Standard plugin API registerable hook. */
665 static enum ld_plugin_status
668 TV_MESSAGE (LDPL_INFO
, "hook called: cleanup.");