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 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
;
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 /* Add a new claimfile on the end of the chain. */
145 static enum ld_plugin_status
146 record_claim_file (const char *file
, off_t filesize
)
148 claim_file_t
*newfile
;
150 newfile
= malloc (sizeof *newfile
);
153 memset (newfile
, 0, sizeof *newfile
);
154 /* Only setup for now is remembering the name to look for. */
155 newfile
->file
.name
= file
;
156 newfile
->file
.filesize
= filesize
;
157 /* Chain it on the end of the list. */
158 *claimfiles_tail_chain_ptr
= newfile
;
159 claimfiles_tail_chain_ptr
= &newfile
->next
;
160 /* Record it as active for receiving symbols to register. */
161 last_claimfile
= newfile
;
165 /* Add a new addfile on the end of the chain. */
166 static enum ld_plugin_status
167 record_add_file (const char *file
, addfile_enum_t type
)
171 newfile
= malloc (sizeof *newfile
);
174 newfile
->next
= NULL
;
175 newfile
->name
= file
;
176 newfile
->type
= type
;
177 /* Chain it on the end of the list. */
178 *addfiles_tail_chain_ptr
= newfile
;
179 addfiles_tail_chain_ptr
= &newfile
->next
;
183 /* Parse a command-line argument string into a symbol definition.
184 Symbol-strings follow the colon-separated format:
185 NAME:VERSION:def:vis:size:COMDATKEY
186 where the fields in capitals are strings and those in lower
187 case are integers. We don't allow to specify a resolution as
188 doing so is not meaningful when calling the add symbols hook. */
189 static enum ld_plugin_status
190 parse_symdefstr (const char *str
, struct ld_plugin_symbol
*sym
)
194 const char *colon1
, *colon2
, *colon5
;
196 /* Locate the colons separating the first two strings. */
197 colon1
= strchr (str
, ':');
200 colon2
= strchr (colon1
+1, ':');
203 /* Name must not be empty (version may be). */
207 /* The fifth colon and trailing comdat key string are optional,
208 but the intermediate ones must all be present. */
209 colon5
= strchr (colon2
+1, ':'); /* Actually only third so far. */
212 colon5
= strchr (colon5
+1, ':'); /* Hopefully fourth now. */
215 colon5
= strchr (colon5
+1, ':'); /* Optional fifth now. */
217 /* Finally we'll use sscanf to parse the numeric fields, then
218 we'll split out the strings which we need to allocate separate
219 storage for anyway so that we can add nul termination. */
220 n
= sscanf (colon2
+ 1, "%i:%i:%lli", &sym
->def
, &sym
->visibility
, &size
);
224 /* Parsed successfully, so allocate strings and fill out fields. */
226 sym
->resolution
= LDPR_UNKNOWN
;
227 sym
->name
= malloc (colon1
- str
+ 1);
230 memcpy (sym
->name
, str
, colon1
- str
);
231 sym
->name
[colon1
- str
] = '\0';
232 if (colon2
> (colon1
+ 1))
234 sym
->version
= malloc (colon2
- colon1
);
237 memcpy (sym
->version
, colon1
+ 1, colon2
- (colon1
+ 1));
238 sym
->version
[colon2
- (colon1
+ 1)] = '\0';
242 if (colon5
&& colon5
[1])
244 sym
->comdat_key
= malloc (strlen (colon5
+ 1) + 1);
245 if (!sym
->comdat_key
)
247 strcpy (sym
->comdat_key
, colon5
+ 1);
254 /* Record a symbol to be added for the last-added claimfile. */
255 static enum ld_plugin_status
256 record_claimed_file_symbol (const char *symdefstr
)
258 struct ld_plugin_symbol sym
;
260 /* Can't add symbols except as belonging to claimed files. */
264 /* If string doesn't parse correctly, give an error. */
265 if (parse_symdefstr (symdefstr
, &sym
) != LDPS_OK
)
268 /* Check for enough space, resize array if needed, and add it. */
269 if (last_claimfile
->n_syms_allocated
== last_claimfile
->n_syms_used
)
271 int new_n_syms
= last_claimfile
->n_syms_allocated
272 ? 2 * last_claimfile
->n_syms_allocated
274 last_claimfile
->symbols
= realloc (last_claimfile
->symbols
,
275 new_n_syms
* sizeof *last_claimfile
->symbols
);
276 if (!last_claimfile
->symbols
)
278 last_claimfile
->n_syms_allocated
= new_n_syms
;
280 last_claimfile
->symbols
[last_claimfile
->n_syms_used
++] = sym
;
285 /* Records the status to return from one of the registered hooks. */
286 static enum ld_plugin_status
287 set_ret_val (const char *whichval
, enum ld_plugin_status retval
)
289 if (!strcmp ("onload", whichval
))
291 else if (!strcmp ("claimfile", whichval
))
292 claim_file_ret
= retval
;
293 else if (!strcmp ("allsymbolsread", whichval
))
294 all_symbols_read_ret
= retval
;
295 else if (!strcmp ("cleanup", whichval
))
296 cleanup_ret
= retval
;
302 /* Records hooks which should be registered. */
303 static enum ld_plugin_status
304 set_register_hook (const char *whichhook
, bfd_boolean yesno
)
306 if (!strcmp ("claimfile", whichhook
))
307 register_claimfile_hook
= yesno
;
308 else if (!strcmp ("allsymbolsread", whichhook
))
309 register_allsymbolsread_hook
= yesno
;
310 else if (!strcmp ("cleanup", whichhook
))
311 register_cleanup_hook
= yesno
;
317 /* Determine type of plugin option and pass to individual parsers. */
318 static enum ld_plugin_status
319 parse_option (const char *opt
)
321 if (!strncmp ("fatal", opt
, 5))
323 TV_MESSAGE (LDPL_FATAL
, "Fatal error");
326 else if (!strncmp ("error", opt
, 5))
328 TV_MESSAGE (LDPL_ERROR
, "Error");
331 else if (!strncmp ("warning", opt
, 7))
333 TV_MESSAGE (LDPL_WARNING
, "Warning");
336 else if (!strncmp ("fail", opt
, 4))
337 return set_ret_val (opt
+ 4, LDPS_ERR
);
338 else if (!strncmp ("pass", opt
, 4))
339 return set_ret_val (opt
+ 4, LDPS_OK
);
340 else if (!strncmp ("register", opt
, 8))
341 return set_register_hook (opt
+ 8, TRUE
);
342 else if (!strncmp ("noregister", opt
, 10))
343 return set_register_hook (opt
+ 10, FALSE
);
344 else if (!strncmp ("claim:", opt
, 6))
345 return record_claim_file (opt
+ 6, 0);
346 else if (!strncmp ("sym:", opt
, 4))
347 return record_claimed_file_symbol (opt
+ 4);
348 else if (!strncmp ("add:", opt
, 4))
349 return record_add_file (opt
+ 4, ADD_FILE
);
350 else if (!strncmp ("lib:", opt
, 4))
351 return record_add_file (opt
+ 4, ADD_LIB
);
352 else if (!strncmp ("dir:", opt
, 4))
353 return record_add_file (opt
+ 4, ADD_DIR
);
354 else if (!strcmp ("dumpresolutions", opt
))
355 dumpresolutions
= TRUE
;
361 /* Handle/record information received in a transfer vector entry. */
362 static enum ld_plugin_status
363 parse_tv_tag (struct ld_plugin_tv
*tv
)
365 #define SETVAR(x) x = tv->tv_u.x
369 return parse_option (tv
->tv_u
.tv_string
);
371 case LDPT_GOLD_VERSION
:
372 case LDPT_GNU_LD_VERSION
:
373 case LDPT_API_VERSION
:
376 case LDPT_OUTPUT_NAME
:
377 output_name
= tv
->tv_u
.tv_string
;
379 case LDPT_LINKER_OUTPUT
:
380 linker_output
= tv
->tv_u
.tv_val
;
382 case LDPT_REGISTER_CLAIM_FILE_HOOK
:
383 SETVAR(tv_register_claim_file
);
385 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
:
386 SETVAR(tv_register_all_symbols_read
);
388 case LDPT_REGISTER_CLEANUP_HOOK
:
389 SETVAR(tv_register_cleanup
);
391 case LDPT_ADD_SYMBOLS
:
392 SETVAR(tv_add_symbols
);
394 case LDPT_GET_SYMBOLS
:
395 SETVAR(tv_get_symbols
);
397 case LDPT_GET_SYMBOLS_V2
:
398 tv_get_symbols_v2
= tv
->tv_u
.tv_get_symbols
;
400 case LDPT_ADD_INPUT_FILE
:
401 SETVAR(tv_add_input_file
);
406 case LDPT_GET_INPUT_FILE
:
407 SETVAR(tv_get_input_file
);
412 case LDPT_RELEASE_INPUT_FILE
:
413 SETVAR(tv_release_input_file
);
415 case LDPT_ADD_INPUT_LIBRARY
:
416 SETVAR(tv_add_input_library
);
418 case LDPT_SET_EXTRA_LIBRARY_PATH
:
419 SETVAR(tv_set_extra_library_path
);
426 /* Standard plugin API entry point. */
427 enum ld_plugin_status
428 onload (struct ld_plugin_tv
*tv
)
430 enum ld_plugin_status rv
;
432 /* This plugin does nothing but dump the tv array. It would
433 be an error if this function was called without one. */
437 /* First entry should always be LDPT_MESSAGE, letting us get
438 hold of it easily so we can send output straight away. */
439 if (tv
[0].tv_tag
== LDPT_MESSAGE
)
440 tv_message
= tv
[0].tv_u
.tv_message
;
443 if ((rv
= parse_tv_tag (tv
)) != LDPS_OK
)
445 while ((tv
++)->tv_tag
!= LDPT_NULL
);
447 /* Register hooks only if instructed by options. */
448 if (register_claimfile_hook
)
450 if (!tv_register_claim_file
)
452 TV_MESSAGE (LDPL_FATAL
, "No register_claim_file hook");
456 (*tv_register_claim_file
) (onclaim_file
);
458 if (register_allsymbolsread_hook
)
460 if (!tv_register_all_symbols_read
)
462 TV_MESSAGE (LDPL_FATAL
, "No register_all_symbols_read hook");
466 (*tv_register_all_symbols_read
) (onall_symbols_read
);
468 if (register_cleanup_hook
)
470 if (!tv_register_cleanup
)
472 TV_MESSAGE (LDPL_FATAL
, "No register_cleanup hook");
476 (*tv_register_cleanup
) (oncleanup
);
479 /* Claim testsuite/ld-plugin/func.c, standalone or in a library. Its
480 size must be SIZE_OF_FUNC_C bytes. */
481 #define SIZE_OF_FUNC_C 248
482 if (onload_ret
== LDPS_OK
483 && (record_claim_file ("func.c", SIZE_OF_FUNC_C
) != LDPS_OK
484 || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
485 || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK
486 || record_claim_file ("libfunc.a", SIZE_OF_FUNC_C
) != LDPS_OK
487 || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
488 || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK
))
489 onload_ret
= LDPS_ERR
;
495 xstrdup (const char *s
)
497 size_t len
= strlen (s
) + 1;
498 char *ret
= malloc (len
+ 1);
499 return (char *) memcpy (ret
, s
, len
);
502 /* Standard plugin API registerable hook. */
503 static enum ld_plugin_status
504 onclaim_file (const struct ld_plugin_input_file
*file
, int *claimed
)
506 /* Let's see if we want to claim this file. */
507 claim_file_t
*claimfile
= claimfiles_list
;
508 size_t len
= strlen (file
->name
);
509 char *name
= xstrdup (file
->name
);
510 char *p
= name
+ len
;
513 /* Only match the file name without the directory part. */
514 islib
= *p
== 'a' && *(p
- 1) == '.';
515 for (; p
!= name
; p
--)
516 if (IS_DIR_SEPARATOR (*p
))
524 /* Claim the file only if the file name and size match and don't
525 match the whole library. */
526 if (!strcmp (p
, claimfile
->file
.name
)
527 && claimfile
->file
.filesize
== file
->filesize
528 && (!islib
|| file
->offset
!= 0))
530 claimfile
= claimfile
->next
;
535 /* If we decided to claim it, record that fact, and add any symbols
536 that were defined for it by plugin options. */
537 *claimed
= (claimfile
!= 0);
540 claimfile
->claimed
= TRUE
;
541 claimfile
->file
= *file
;
542 if (claimfile
->n_syms_used
&& !tv_add_symbols
)
544 else if (claimfile
->n_syms_used
)
545 return (*tv_add_symbols
) (claimfile
->file
.handle
,
546 claimfile
->n_syms_used
, claimfile
->symbols
);
549 return claim_file_ret
;
552 /* Standard plugin API registerable hook. */
553 static enum ld_plugin_status
554 onall_symbols_read (void)
556 static const char *resolutions
[] =
560 "LDPR_PREVAILING_DEF",
561 "LDPR_PREVAILING_DEF_IRONLY",
562 "LDPR_PREEMPTED_REG",
565 "LDPR_RESOLVED_EXEC",
567 "LDPR_PREVAILING_DEF_IRONLY_EXP",
569 claim_file_t
*claimfile
= dumpresolutions
? claimfiles_list
: NULL
;
570 add_file_t
*addfile
= addfiles_list
;
571 struct ld_plugin_input_file file
;
576 TV_MESSAGE (LDPL_INFO
, "hook called: all symbols read.");
577 for ( ; claimfile
; claimfile
= claimfile
->next
)
579 enum ld_plugin_status rv
;
581 if (claimfile
->n_syms_used
&& !tv_get_symbols_v2
)
583 else if (!claimfile
->n_syms_used
)
585 else if (!claimfile
->file
.handle
)
587 rv
= tv_get_input_file (claimfile
->file
.handle
, &file
);
590 TV_MESSAGE (LDPL_INFO
, "Input: %s (%s)", file
.name
,
591 claimfile
->file
.name
);
592 rv
= tv_get_view (claimfile
->file
.handle
, &view
);
595 #define EXPECTED_VIEW "/* The first line of this file must match the expectation of"
596 #define EXPECTED_VIEW_LENGTH (sizeof (EXPECTED_VIEW) - 1)
597 if (file
.filesize
!= SIZE_OF_FUNC_C
598 || SIZE_OF_FUNC_C
< sizeof EXPECTED_VIEW_LENGTH
599 || memcmp (view
, EXPECTED_VIEW
, EXPECTED_VIEW_LENGTH
) != 0)
601 char result
[EXPECTED_VIEW_LENGTH
+ 1];
602 memcpy (result
, view
, sizeof (result
));
603 result
[EXPECTED_VIEW_LENGTH
] = '\0';
604 TV_MESSAGE (LDPL_INFO
, "Incorrect view:");
605 TV_MESSAGE (LDPL_INFO
, " Expect: " EXPECTED_VIEW
);
606 TV_MESSAGE (LDPL_INFO
, " Result: %s", result
);
608 rv
= tv_get_symbols_v2 (claimfile
->file
.handle
, claimfile
->n_syms_used
,
612 for (n
= 0; n
< claimfile
->n_syms_used
; n
++)
613 TV_MESSAGE (LDPL_INFO
, "Sym: '%s%s%s' Resolution: %s",
614 claimfile
->symbols
[n
].name
,
615 claimfile
->symbols
[n
].version
? "@" : "",
616 (claimfile
->symbols
[n
].version
617 ? claimfile
->symbols
[n
].version
: ""),
618 resolutions
[claimfile
->symbols
[n
].resolution
]);
619 fd
= claimfile
->file
.fd
;
620 filename
= xstrdup (claimfile
->file
.name
);
621 rv
= tv_release_input_file (claimfile
->file
.handle
);
627 if (read (fd
, buffer
, sizeof (buffer
)) >= 0)
629 TV_MESSAGE (LDPL_FATAL
, "Unreleased file descriptor on: %s",
630 claimfile
->file
.name
);
636 for ( ; addfile
; addfile
= addfile
->next
)
638 enum ld_plugin_status rv
;
639 if (addfile
->type
== ADD_LIB
&& tv_add_input_library
)
640 rv
= (*tv_add_input_library
) (addfile
->name
);
641 else if (addfile
->type
== ADD_FILE
&& tv_add_input_file
)
642 rv
= (*tv_add_input_file
) (addfile
->name
);
643 else if (addfile
->type
== ADD_DIR
&& tv_set_extra_library_path
)
644 rv
= (*tv_set_extra_library_path
) (addfile
->name
);
651 return all_symbols_read_ret
;
654 /* Standard plugin API registerable hook. */
655 static enum ld_plugin_status
658 TV_MESSAGE (LDPL_INFO
, "hook called: cleanup.");