tizen 2.3 release
[external/binutils.git] / gold / testsuite / plugin_test.c
1 /* test_plugin.c -- simple linker plugin test
2
3    Copyright 2008, 2009 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
267   (*message)(LDPL_INFO,
268              "%s: claim file hook called (offset = %ld, size = %ld)",
269              file->name, (long)file->offset, (long)file->filesize);
270
271   /* Look for the beginning of output from readelf -s.  */
272   irfile = fdopen(file->fd, "r");
273   (void)fseek(irfile, file->offset, SEEK_SET);
274   end_offset = file->offset + file->filesize;
275   len = fread(buf, 1, 13, irfile);
276   if (len < 13 || strncmp(buf, "\nSymbol table", 13) != 0)
277     return LDPS_OK;
278
279   /* Skip the two header lines.  */
280   (void) fgets(buf, sizeof(buf), irfile);
281   (void) fgets(buf, sizeof(buf), irfile);
282
283   if (add_symbols == NULL)
284     {
285       fprintf(stderr, "tv_add_symbols interface missing\n");
286       return LDPS_ERR;
287     }
288
289   /* Parse the output from readelf. The columns are:
290      Index Value Size Type Binding Visibility Section Name.  */
291   syms = (struct ld_plugin_symbol*)malloc(sizeof(struct ld_plugin_symbol) * 8);
292   if (syms == NULL)
293     return LDPS_ERR;
294   maxsyms = 8;
295   while (ftell(irfile) < end_offset
296          && fgets(buf, sizeof(buf), irfile) != NULL)
297     {
298       parse_readelf_line(buf, &info);
299
300       /* Ignore local symbols.  */
301       if (strncmp(info.bind, "LOCAL", 5) == 0)
302         continue;
303
304       weak = strncmp(info.bind, "WEAK", 4) == 0;
305       if (strncmp(info.sect, "UND", 3) == 0)
306         def = weak ? LDPK_WEAKUNDEF : LDPK_UNDEF;
307       else if (strncmp(info.sect, "COM", 3) == 0)
308         def = LDPK_COMMON;
309       else
310         def = weak ? LDPK_WEAKDEF : LDPK_DEF;
311
312       if (strncmp(info.vis, "INTERNAL", 8) == 0)
313         vis = LDPV_INTERNAL;
314       else if (strncmp(info.vis, "HIDDEN", 6) == 0)
315         vis = LDPV_HIDDEN;
316       else if (strncmp(info.vis, "PROTECTED", 9) == 0)
317         vis = LDPV_PROTECTED;
318       else
319         vis = LDPV_DEFAULT;
320
321       /* If the symbol is listed in the options list, special-case
322          it as a comdat symbol.  */
323       is_comdat = 0;
324       for (i = 0; i < nopts; ++i)
325         {
326           if (info.name != NULL && strcmp(info.name, opts[i]) == 0)
327             {
328               is_comdat = 1;
329               break;
330             }
331         }
332
333       if (nsyms >= maxsyms)
334         {
335           syms = (struct ld_plugin_symbol*)
336             realloc(syms, sizeof(struct ld_plugin_symbol) * maxsyms * 2);
337           if (syms == NULL)
338             return LDPS_ERR;
339           maxsyms *= 2;
340         }
341
342       if (info.name == NULL)
343         syms[nsyms].name = NULL;
344       else
345         {
346           len = strlen(info.name);
347           syms[nsyms].name = malloc(len + 1);
348           strncpy(syms[nsyms].name, info.name, len + 1);
349         }
350       syms[nsyms].version = NULL;
351       syms[nsyms].def = def;
352       syms[nsyms].visibility = vis;
353       syms[nsyms].size = info.size;
354       syms[nsyms].comdat_key = is_comdat ? syms[nsyms].name : NULL;
355       syms[nsyms].resolution = LDPR_UNKNOWN;
356       ++nsyms;
357     }
358
359   claimed_file = (struct claimed_file*) malloc(sizeof(struct claimed_file));
360   if (claimed_file == NULL)
361     return LDPS_ERR;
362
363   claimed_file->name = file->name;
364   claimed_file->handle = file->handle;
365   claimed_file->nsyms = nsyms;
366   claimed_file->syms = syms;
367   claimed_file->next = NULL;
368   if (last_claimed_file == NULL)
369     first_claimed_file = claimed_file;
370   else
371     last_claimed_file->next = claimed_file;
372   last_claimed_file = claimed_file;
373
374   (*message)(LDPL_INFO, "%s: claiming file, adding %d symbols",
375              file->name, nsyms);
376
377   if (nsyms > 0)
378     (*add_symbols)(file->handle, nsyms, syms);
379
380   *claimed = 1;
381   return LDPS_OK;
382 }
383
384 enum ld_plugin_status
385 all_symbols_read_hook(void)
386 {
387   int i;
388   const char* res;
389   struct claimed_file* claimed_file;
390   struct ld_plugin_input_file file;
391   FILE* irfile;
392   off_t end_offset;
393   struct sym_info info;
394   int len;
395   char buf[160];
396   char* p;
397   const char* filename;
398
399   (*message)(LDPL_INFO, "all symbols read hook called");
400
401   if (get_symbols_v2 == NULL)
402     {
403       fprintf(stderr, "tv_get_symbols (v2) interface missing\n");
404       return LDPS_ERR;
405     }
406
407   for (claimed_file = first_claimed_file;
408        claimed_file != NULL;
409        claimed_file = claimed_file->next)
410     {
411       (*get_symbols_v2)(claimed_file->handle, claimed_file->nsyms,
412                      claimed_file->syms);
413
414       for (i = 0; i < claimed_file->nsyms; ++i)
415         {
416           switch (claimed_file->syms[i].resolution)
417             {
418             case LDPR_UNKNOWN:
419               res = "UNKNOWN";
420               break;
421             case LDPR_UNDEF:
422               res = "UNDEF";
423               break;
424             case LDPR_PREVAILING_DEF:
425               res = "PREVAILING_DEF_REG";
426               break;
427             case LDPR_PREVAILING_DEF_IRONLY:
428               res = "PREVAILING_DEF_IRONLY";
429               break;
430             case LDPR_PREVAILING_DEF_IRONLY_EXP:
431               res = "PREVAILING_DEF_IRONLY_EXP";
432               break;
433             case LDPR_PREEMPTED_REG:
434               res = "PREEMPTED_REG";
435               break;
436             case LDPR_PREEMPTED_IR:
437               res = "PREEMPTED_IR";
438               break;
439             case LDPR_RESOLVED_IR:
440               res = "RESOLVED_IR";
441               break;
442             case LDPR_RESOLVED_EXEC:
443               res = "RESOLVED_EXEC";
444               break;
445             case LDPR_RESOLVED_DYN:
446               res = "RESOLVED_DYN";
447               break;
448             default:
449               res = "?";
450               break;
451             }
452           (*message)(LDPL_INFO, "%s: %s: %s", claimed_file->name,
453                      claimed_file->syms[i].name, res);
454         }
455     }
456
457   if (add_input_file == NULL)
458     {
459       fprintf(stderr, "tv_add_input_file interface missing\n");
460       return LDPS_ERR;
461     }
462   if (get_input_file == NULL)
463     {
464       fprintf(stderr, "tv_get_input_file interface missing\n");
465       return LDPS_ERR;
466     }
467   if (release_input_file == NULL)
468     {
469       fprintf(stderr, "tv_release_input_file interface missing\n");
470       return LDPS_ERR;
471     }
472
473   for (claimed_file = first_claimed_file;
474        claimed_file != NULL;
475        claimed_file = claimed_file->next)
476     {
477       (*get_input_file) (claimed_file->handle, &file);
478
479       /* Look for the beginning of output from readelf -s.  */
480       irfile = fdopen(file.fd, "r");
481       (void)fseek(irfile, file.offset, SEEK_SET);
482       end_offset = file.offset + file.filesize;
483       len = fread(buf, 1, 13, irfile);
484       if (len < 13 || strncmp(buf, "\nSymbol table", 13) != 0)
485         {
486           fprintf(stderr, "%s: can't re-read original input file\n",
487                   claimed_file->name);
488           return LDPS_ERR;
489         }
490
491       /* Skip the two header lines.  */
492       (void) fgets(buf, sizeof(buf), irfile);
493       (void) fgets(buf, sizeof(buf), irfile);
494
495       filename = NULL;
496       while (ftell(irfile) < end_offset
497              && fgets(buf, sizeof(buf), irfile) != NULL)
498         {
499           parse_readelf_line(buf, &info);
500
501           /* Look for file name.  */
502           if (strncmp(info.type, "FILE", 4) == 0)
503             {
504               len = strlen(info.name);
505               p = malloc(len + 1);
506               strncpy(p, info.name, len + 1);
507               filename = p;
508               break;
509             }
510         }
511
512       (*release_input_file) (claimed_file->handle);
513
514       if (filename == NULL)
515         filename = claimed_file->name;
516
517       if (claimed_file->nsyms == 0)
518         continue;
519
520       if (strlen(filename) >= sizeof(buf))
521         {
522           (*message)(LDPL_FATAL, "%s: filename too long", filename);
523           return LDPS_ERR;
524         }
525       strcpy(buf, filename);
526       p = strrchr(buf, '.');
527       if (p == NULL
528           || (strcmp(p, ".syms") != 0
529               && strcmp(p, ".c") != 0
530               && strcmp(p, ".cc") != 0))
531         {
532           (*message)(LDPL_FATAL, "%s: filename has unknown suffix",
533                      filename);
534           return LDPS_ERR;
535         }
536       p[1] = 'o';
537       p[2] = '\0';
538       (*message)(LDPL_INFO, "%s: adding new input file", buf);
539       (*add_input_file)(buf);
540     }
541
542   return LDPS_OK;
543 }
544
545 enum ld_plugin_status
546 cleanup_hook(void)
547 {
548   (*message)(LDPL_INFO, "cleanup hook called");
549   return LDPS_OK;
550 }
551
552 static void
553 parse_readelf_line(char* p, struct sym_info* info)
554 {
555   int len;
556
557   p += strspn(p, " ");
558
559   /* Index field.  */
560   p += strcspn(p, " ");
561   p += strspn(p, " ");
562
563   /* Value field.  */
564   p += strcspn(p, " ");
565   p += strspn(p, " ");
566
567   /* Size field.  */
568   info->size = atoi(p);
569   p += strcspn(p, " ");
570   p += strspn(p, " ");
571
572   /* Type field.  */
573   info->type = p;
574   p += strcspn(p, " ");
575   p += strspn(p, " ");
576
577   /* Binding field.  */
578   info->bind = p;
579   p += strcspn(p, " ");
580   p += strspn(p, " ");
581
582   /* Visibility field.  */
583   info->vis = p;
584   p += strcspn(p, " ");
585   p += strspn(p, " ");
586
587   /* Section field.  */
588   info->sect = p;
589   p += strcspn(p, " ");
590   p += strspn(p, " ");
591
592   /* Name field.  */
593   /* FIXME:  Look for version.  */
594   len = strlen(p);
595   if (len == 0)
596     p = NULL;
597   else if (p[len-1] == '\n')
598     p[--len] = '\0';
599   info->name = p;
600 }