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