1 /* Test plugin for the GNU linker.
2 Copyright (C) 2010-2020 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 #include "plugin-api.h"
24 /* For ARRAY_SIZE macro only - we don't link the library itself. */
25 #include "libiberty.h"
27 #include <ctype.h> /* For isdigit. */
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
= FALSE
;
125 static bfd_boolean register_allsymbolsread_hook
= FALSE
;
126 static bfd_boolean register_cleanup_hook
= FALSE
;
127 static bfd_boolean dumpresolutions
= FALSE
;
129 /* The master list of all claimable/claimed files. */
130 static claim_file_t
*claimfiles_list
= NULL
;
132 /* We keep a tail pointer for easy linking on the end. */
133 static claim_file_t
**claimfiles_tail_chain_ptr
= &claimfiles_list
;
135 /* The last claimed file added to the list, for receiving syms. */
136 static claim_file_t
*last_claimfile
= NULL
;
138 /* The master list of all files to add to the final link. */
139 static add_file_t
*addfiles_list
= NULL
;
141 /* We keep a tail pointer for easy linking on the end. */
142 static add_file_t
**addfiles_tail_chain_ptr
= &addfiles_list
;
144 /* Number of bytes read in claim file before deciding if the file can be
146 static int bytes_to_read_before_claim
= 0;
148 /* Add a new claimfile on the end of the chain. */
149 static enum ld_plugin_status
150 record_claim_file (const char *file
)
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 /* Chain it on the end of the list. */
161 *claimfiles_tail_chain_ptr
= newfile
;
162 claimfiles_tail_chain_ptr
= &newfile
->next
;
163 /* Record it as active for receiving symbols to register. */
164 last_claimfile
= newfile
;
168 /* How many bytes to read before claiming (or not) an input file. */
169 static enum ld_plugin_status
170 record_read_length (const char *length
)
175 while (*tmp
!= '\0' && isdigit (*tmp
))
177 if (*tmp
!= '\0' || *length
== '\0')
179 fprintf (stderr
, "APB: Bad length string: %s\n", tmp
);
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, "%i:%i:%lli", &sym
->def
, &sym
->visibility
, &size
);
246 /* Parsed successfully, so allocate strings and fill out fields. */
248 sym
->resolution
= LDPR_UNKNOWN
;
249 sym
->name
= malloc (colon1
- str
+ 1);
252 memcpy (sym
->name
, str
, colon1
- str
);
253 sym
->name
[colon1
- str
] = '\0';
254 if (colon2
> (colon1
+ 1))
256 sym
->version
= malloc (colon2
- colon1
);
259 memcpy (sym
->version
, colon1
+ 1, colon2
- (colon1
+ 1));
260 sym
->version
[colon2
- (colon1
+ 1)] = '\0';
264 if (colon5
&& colon5
[1])
266 sym
->comdat_key
= malloc (strlen (colon5
+ 1) + 1);
267 if (!sym
->comdat_key
)
269 strcpy (sym
->comdat_key
, colon5
+ 1);
276 /* Record a symbol to be added for the last-added claimfile. */
277 static enum ld_plugin_status
278 record_claimed_file_symbol (const char *symdefstr
)
280 struct ld_plugin_symbol sym
;
282 /* Can't add symbols except as belonging to claimed files. */
286 /* If string doesn't parse correctly, give an error. */
287 if (parse_symdefstr (symdefstr
, &sym
) != LDPS_OK
)
290 /* Check for enough space, resize array if needed, and add it. */
291 if (last_claimfile
->n_syms_allocated
== last_claimfile
->n_syms_used
)
293 int new_n_syms
= last_claimfile
->n_syms_allocated
294 ? 2 * last_claimfile
->n_syms_allocated
296 last_claimfile
->symbols
= realloc (last_claimfile
->symbols
,
297 new_n_syms
* sizeof *last_claimfile
->symbols
);
298 if (!last_claimfile
->symbols
)
300 last_claimfile
->n_syms_allocated
= new_n_syms
;
302 last_claimfile
->symbols
[last_claimfile
->n_syms_used
++] = sym
;
307 /* Records the status to return from one of the registered hooks. */
308 static enum ld_plugin_status
309 set_ret_val (const char *whichval
, enum ld_plugin_status retval
)
311 if (!strcmp ("onload", whichval
))
313 else if (!strcmp ("claimfile", whichval
))
314 claim_file_ret
= retval
;
315 else if (!strcmp ("allsymbolsread", whichval
))
316 all_symbols_read_ret
= retval
;
317 else if (!strcmp ("cleanup", whichval
))
318 cleanup_ret
= retval
;
324 /* Records hooks which should be registered. */
325 static enum ld_plugin_status
326 set_register_hook (const char *whichhook
, bfd_boolean yesno
)
328 if (!strcmp ("claimfile", whichhook
))
329 register_claimfile_hook
= yesno
;
330 else if (!strcmp ("allsymbolsread", whichhook
))
331 register_allsymbolsread_hook
= yesno
;
332 else if (!strcmp ("cleanup", whichhook
))
333 register_cleanup_hook
= yesno
;
339 /* Determine type of plugin option and pass to individual parsers. */
340 static enum ld_plugin_status
341 parse_option (const char *opt
)
343 if (!strncmp ("fail", opt
, 4))
344 return set_ret_val (opt
+ 4, LDPS_ERR
);
345 else if (!strncmp ("pass", opt
, 4))
346 return set_ret_val (opt
+ 4, LDPS_OK
);
347 else if (!strncmp ("register", opt
, 8))
348 return set_register_hook (opt
+ 8, TRUE
);
349 else if (!strncmp ("noregister", opt
, 10))
350 return set_register_hook (opt
+ 10, FALSE
);
351 else if (!strncmp ("claim:", opt
, 6))
352 return record_claim_file (opt
+ 6);
353 else if (!strncmp ("read:", opt
, 5))
354 return record_read_length (opt
+ 5);
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 /* Output contents of transfer vector array entry in human-readable form. */
372 dump_tv_tag (size_t n
, struct ld_plugin_tv
*tv
)
378 for (tag
= 0; tag
< ARRAY_SIZE (tag_names
); tag
++)
379 if (tag_names
[tag
].tag
== tv
->tv_tag
)
381 sprintf (unknownbuf
, "unknown tag #%d", tv
->tv_tag
);
382 name
= (tag
< ARRAY_SIZE (tag_names
)) ? tag_names
[tag
].name
: unknownbuf
;
386 case LDPT_OUTPUT_NAME
:
387 TV_MESSAGE (LDPL_INFO
, "tv[%d]: %s '%s'", n
, name
,
390 case LDPT_REGISTER_CLAIM_FILE_HOOK
:
391 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
:
392 case LDPT_REGISTER_CLEANUP_HOOK
:
393 case LDPT_ADD_SYMBOLS
:
394 case LDPT_GET_SYMBOLS
:
395 case LDPT_GET_SYMBOLS_V2
:
396 case LDPT_ADD_INPUT_FILE
:
398 case LDPT_GET_INPUT_FILE
:
400 case LDPT_RELEASE_INPUT_FILE
:
401 case LDPT_ADD_INPUT_LIBRARY
:
402 case LDPT_SET_EXTRA_LIBRARY_PATH
:
403 TV_MESSAGE (LDPL_INFO
, "tv[%d]: %s func@0x%p", n
, name
,
404 (void *)(tv
->tv_u
.tv_message
));
407 case LDPT_API_VERSION
:
408 case LDPT_GOLD_VERSION
:
409 case LDPT_LINKER_OUTPUT
:
410 case LDPT_GNU_LD_VERSION
:
412 TV_MESSAGE (LDPL_INFO
, "tv[%d]: %s value %W (%d)", n
, name
,
413 (bfd_vma
)tv
->tv_u
.tv_val
, tv
->tv_u
.tv_val
);
418 /* Handle/record information received in a transfer vector entry. */
419 static enum ld_plugin_status
420 parse_tv_tag (struct ld_plugin_tv
*tv
)
422 #define SETVAR(x) x = tv->tv_u.x
426 return parse_option (tv
->tv_u
.tv_string
);
428 case LDPT_GOLD_VERSION
:
429 case LDPT_GNU_LD_VERSION
:
430 case LDPT_API_VERSION
:
433 case LDPT_OUTPUT_NAME
:
434 output_name
= tv
->tv_u
.tv_string
;
436 case LDPT_LINKER_OUTPUT
:
437 linker_output
= tv
->tv_u
.tv_val
;
439 case LDPT_REGISTER_CLAIM_FILE_HOOK
:
440 SETVAR(tv_register_claim_file
);
442 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
:
443 SETVAR(tv_register_all_symbols_read
);
445 case LDPT_REGISTER_CLEANUP_HOOK
:
446 SETVAR(tv_register_cleanup
);
448 case LDPT_ADD_SYMBOLS
:
449 SETVAR(tv_add_symbols
);
451 case LDPT_GET_SYMBOLS
:
452 SETVAR(tv_get_symbols
);
454 case LDPT_GET_SYMBOLS_V2
:
455 tv_get_symbols_v2
= tv
->tv_u
.tv_get_symbols
;
457 case LDPT_ADD_INPUT_FILE
:
458 SETVAR(tv_add_input_file
);
463 case LDPT_GET_INPUT_FILE
:
464 SETVAR(tv_get_input_file
);
469 case LDPT_RELEASE_INPUT_FILE
:
470 SETVAR(tv_release_input_file
);
472 case LDPT_ADD_INPUT_LIBRARY
:
473 SETVAR(tv_add_input_library
);
475 case LDPT_SET_EXTRA_LIBRARY_PATH
:
476 SETVAR(tv_set_extra_library_path
);
483 /* Record any useful information in transfer vector entry and display
484 it in human-readable form using the plugin API message() callback. */
485 enum ld_plugin_status
486 parse_and_dump_tv_tag (size_t n
, struct ld_plugin_tv
*tv
)
488 enum ld_plugin_status rv
= parse_tv_tag (tv
);
493 /* Standard plugin API entry point. */
494 enum ld_plugin_status
495 onload (struct ld_plugin_tv
*tv
)
498 enum ld_plugin_status rv
;
500 /* This plugin does nothing but dump the tv array. It would
501 be an error if this function was called without one. */
505 /* First entry should always be LDPT_MESSAGE, letting us get
506 hold of it easily so we can send output straight away. */
507 if (tv
[0].tv_tag
== LDPT_MESSAGE
)
508 tv_message
= tv
[0].tv_u
.tv_message
;
511 TV_MESSAGE (LDPL_INFO
, "Hello from testplugin.");
514 if ((rv
= parse_and_dump_tv_tag (n
++, tv
)) != LDPS_OK
)
516 while ((tv
++)->tv_tag
!= LDPT_NULL
);
518 /* Register hooks only if instructed by options. */
519 if (register_claimfile_hook
)
521 if (!tv_register_claim_file
)
523 TV_MESSAGE (LDPL_FATAL
, "No register_claim_file hook");
527 (*tv_register_claim_file
) (onclaim_file
);
529 if (register_allsymbolsread_hook
)
531 if (!tv_register_all_symbols_read
)
533 TV_MESSAGE (LDPL_FATAL
, "No register_all_symbols_read hook");
537 (*tv_register_all_symbols_read
) (onall_symbols_read
);
539 if (register_cleanup_hook
)
541 if (!tv_register_cleanup
)
543 TV_MESSAGE (LDPL_FATAL
, "No register_cleanup hook");
547 (*tv_register_cleanup
) (oncleanup
);
553 /* Standard plugin API registerable hook. */
554 static enum ld_plugin_status
555 onclaim_file (const struct ld_plugin_input_file
*file
, int *claimed
)
557 /* Possible read of some bytes out of the input file into a buffer. This
558 simulates a plugin that reads some file content in order to decide if
559 the file should be claimed or not. */
560 if (bytes_to_read_before_claim
> 0)
562 char *buffer
= malloc (bytes_to_read_before_claim
);
566 if (read (file
->fd
, buffer
, bytes_to_read_before_claim
) < 0)
571 /* Let's see if we want to claim this file. */
572 claim_file_t
*claimfile
= claimfiles_list
;
575 if (!strcmp (file
->name
, claimfile
->file
.name
))
577 claimfile
= claimfile
->next
;
580 /* Inform the user/testsuite. */
581 TV_MESSAGE (LDPL_INFO
, "hook called: claim_file %s [@%ld/%ld] %s",
582 file
->name
, (long)file
->offset
, (long)file
->filesize
,
583 claimfile
? "CLAIMED" : "not claimed");
586 /* If we decided to claim it, record that fact, and add any symbols
587 that were defined for it by plugin options. */
588 *claimed
= (claimfile
!= 0);
591 claimfile
->claimed
= TRUE
;
592 claimfile
->file
= *file
;
593 if (claimfile
->n_syms_used
&& !tv_add_symbols
)
595 else if (claimfile
->n_syms_used
)
596 return (*tv_add_symbols
) (claimfile
->file
.handle
,
597 claimfile
->n_syms_used
, claimfile
->symbols
);
600 return claim_file_ret
;
603 /* Standard plugin API registerable hook. */
604 static enum ld_plugin_status
605 onall_symbols_read (void)
607 static const char *resolutions
[] =
611 "LDPR_PREVAILING_DEF",
612 "LDPR_PREVAILING_DEF_IRONLY",
613 "LDPR_PREEMPTED_REG",
616 "LDPR_RESOLVED_EXEC",
618 "LDPR_PREVAILING_DEF_IRONLY_EXP",
620 claim_file_t
*claimfile
= dumpresolutions
? claimfiles_list
: NULL
;
621 add_file_t
*addfile
= addfiles_list
;
622 TV_MESSAGE (LDPL_INFO
, "hook called: all symbols read.");
623 for ( ; claimfile
; claimfile
= claimfile
->next
)
625 enum ld_plugin_status rv
;
627 if (claimfile
->n_syms_used
&& !tv_get_symbols_v2
)
629 else if (!claimfile
->n_syms_used
)
631 rv
= tv_get_symbols_v2 (claimfile
->file
.handle
, claimfile
->n_syms_used
,
635 for (n
= 0; n
< claimfile
->n_syms_used
; n
++)
636 TV_MESSAGE (LDPL_INFO
, "Sym: '%s%s%s' Resolution: %s",
637 claimfile
->symbols
[n
].name
,
638 claimfile
->symbols
[n
].version
? "@" : "",
639 (claimfile
->symbols
[n
].version
640 ? claimfile
->symbols
[n
].version
: ""),
641 resolutions
[claimfile
->symbols
[n
].resolution
]);
643 for ( ; addfile
; addfile
= addfile
->next
)
645 enum ld_plugin_status rv
;
646 if (addfile
->type
== ADD_LIB
&& tv_add_input_library
)
647 rv
= (*tv_add_input_library
) (addfile
->name
);
648 else if (addfile
->type
== ADD_FILE
&& tv_add_input_file
)
649 rv
= (*tv_add_input_file
) (addfile
->name
);
650 else if (addfile
->type
== ADD_DIR
&& tv_set_extra_library_path
)
651 rv
= (*tv_set_extra_library_path
) (addfile
->name
);
658 return all_symbols_read_ret
;
661 /* Standard plugin API registerable hook. */
662 static enum ld_plugin_status
665 TV_MESSAGE (LDPL_INFO
, "hook called: cleanup.");