]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gold/testsuite/plugin_test.c
Add plugin functionality for link-time optimization (LTO).
[thirdparty/binutils-gdb.git] / gold / testsuite / plugin_test.c
CommitLineData
89fc3421
CC
1/* test_plugin.c -- simple linker plugin test
2
3 Copyright 2008 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
28struct 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
37static struct claimed_file* first_claimed_file = NULL;
38static struct claimed_file* last_claimed_file = NULL;
39
40static ld_plugin_register_claim_file register_claim_file_hook = NULL;
41static ld_plugin_register_all_symbols_read register_all_symbols_read_hook = NULL;
42static ld_plugin_register_cleanup register_cleanup_hook = NULL;
43static ld_plugin_add_symbols add_symbols = NULL;
44static ld_plugin_get_symbols get_symbols = NULL;
45static ld_plugin_add_input_file add_input_file = NULL;
46static ld_plugin_message message = NULL;
47
48#define MAXOPTS 10
49
50static const char *opts[MAXOPTS];
51static int nopts = 0;
52
53enum ld_plugin_status onload(struct ld_plugin_tv *tv);
54enum ld_plugin_status claim_file_hook(const struct ld_plugin_input_file *file,
55 int *claimed);
56enum ld_plugin_status all_symbols_read_hook(void);
57enum ld_plugin_status cleanup_hook(void);
58
59enum ld_plugin_status
60onload(struct ld_plugin_tv *tv)
61{
62 struct ld_plugin_tv *entry;
63 int api_version = 0;
64 int gold_version = 0;
65 int i;
66
67 for (entry = tv; entry->tv_tag != LDPT_NULL; ++entry)
68 {
69 switch (entry->tv_tag)
70 {
71 case LDPT_API_VERSION:
72 api_version = entry->tv_u.tv_val;
73 break;
74 case LDPT_GOLD_VERSION:
75 gold_version = entry->tv_u.tv_val;
76 break;
77 case LDPT_LINKER_OUTPUT:
78 break;
79 case LDPT_OPTION:
80 if (nopts < MAXOPTS)
81 opts[nopts++] = entry->tv_u.tv_string;
82 break;
83 case LDPT_REGISTER_CLAIM_FILE_HOOK:
84 register_claim_file_hook = entry->tv_u.tv_register_claim_file;
85 break;
86 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
87 register_all_symbols_read_hook =
88 entry->tv_u.tv_register_all_symbols_read;
89 break;
90 case LDPT_REGISTER_CLEANUP_HOOK:
91 register_cleanup_hook = entry->tv_u.tv_register_cleanup;
92 break;
93 case LDPT_ADD_SYMBOLS:
94 add_symbols = entry->tv_u.tv_add_symbols;
95 break;
96 case LDPT_GET_SYMBOLS:
97 get_symbols = entry->tv_u.tv_get_symbols;
98 break;
99 case LDPT_ADD_INPUT_FILE:
100 add_input_file = entry->tv_u.tv_add_input_file;
101 break;
102 case LDPT_MESSAGE:
103 message = entry->tv_u.tv_message;
104 break;
105 default:
106 break;
107 }
108 }
109
110 if (message == NULL)
111 {
112 fprintf(stderr, "tv_message interface missing\n");
113 return LDPS_ERR;
114 }
115
116 if (register_claim_file_hook == NULL)
117 {
118 fprintf(stderr, "tv_register_claim_file_hook interface missing\n");
119 return LDPS_ERR;
120 }
121
122 if (register_all_symbols_read_hook == NULL)
123 {
124 fprintf(stderr, "tv_register_all_symbols_read_hook interface missing\n");
125 return LDPS_ERR;
126 }
127
128 if (register_cleanup_hook == NULL)
129 {
130 fprintf(stderr, "tv_register_cleanup_hook interface missing\n");
131 return LDPS_ERR;
132 }
133
134 (*message)(LDPL_INFO, "API version: %d", api_version);
135 (*message)(LDPL_INFO, "gold version: %d", gold_version);
136
137 for (i = 0; i < nopts; ++i)
138 (*message)(LDPL_INFO, "option: %s", opts[i]);
139
140 if ((*register_claim_file_hook)(claim_file_hook) != LDPS_OK)
141 {
142 (*message)(LDPL_ERROR, "error registering claim file hook");
143 return LDPS_ERR;
144 }
145
146 if ((*register_all_symbols_read_hook)(all_symbols_read_hook) != LDPS_OK)
147 {
148 (*message)(LDPL_ERROR, "error registering all symbols read hook");
149 return LDPS_ERR;
150 }
151
152 if ((*register_cleanup_hook)(cleanup_hook) != LDPS_OK)
153 {
154 (*message)(LDPL_ERROR, "error registering cleanup hook");
155 return LDPS_ERR;
156 }
157
158 return LDPS_OK;
159}
160
161enum ld_plugin_status
162claim_file_hook (const struct ld_plugin_input_file* file, int* claimed)
163{
164 int len;
165 char buf[160];
166 struct claimed_file* claimed_file;
167 struct ld_plugin_symbol* syms;
168 int nsyms = 0;
169 int maxsyms = 0;
170 FILE* irfile;
171 char *p;
172 char *pbind;
173 char *pvis;
174 char *psect;
175 int weak;
176 int def;
177 int vis;
178 int size;
179 char* name;
180 int is_comdat;
181 int i;
182
183 (*message)(LDPL_INFO,
184 "%s: claim file hook called (offset = %ld, size = %ld)",
185 file->name, (long)file->offset, (long)file->filesize);
186
187 /* Look for the beginning of output from readelf -s. */
188 irfile = fdopen(file->fd, "r");
189 (void)fseek(irfile, file->offset, SEEK_SET);
190 len = fread(buf, 1, 13, irfile);
191 if (len < 13 || strncmp(buf, "\nSymbol table", 13) != 0)
192 return LDPS_OK;
193
194 /* Skip the two header lines. */
195 (void) fgets(buf, sizeof(buf), irfile);
196 (void) fgets(buf, sizeof(buf), irfile);
197
198 if (add_symbols == NULL)
199 {
200 fprintf(stderr, "tv_add_symbols interface missing\n");
201 return LDPS_ERR;
202 }
203
204 /* Parse the output from readelf. The columns are:
205 Index Value Size Type Binding Visibility Section Name. */
206 syms = (struct ld_plugin_symbol*)malloc(sizeof(struct ld_plugin_symbol) * 8);
207 if (syms == NULL)
208 return LDPS_ERR;
209 maxsyms = 8;
210 while (fgets(buf, sizeof(buf), irfile) != NULL)
211 {
212 p = buf;
213 p += strspn(p, " ");
214
215 /* Index field. */
216 p += strcspn(p, " ");
217 p += strspn(p, " ");
218
219 /* Value field. */
220 p += strcspn(p, " ");
221 p += strspn(p, " ");
222
223 /* Size field. */
224 size = atoi(p);
225 p += strcspn(p, " ");
226 p += strspn(p, " ");
227
228 /* Type field. */
229 p += strcspn(p, " ");
230 p += strspn(p, " ");
231
232 /* Binding field. */
233 pbind = p;
234 p += strcspn(p, " ");
235 p += strspn(p, " ");
236
237 /* Visibility field. */
238 pvis = p;
239 p += strcspn(p, " ");
240 p += strspn(p, " ");
241
242 /* Section field. */
243 psect = p;
244 p += strcspn(p, " ");
245 p += strspn(p, " ");
246
247 /* Name field. */
248 /* FIXME: Look for version. */
249 len = strlen(p);
250 if (p[len-1] == '\n')
251 p[--len] = '\0';
252 name = malloc(len + 1);
253 strncpy(name, p, len + 1);
254
255 /* Ignore local symbols. */
256 if (strncmp(pbind, "LOCAL", 5) == 0)
257 continue;
258
259 weak = strncmp(pbind, "WEAK", 4) == 0;
260 if (strncmp(psect, "UND", 3) == 0)
261 def = weak ? LDPK_WEAKUNDEF : LDPK_UNDEF;
262 else if (strncmp(psect, "COM", 3) == 0)
263 def = LDPK_COMMON;
264 else
265 def = weak ? LDPK_WEAKDEF : LDPK_DEF;
266
267 if (strncmp(pvis, "INTERNAL", 8) == 0)
268 vis = LDPV_INTERNAL;
269 else if (strncmp(pvis, "HIDDEN", 6) == 0)
270 vis = LDPV_HIDDEN;
271 else if (strncmp(pvis, "PROTECTED", 9) == 0)
272 vis = LDPV_PROTECTED;
273 else
274 vis = LDPV_DEFAULT;
275
276 /* If the symbol is listed in the options list, special-case
277 it as a comdat symbol. */
278 is_comdat = 0;
279 for (i = 0; i < nopts; ++i)
280 {
281 if (name != NULL && strcmp(name, opts[i]) == 0)
282 {
283 is_comdat = 1;
284 break;
285 }
286 }
287
288 if (nsyms >= maxsyms)
289 {
290 syms = (struct ld_plugin_symbol*)
291 realloc(syms, sizeof(struct ld_plugin_symbol) * maxsyms * 2);
292 if (syms == NULL)
293 return LDPS_ERR;
294 maxsyms *= 2;
295 }
296
297 syms[nsyms].name = name;
298 syms[nsyms].version = NULL;
299 syms[nsyms].def = def;
300 syms[nsyms].visibility = vis;
301 syms[nsyms].size = size;
302 syms[nsyms].comdat_key = is_comdat ? name : NULL;
303 syms[nsyms].resolution = LDPR_UNKNOWN;
304 ++nsyms;
305 }
306
307 claimed_file = (struct claimed_file*) malloc(sizeof(struct claimed_file));
308 if (claimed_file == NULL)
309 return LDPS_ERR;
310
311 claimed_file->name = file->name;
312 claimed_file->handle = file->handle;
313 claimed_file->nsyms = nsyms;
314 claimed_file->syms = syms;
315 claimed_file->next = NULL;
316 if (last_claimed_file == NULL)
317 first_claimed_file = claimed_file;
318 else
319 last_claimed_file->next = claimed_file;
320 last_claimed_file = claimed_file;
321
322 (*add_symbols)(file->handle, nsyms, syms);
323
324 *claimed = 1;
325 return LDPS_OK;
326}
327
328enum ld_plugin_status
329all_symbols_read_hook(void)
330{
331 int i;
332 const char* res;
333 struct claimed_file* claimed_file;
334 char buf[160];
335 char *p;
336
337 (*message)(LDPL_INFO, "all symbols read hook called");
338
339 if (get_symbols == NULL)
340 {
341 fprintf(stderr, "tv_get_symbols interface missing\n");
342 return LDPS_ERR;
343 }
344
345 for (claimed_file = first_claimed_file;
346 claimed_file != NULL;
347 claimed_file = claimed_file->next)
348 {
349 (*get_symbols)(claimed_file->handle, claimed_file->nsyms,
350 claimed_file->syms);
351 for (i = 0; i < claimed_file->nsyms; ++i)
352 {
353 switch (claimed_file->syms[i].resolution)
354 {
355 case LDPR_UNKNOWN:
356 res = "UNKNOWN";
357 break;
358 case LDPR_UNDEF:
359 res = "UNDEF";
360 break;
361 case LDPR_PREVAILING_DEF:
362 res = "PREVAILING_DEF_REG";
363 break;
364 case LDPR_PREVAILING_DEF_IRONLY:
365 res = "PREVAILING_DEF_IRONLY";
366 break;
367 case LDPR_PREEMPTED_REG:
368 res = "PREEMPTED_REG";
369 break;
370 case LDPR_PREEMPTED_IR:
371 res = "PREEMPTED_IR";
372 break;
373 case LDPR_RESOLVED_IR:
374 res = "RESOLVED_IR";
375 break;
376 case LDPR_RESOLVED_EXEC:
377 res = "RESOLVED_EXEC";
378 break;
379 case LDPR_RESOLVED_DYN:
380 res = "RESOLVED_DYN";
381 break;
382 default:
383 res = "?";
384 break;
385 }
386 (*message)(LDPL_INFO, "%s: %s: %s", claimed_file->name,
387 claimed_file->syms[i].name, res);
388 }
389 }
390
391 if (add_input_file == NULL)
392 {
393 fprintf(stderr, "tv_add_input_file interface missing\n");
394 return LDPS_ERR;
395 }
396
397 for (claimed_file = first_claimed_file;
398 claimed_file != NULL;
399 claimed_file = claimed_file->next)
400 {
401 if (strlen(claimed_file->name) >= sizeof(buf))
402 {
403 (*message)(LDPL_FATAL, "%s: filename too long", claimed_file->name);
404 return LDPS_ERR;
405 }
406 strcpy(buf, claimed_file->name);
407 p = strrchr(buf, '.');
408 if (p == NULL || strcmp(p, ".syms") != 0)
409 {
410 (*message)(LDPL_FATAL, "%s: filename must have '.syms' suffix",
411 claimed_file->name);
412 return LDPS_ERR;
413 }
414 p[1] = 'o';
415 p[2] = '\0';
416 (*add_input_file)(buf);
417 }
418
419 return LDPS_OK;
420}
421
422enum ld_plugin_status
423cleanup_hook(void)
424{
425 (*message)(LDPL_INFO, "cleanup hook called");
426 return LDPS_OK;
427}