2011-02-28 Kai Tietz <kai.tietz@onevision.com>
[platform/upstream/binutils.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 }