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