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