2011-05-25 Tristan Gingold <gingold@adacore.com>
[binutils.git] / ld / testplug.c
blobf04df10fa051061262cb77fcc4d38e1acdfd2b11
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. */
21 #include "sysdep.h"
22 #include "bfd.h"
23 #include "plugin-api.h"
24 /* For ARRAY_SIZE macro only - we don't link the library itself. */
25 #include "libiberty.h"
27 extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
28 static enum ld_plugin_status onclaim_file (const struct ld_plugin_input_file *file,
29 int *claimed);
30 static enum ld_plugin_status onall_symbols_read (void);
31 static enum ld_plugin_status oncleanup (void);
33 /* Helper for calling plugin api message function. */
34 #define TV_MESSAGE if (tv_message) (*tv_message)
36 /* Struct for recording files to claim / files claimed. */
37 typedef struct claim_file
39 struct claim_file *next;
40 struct ld_plugin_input_file file;
41 bfd_boolean claimed;
42 struct ld_plugin_symbol *symbols;
43 int n_syms_allocated;
44 int n_syms_used;
45 } claim_file_t;
47 /* Types of things that can be added at all symbols read time. */
48 typedef enum addfile_enum
50 ADD_FILE,
51 ADD_LIB,
52 ADD_DIR
53 } addfile_enum_t;
55 /* Struct for recording files to add to final link. */
56 typedef struct add_file
58 struct add_file *next;
59 const char *name;
60 addfile_enum_t type;
61 } add_file_t;
63 /* Helper macro for defining array of transfer vector tags and names. */
64 #define ADDENTRY(tag) { tag, #tag }
66 /* Struct for looking up human-readable versions of tag names. */
67 typedef struct tag_name
69 enum ld_plugin_tag tag;
70 const char *name;
71 } tag_name_t;
73 /* Array of all known tags and their names. */
74 static const tag_name_t tag_names[] =
76 ADDENTRY(LDPT_NULL),
77 ADDENTRY(LDPT_API_VERSION),
78 ADDENTRY(LDPT_GOLD_VERSION),
79 ADDENTRY(LDPT_LINKER_OUTPUT),
80 ADDENTRY(LDPT_OPTION),
81 ADDENTRY(LDPT_REGISTER_CLAIM_FILE_HOOK),
82 ADDENTRY(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK),
83 ADDENTRY(LDPT_REGISTER_CLEANUP_HOOK),
84 ADDENTRY(LDPT_ADD_SYMBOLS),
85 ADDENTRY(LDPT_GET_SYMBOLS),
86 ADDENTRY(LDPT_ADD_INPUT_FILE),
87 ADDENTRY(LDPT_MESSAGE),
88 ADDENTRY(LDPT_GET_INPUT_FILE),
89 ADDENTRY(LDPT_RELEASE_INPUT_FILE),
90 ADDENTRY(LDPT_ADD_INPUT_LIBRARY),
91 ADDENTRY(LDPT_OUTPUT_NAME),
92 ADDENTRY(LDPT_SET_EXTRA_LIBRARY_PATH),
93 ADDENTRY(LDPT_GNU_LD_VERSION)
96 /* Function pointers to cache hooks passed at onload time. */
97 static ld_plugin_register_claim_file tv_register_claim_file = 0;
98 static ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0;
99 static ld_plugin_register_cleanup tv_register_cleanup = 0;
100 static ld_plugin_add_symbols tv_add_symbols = 0;
101 static ld_plugin_get_symbols tv_get_symbols = 0;
102 static ld_plugin_add_input_file tv_add_input_file = 0;
103 static ld_plugin_message tv_message = 0;
104 static ld_plugin_get_input_file tv_get_input_file = 0;
105 static ld_plugin_release_input_file tv_release_input_file = 0;
106 static ld_plugin_add_input_library tv_add_input_library = 0;
107 static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0;
109 /* Other cached info from the transfer vector. */
110 static enum ld_plugin_output_file_type linker_output;
111 static const char *output_name;
113 /* Behaviour control flags set by plugin options. */
114 static enum ld_plugin_status onload_ret = LDPS_OK;
115 static enum ld_plugin_status claim_file_ret = LDPS_OK;
116 static enum ld_plugin_status all_symbols_read_ret = LDPS_OK;
117 static enum ld_plugin_status cleanup_ret = LDPS_OK;
118 static bfd_boolean register_claimfile_hook = FALSE;
119 static bfd_boolean register_allsymbolsread_hook = FALSE;
120 static bfd_boolean register_cleanup_hook = FALSE;
121 static bfd_boolean dumpresolutions = FALSE;
123 /* The master list of all claimable/claimed files. */
124 static claim_file_t *claimfiles_list = NULL;
126 /* We keep a tail pointer for easy linking on the end. */
127 static claim_file_t **claimfiles_tail_chain_ptr = &claimfiles_list;
129 /* The last claimed file added to the list, for receiving syms. */
130 static claim_file_t *last_claimfile = NULL;
132 /* The master list of all files to add to the final link. */
133 static add_file_t *addfiles_list = NULL;
135 /* We keep a tail pointer for easy linking on the end. */
136 static add_file_t **addfiles_tail_chain_ptr = &addfiles_list;
138 /* Add a new claimfile on the end of the chain. */
139 static enum ld_plugin_status
140 record_claim_file (const char *file)
142 claim_file_t *newfile;
144 newfile = malloc (sizeof *newfile);
145 if (!newfile)
146 return LDPS_ERR;
147 memset (newfile, 0, sizeof *newfile);
148 /* Only setup for now is remembering the name to look for. */
149 newfile->file.name = file;
150 /* Chain it on the end of the list. */
151 *claimfiles_tail_chain_ptr = newfile;
152 claimfiles_tail_chain_ptr = &newfile->next;
153 /* Record it as active for receiving symbols to register. */
154 last_claimfile = newfile;
155 return LDPS_OK;
158 /* Add a new addfile on the end of the chain. */
159 static enum ld_plugin_status
160 record_add_file (const char *file, addfile_enum_t type)
162 add_file_t *newfile;
164 newfile = malloc (sizeof *newfile);
165 if (!newfile)
166 return LDPS_ERR;
167 newfile->next = NULL;
168 newfile->name = file;
169 newfile->type = type;;
170 /* Chain it on the end of the list. */
171 *addfiles_tail_chain_ptr = newfile;
172 addfiles_tail_chain_ptr = &newfile->next;
173 return LDPS_OK;
176 /* Parse a command-line argument string into a symbol definition.
177 Symbol-strings follow the colon-separated format:
178 NAME:VERSION:def:vis:size:COMDATKEY
179 where the fields in capitals are strings and those in lower
180 case are integers. We don't allow to specify a resolution as
181 doing so is not meaningful when calling the add symbols hook. */
182 static enum ld_plugin_status
183 parse_symdefstr (const char *str, struct ld_plugin_symbol *sym)
185 int n;
186 long long size;
187 const char *colon1, *colon2, *colon5;
189 /* Locate the colons separating the first two strings. */
190 colon1 = strchr (str, ':');
191 if (!colon1)
192 return LDPS_ERR;
193 colon2 = strchr (colon1+1, ':');
194 if (!colon2)
195 return LDPS_ERR;
196 /* Name must not be empty (version may be). */
197 if (colon1 == str)
198 return LDPS_ERR;
200 /* The fifth colon and trailing comdat key string are optional,
201 but the intermediate ones must all be present. */
202 colon5 = strchr (colon2+1, ':'); /* Actually only third so far. */
203 if (!colon5)
204 return LDPS_ERR;
205 colon5 = strchr (colon5+1, ':'); /* Hopefully fourth now. */
206 if (!colon5)
207 return LDPS_ERR;
208 colon5 = strchr (colon5+1, ':'); /* Optional fifth now. */
210 /* Finally we'll use sscanf to parse the numeric fields, then
211 we'll split out the strings which we need to allocate separate
212 storage for anyway so that we can add nul termination. */
213 n = sscanf (colon2 + 1, "%i:%i:%lli", &sym->def, &sym->visibility, &size);
214 if (n != 3)
215 return LDPS_ERR;
217 /* Parsed successfully, so allocate strings and fill out fields. */
218 sym->size = size;
219 sym->resolution = LDPR_UNKNOWN;
220 sym->name = malloc (colon1 - str + 1);
221 if (!sym->name)
222 return LDPS_ERR;
223 memcpy (sym->name, str, colon1 - str);
224 sym->name[colon1 - str] = '\0';
225 if (colon2 > (colon1 + 1))
227 sym->version = malloc (colon2 - colon1);
228 if (!sym->version)
229 return LDPS_ERR;
230 memcpy (sym->version, colon1 + 1, colon2 - (colon1 + 1));
231 sym->version[colon2 - (colon1 + 1)] = '\0';
233 else
234 sym->version = NULL;
235 if (colon5 && colon5[1])
237 sym->comdat_key = malloc (strlen (colon5 + 1) + 1);
238 if (!sym->comdat_key)
239 return LDPS_ERR;
240 strcpy (sym->comdat_key, colon5 + 1);
242 else
243 sym->comdat_key = 0;
244 return LDPS_OK;
247 /* Record a symbol to be added for the last-added claimfile. */
248 static enum ld_plugin_status
249 record_claimed_file_symbol (const char *symdefstr)
251 struct ld_plugin_symbol sym;
253 /* Can't add symbols except as belonging to claimed files. */
254 if (!last_claimfile)
255 return LDPS_ERR;
257 /* If string doesn't parse correctly, give an error. */
258 if (parse_symdefstr (symdefstr, &sym) != LDPS_OK)
259 return LDPS_ERR;
261 /* Check for enough space, resize array if needed, and add it. */
262 if (last_claimfile->n_syms_allocated == last_claimfile->n_syms_used)
264 int new_n_syms = last_claimfile->n_syms_allocated
265 ? 2 * last_claimfile->n_syms_allocated
266 : 10;
267 last_claimfile->symbols = realloc (last_claimfile->symbols,
268 new_n_syms * sizeof *last_claimfile->symbols);
269 if (!last_claimfile->symbols)
270 return LDPS_ERR;
271 last_claimfile->n_syms_allocated = new_n_syms;
273 last_claimfile->symbols[last_claimfile->n_syms_used++] = sym;
275 return LDPS_OK;
278 /* Records the status to return from one of the registered hooks. */
279 static enum ld_plugin_status
280 set_ret_val (const char *whichval, enum ld_plugin_status retval)
282 if (!strcmp ("onload", whichval))
283 onload_ret = retval;
284 else if (!strcmp ("claimfile", whichval))
285 claim_file_ret = retval;
286 else if (!strcmp ("allsymbolsread", whichval))
287 all_symbols_read_ret = retval;
288 else if (!strcmp ("cleanup", whichval))
289 cleanup_ret = retval;
290 else
291 return LDPS_ERR;
292 return LDPS_OK;
295 /* Records hooks which should be registered. */
296 static enum ld_plugin_status
297 set_register_hook (const char *whichhook, bfd_boolean yesno)
299 if (!strcmp ("claimfile", whichhook))
300 register_claimfile_hook = yesno;
301 else if (!strcmp ("allsymbolsread", whichhook))
302 register_allsymbolsread_hook = yesno;
303 else if (!strcmp ("cleanup", whichhook))
304 register_cleanup_hook = yesno;
305 else
306 return LDPS_ERR;
307 return LDPS_OK;
310 /* Determine type of plugin option and pass to individual parsers. */
311 static enum ld_plugin_status
312 parse_option (const char *opt)
314 if (!strncmp ("fail", opt, 4))
315 return set_ret_val (opt + 4, LDPS_ERR);
316 else if (!strncmp ("pass", opt, 4))
317 return set_ret_val (opt + 4, LDPS_OK);
318 else if (!strncmp ("register", opt, 8))
319 return set_register_hook (opt + 8, TRUE);
320 else if (!strncmp ("noregister", opt, 10))
321 return set_register_hook (opt + 10, FALSE);
322 else if (!strncmp ("claim:", opt, 6))
323 return record_claim_file (opt + 6);
324 else if (!strncmp ("sym:", opt, 4))
325 return record_claimed_file_symbol (opt + 4);
326 else if (!strncmp ("add:", opt, 4))
327 return record_add_file (opt + 4, ADD_FILE);
328 else if (!strncmp ("lib:", opt, 4))
329 return record_add_file (opt + 4, ADD_LIB);
330 else if (!strncmp ("dir:", opt, 4))
331 return record_add_file (opt + 4, ADD_DIR);
332 else if (!strcmp ("dumpresolutions", opt))
333 dumpresolutions = TRUE;
334 else
335 return LDPS_ERR;
336 return LDPS_OK;
339 /* Output contents of transfer vector array entry in human-readable form. */
340 static void
341 dump_tv_tag (size_t n, struct ld_plugin_tv *tv)
343 size_t tag;
344 char unknownbuf[40];
345 const char *name;
347 for (tag = 0; tag < ARRAY_SIZE (tag_names); tag++)
348 if (tag_names[tag].tag == tv->tv_tag)
349 break;
350 sprintf (unknownbuf, "unknown tag #%d", tv->tv_tag);
351 name = (tag < ARRAY_SIZE (tag_names)) ? tag_names[tag].name : unknownbuf;
352 switch (tv->tv_tag)
354 case LDPT_OPTION:
355 case LDPT_OUTPUT_NAME:
356 TV_MESSAGE (LDPL_INFO, "tv[%d]: %s '%s'", n, name,
357 tv->tv_u.tv_string);
358 break;
359 case LDPT_REGISTER_CLAIM_FILE_HOOK:
360 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
361 case LDPT_REGISTER_CLEANUP_HOOK:
362 case LDPT_ADD_SYMBOLS:
363 case LDPT_GET_SYMBOLS:
364 case LDPT_ADD_INPUT_FILE:
365 case LDPT_MESSAGE:
366 case LDPT_GET_INPUT_FILE:
367 case LDPT_RELEASE_INPUT_FILE:
368 case LDPT_ADD_INPUT_LIBRARY:
369 case LDPT_SET_EXTRA_LIBRARY_PATH:
370 TV_MESSAGE (LDPL_INFO, "tv[%d]: %s func@0x%p", n, name,
371 (void *)(tv->tv_u.tv_message));
372 break;
373 case LDPT_NULL:
374 case LDPT_API_VERSION:
375 case LDPT_GOLD_VERSION:
376 case LDPT_LINKER_OUTPUT:
377 case LDPT_GNU_LD_VERSION:
378 default:
379 TV_MESSAGE (LDPL_INFO, "tv[%d]: %s value %W (%d)", n, name,
380 (bfd_vma)tv->tv_u.tv_val, tv->tv_u.tv_val);
381 break;
385 /* Handle/record information received in a transfer vector entry. */
386 static enum ld_plugin_status
387 parse_tv_tag (struct ld_plugin_tv *tv)
389 #define SETVAR(x) x = tv->tv_u.x
390 switch (tv->tv_tag)
392 case LDPT_OPTION:
393 return parse_option (tv->tv_u.tv_string);
394 case LDPT_NULL:
395 case LDPT_GOLD_VERSION:
396 case LDPT_GNU_LD_VERSION:
397 case LDPT_API_VERSION:
398 default:
399 break;
400 case LDPT_OUTPUT_NAME:
401 output_name = tv->tv_u.tv_string;
402 break;
403 case LDPT_LINKER_OUTPUT:
404 linker_output = tv->tv_u.tv_val;
405 break;
406 case LDPT_REGISTER_CLAIM_FILE_HOOK:
407 SETVAR(tv_register_claim_file);
408 break;
409 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
410 SETVAR(tv_register_all_symbols_read);
411 break;
412 case LDPT_REGISTER_CLEANUP_HOOK:
413 SETVAR(tv_register_cleanup);
414 break;
415 case LDPT_ADD_SYMBOLS:
416 SETVAR(tv_add_symbols);
417 break;
418 case LDPT_GET_SYMBOLS:
419 SETVAR(tv_get_symbols);
420 break;
421 case LDPT_ADD_INPUT_FILE:
422 SETVAR(tv_add_input_file);
423 break;
424 case LDPT_MESSAGE:
425 SETVAR(tv_message);
426 break;
427 case LDPT_GET_INPUT_FILE:
428 SETVAR(tv_get_input_file);
429 break;
430 case LDPT_RELEASE_INPUT_FILE:
431 SETVAR(tv_release_input_file);
432 break;
433 case LDPT_ADD_INPUT_LIBRARY:
434 SETVAR(tv_add_input_library);
435 break;
436 case LDPT_SET_EXTRA_LIBRARY_PATH:
437 SETVAR(tv_set_extra_library_path);
438 break;
440 #undef SETVAR
441 return LDPS_OK;
444 /* Record any useful information in transfer vector entry and display
445 it in human-readable form using the plugin API message() callback. */
446 enum ld_plugin_status
447 parse_and_dump_tv_tag (size_t n, struct ld_plugin_tv *tv)
449 enum ld_plugin_status rv = parse_tv_tag (tv);
450 dump_tv_tag (n, tv);
451 return rv;
454 /* Standard plugin API entry point. */
455 enum ld_plugin_status
456 onload (struct ld_plugin_tv *tv)
458 size_t n = 0;
459 enum ld_plugin_status rv;
461 /* This plugin does nothing but dump the tv array. It would
462 be an error if this function was called without one. */
463 if (!tv)
464 return LDPS_ERR;
466 /* First entry should always be LDPT_MESSAGE, letting us get
467 hold of it easily so we can send output straight away. */
468 if (tv[0].tv_tag == LDPT_MESSAGE)
469 tv_message = tv[0].tv_u.tv_message;
471 fflush (NULL);
472 TV_MESSAGE (LDPL_INFO, "Hello from testplugin.");
475 if ((rv = parse_and_dump_tv_tag (n++, tv)) != LDPS_OK)
476 return rv;
477 while ((tv++)->tv_tag != LDPT_NULL);
479 /* Register hooks only if instructed by options. */
480 if (register_claimfile_hook)
482 if (!tv_register_claim_file)
484 TV_MESSAGE (LDPL_FATAL, "No register_claim_file hook");
485 fflush (NULL);
486 return LDPS_ERR;
488 (*tv_register_claim_file) (onclaim_file);
490 if (register_allsymbolsread_hook)
492 if (!tv_register_all_symbols_read)
494 TV_MESSAGE (LDPL_FATAL, "No register_all_symbols_read hook");
495 fflush (NULL);
496 return LDPS_ERR;
498 (*tv_register_all_symbols_read) (onall_symbols_read);
500 if (register_cleanup_hook)
502 if (!tv_register_cleanup)
504 TV_MESSAGE (LDPL_FATAL, "No register_cleanup hook");
505 fflush (NULL);
506 return LDPS_ERR;
508 (*tv_register_cleanup) (oncleanup);
510 fflush (NULL);
511 return onload_ret;
514 /* Standard plugin API registerable hook. */
515 static enum ld_plugin_status
516 onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
518 /* Let's see if we want to claim this file. */
519 claim_file_t *claimfile = claimfiles_list;
520 while (claimfile)
522 if (!strcmp (file->name, claimfile->file.name))
523 break;
524 claimfile = claimfile->next;
527 /* Inform the user/testsuite. */
528 TV_MESSAGE (LDPL_INFO, "hook called: claim_file %s [@%ld/%ld] %s",
529 file->name, (long)file->offset, (long)file->filesize,
530 claimfile ? "CLAIMED" : "not claimed");
531 fflush (NULL);
533 /* If we decided to claim it, record that fact, and add any symbols
534 that were defined for it by plugin options. */
535 *claimed = (claimfile != 0);
536 if (claimfile)
538 claimfile->claimed = TRUE;
539 claimfile->file = *file;
540 if (claimfile->n_syms_used && !tv_add_symbols)
541 return LDPS_ERR;
542 else if (claimfile->n_syms_used)
543 return (*tv_add_symbols) (claimfile->file.handle,
544 claimfile->n_syms_used, claimfile->symbols);
547 return claim_file_ret;
550 /* Standard plugin API registerable hook. */
551 static enum ld_plugin_status
552 onall_symbols_read (void)
554 static const char *resolutions[] =
556 "LDPR_UNKNOWN",
557 "LDPR_UNDEF",
558 "LDPR_PREVAILING_DEF",
559 "LDPR_PREVAILING_DEF_IRONLY",
560 "LDPR_PREEMPTED_REG",
561 "LDPR_PREEMPTED_IR",
562 "LDPR_RESOLVED_IR",
563 "LDPR_RESOLVED_EXEC",
564 "LDPR_RESOLVED_DYN",
566 claim_file_t *claimfile = dumpresolutions ? claimfiles_list : NULL;
567 add_file_t *addfile = addfiles_list;
568 TV_MESSAGE (LDPL_INFO, "hook called: all symbols read.");
569 for ( ; claimfile; claimfile = claimfile->next)
571 enum ld_plugin_status rv;
572 int n;
573 if (claimfile->n_syms_used && !tv_get_symbols)
574 return LDPS_ERR;
575 else if (!claimfile->n_syms_used)
576 continue;
577 rv = tv_get_symbols (claimfile->file.handle, claimfile->n_syms_used,
578 claimfile->symbols);
579 if (rv != LDPS_OK)
580 return rv;
581 for (n = 0; n < claimfile->n_syms_used; n++)
582 TV_MESSAGE (LDPL_INFO, "Sym: '%s%s%s' Resolution: %s",
583 claimfile->symbols[n].name,
584 claimfile->symbols[n].version ? "@" : "",
585 (claimfile->symbols[n].version
586 ? claimfile->symbols[n].version : ""),
587 resolutions[claimfile->symbols[n].resolution]);
589 for ( ; addfile ; addfile = addfile->next)
591 enum ld_plugin_status rv;
592 if (addfile->type == ADD_LIB && tv_add_input_library)
593 rv = (*tv_add_input_library) (addfile->name);
594 else if (addfile->type == ADD_FILE && tv_add_input_file)
595 rv = (*tv_add_input_file) (addfile->name);
596 else if (addfile->type == ADD_DIR && tv_set_extra_library_path)
597 rv = (*tv_set_extra_library_path) (addfile->name);
598 else
599 rv = LDPS_ERR;
600 if (rv != LDPS_OK)
601 return rv;
603 fflush (NULL);
604 return all_symbols_read_ret;
607 /* Standard plugin API registerable hook. */
608 static enum ld_plugin_status
609 oncleanup (void)
611 TV_MESSAGE (LDPL_INFO, "hook called: cleanup.");
612 fflush (NULL);
613 return cleanup_ret;