1 /* Test plugin for the GNU linker.
2 Copyright (C) 2010-2024 Free Software Foundation, Inc.
4 This file is part of the GNU Binutils.
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.
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.
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. */
23 #if BFD_SUPPORTS_PLUGINS
24 #include "plugin-api.h"
25 /* For ARRAY_SIZE macro only - we don't link the library itself. */
26 #include "libiberty.h"
28 #include <ctype.h> /* For isdigit. */
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
= false;
128 static bool register_allsymbolsread_hook
= false;
129 static bool register_cleanup_hook
= false;
130 static bool dumpresolutions
= false;
132 /* The master list of all claimable/claimed files. */
133 static claim_file_t
*claimfiles_list
= NULL
;
135 /* We keep a tail pointer for easy linking on the end. */
136 static claim_file_t
**claimfiles_tail_chain_ptr
= &claimfiles_list
;
138 /* The last claimed file added to the list, for receiving syms. */
139 static claim_file_t
*last_claimfile
= NULL
;
141 /* The master list of all files to add to the final link. */
142 static add_file_t
*addfiles_list
= NULL
;
144 /* We keep a tail pointer for easy linking on the end. */
145 static add_file_t
**addfiles_tail_chain_ptr
= &addfiles_list
;
147 /* Number of bytes read in claim file before deciding if the file can be
149 static int bytes_to_read_before_claim
= 0;
151 /* Add a new claimfile on the end of the chain. */
152 static enum ld_plugin_status
153 record_claim_file (const char *file
)
155 claim_file_t
*newfile
;
157 newfile
= malloc (sizeof *newfile
);
160 memset (newfile
, 0, sizeof *newfile
);
161 /* Only setup for now is remembering the name to look for. */
162 newfile
->file
.name
= file
;
163 /* Chain it on the end of the list. */
164 *claimfiles_tail_chain_ptr
= newfile
;
165 claimfiles_tail_chain_ptr
= &newfile
->next
;
166 /* Record it as active for receiving symbols to register. */
167 last_claimfile
= newfile
;
171 /* How many bytes to read before claiming (or not) an input file. */
172 static enum ld_plugin_status
173 record_read_length (const char *length
)
178 while (*tmp
!= '\0' && isdigit (*tmp
))
180 if (*tmp
!= '\0' || *length
== '\0')
183 bytes_to_read_before_claim
= atoi (length
);
187 /* Add a new addfile on the end of the chain. */
188 static enum ld_plugin_status
189 record_add_file (const char *file
, addfile_enum_t type
)
193 newfile
= malloc (sizeof *newfile
);
196 newfile
->next
= NULL
;
197 newfile
->name
= file
;
198 newfile
->type
= type
;
199 /* Chain it on the end of the list. */
200 *addfiles_tail_chain_ptr
= newfile
;
201 addfiles_tail_chain_ptr
= &newfile
->next
;
205 /* Parse a command-line argument string into a symbol definition.
206 Symbol-strings follow the colon-separated format:
207 NAME:VERSION:def:vis:size:COMDATKEY
208 where the fields in capitals are strings and those in lower
209 case are integers. We don't allow to specify a resolution as
210 doing so is not meaningful when calling the add symbols hook. */
211 static enum ld_plugin_status
212 parse_symdefstr (const char *str
, struct ld_plugin_symbol
*sym
)
216 const char *colon1
, *colon2
, *colon5
;
218 /* Locate the colons separating the first two strings. */
219 colon1
= strchr (str
, ':');
222 colon2
= strchr (colon1
+1, ':');
225 /* Name must not be empty (version may be). */
229 /* The fifth colon and trailing comdat key string are optional,
230 but the intermediate ones must all be present. */
231 colon5
= strchr (colon2
+1, ':'); /* Actually only third so far. */
234 colon5
= strchr (colon5
+1, ':'); /* Hopefully fourth now. */
237 colon5
= strchr (colon5
+1, ':'); /* Optional fifth now. */
239 /* Finally we'll use sscanf to parse the numeric fields, then
240 we'll split out the strings which we need to allocate separate
241 storage for anyway so that we can add nul termination. */
242 n
= sscanf (colon2
+ 1, "%hhi:%i:%lli", &sym
->def
, &sym
->visibility
, &size
);
246 /* Parsed successfully, so allocate strings and fill out fields. */
249 sym
->section_kind
= 0;
250 sym
->symbol_type
= 0;
251 sym
->resolution
= LDPR_UNKNOWN
;
252 sym
->name
= malloc (colon1
- str
+ 1);
255 memcpy (sym
->name
, str
, colon1
- str
);
256 sym
->name
[colon1
- str
] = '\0';
257 if (colon2
> (colon1
+ 1))
259 sym
->version
= malloc (colon2
- colon1
);
262 memcpy (sym
->version
, colon1
+ 1, colon2
- (colon1
+ 1));
263 sym
->version
[colon2
- (colon1
+ 1)] = '\0';
267 if (colon5
&& colon5
[1])
269 sym
->comdat_key
= malloc (strlen (colon5
+ 1) + 1);
270 if (!sym
->comdat_key
)
272 strcpy (sym
->comdat_key
, colon5
+ 1);
279 /* Record a symbol to be added for the last-added claimfile. */
280 static enum ld_plugin_status
281 record_claimed_file_symbol (const char *symdefstr
)
283 struct ld_plugin_symbol sym
;
285 /* Can't add symbols except as belonging to claimed files. */
289 /* If string doesn't parse correctly, give an error. */
290 if (parse_symdefstr (symdefstr
, &sym
) != LDPS_OK
)
293 /* Check for enough space, resize array if needed, and add it. */
294 if (last_claimfile
->n_syms_allocated
== last_claimfile
->n_syms_used
)
296 int new_n_syms
= last_claimfile
->n_syms_allocated
297 ? 2 * last_claimfile
->n_syms_allocated
299 last_claimfile
->symbols
= realloc (last_claimfile
->symbols
,
300 new_n_syms
* sizeof *last_claimfile
->symbols
);
301 if (!last_claimfile
->symbols
)
303 last_claimfile
->n_syms_allocated
= new_n_syms
;
305 last_claimfile
->symbols
[last_claimfile
->n_syms_used
++] = sym
;
310 /* Records the status to return from one of the registered hooks. */
311 static enum ld_plugin_status
312 set_ret_val (const char *whichval
, enum ld_plugin_status retval
)
314 if (!strcmp ("onload", whichval
))
316 else if (!strcmp ("claimfile", whichval
))
317 claim_file_ret
= retval
;
318 else if (!strcmp ("allsymbolsread", whichval
))
319 all_symbols_read_ret
= retval
;
320 else if (!strcmp ("cleanup", whichval
))
321 cleanup_ret
= retval
;
327 /* Records hooks which should be registered. */
328 static enum ld_plugin_status
329 set_register_hook (const char *whichhook
, bool yesno
)
331 if (!strcmp ("claimfile", whichhook
))
332 register_claimfile_hook
= yesno
;
333 else if (!strcmp ("allsymbolsread", whichhook
))
334 register_allsymbolsread_hook
= yesno
;
335 else if (!strcmp ("cleanup", whichhook
))
336 register_cleanup_hook
= yesno
;
342 /* Determine type of plugin option and pass to individual parsers. */
343 static enum ld_plugin_status
344 parse_option (const char *opt
)
346 if (!strncmp ("fail", opt
, 4))
347 return set_ret_val (opt
+ 4, LDPS_ERR
);
348 else if (!strncmp ("pass", opt
, 4))
349 return set_ret_val (opt
+ 4, LDPS_OK
);
350 else if (!strncmp ("register", opt
, 8))
351 return set_register_hook (opt
+ 8, true);
352 else if (!strncmp ("noregister", opt
, 10))
353 return set_register_hook (opt
+ 10, false);
354 else if (!strncmp ("claim:", opt
, 6))
355 return record_claim_file (opt
+ 6);
356 else if (!strncmp ("read:", opt
, 5))
357 return record_read_length (opt
+ 5);
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 /* Output contents of transfer vector array entry in human-readable form. */
375 dump_tv_tag (size_t n
, struct ld_plugin_tv
*tv
)
381 for (tag
= 0; tag
< ARRAY_SIZE (tag_names
); tag
++)
382 if (tag_names
[tag
].tag
== tv
->tv_tag
)
384 sprintf (unknownbuf
, "unknown tag #%d", tv
->tv_tag
);
385 name
= (tag
< ARRAY_SIZE (tag_names
)) ? tag_names
[tag
].name
: unknownbuf
;
389 case LDPT_OUTPUT_NAME
:
390 TV_MESSAGE (LDPL_INFO
, "tv[%d]: %s '%s'", n
, name
,
393 case LDPT_REGISTER_CLAIM_FILE_HOOK
:
394 case LDPT_REGISTER_CLAIM_FILE_HOOK_V2
:
395 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
:
396 case LDPT_REGISTER_CLEANUP_HOOK
:
397 case LDPT_ADD_SYMBOLS
:
398 case LDPT_GET_SYMBOLS
:
399 case LDPT_GET_SYMBOLS_V2
:
400 case LDPT_ADD_INPUT_FILE
:
402 case LDPT_GET_INPUT_FILE
:
404 case LDPT_RELEASE_INPUT_FILE
:
405 case LDPT_ADD_INPUT_LIBRARY
:
406 case LDPT_SET_EXTRA_LIBRARY_PATH
:
407 TV_MESSAGE (LDPL_INFO
, "tv[%d]: %s func@0x%p", n
, name
,
408 (void *)(tv
->tv_u
.tv_message
));
411 case LDPT_API_VERSION
:
412 case LDPT_GOLD_VERSION
:
413 case LDPT_LINKER_OUTPUT
:
414 case LDPT_GNU_LD_VERSION
:
416 TV_MESSAGE (LDPL_INFO
, "tv[%d]: %s value %W (%d)", n
, name
,
417 (bfd_vma
)tv
->tv_u
.tv_val
, tv
->tv_u
.tv_val
);
422 /* Handle/record information received in a transfer vector entry. */
423 static enum ld_plugin_status
424 parse_tv_tag (struct ld_plugin_tv
*tv
)
426 #define SETVAR(x) x = tv->tv_u.x
430 return parse_option (tv
->tv_u
.tv_string
);
432 case LDPT_GOLD_VERSION
:
433 case LDPT_GNU_LD_VERSION
:
434 case LDPT_API_VERSION
:
437 case LDPT_OUTPUT_NAME
:
438 output_name
= tv
->tv_u
.tv_string
;
440 case LDPT_LINKER_OUTPUT
:
441 linker_output
= tv
->tv_u
.tv_val
;
443 case LDPT_REGISTER_CLAIM_FILE_HOOK
:
444 SETVAR(tv_register_claim_file
);
446 case LDPT_REGISTER_CLAIM_FILE_HOOK_V2
:
447 SETVAR(tv_register_claim_file_v2
);
449 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
:
450 SETVAR(tv_register_all_symbols_read
);
452 case LDPT_REGISTER_CLEANUP_HOOK
:
453 SETVAR(tv_register_cleanup
);
455 case LDPT_ADD_SYMBOLS
:
456 SETVAR(tv_add_symbols
);
458 case LDPT_GET_SYMBOLS
:
459 SETVAR(tv_get_symbols
);
461 case LDPT_GET_SYMBOLS_V2
:
462 tv_get_symbols_v2
= tv
->tv_u
.tv_get_symbols
;
464 case LDPT_ADD_INPUT_FILE
:
465 SETVAR(tv_add_input_file
);
470 case LDPT_GET_INPUT_FILE
:
471 SETVAR(tv_get_input_file
);
476 case LDPT_RELEASE_INPUT_FILE
:
477 SETVAR(tv_release_input_file
);
479 case LDPT_ADD_INPUT_LIBRARY
:
480 SETVAR(tv_add_input_library
);
482 case LDPT_SET_EXTRA_LIBRARY_PATH
:
483 SETVAR(tv_set_extra_library_path
);
490 /* Record any useful information in transfer vector entry and display
491 it in human-readable form using the plugin API message() callback. */
492 enum ld_plugin_status
493 parse_and_dump_tv_tag (size_t n
, struct ld_plugin_tv
*tv
)
495 enum ld_plugin_status rv
= parse_tv_tag (tv
);
500 /* Standard plugin API entry point. */
501 enum ld_plugin_status
502 onload (struct ld_plugin_tv
*tv
)
505 enum ld_plugin_status rv
;
507 /* This plugin does nothing but dump the tv array. It would
508 be an error if this function was called without one. */
512 /* First entry should always be LDPT_MESSAGE, letting us get
513 hold of it easily so we can send output straight away. */
514 if (tv
[0].tv_tag
== LDPT_MESSAGE
)
515 tv_message
= tv
[0].tv_u
.tv_message
;
518 TV_MESSAGE (LDPL_INFO
, "Hello from testplugin.");
521 if ((rv
= parse_and_dump_tv_tag (n
++, tv
)) != LDPS_OK
)
523 while ((tv
++)->tv_tag
!= LDPT_NULL
);
525 /* Register hooks only if instructed by options. */
526 if (register_claimfile_hook
)
528 if (!tv_register_claim_file
)
530 TV_MESSAGE (LDPL_FATAL
, "No register_claim_file hook");
534 (*tv_register_claim_file
) (onclaim_file
);
536 if (register_allsymbolsread_hook
)
538 if (!tv_register_all_symbols_read
)
540 TV_MESSAGE (LDPL_FATAL
, "No register_all_symbols_read hook");
544 (*tv_register_all_symbols_read
) (onall_symbols_read
);
546 if (register_cleanup_hook
)
548 if (!tv_register_cleanup
)
550 TV_MESSAGE (LDPL_FATAL
, "No register_cleanup hook");
554 (*tv_register_cleanup
) (oncleanup
);
560 /* Standard plugin API registerable hook. */
561 static enum ld_plugin_status
562 onclaim_file (const struct ld_plugin_input_file
*file
, int *claimed
)
564 /* Possible read of some bytes out of the input file into a buffer. This
565 simulates a plugin that reads some file content in order to decide if
566 the file should be claimed or not. */
567 if (bytes_to_read_before_claim
> 0)
569 char *buffer
= malloc (bytes_to_read_before_claim
);
573 if (read (file
->fd
, buffer
, bytes_to_read_before_claim
) < 0)
578 /* Let's see if we want to claim this file. */
579 claim_file_t
*claimfile
= claimfiles_list
;
582 if (!strcmp (file
->name
, claimfile
->file
.name
))
584 claimfile
= claimfile
->next
;
587 /* Inform the user/testsuite. */
588 TV_MESSAGE (LDPL_INFO
, "hook called: claim_file %s [@%ld/%ld] %s",
589 file
->name
, (long)file
->offset
, (long)file
->filesize
,
590 claimfile
? "CLAIMED" : "not claimed");
593 /* If we decided to claim it, record that fact, and add any symbols
594 that were defined for it by plugin options. */
595 *claimed
= (claimfile
!= 0);
598 claimfile
->claimed
= true;
599 claimfile
->file
= *file
;
600 if (claimfile
->n_syms_used
&& !tv_add_symbols
)
602 else if (claimfile
->n_syms_used
)
603 return (*tv_add_symbols
) (claimfile
->file
.handle
,
604 claimfile
->n_syms_used
, claimfile
->symbols
);
607 return claim_file_ret
;
610 /* Standard plugin API registerable hook. */
611 static enum ld_plugin_status
612 onall_symbols_read (void)
614 static const char *resolutions
[] =
618 "LDPR_PREVAILING_DEF",
619 "LDPR_PREVAILING_DEF_IRONLY",
620 "LDPR_PREEMPTED_REG",
623 "LDPR_RESOLVED_EXEC",
625 "LDPR_PREVAILING_DEF_IRONLY_EXP",
627 claim_file_t
*claimfile
= dumpresolutions
? claimfiles_list
: NULL
;
628 add_file_t
*addfile
= addfiles_list
;
629 TV_MESSAGE (LDPL_INFO
, "hook called: all symbols read.");
630 for ( ; claimfile
; claimfile
= claimfile
->next
)
632 enum ld_plugin_status rv
;
634 if (claimfile
->n_syms_used
&& !tv_get_symbols_v2
)
636 else if (!claimfile
->n_syms_used
)
638 rv
= tv_get_symbols_v2 (claimfile
->file
.handle
, claimfile
->n_syms_used
,
642 for (n
= 0; n
< claimfile
->n_syms_used
; n
++)
643 TV_MESSAGE (LDPL_INFO
, "Sym: '%s%s%s' Resolution: %s",
644 claimfile
->symbols
[n
].name
,
645 claimfile
->symbols
[n
].version
? "@" : "",
646 (claimfile
->symbols
[n
].version
647 ? claimfile
->symbols
[n
].version
: ""),
648 resolutions
[claimfile
->symbols
[n
].resolution
]);
650 for ( ; addfile
; addfile
= addfile
->next
)
652 enum ld_plugin_status rv
;
653 if (addfile
->type
== ADD_LIB
&& tv_add_input_library
)
654 rv
= (*tv_add_input_library
) (addfile
->name
);
655 else if (addfile
->type
== ADD_FILE
&& tv_add_input_file
)
656 rv
= (*tv_add_input_file
) (addfile
->name
);
657 else if (addfile
->type
== ADD_DIR
&& tv_set_extra_library_path
)
658 rv
= (*tv_set_extra_library_path
) (addfile
->name
);
665 return all_symbols_read_ret
;
668 /* Standard plugin API registerable hook. */
669 static enum ld_plugin_status
672 TV_MESSAGE (LDPL_INFO
, "hook called: cleanup.");
676 #endif /* BFD_SUPPORTS_PLUGINS */