]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - ld/testplug.c
2011-02-28 Kai Tietz <kai.tietz@onevision.com>
[thirdparty/binutils-gdb.git] / ld / testplug.c
1 /* Test plugin for the GNU linker.
2 Copyright 2010 Free Software Foundation, Inc.
3
4 This file is part of the GNU Binutils.
5
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.
10
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.
15
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. */
20
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"
26 #include "filenames.h"
27
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,
30 int *claimed);
31 static enum ld_plugin_status onall_symbols_read (void);
32 static enum ld_plugin_status oncleanup (void);
33
34 /* Helper for calling plugin api message function. */
35 #define TV_MESSAGE if (tv_message) (*tv_message)
36
37 /* Struct for recording files to claim / files claimed. */
38 typedef struct claim_file
39 {
40 struct claim_file *next;
41 struct ld_plugin_input_file file;
42 bfd_boolean claimed;
43 struct ld_plugin_symbol *symbols;
44 int n_syms_allocated;
45 int n_syms_used;
46 } claim_file_t;
47
48 /* Types of things that can be added at all symbols read time. */
49 typedef enum addfile_enum
50 {
51 ADD_FILE,
52 ADD_LIB,
53 ADD_DIR
54 } addfile_enum_t;
55
56 /* Struct for recording files to add to final link. */
57 typedef struct add_file
58 {
59 struct add_file *next;
60 const char *name;
61 addfile_enum_t type;
62 } add_file_t;
63
64 /* Helper macro for defining array of transfer vector tags and names. */
65 #define ADDENTRY(tag) { tag, #tag }
66
67 /* Struct for looking up human-readable versions of tag names. */
68 typedef struct tag_name
69 {
70 enum ld_plugin_tag tag;
71 const char *name;
72 } tag_name_t;
73
74 /* Array of all known tags and their names. */
75 static const tag_name_t tag_names[] =
76 {
77 ADDENTRY(LDPT_NULL),
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)
95 };
96
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;
109
110 /* Other cached info from the transfer vector. */
111 static enum ld_plugin_output_file_type linker_output;
112 static const char *output_name;
113
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;
123
124 /* The master list of all claimable/claimed files. */
125 static claim_file_t *claimfiles_list = NULL;
126
127 /* We keep a tail pointer for easy linking on the end. */
128 static claim_file_t **claimfiles_tail_chain_ptr = &claimfiles_list;
129
130 /* The last claimed file added to the list, for receiving syms. */
131 static claim_file_t *last_claimfile = NULL;
132
133 /* The master list of all files to add to the final link. */
134 static add_file_t *addfiles_list = NULL;
135
136 /* We keep a tail pointer for easy linking on the end. */
137 static add_file_t **addfiles_tail_chain_ptr = &addfiles_list;
138
139 /* Add a new claimfile on the end of the chain. */
140 static enum ld_plugin_status
141 record_claim_file (const char *file)
142 {
143 claim_file_t *newfile;
144
145 newfile = malloc (sizeof *newfile);
146 if (!newfile)
147 return LDPS_ERR;
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;
156 return LDPS_OK;
157 }
158
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)
162 {
163 add_file_t *newfile;
164
165 newfile = malloc (sizeof *newfile);
166 if (!newfile)
167 return LDPS_ERR;
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;
174 return LDPS_OK;
175 }
176
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)
185 {
186 int n;
187 long long size;
188 const char *colon1, *colon2, *colon5;
189
190 /* Locate the colons separating the first two strings. */
191 colon1 = strchr (str, ':');
192 if (!colon1)
193 return LDPS_ERR;
194 colon2 = strchr (colon1+1, ':');
195 if (!colon2)
196 return LDPS_ERR;
197 /* Name must not be empty (version may be). */
198 if (colon1 == str)
199 return LDPS_ERR;
200
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. */
204 if (!colon5)
205 return LDPS_ERR;
206 colon5 = strchr (colon5+1, ':'); /* Hopefully fourth now. */
207 if (!colon5)
208 return LDPS_ERR;
209 colon5 = strchr (colon5+1, ':'); /* Optional fifth now. */
210
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);
215 if (n != 3)
216 return LDPS_ERR;
217
218 /* Parsed successfully, so allocate strings and fill out fields. */
219 sym->size = size;
220 sym->resolution = LDPR_UNKNOWN;
221 sym->name = malloc (colon1 - str + 1);
222 if (!sym->name)
223 return LDPS_ERR;
224 memcpy (sym->name, str, colon1 - str);
225 sym->name[colon1 - str] = '\0';
226 if (colon2 > (colon1 + 1))
227 {
228 sym->version = malloc (colon2 - colon1);
229 if (!sym->version)
230 return LDPS_ERR;
231 memcpy (sym->version, colon1 + 1, colon2 - (colon1 + 1));
232 sym->version[colon2 - (colon1 + 1)] = '\0';
233 }
234 else
235 sym->version = NULL;
236 if (colon5 && colon5[1])
237 {
238 sym->comdat_key = malloc (strlen (colon5 + 1) + 1);
239 if (!sym->comdat_key)
240 return LDPS_ERR;
241 strcpy (sym->comdat_key, colon5 + 1);
242 }
243 else
244 sym->comdat_key = 0;
245 return LDPS_OK;
246 }
247
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)
251 {
252 struct ld_plugin_symbol sym;
253
254 /* Can't add symbols except as belonging to claimed files. */
255 if (!last_claimfile)
256 return LDPS_ERR;
257
258 /* If string doesn't parse correctly, give an error. */
259 if (parse_symdefstr (symdefstr, &sym) != LDPS_OK)
260 return LDPS_ERR;
261
262 /* Check for enough space, resize array if needed, and add it. */
263 if (last_claimfile->n_syms_allocated == last_claimfile->n_syms_used)
264 {
265 int new_n_syms = last_claimfile->n_syms_allocated
266 ? 2 * last_claimfile->n_syms_allocated
267 : 10;
268 last_claimfile->symbols = realloc (last_claimfile->symbols,
269 new_n_syms * sizeof *last_claimfile->symbols);
270 if (!last_claimfile->symbols)
271 return LDPS_ERR;
272 last_claimfile->n_syms_allocated = new_n_syms;
273 }
274 last_claimfile->symbols[last_claimfile->n_syms_used++] = sym;
275
276 return LDPS_OK;
277 }
278
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)
282 {
283 if (!strcmp ("onload", whichval))
284 onload_ret = retval;
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;
291 else
292 return LDPS_ERR;
293 return LDPS_OK;
294 }
295
296 /* Records hooks which should be registered. */
297 static enum ld_plugin_status
298 set_register_hook (const char *whichhook, bfd_boolean yesno)
299 {
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;
306 else
307 return LDPS_ERR;
308 return LDPS_OK;
309 }
310
311 /* Determine type of plugin option and pass to individual parsers. */
312 static enum ld_plugin_status
313 parse_option (const char *opt)
314 {
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;
335 else
336 return LDPS_ERR;
337 return LDPS_OK;
338 }
339
340 /* Output contents of transfer vector array entry in human-readable form. */
341 static void
342 dump_tv_tag (size_t n, struct ld_plugin_tv *tv)
343 {
344 size_t tag;
345 char unknownbuf[40];
346 const char *name;
347
348 for (tag = 0; tag < ARRAY_SIZE (tag_names); tag++)
349 if (tag_names[tag].tag == tv->tv_tag)
350 break;
351 sprintf (unknownbuf, "unknown tag #%d", tv->tv_tag);
352 name = (tag < ARRAY_SIZE (tag_names)) ? tag_names[tag].name : unknownbuf;
353 switch (tv->tv_tag)
354 {
355 case LDPT_OPTION:
356 case LDPT_OUTPUT_NAME:
357 TV_MESSAGE (LDPL_INFO, "tv[%d]: %s '%s'", n, name,
358 tv->tv_u.tv_string);
359 break;
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:
366 case LDPT_MESSAGE:
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));
373 break;
374 case LDPT_NULL:
375 case LDPT_API_VERSION:
376 case LDPT_GOLD_VERSION:
377 case LDPT_LINKER_OUTPUT:
378 case LDPT_GNU_LD_VERSION:
379 default:
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);
382 break;
383 }
384 }
385
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)
389 {
390 #define SETVAR(x) x = tv->tv_u.x
391 switch (tv->tv_tag)
392 {
393 case LDPT_OPTION:
394 return parse_option (tv->tv_u.tv_string);
395 case LDPT_NULL:
396 case LDPT_GOLD_VERSION:
397 case LDPT_GNU_LD_VERSION:
398 case LDPT_API_VERSION:
399 default:
400 break;
401 case LDPT_OUTPUT_NAME:
402 output_name = tv->tv_u.tv_string;
403 break;
404 case LDPT_LINKER_OUTPUT:
405 linker_output = tv->tv_u.tv_val;
406 break;
407 case LDPT_REGISTER_CLAIM_FILE_HOOK:
408 SETVAR(tv_register_claim_file);
409 break;
410 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
411 SETVAR(tv_register_all_symbols_read);
412 break;
413 case LDPT_REGISTER_CLEANUP_HOOK:
414 SETVAR(tv_register_cleanup);
415 break;
416 case LDPT_ADD_SYMBOLS:
417 SETVAR(tv_add_symbols);
418 break;
419 case LDPT_GET_SYMBOLS:
420 SETVAR(tv_get_symbols);
421 break;
422 case LDPT_ADD_INPUT_FILE:
423 SETVAR(tv_add_input_file);
424 break;
425 case LDPT_MESSAGE:
426 SETVAR(tv_message);
427 break;
428 case LDPT_GET_INPUT_FILE:
429 SETVAR(tv_get_input_file);
430 break;
431 case LDPT_RELEASE_INPUT_FILE:
432 SETVAR(tv_release_input_file);
433 break;
434 case LDPT_ADD_INPUT_LIBRARY:
435 SETVAR(tv_add_input_library);
436 break;
437 case LDPT_SET_EXTRA_LIBRARY_PATH:
438 SETVAR(tv_set_extra_library_path);
439 break;
440 }
441 #undef SETVAR
442 return LDPS_OK;
443 }
444
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)
449 {
450 enum ld_plugin_status rv = parse_tv_tag (tv);
451 dump_tv_tag (n, tv);
452 return rv;
453 }
454
455 /* Standard plugin API entry point. */
456 enum ld_plugin_status
457 onload (struct ld_plugin_tv *tv)
458 {
459 size_t n = 0;
460 enum ld_plugin_status rv;
461
462 /* This plugin does nothing but dump the tv array. It would
463 be an error if this function was called without one. */
464 if (!tv)
465 return LDPS_ERR;
466
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;
471
472 fflush (NULL);
473 TV_MESSAGE (LDPL_INFO, "Hello from testplugin.");
474
475 do
476 if ((rv = parse_and_dump_tv_tag (n++, tv)) != LDPS_OK)
477 return rv;
478 while ((tv++)->tv_tag != LDPT_NULL);
479
480 /* Register hooks only if instructed by options. */
481 if (register_claimfile_hook)
482 {
483 if (!tv_register_claim_file)
484 {
485 TV_MESSAGE (LDPL_FATAL, "No register_claim_file hook");
486 fflush (NULL);
487 return LDPS_ERR;
488 }
489 (*tv_register_claim_file) (onclaim_file);
490 }
491 if (register_allsymbolsread_hook)
492 {
493 if (!tv_register_all_symbols_read)
494 {
495 TV_MESSAGE (LDPL_FATAL, "No register_all_symbols_read hook");
496 fflush (NULL);
497 return LDPS_ERR;
498 }
499 (*tv_register_all_symbols_read) (onall_symbols_read);
500 }
501 if (register_cleanup_hook)
502 {
503 if (!tv_register_cleanup)
504 {
505 TV_MESSAGE (LDPL_FATAL, "No register_cleanup hook");
506 fflush (NULL);
507 return LDPS_ERR;
508 }
509 (*tv_register_cleanup) (oncleanup);
510 }
511 fflush (NULL);
512 return onload_ret;
513 }
514
515 /* Standard plugin API registerable hook. */
516 static enum ld_plugin_status
517 onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
518 {
519 /* Let's see if we want to claim this file. */
520 claim_file_t *claimfile = claimfiles_list;
521 while (claimfile)
522 {
523 if (!filename_cmp (file->name, claimfile->file.name))
524 break;
525 claimfile = claimfile->next;
526 }
527
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");
532 fflush (NULL);
533
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);
537 if (claimfile)
538 {
539 claimfile->claimed = TRUE;
540 claimfile->file = *file;
541 if (claimfile->n_syms_used && !tv_add_symbols)
542 return LDPS_ERR;
543 else if (claimfile->n_syms_used)
544 return (*tv_add_symbols) (claimfile->file.handle,
545 claimfile->n_syms_used, claimfile->symbols);
546 }
547
548 return claim_file_ret;
549 }
550
551 /* Standard plugin API registerable hook. */
552 static enum ld_plugin_status
553 onall_symbols_read (void)
554 {
555 static const char *resolutions[] =
556 {
557 "LDPR_UNKNOWN",
558 "LDPR_UNDEF",
559 "LDPR_PREVAILING_DEF",
560 "LDPR_PREVAILING_DEF_IRONLY",
561 "LDPR_PREEMPTED_REG",
562 "LDPR_PREEMPTED_IR",
563 "LDPR_RESOLVED_IR",
564 "LDPR_RESOLVED_EXEC",
565 "LDPR_RESOLVED_DYN",
566 };
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)
571 {
572 enum ld_plugin_status rv;
573 int n;
574 if (claimfile->n_syms_used && !tv_get_symbols)
575 return LDPS_ERR;
576 else if (!claimfile->n_syms_used)
577 continue;
578 rv = tv_get_symbols (claimfile->file.handle, claimfile->n_syms_used,
579 claimfile->symbols);
580 if (rv != LDPS_OK)
581 return rv;
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]);
589 }
590 for ( ; addfile ; addfile = addfile->next)
591 {
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);
599 else
600 rv = LDPS_ERR;
601 if (rv != LDPS_OK)
602 return rv;
603 }
604 fflush (NULL);
605 return all_symbols_read_ret;
606 }
607
608 /* Standard plugin API registerable hook. */
609 static enum ld_plugin_status
610 oncleanup (void)
611 {
612 TV_MESSAGE (LDPL_INFO, "hook called: cleanup.");
613 fflush (NULL);
614 return cleanup_ret;
615 }