* gold.cc (gold_exit): Call plugin cleanup handlers on exit.
[binutils-gdb.git] / gold / testsuite / plugin_test.c
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
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 static struct claimed_file* first_claimed_file = NULL;
38 static struct claimed_file* last_claimed_file = NULL;
39
40 static ld_plugin_register_claim_file register_claim_file_hook = NULL;
41 static ld_plugin_register_all_symbols_read register_all_symbols_read_hook = NULL;
42 static ld_plugin_register_cleanup register_cleanup_hook = NULL;
43 static ld_plugin_add_symbols add_symbols = NULL;
44 static ld_plugin_get_symbols get_symbols = NULL;
45 static ld_plugin_add_input_file add_input_file = NULL;
46 static ld_plugin_message message = NULL;
47
48 #define MAXOPTS 10
49
50 static const char *opts[MAXOPTS];
51 static int nopts = 0;
52
53 enum ld_plugin_status onload(struct ld_plugin_tv *tv);
54 enum ld_plugin_status claim_file_hook(const struct ld_plugin_input_file *file,
55 int *claimed);
56 enum ld_plugin_status all_symbols_read_hook(void);
57 enum ld_plugin_status cleanup_hook(void);
58
59 enum ld_plugin_status
60 onload(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
161 enum ld_plugin_status
162 claim_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 (*message)(LDPL_INFO, "%s: claiming file, adding %d symbols",
323 file->name, nsyms);
324
325 if (nsyms > 0)
326 (*add_symbols)(file->handle, nsyms, syms);
327
328 *claimed = 1;
329 return LDPS_OK;
330 }
331
332 enum ld_plugin_status
333 all_symbols_read_hook(void)
334 {
335 int i;
336 const char* res;
337 struct claimed_file* claimed_file;
338 char buf[160];
339 char *p;
340
341 (*message)(LDPL_INFO, "all symbols read hook called");
342
343 if (get_symbols == NULL)
344 {
345 fprintf(stderr, "tv_get_symbols interface missing\n");
346 return LDPS_ERR;
347 }
348
349 for (claimed_file = first_claimed_file;
350 claimed_file != NULL;
351 claimed_file = claimed_file->next)
352 {
353 (*get_symbols)(claimed_file->handle, claimed_file->nsyms,
354 claimed_file->syms);
355 for (i = 0; i < claimed_file->nsyms; ++i)
356 {
357 switch (claimed_file->syms[i].resolution)
358 {
359 case LDPR_UNKNOWN:
360 res = "UNKNOWN";
361 break;
362 case LDPR_UNDEF:
363 res = "UNDEF";
364 break;
365 case LDPR_PREVAILING_DEF:
366 res = "PREVAILING_DEF_REG";
367 break;
368 case LDPR_PREVAILING_DEF_IRONLY:
369 res = "PREVAILING_DEF_IRONLY";
370 break;
371 case LDPR_PREEMPTED_REG:
372 res = "PREEMPTED_REG";
373 break;
374 case LDPR_PREEMPTED_IR:
375 res = "PREEMPTED_IR";
376 break;
377 case LDPR_RESOLVED_IR:
378 res = "RESOLVED_IR";
379 break;
380 case LDPR_RESOLVED_EXEC:
381 res = "RESOLVED_EXEC";
382 break;
383 case LDPR_RESOLVED_DYN:
384 res = "RESOLVED_DYN";
385 break;
386 default:
387 res = "?";
388 break;
389 }
390 (*message)(LDPL_INFO, "%s: %s: %s", claimed_file->name,
391 claimed_file->syms[i].name, res);
392 }
393 }
394
395 if (add_input_file == NULL)
396 {
397 fprintf(stderr, "tv_add_input_file interface missing\n");
398 return LDPS_ERR;
399 }
400
401 for (claimed_file = first_claimed_file;
402 claimed_file != NULL;
403 claimed_file = claimed_file->next)
404 {
405 if (claimed_file->nsyms == 0)
406 continue;
407 if (strlen(claimed_file->name) >= sizeof(buf))
408 {
409 (*message)(LDPL_FATAL, "%s: filename too long", claimed_file->name);
410 return LDPS_ERR;
411 }
412 strcpy(buf, claimed_file->name);
413 p = strrchr(buf, '.');
414 if (p == NULL || strcmp(p, ".syms") != 0)
415 {
416 (*message)(LDPL_FATAL, "%s: filename must have '.syms' suffix",
417 claimed_file->name);
418 return LDPS_ERR;
419 }
420 p[1] = 'o';
421 p[2] = '\0';
422 (*add_input_file)(buf);
423 }
424
425 return LDPS_OK;
426 }
427
428 enum ld_plugin_status
429 cleanup_hook(void)
430 {
431 (*message)(LDPL_INFO, "cleanup hook called");
432 return LDPS_OK;
433 }