PR ld/10515
[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 #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 }