Upload Tizen:Base source
[external/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
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);
32
33 /* Helper for calling plugin api message function.  */
34 #define TV_MESSAGE if (tv_message) (*tv_message)
35
36 /* Struct for recording files to claim / files claimed.  */
37 typedef struct claim_file
38 {
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;
46
47 /* Types of things that can be added at all symbols read time.  */
48 typedef enum addfile_enum
49 {
50   ADD_FILE,
51   ADD_LIB,
52   ADD_DIR
53 } addfile_enum_t;
54
55 /* Struct for recording files to add to final link.  */
56 typedef struct add_file
57 {
58   struct add_file *next;
59   const char *name;
60   addfile_enum_t type;
61 } add_file_t;
62
63 /* Helper macro for defining array of transfer vector tags and names.  */
64 #define ADDENTRY(tag) { tag, #tag }
65
66 /* Struct for looking up human-readable versions of tag names.  */
67 typedef struct tag_name
68 {
69   enum ld_plugin_tag tag;
70   const char *name;
71 } tag_name_t;
72
73 /* Array of all known tags and their names.  */
74 static const tag_name_t tag_names[] =
75 {
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)
94 };
95
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;
108
109 /* Other cached info from the transfer vector.  */
110 static enum ld_plugin_output_file_type linker_output;
111 static const char *output_name;
112
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;
122
123 /* The master list of all claimable/claimed files.  */
124 static claim_file_t *claimfiles_list = NULL;
125
126 /* We keep a tail pointer for easy linking on the end.  */
127 static claim_file_t **claimfiles_tail_chain_ptr = &claimfiles_list;
128
129 /* The last claimed file added to the list, for receiving syms.  */
130 static claim_file_t *last_claimfile = NULL;
131
132 /* The master list of all files to add to the final link.  */
133 static add_file_t *addfiles_list = NULL;
134
135 /* We keep a tail pointer for easy linking on the end.  */
136 static add_file_t **addfiles_tail_chain_ptr = &addfiles_list;
137
138 /* Add a new claimfile on the end of the chain.  */
139 static enum ld_plugin_status
140 record_claim_file (const char *file)
141 {
142   claim_file_t *newfile;
143
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;
156 }
157
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)
161 {
162   add_file_t *newfile;
163
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;
174 }
175
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)
184 {
185   int n;
186   long long size;
187   const char *colon1, *colon2, *colon5;
188
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;
199
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.  */
209
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;
216
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))
226     {
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';
232     }
233   else
234     sym->version = NULL;
235   if (colon5 && colon5[1])
236     {
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);
241     }
242   else
243     sym->comdat_key = 0;
244   return LDPS_OK;
245 }
246
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)
250 {
251   struct ld_plugin_symbol sym;
252
253   /* Can't add symbols except as belonging to claimed files.  */
254   if (!last_claimfile)
255     return LDPS_ERR;
256
257   /* If string doesn't parse correctly, give an error.  */
258   if (parse_symdefstr (symdefstr, &sym) != LDPS_OK)
259     return LDPS_ERR;
260
261   /* Check for enough space, resize array if needed, and add it.  */
262   if (last_claimfile->n_syms_allocated == last_claimfile->n_syms_used)
263     {
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;
272     }
273   last_claimfile->symbols[last_claimfile->n_syms_used++] = sym;
274
275   return LDPS_OK;
276 }
277
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)
281 {
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;
293 }
294
295 /* Records hooks which should be registered.  */
296 static enum ld_plugin_status
297 set_register_hook (const char *whichhook, bfd_boolean yesno)
298 {
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;
308 }
309
310 /* Determine type of plugin option and pass to individual parsers.  */
311 static enum ld_plugin_status
312 parse_option (const char *opt)
313 {
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;
337 }
338
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)
342 {
343   size_t tag;
344   char unknownbuf[40];
345   const char *name;
346
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)
353     {
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;
382     }
383 }
384
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)
388 {
389 #define SETVAR(x) x = tv->tv_u.x
390   switch (tv->tv_tag)
391     {
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;
439     }
440 #undef SETVAR
441   return LDPS_OK;
442 }
443
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)
448 {
449   enum ld_plugin_status rv = parse_tv_tag (tv);
450   dump_tv_tag (n, tv);
451   return rv;
452 }
453
454 /* Standard plugin API entry point.  */
455 enum ld_plugin_status
456 onload (struct ld_plugin_tv *tv)
457 {
458   size_t n = 0;
459   enum ld_plugin_status rv;
460
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;
465
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;
470
471   fflush (NULL);
472   TV_MESSAGE (LDPL_INFO, "Hello from testplugin.");
473
474   do
475     if ((rv = parse_and_dump_tv_tag (n++, tv)) != LDPS_OK)
476       return rv;
477   while ((tv++)->tv_tag != LDPT_NULL);
478
479   /* Register hooks only if instructed by options.  */
480   if (register_claimfile_hook)
481     {
482       if (!tv_register_claim_file)
483         {
484           TV_MESSAGE (LDPL_FATAL, "No register_claim_file hook");
485           fflush (NULL);
486           return LDPS_ERR;
487         }
488       (*tv_register_claim_file) (onclaim_file);
489     }
490   if (register_allsymbolsread_hook)
491     {
492       if (!tv_register_all_symbols_read)
493         {
494           TV_MESSAGE (LDPL_FATAL, "No register_all_symbols_read hook");
495           fflush (NULL);
496           return LDPS_ERR;
497         }
498       (*tv_register_all_symbols_read) (onall_symbols_read);
499     }
500   if (register_cleanup_hook)
501     {
502       if (!tv_register_cleanup)
503         {
504           TV_MESSAGE (LDPL_FATAL, "No register_cleanup hook");
505           fflush (NULL);
506           return LDPS_ERR;
507         }
508       (*tv_register_cleanup) (oncleanup);
509     }
510   fflush (NULL);
511   return onload_ret;
512 }
513
514 /* Standard plugin API registerable hook.  */
515 static enum ld_plugin_status
516 onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
517 {
518   /* Let's see if we want to claim this file.  */
519   claim_file_t *claimfile = claimfiles_list;
520   while (claimfile)
521     {
522       if (!strcmp (file->name, claimfile->file.name))
523         break;
524       claimfile = claimfile->next;
525     }
526
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);
532
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)
537     {
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);
545     }
546
547   return claim_file_ret;
548 }
549
550 /* Standard plugin API registerable hook.  */
551 static enum ld_plugin_status
552 onall_symbols_read (void)
553 {
554   static const char *resolutions[] =
555     {
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",
565     };
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)
570     {
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]);
588     }
589   for ( ; addfile ; addfile = addfile->next)
590     {
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;
602     }
603   fflush (NULL);
604   return all_symbols_read_ret;
605 }
606
607 /* Standard plugin API registerable hook.  */
608 static enum ld_plugin_status
609 oncleanup (void)
610 {
611   TV_MESSAGE (LDPL_INFO, "hook called: cleanup.");
612   fflush (NULL);
613   return cleanup_ret;
614 }