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