Automatic date update in version.in
[external/binutils.git] / gold / testsuite / plugin_test.c
1 /* test_plugin.c -- simple linker plugin test
2
3    Copyright (C) 2008-2019 Free Software Foundation, Inc.
4    Written by Cary Coutant <ccoutant@google.com>.
5
6    This file is part of gold.
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
21    MA 02110-1301, USA.  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include "plugin-api.h"
31
32 struct claimed_file
33 {
34   const char* name;
35   void* handle;
36   int nsyms;
37   struct ld_plugin_symbol* syms;
38   struct claimed_file* next;
39 };
40
41 struct sym_info
42 {
43   int size;
44   char* type;
45   char* bind;
46   char* vis;
47   char* sect;
48   char* name;
49   char* ver;
50 };
51
52 static struct claimed_file* first_claimed_file = NULL;
53 static struct claimed_file* last_claimed_file = NULL;
54
55 static ld_plugin_register_claim_file register_claim_file_hook = NULL;
56 static ld_plugin_register_all_symbols_read register_all_symbols_read_hook = NULL;
57 static ld_plugin_register_cleanup register_cleanup_hook = NULL;
58 static ld_plugin_add_symbols add_symbols = NULL;
59 static ld_plugin_get_symbols get_symbols = NULL;
60 static ld_plugin_get_symbols get_symbols_v2 = NULL;
61 static ld_plugin_get_symbols get_symbols_v3 = NULL;
62 static ld_plugin_add_input_file add_input_file = NULL;
63 static ld_plugin_message message = NULL;
64 static ld_plugin_get_input_file get_input_file = NULL;
65 static ld_plugin_release_input_file release_input_file = NULL;
66 static ld_plugin_get_input_section_count get_input_section_count = NULL;
67 static ld_plugin_get_input_section_type get_input_section_type = NULL;
68 static ld_plugin_get_input_section_name get_input_section_name = NULL;
69 static ld_plugin_get_input_section_contents get_input_section_contents = NULL;
70 static ld_plugin_update_section_order update_section_order = NULL;
71 static ld_plugin_allow_section_ordering allow_section_ordering = NULL;
72 static ld_plugin_get_wrap_symbols get_wrap_symbols = NULL;
73
74 #define MAXOPTS 10
75
76 static const char *opts[MAXOPTS];
77 static int nopts = 0;
78
79 enum ld_plugin_status onload(struct ld_plugin_tv *tv);
80 enum ld_plugin_status claim_file_hook(const struct ld_plugin_input_file *file,
81                                       int *claimed);
82 enum ld_plugin_status all_symbols_read_hook(void);
83 enum ld_plugin_status cleanup_hook(void);
84
85 static void parse_readelf_line(char*, struct sym_info*);
86
87 enum ld_plugin_status
88 onload(struct ld_plugin_tv *tv)
89 {
90   struct ld_plugin_tv *entry;
91   int api_version = 0;
92   int gold_version = 0;
93   int i;
94
95   for (entry = tv; entry->tv_tag != LDPT_NULL; ++entry)
96     {
97       switch (entry->tv_tag)
98         {
99         case LDPT_API_VERSION:
100           api_version = entry->tv_u.tv_val;
101           break;
102         case LDPT_GOLD_VERSION:
103           gold_version = entry->tv_u.tv_val;
104           break;
105         case LDPT_LINKER_OUTPUT:
106           break;
107         case LDPT_OPTION:
108           if (nopts < MAXOPTS)
109             opts[nopts++] = entry->tv_u.tv_string;
110           break;
111         case LDPT_REGISTER_CLAIM_FILE_HOOK:
112           register_claim_file_hook = entry->tv_u.tv_register_claim_file;
113           break;
114         case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
115           register_all_symbols_read_hook =
116             entry->tv_u.tv_register_all_symbols_read;
117           break;
118         case LDPT_REGISTER_CLEANUP_HOOK:
119           register_cleanup_hook = entry->tv_u.tv_register_cleanup;
120           break;
121         case LDPT_ADD_SYMBOLS:
122           add_symbols = entry->tv_u.tv_add_symbols;
123           break;
124         case LDPT_GET_SYMBOLS:
125           get_symbols = entry->tv_u.tv_get_symbols;
126           break;
127         case LDPT_GET_SYMBOLS_V2:
128           get_symbols_v2 = entry->tv_u.tv_get_symbols;
129           break;
130         case LDPT_GET_SYMBOLS_V3:
131           get_symbols_v3 = entry->tv_u.tv_get_symbols;
132           break;
133         case LDPT_ADD_INPUT_FILE:
134           add_input_file = entry->tv_u.tv_add_input_file;
135           break;
136         case LDPT_MESSAGE:
137           message = entry->tv_u.tv_message;
138           break;
139         case LDPT_GET_INPUT_FILE:
140           get_input_file = entry->tv_u.tv_get_input_file;
141           break;
142         case LDPT_RELEASE_INPUT_FILE:
143           release_input_file = entry->tv_u.tv_release_input_file;
144           break;
145         case LDPT_GET_INPUT_SECTION_COUNT:
146           get_input_section_count = *entry->tv_u.tv_get_input_section_count;
147           break;
148         case LDPT_GET_INPUT_SECTION_TYPE:
149           get_input_section_type = *entry->tv_u.tv_get_input_section_type;
150           break;
151         case LDPT_GET_INPUT_SECTION_NAME:
152           get_input_section_name = *entry->tv_u.tv_get_input_section_name;
153           break;
154         case LDPT_GET_INPUT_SECTION_CONTENTS:
155           get_input_section_contents = *entry->tv_u.tv_get_input_section_contents;
156           break;
157         case LDPT_UPDATE_SECTION_ORDER:
158           update_section_order = *entry->tv_u.tv_update_section_order;
159           break;
160         case LDPT_ALLOW_SECTION_ORDERING:
161           allow_section_ordering = *entry->tv_u.tv_allow_section_ordering;
162           break;
163         case LDPT_GET_WRAP_SYMBOLS:
164           get_wrap_symbols = *entry->tv_u.tv_get_wrap_symbols;
165           break;
166         default:
167           break;
168         }
169     }
170
171   if (message == NULL)
172     {
173       fprintf(stderr, "tv_message interface missing\n");
174       return LDPS_ERR;
175     }
176
177   if (register_claim_file_hook == NULL)
178     {
179       fprintf(stderr, "tv_register_claim_file_hook interface missing\n");
180       return LDPS_ERR;
181     }
182
183   if (register_all_symbols_read_hook == NULL)
184     {
185       fprintf(stderr, "tv_register_all_symbols_read_hook interface missing\n");
186       return LDPS_ERR;
187     }
188
189   if (register_cleanup_hook == NULL)
190     {
191       fprintf(stderr, "tv_register_cleanup_hook interface missing\n");
192       return LDPS_ERR;
193     }
194
195   (*message)(LDPL_INFO, "API version:   %d", api_version);
196   (*message)(LDPL_INFO, "gold version:  %d", gold_version);
197
198   for (i = 0; i < nopts; ++i)
199     (*message)(LDPL_INFO, "option: %s", opts[i]);
200
201   if ((*register_claim_file_hook)(claim_file_hook) != LDPS_OK)
202     {
203       (*message)(LDPL_ERROR, "error registering claim file hook");
204       return LDPS_ERR;
205     }
206
207   if ((*register_all_symbols_read_hook)(all_symbols_read_hook) != LDPS_OK)
208     {
209       (*message)(LDPL_ERROR, "error registering all symbols read hook");
210       return LDPS_ERR;
211     }
212
213   if ((*register_cleanup_hook)(cleanup_hook) != LDPS_OK)
214     {
215       (*message)(LDPL_ERROR, "error registering cleanup hook");
216       return LDPS_ERR;
217     }
218
219   if (get_input_section_count == NULL)
220     {
221       fprintf(stderr, "tv_get_input_section_count interface missing\n");
222       return LDPS_ERR;
223     }
224
225   if (get_input_section_type == NULL)
226     {
227       fprintf(stderr, "tv_get_input_section_type interface missing\n");
228       return LDPS_ERR;
229     }
230
231   if (get_input_section_name == NULL)
232     {
233       fprintf(stderr, "tv_get_input_section_name interface missing\n");
234       return LDPS_ERR;
235     }
236
237   if (get_input_section_contents == NULL)
238     {
239       fprintf(stderr, "tv_get_input_section_contents interface missing\n");
240       return LDPS_ERR;
241     }
242
243   if (update_section_order == NULL)
244     {
245       fprintf(stderr, "tv_update_section_order interface missing\n");
246       return LDPS_ERR;
247     }
248
249   if (allow_section_ordering == NULL)
250     {
251       fprintf(stderr, "tv_allow_section_ordering interface missing\n");
252       return LDPS_ERR;
253     }
254
255   if (get_wrap_symbols == NULL)
256     {
257       fprintf(stderr, "tv_get_wrap_symbols interface missing\n");
258       return LDPS_ERR;
259     }
260   else
261     {
262       const char **wrap_symbols;
263       uint64_t count = 0;
264       if (get_wrap_symbols(&count, &wrap_symbols) == LDPS_OK)
265         {
266           (*message)(LDPL_INFO, "Number of wrap symbols = %lu", count);
267           for (; count > 0; --count)
268             (*message)(LDPL_INFO, "Wrap symbol %s", wrap_symbols[count - 1]);
269         }
270       else
271         {
272           fprintf(stderr, "tv_get_wrap_symbols interface call failed\n");
273           return LDPS_ERR;
274         }
275     }
276
277   return LDPS_OK;
278 }
279
280 enum ld_plugin_status
281 claim_file_hook (const struct ld_plugin_input_file* file, int* claimed)
282 {
283   int len;
284   off_t end_offset;
285   char buf[160];
286   struct claimed_file* claimed_file;
287   struct ld_plugin_symbol* syms;
288   int nsyms = 0;
289   int maxsyms = 0;
290   FILE* irfile;
291   struct sym_info info;
292   int weak;
293   int def;
294   int vis;
295   int is_comdat;
296   int i;
297   int irfile_was_opened = 0;
298   char syms_name[80];
299
300   (*message)(LDPL_INFO,
301              "%s: claim file hook called (offset = %ld, size = %ld)",
302              file->name, (long)file->offset, (long)file->filesize);
303
304   /* Look for matching syms file for an archive member.  */
305   if (file->offset == 0)
306     snprintf(syms_name, sizeof(syms_name), "%s.syms", file->name);
307   else
308     snprintf(syms_name, sizeof(syms_name), "%s-%d.syms",
309              file->name, (int)file->offset);
310   irfile = fopen(syms_name, "r");
311   if (irfile != NULL)
312     {
313       irfile_was_opened = 1;
314       end_offset = 1 << 20;
315     }
316
317   /* Otherwise, see if the file itself is a syms file.  */
318   if (!irfile_was_opened)
319     {
320       irfile = fdopen(file->fd, "r");
321       (void)fseek(irfile, file->offset, SEEK_SET);
322       end_offset = file->offset + file->filesize;
323     }
324
325   /* Look for the beginning of output from readelf -s.  */
326   len = fread(buf, 1, 13, irfile);
327   if (len < 13 || strncmp(buf, "\nSymbol table", 13) != 0)
328     return LDPS_OK;
329
330   /* Skip the two header lines.  */
331   (void) fgets(buf, sizeof(buf), irfile);
332   (void) fgets(buf, sizeof(buf), irfile);
333
334   if (add_symbols == NULL)
335     {
336       fprintf(stderr, "tv_add_symbols interface missing\n");
337       return LDPS_ERR;
338     }
339
340   /* Parse the output from readelf. The columns are:
341      Index Value Size Type Binding Visibility Section Name.  */
342   syms = (struct ld_plugin_symbol*)malloc(sizeof(struct ld_plugin_symbol) * 8);
343   if (syms == NULL)
344     return LDPS_ERR;
345   maxsyms = 8;
346   while (ftell(irfile) < end_offset
347          && fgets(buf, sizeof(buf), irfile) != NULL)
348     {
349       parse_readelf_line(buf, &info);
350
351       /* Ignore local symbols.  */
352       if (strncmp(info.bind, "LOCAL", 5) == 0)
353         continue;
354
355       weak = strncmp(info.bind, "WEAK", 4) == 0;
356       if (strncmp(info.sect, "UND", 3) == 0)
357         def = weak ? LDPK_WEAKUNDEF : LDPK_UNDEF;
358       else if (strncmp(info.sect, "COM", 3) == 0)
359         def = LDPK_COMMON;
360       else
361         def = weak ? LDPK_WEAKDEF : LDPK_DEF;
362
363       if (strncmp(info.vis, "INTERNAL", 8) == 0)
364         vis = LDPV_INTERNAL;
365       else if (strncmp(info.vis, "HIDDEN", 6) == 0)
366         vis = LDPV_HIDDEN;
367       else if (strncmp(info.vis, "PROTECTED", 9) == 0)
368         vis = LDPV_PROTECTED;
369       else
370         vis = LDPV_DEFAULT;
371
372       /* If the symbol is listed in the options list, special-case
373          it as a comdat symbol.  */
374       is_comdat = 0;
375       for (i = 0; i < nopts; ++i)
376         {
377           if (info.name != NULL && strcmp(info.name, opts[i]) == 0)
378             {
379               is_comdat = 1;
380               break;
381             }
382         }
383
384       if (nsyms >= maxsyms)
385         {
386           syms = (struct ld_plugin_symbol*)
387             realloc(syms, sizeof(struct ld_plugin_symbol) * maxsyms * 2);
388           if (syms == NULL)
389             return LDPS_ERR;
390           maxsyms *= 2;
391         }
392
393       if (info.name == NULL)
394         syms[nsyms].name = NULL;
395       else
396         {
397           len = strlen(info.name);
398           syms[nsyms].name = malloc(len + 1);
399           strncpy(syms[nsyms].name, info.name, len + 1);
400         }
401       if (info.ver == NULL)
402         syms[nsyms].version = NULL;
403       else
404         {
405           len = strlen(info.ver);
406           syms[nsyms].version = malloc(len + 1);
407           strncpy(syms[nsyms].version, info.ver, len + 1);
408         }
409       syms[nsyms].def = def;
410       syms[nsyms].visibility = vis;
411       syms[nsyms].size = info.size;
412       syms[nsyms].comdat_key = is_comdat ? syms[nsyms].name : NULL;
413       syms[nsyms].resolution = LDPR_UNKNOWN;
414       ++nsyms;
415     }
416
417   claimed_file = (struct claimed_file*) malloc(sizeof(struct claimed_file));
418   if (claimed_file == NULL)
419     return LDPS_ERR;
420
421   claimed_file->name = file->name;
422   claimed_file->handle = file->handle;
423   claimed_file->nsyms = nsyms;
424   claimed_file->syms = syms;
425   claimed_file->next = NULL;
426   if (last_claimed_file == NULL)
427     first_claimed_file = claimed_file;
428   else
429     last_claimed_file->next = claimed_file;
430   last_claimed_file = claimed_file;
431
432   (*message)(LDPL_INFO, "%s: claiming file, adding %d symbols",
433              file->name, nsyms);
434
435   if (nsyms > 0)
436     (*add_symbols)(file->handle, nsyms, syms);
437
438   *claimed = 1;
439   if (irfile_was_opened)
440     fclose(irfile);
441   return LDPS_OK;
442 }
443
444 enum ld_plugin_status
445 all_symbols_read_hook(void)
446 {
447   int i;
448   const char* res;
449   struct claimed_file* claimed_file;
450   struct ld_plugin_input_file file;
451   FILE* irfile;
452   off_t end_offset;
453   struct sym_info info;
454   int len;
455   char buf[160];
456   char* p;
457   const char* filename;
458
459   (*message)(LDPL_INFO, "all symbols read hook called");
460
461   if (get_symbols_v3 == NULL)
462     {
463       fprintf(stderr, "tv_get_symbols (v3) interface missing\n");
464       return LDPS_ERR;
465     }
466
467   for (claimed_file = first_claimed_file;
468        claimed_file != NULL;
469        claimed_file = claimed_file->next)
470     {
471       enum ld_plugin_status status = (*get_symbols_v3)(
472           claimed_file->handle, claimed_file->nsyms, claimed_file->syms);
473       if (status == LDPS_NO_SYMS)
474         {
475           (*message)(LDPL_INFO, "%s: no symbols", claimed_file->name);
476           continue;
477         }
478
479       for (i = 0; i < claimed_file->nsyms; ++i)
480         {
481           switch (claimed_file->syms[i].resolution)
482             {
483             case LDPR_UNKNOWN:
484               res = "UNKNOWN";
485               break;
486             case LDPR_UNDEF:
487               res = "UNDEF";
488               break;
489             case LDPR_PREVAILING_DEF:
490               res = "PREVAILING_DEF_REG";
491               break;
492             case LDPR_PREVAILING_DEF_IRONLY:
493               res = "PREVAILING_DEF_IRONLY";
494               break;
495             case LDPR_PREVAILING_DEF_IRONLY_EXP:
496               res = "PREVAILING_DEF_IRONLY_EXP";
497               break;
498             case LDPR_PREEMPTED_REG:
499               res = "PREEMPTED_REG";
500               break;
501             case LDPR_PREEMPTED_IR:
502               res = "PREEMPTED_IR";
503               break;
504             case LDPR_RESOLVED_IR:
505               res = "RESOLVED_IR";
506               break;
507             case LDPR_RESOLVED_EXEC:
508               res = "RESOLVED_EXEC";
509               break;
510             case LDPR_RESOLVED_DYN:
511               res = "RESOLVED_DYN";
512               break;
513             default:
514               res = "?";
515               break;
516             }
517           (*message)(LDPL_INFO, "%s: %s: %s", claimed_file->name,
518                      claimed_file->syms[i].name, res);
519         }
520     }
521
522   if (add_input_file == NULL)
523     {
524       fprintf(stderr, "tv_add_input_file interface missing\n");
525       return LDPS_ERR;
526     }
527   if (get_input_file == NULL)
528     {
529       fprintf(stderr, "tv_get_input_file interface missing\n");
530       return LDPS_ERR;
531     }
532   if (release_input_file == NULL)
533     {
534       fprintf(stderr, "tv_release_input_file interface missing\n");
535       return LDPS_ERR;
536     }
537
538   for (claimed_file = first_claimed_file;
539        claimed_file != NULL;
540        claimed_file = claimed_file->next)
541     {
542       int irfile_was_opened = 0;
543       char syms_name[80];
544
545       (*get_input_file) (claimed_file->handle, &file);
546
547       if (file.offset == 0)
548         snprintf(syms_name, sizeof(syms_name), "%s.syms", file.name);
549       else
550         snprintf(syms_name, sizeof(syms_name), "%s-%d.syms",
551                  file.name, (int)file.offset);
552       irfile = fopen(syms_name, "r");
553       if (irfile != NULL)
554         {
555           irfile_was_opened = 1;
556           end_offset = 1 << 20;
557         }
558
559       if (!irfile_was_opened)
560         {
561           irfile = fdopen(file.fd, "r");
562           (void)fseek(irfile, file.offset, SEEK_SET);
563           end_offset = file.offset + file.filesize;
564         }
565
566       /* Look for the beginning of output from readelf -s.  */
567       len = fread(buf, 1, 13, irfile);
568       if (len < 13 || strncmp(buf, "\nSymbol table", 13) != 0)
569         {
570           fprintf(stderr, "%s: can't re-read original input file\n",
571                   claimed_file->name);
572           return LDPS_ERR;
573         }
574
575       /* Skip the two header lines.  */
576       (void) fgets(buf, sizeof(buf), irfile);
577       (void) fgets(buf, sizeof(buf), irfile);
578
579       filename = NULL;
580       while (ftell(irfile) < end_offset
581              && fgets(buf, sizeof(buf), irfile) != NULL)
582         {
583           parse_readelf_line(buf, &info);
584
585           /* Look for file name.  */
586           if (strncmp(info.type, "FILE", 4) == 0)
587             {
588               len = strlen(info.name);
589               p = malloc(len + 1);
590               strncpy(p, info.name, len + 1);
591               filename = p;
592               break;
593             }
594         }
595
596       if (irfile_was_opened)
597         fclose(irfile);
598
599       (*release_input_file) (claimed_file->handle);
600
601       if (filename == NULL)
602         filename = claimed_file->name;
603
604       if (claimed_file->nsyms == 0)
605         continue;
606
607       if (strlen(filename) >= sizeof(buf))
608         {
609           (*message)(LDPL_FATAL, "%s: filename too long", filename);
610           return LDPS_ERR;
611         }
612       strcpy(buf, filename);
613       p = strrchr(buf, '.');
614       if (p == NULL
615           || (strcmp(p, ".syms") != 0
616               && strcmp(p, ".c") != 0
617               && strcmp(p, ".cc") != 0))
618         {
619           (*message)(LDPL_FATAL, "%s: filename has unknown suffix",
620                      filename);
621           return LDPS_ERR;
622         }
623       p[1] = 'o';
624       p[2] = '\0';
625       (*message)(LDPL_INFO, "%s: adding new input file", buf);
626       (*add_input_file)(buf);
627     }
628
629   return LDPS_OK;
630 }
631
632 enum ld_plugin_status
633 cleanup_hook(void)
634 {
635   (*message)(LDPL_INFO, "cleanup hook called");
636   return LDPS_OK;
637 }
638
639 static void
640 parse_readelf_line(char* p, struct sym_info* info)
641 {
642   int len;
643
644   p += strspn(p, " ");
645
646   /* Index field.  */
647   p += strcspn(p, " ");
648   p += strspn(p, " ");
649
650   /* Value field.  */
651   p += strcspn(p, " ");
652   p += strspn(p, " ");
653
654   /* Size field.  */
655   info->size = atoi(p);
656   p += strcspn(p, " ");
657   p += strspn(p, " ");
658
659   /* Type field.  */
660   info->type = p;
661   p += strcspn(p, " ");
662   p += strspn(p, " ");
663
664   /* Binding field.  */
665   info->bind = p;
666   p += strcspn(p, " ");
667   p += strspn(p, " ");
668
669   /* Visibility field.  */
670   info->vis = p;
671   p += strcspn(p, " ");
672   p += strspn(p, " ");
673
674   if (*p == '[')
675     {
676       /* Skip st_other.  */
677       p += strcspn(p, "]");
678       p += strspn(p, "] ");
679     }
680
681   /* Section field.  */
682   info->sect = p;
683   p += strcspn(p, " ");
684   p += strspn(p, " ");
685
686   /* Name field.  */
687   len = strcspn(p, "@\n");
688   if (len > 0 && p[len] == '@')
689     {
690       /* Get the symbol version.  */
691       char* vp = p + len;
692       int vlen;
693
694       vp += strspn(vp, "@");
695       vlen = strcspn(vp, "\n");
696       vp[vlen] = '\0';
697       if (vlen > 0)
698         info->ver = vp;
699       else
700         info->ver = NULL;
701     }
702   else
703     info->ver = NULL;
704   p[len] = '\0';
705   if (len > 0)
706     info->name = p;
707   else
708     info->name = NULL;
709 }