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