X86: Update opcode-suffix.d
[binutils-gdb.git] / gold / testsuite / plugin_test.c
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
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
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
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
599 cleanup_hook(void)
600 {
601 (*message)(LDPL_INFO, "cleanup hook called");
602 return LDPS_OK;
603 }
604
605 static void
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 }