1 /* Test plugin for the GNU linker.
2 Copyright 2010 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"
26 #include "filenames.h"
28 extern enum ld_plugin_status
onload (struct ld_plugin_tv
*tv
);
29 static enum ld_plugin_status
onclaim_file (const struct ld_plugin_input_file
*file
,
31 static enum ld_plugin_status
onall_symbols_read (void);
32 static enum ld_plugin_status
oncleanup (void);
34 /* Helper for calling plugin api message function. */
35 #define TV_MESSAGE if (tv_message) (*tv_message)
37 /* Struct for recording files to claim / files claimed. */
38 typedef struct claim_file
40 struct claim_file
*next
;
41 struct ld_plugin_input_file file
;
43 struct ld_plugin_symbol
*symbols
;
48 /* Types of things that can be added at all symbols read time. */
49 typedef enum addfile_enum
56 /* Struct for recording files to add to final link. */
57 typedef struct add_file
59 struct add_file
*next
;
64 /* Helper macro for defining array of transfer vector tags and names. */
65 #define ADDENTRY(tag) { tag, #tag }
67 /* Struct for looking up human-readable versions of tag names. */
68 typedef struct tag_name
70 enum ld_plugin_tag tag
;
74 /* Array of all known tags and their names. */
75 static const tag_name_t tag_names
[] =
78 ADDENTRY(LDPT_API_VERSION
),
79 ADDENTRY(LDPT_GOLD_VERSION
),
80 ADDENTRY(LDPT_LINKER_OUTPUT
),
81 ADDENTRY(LDPT_OPTION
),
82 ADDENTRY(LDPT_REGISTER_CLAIM_FILE_HOOK
),
83 ADDENTRY(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
),
84 ADDENTRY(LDPT_REGISTER_CLEANUP_HOOK
),
85 ADDENTRY(LDPT_ADD_SYMBOLS
),
86 ADDENTRY(LDPT_GET_SYMBOLS
),
87 ADDENTRY(LDPT_ADD_INPUT_FILE
),
88 ADDENTRY(LDPT_MESSAGE
),
89 ADDENTRY(LDPT_GET_INPUT_FILE
),
90 ADDENTRY(LDPT_RELEASE_INPUT_FILE
),
91 ADDENTRY(LDPT_ADD_INPUT_LIBRARY
),
92 ADDENTRY(LDPT_OUTPUT_NAME
),
93 ADDENTRY(LDPT_SET_EXTRA_LIBRARY_PATH
),
94 ADDENTRY(LDPT_GNU_LD_VERSION
)
97 /* Function pointers to cache hooks passed at onload time. */
98 static ld_plugin_register_claim_file tv_register_claim_file
= 0;
99 static ld_plugin_register_all_symbols_read tv_register_all_symbols_read
= 0;
100 static ld_plugin_register_cleanup tv_register_cleanup
= 0;
101 static ld_plugin_add_symbols tv_add_symbols
= 0;
102 static ld_plugin_get_symbols tv_get_symbols
= 0;
103 static ld_plugin_add_input_file tv_add_input_file
= 0;
104 static ld_plugin_message tv_message
= 0;
105 static ld_plugin_get_input_file tv_get_input_file
= 0;
106 static ld_plugin_release_input_file tv_release_input_file
= 0;
107 static ld_plugin_add_input_library tv_add_input_library
= 0;
108 static ld_plugin_set_extra_library_path tv_set_extra_library_path
= 0;
110 /* Other cached info from the transfer vector. */
111 static enum ld_plugin_output_file_type linker_output
;
112 static const char *output_name
;
114 /* Behaviour control flags set by plugin options. */
115 static enum ld_plugin_status onload_ret
= LDPS_OK
;
116 static enum ld_plugin_status claim_file_ret
= LDPS_OK
;
117 static enum ld_plugin_status all_symbols_read_ret
= LDPS_OK
;
118 static enum ld_plugin_status cleanup_ret
= LDPS_OK
;
119 static bfd_boolean register_claimfile_hook
= FALSE
;
120 static bfd_boolean register_allsymbolsread_hook
= FALSE
;
121 static bfd_boolean register_cleanup_hook
= FALSE
;
122 static bfd_boolean dumpresolutions
= FALSE
;
124 /* The master list of all claimable/claimed files. */
125 static claim_file_t
*claimfiles_list
= NULL
;
127 /* We keep a tail pointer for easy linking on the end. */
128 static claim_file_t
**claimfiles_tail_chain_ptr
= &claimfiles_list
;
130 /* The last claimed file added to the list, for receiving syms. */
131 static claim_file_t
*last_claimfile
= NULL
;
133 /* The master list of all files to add to the final link. */
134 static add_file_t
*addfiles_list
= NULL
;
136 /* We keep a tail pointer for easy linking on the end. */
137 static add_file_t
**addfiles_tail_chain_ptr
= &addfiles_list
;
139 /* Add a new claimfile on the end of the chain. */
140 static enum ld_plugin_status
141 record_claim_file (const char *file
)
143 claim_file_t
*newfile
;
145 newfile
= malloc (sizeof *newfile
);
148 memset (newfile
, 0, sizeof *newfile
);
149 /* Only setup for now is remembering the name to look for. */
150 newfile
->file
.name
= file
;
151 /* Chain it on the end of the list. */
152 *claimfiles_tail_chain_ptr
= newfile
;
153 claimfiles_tail_chain_ptr
= &newfile
->next
;
154 /* Record it as active for receiving symbols to register. */
155 last_claimfile
= newfile
;
159 /* Add a new addfile on the end of the chain. */
160 static enum ld_plugin_status
161 record_add_file (const char *file
, addfile_enum_t type
)
165 newfile
= malloc (sizeof *newfile
);
168 newfile
->next
= NULL
;
169 newfile
->name
= file
;
170 newfile
->type
= type
;;
171 /* Chain it on the end of the list. */
172 *addfiles_tail_chain_ptr
= newfile
;
173 addfiles_tail_chain_ptr
= &newfile
->next
;
177 /* Parse a command-line argument string into a symbol definition.
178 Symbol-strings follow the colon-separated format:
179 NAME:VERSION:def:vis:size:COMDATKEY
180 where the fields in capitals are strings and those in lower
181 case are integers. We don't allow to specify a resolution as
182 doing so is not meaningful when calling the add symbols hook. */
183 static enum ld_plugin_status
184 parse_symdefstr (const char *str
, struct ld_plugin_symbol
*sym
)
188 const char *colon1
, *colon2
, *colon5
;
190 /* Locate the colons separating the first two strings. */
191 colon1
= strchr (str
, ':');
194 colon2
= strchr (colon1
+1, ':');
197 /* Name must not be empty (version may be). */
201 /* The fifth colon and trailing comdat key string are optional,
202 but the intermediate ones must all be present. */
203 colon5
= strchr (colon2
+1, ':'); /* Actually only third so far. */
206 colon5
= strchr (colon5
+1, ':'); /* Hopefully fourth now. */
209 colon5
= strchr (colon5
+1, ':'); /* Optional fifth now. */
211 /* Finally we'll use sscanf to parse the numeric fields, then
212 we'll split out the strings which we need to allocate separate
213 storage for anyway so that we can add nul termination. */
214 n
= sscanf (colon2
+ 1, "%i:%i:%lli", &sym
->def
, &sym
->visibility
, &size
);
218 /* Parsed successfully, so allocate strings and fill out fields. */
220 sym
->resolution
= LDPR_UNKNOWN
;
221 sym
->name
= malloc (colon1
- str
+ 1);
224 memcpy (sym
->name
, str
, colon1
- str
);
225 sym
->name
[colon1
- str
] = '\0';
226 if (colon2
> (colon1
+ 1))
228 sym
->version
= malloc (colon2
- colon1
);
231 memcpy (sym
->version
, colon1
+ 1, colon2
- (colon1
+ 1));
232 sym
->version
[colon2
- (colon1
+ 1)] = '\0';
236 if (colon5
&& colon5
[1])
238 sym
->comdat_key
= malloc (strlen (colon5
+ 1) + 1);
239 if (!sym
->comdat_key
)
241 strcpy (sym
->comdat_key
, colon5
+ 1);
248 /* Record a symbol to be added for the last-added claimfile. */
249 static enum ld_plugin_status
250 record_claimed_file_symbol (const char *symdefstr
)
252 struct ld_plugin_symbol sym
;
254 /* Can't add symbols except as belonging to claimed files. */
258 /* If string doesn't parse correctly, give an error. */
259 if (parse_symdefstr (symdefstr
, &sym
) != LDPS_OK
)
262 /* Check for enough space, resize array if needed, and add it. */
263 if (last_claimfile
->n_syms_allocated
== last_claimfile
->n_syms_used
)
265 int new_n_syms
= last_claimfile
->n_syms_allocated
266 ? 2 * last_claimfile
->n_syms_allocated
268 last_claimfile
->symbols
= realloc (last_claimfile
->symbols
,
269 new_n_syms
* sizeof *last_claimfile
->symbols
);
270 if (!last_claimfile
->symbols
)
272 last_claimfile
->n_syms_allocated
= new_n_syms
;
274 last_claimfile
->symbols
[last_claimfile
->n_syms_used
++] = sym
;
279 /* Records the status to return from one of the registered hooks. */
280 static enum ld_plugin_status
281 set_ret_val (const char *whichval
, enum ld_plugin_status retval
)
283 if (!strcmp ("onload", whichval
))
285 else if (!strcmp ("claimfile", whichval
))
286 claim_file_ret
= retval
;
287 else if (!strcmp ("allsymbolsread", whichval
))
288 all_symbols_read_ret
= retval
;
289 else if (!strcmp ("cleanup", whichval
))
290 cleanup_ret
= retval
;
296 /* Records hooks which should be registered. */
297 static enum ld_plugin_status
298 set_register_hook (const char *whichhook
, bfd_boolean yesno
)
300 if (!strcmp ("claimfile", whichhook
))
301 register_claimfile_hook
= yesno
;
302 else if (!strcmp ("allsymbolsread", whichhook
))
303 register_allsymbolsread_hook
= yesno
;
304 else if (!strcmp ("cleanup", whichhook
))
305 register_cleanup_hook
= yesno
;
311 /* Determine type of plugin option and pass to individual parsers. */
312 static enum ld_plugin_status
313 parse_option (const char *opt
)
315 if (!strncmp ("fail", opt
, 4))
316 return set_ret_val (opt
+ 4, LDPS_ERR
);
317 else if (!strncmp ("pass", opt
, 4))
318 return set_ret_val (opt
+ 4, LDPS_OK
);
319 else if (!strncmp ("register", opt
, 8))
320 return set_register_hook (opt
+ 8, TRUE
);
321 else if (!strncmp ("noregister", opt
, 10))
322 return set_register_hook (opt
+ 10, FALSE
);
323 else if (!strncmp ("claim:", opt
, 6))
324 return record_claim_file (opt
+ 6);
325 else if (!strncmp ("sym:", opt
, 4))
326 return record_claimed_file_symbol (opt
+ 4);
327 else if (!strncmp ("add:", opt
, 4))
328 return record_add_file (opt
+ 4, ADD_FILE
);
329 else if (!strncmp ("lib:", opt
, 4))
330 return record_add_file (opt
+ 4, ADD_LIB
);
331 else if (!strncmp ("dir:", opt
, 4))
332 return record_add_file (opt
+ 4, ADD_DIR
);
333 else if (!strcmp ("dumpresolutions", opt
))
334 dumpresolutions
= TRUE
;
340 /* Output contents of transfer vector array entry in human-readable form. */
342 dump_tv_tag (size_t n
, struct ld_plugin_tv
*tv
)
348 for (tag
= 0; tag
< ARRAY_SIZE (tag_names
); tag
++)
349 if (tag_names
[tag
].tag
== tv
->tv_tag
)
351 sprintf (unknownbuf
, "unknown tag #%d", tv
->tv_tag
);
352 name
= (tag
< ARRAY_SIZE (tag_names
)) ? tag_names
[tag
].name
: unknownbuf
;
356 case LDPT_OUTPUT_NAME
:
357 TV_MESSAGE (LDPL_INFO
, "tv[%d]: %s '%s'", n
, name
,
360 case LDPT_REGISTER_CLAIM_FILE_HOOK
:
361 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
:
362 case LDPT_REGISTER_CLEANUP_HOOK
:
363 case LDPT_ADD_SYMBOLS
:
364 case LDPT_GET_SYMBOLS
:
365 case LDPT_ADD_INPUT_FILE
:
367 case LDPT_GET_INPUT_FILE
:
368 case LDPT_RELEASE_INPUT_FILE
:
369 case LDPT_ADD_INPUT_LIBRARY
:
370 case LDPT_SET_EXTRA_LIBRARY_PATH
:
371 TV_MESSAGE (LDPL_INFO
, "tv[%d]: %s func@0x%p", n
, name
,
372 (void *)(tv
->tv_u
.tv_message
));
375 case LDPT_API_VERSION
:
376 case LDPT_GOLD_VERSION
:
377 case LDPT_LINKER_OUTPUT
:
378 case LDPT_GNU_LD_VERSION
:
380 TV_MESSAGE (LDPL_INFO
, "tv[%d]: %s value %W (%d)", n
, name
,
381 (bfd_vma
)tv
->tv_u
.tv_val
, tv
->tv_u
.tv_val
);
386 /* Handle/record information received in a transfer vector entry. */
387 static enum ld_plugin_status
388 parse_tv_tag (struct ld_plugin_tv
*tv
)
390 #define SETVAR(x) x = tv->tv_u.x
394 return parse_option (tv
->tv_u
.tv_string
);
396 case LDPT_GOLD_VERSION
:
397 case LDPT_GNU_LD_VERSION
:
398 case LDPT_API_VERSION
:
401 case LDPT_OUTPUT_NAME
:
402 output_name
= tv
->tv_u
.tv_string
;
404 case LDPT_LINKER_OUTPUT
:
405 linker_output
= tv
->tv_u
.tv_val
;
407 case LDPT_REGISTER_CLAIM_FILE_HOOK
:
408 SETVAR(tv_register_claim_file
);
410 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
:
411 SETVAR(tv_register_all_symbols_read
);
413 case LDPT_REGISTER_CLEANUP_HOOK
:
414 SETVAR(tv_register_cleanup
);
416 case LDPT_ADD_SYMBOLS
:
417 SETVAR(tv_add_symbols
);
419 case LDPT_GET_SYMBOLS
:
420 SETVAR(tv_get_symbols
);
422 case LDPT_ADD_INPUT_FILE
:
423 SETVAR(tv_add_input_file
);
428 case LDPT_GET_INPUT_FILE
:
429 SETVAR(tv_get_input_file
);
431 case LDPT_RELEASE_INPUT_FILE
:
432 SETVAR(tv_release_input_file
);
434 case LDPT_ADD_INPUT_LIBRARY
:
435 SETVAR(tv_add_input_library
);
437 case LDPT_SET_EXTRA_LIBRARY_PATH
:
438 SETVAR(tv_set_extra_library_path
);
445 /* Record any useful information in transfer vector entry and display
446 it in human-readable form using the plugin API message() callback. */
447 enum ld_plugin_status
448 parse_and_dump_tv_tag (size_t n
, struct ld_plugin_tv
*tv
)
450 enum ld_plugin_status rv
= parse_tv_tag (tv
);
455 /* Standard plugin API entry point. */
456 enum ld_plugin_status
457 onload (struct ld_plugin_tv
*tv
)
460 enum ld_plugin_status rv
;
462 /* This plugin does nothing but dump the tv array. It would
463 be an error if this function was called without one. */
467 /* First entry should always be LDPT_MESSAGE, letting us get
468 hold of it easily so we can send output straight away. */
469 if (tv
[0].tv_tag
== LDPT_MESSAGE
)
470 tv_message
= tv
[0].tv_u
.tv_message
;
473 TV_MESSAGE (LDPL_INFO
, "Hello from testplugin.");
476 if ((rv
= parse_and_dump_tv_tag (n
++, tv
)) != LDPS_OK
)
478 while ((tv
++)->tv_tag
!= LDPT_NULL
);
480 /* Register hooks only if instructed by options. */
481 if (register_claimfile_hook
)
483 if (!tv_register_claim_file
)
485 TV_MESSAGE (LDPL_FATAL
, "No register_claim_file hook");
489 (*tv_register_claim_file
) (onclaim_file
);
491 if (register_allsymbolsread_hook
)
493 if (!tv_register_all_symbols_read
)
495 TV_MESSAGE (LDPL_FATAL
, "No register_all_symbols_read hook");
499 (*tv_register_all_symbols_read
) (onall_symbols_read
);
501 if (register_cleanup_hook
)
503 if (!tv_register_cleanup
)
505 TV_MESSAGE (LDPL_FATAL
, "No register_cleanup hook");
509 (*tv_register_cleanup
) (oncleanup
);
515 /* Standard plugin API registerable hook. */
516 static enum ld_plugin_status
517 onclaim_file (const struct ld_plugin_input_file
*file
, int *claimed
)
519 /* Let's see if we want to claim this file. */
520 claim_file_t
*claimfile
= claimfiles_list
;
523 if (!filename_cmp (file
->name
, claimfile
->file
.name
))
525 claimfile
= claimfile
->next
;
528 /* Inform the user/testsuite. */
529 TV_MESSAGE (LDPL_INFO
, "hook called: claim_file %s [@%ld/%ld] %s",
530 file
->name
, (long)file
->offset
, (long)file
->filesize
,
531 claimfile
? "CLAIMED" : "not claimed");
534 /* If we decided to claim it, record that fact, and add any symbols
535 that were defined for it by plugin options. */
536 *claimed
= (claimfile
!= 0);
539 claimfile
->claimed
= TRUE
;
540 claimfile
->file
= *file
;
541 if (claimfile
->n_syms_used
&& !tv_add_symbols
)
543 else if (claimfile
->n_syms_used
)
544 return (*tv_add_symbols
) (claimfile
->file
.handle
,
545 claimfile
->n_syms_used
, claimfile
->symbols
);
548 return claim_file_ret
;
551 /* Standard plugin API registerable hook. */
552 static enum ld_plugin_status
553 onall_symbols_read (void)
555 static const char *resolutions
[] =
559 "LDPR_PREVAILING_DEF",
560 "LDPR_PREVAILING_DEF_IRONLY",
561 "LDPR_PREEMPTED_REG",
564 "LDPR_RESOLVED_EXEC",
567 claim_file_t
*claimfile
= dumpresolutions
? claimfiles_list
: NULL
;
568 add_file_t
*addfile
= addfiles_list
;
569 TV_MESSAGE (LDPL_INFO
, "hook called: all symbols read.");
570 for ( ; claimfile
; claimfile
= claimfile
->next
)
572 enum ld_plugin_status rv
;
574 if (claimfile
->n_syms_used
&& !tv_get_symbols
)
576 else if (!claimfile
->n_syms_used
)
578 rv
= tv_get_symbols (claimfile
->file
.handle
, claimfile
->n_syms_used
,
582 for (n
= 0; n
< claimfile
->n_syms_used
; n
++)
583 TV_MESSAGE (LDPL_INFO
, "Sym: '%s%s%s' Resolution: %s",
584 claimfile
->symbols
[n
].name
,
585 claimfile
->symbols
[n
].version
? "@" : "",
586 (claimfile
->symbols
[n
].version
587 ? claimfile
->symbols
[n
].version
: ""),
588 resolutions
[claimfile
->symbols
[n
].resolution
]);
590 for ( ; addfile
; addfile
= addfile
->next
)
592 enum ld_plugin_status rv
;
593 if (addfile
->type
== ADD_LIB
&& tv_add_input_library
)
594 rv
= (*tv_add_input_library
) (addfile
->name
);
595 else if (addfile
->type
== ADD_FILE
&& tv_add_input_file
)
596 rv
= (*tv_add_input_file
) (addfile
->name
);
597 else if (addfile
->type
== ADD_DIR
&& tv_set_extra_library_path
)
598 rv
= (*tv_set_extra_library_path
) (addfile
->name
);
605 return all_symbols_read_ret
;
608 /* Standard plugin API registerable hook. */
609 static enum ld_plugin_status
612 TV_MESSAGE (LDPL_INFO
, "hook called: cleanup.");