gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / ld / testplug3.c
CommitLineData
439b7f41
L
1/* Test plugin for the GNU linker. Check non-object IR file and calling
2 release_input_file from onclaim_file.
b3adc24a 3 Copyright (C) 2015-2020 Free Software Foundation, Inc.
439b7f41
L
4
5 This file is part of the GNU Binutils.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20 MA 02110-1301, USA. */
21
22#include "sysdep.h"
23#include "bfd.h"
24#include "plugin-api.h"
25#include "filenames.h"
26/* For ARRAY_SIZE macro only - we don't link the library itself. */
27#include "libiberty.h"
28
29extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
30static enum ld_plugin_status onclaim_file (const struct ld_plugin_input_file *file,
31 int *claimed);
32static enum ld_plugin_status onall_symbols_read (void);
33static enum ld_plugin_status oncleanup (void);
34
35/* Helper for calling plugin api message function. */
36#define TV_MESSAGE if (tv_message) (*tv_message)
37
38/* Struct for recording files to claim / files claimed. */
39typedef struct claim_file
40{
41 struct claim_file *next;
42 struct ld_plugin_input_file file;
43 bfd_boolean claimed;
44 struct ld_plugin_symbol *symbols;
45 int n_syms_allocated;
46 int n_syms_used;
47} claim_file_t;
48
49/* Types of things that can be added at all symbols read time. */
50typedef enum addfile_enum
51{
52 ADD_FILE,
53 ADD_LIB,
54 ADD_DIR
55} addfile_enum_t;
56
57/* Struct for recording files to add to final link. */
58typedef struct add_file
59{
60 struct add_file *next;
61 const char *name;
62 addfile_enum_t type;
63} add_file_t;
64
65/* Helper macro for defining array of transfer vector tags and names. */
66#define ADDENTRY(tag) { tag, #tag }
67
68/* Struct for looking up human-readable versions of tag names. */
69typedef struct tag_name
70{
71 enum ld_plugin_tag tag;
72 const char *name;
73} tag_name_t;
74
75/* Array of all known tags and their names. */
76static const tag_name_t tag_names[] =
77{
78 ADDENTRY(LDPT_NULL),
79 ADDENTRY(LDPT_API_VERSION),
80 ADDENTRY(LDPT_GOLD_VERSION),
81 ADDENTRY(LDPT_LINKER_OUTPUT),
82 ADDENTRY(LDPT_OPTION),
83 ADDENTRY(LDPT_REGISTER_CLAIM_FILE_HOOK),
84 ADDENTRY(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK),
85 ADDENTRY(LDPT_REGISTER_CLEANUP_HOOK),
86 ADDENTRY(LDPT_ADD_SYMBOLS),
87 ADDENTRY(LDPT_GET_SYMBOLS),
88 ADDENTRY(LDPT_GET_SYMBOLS_V2),
89 ADDENTRY(LDPT_ADD_INPUT_FILE),
90 ADDENTRY(LDPT_MESSAGE),
91 ADDENTRY(LDPT_GET_INPUT_FILE),
92 ADDENTRY(LDPT_GET_VIEW),
93 ADDENTRY(LDPT_RELEASE_INPUT_FILE),
94 ADDENTRY(LDPT_ADD_INPUT_LIBRARY),
95 ADDENTRY(LDPT_OUTPUT_NAME),
96 ADDENTRY(LDPT_SET_EXTRA_LIBRARY_PATH),
97 ADDENTRY(LDPT_GNU_LD_VERSION)
98};
99
100/* Function pointers to cache hooks passed at onload time. */
101static ld_plugin_register_claim_file tv_register_claim_file = 0;
102static ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0;
103static ld_plugin_register_cleanup tv_register_cleanup = 0;
104static ld_plugin_add_symbols tv_add_symbols = 0;
105static ld_plugin_get_symbols tv_get_symbols = 0;
106static ld_plugin_get_symbols tv_get_symbols_v2 = 0;
107static ld_plugin_add_input_file tv_add_input_file = 0;
108static ld_plugin_message tv_message = 0;
109static ld_plugin_get_input_file tv_get_input_file = 0;
110static ld_plugin_get_view tv_get_view = 0;
111static ld_plugin_release_input_file tv_release_input_file = 0;
112static ld_plugin_add_input_library tv_add_input_library = 0;
113static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0;
114
115/* Other cached info from the transfer vector. */
116static enum ld_plugin_output_file_type linker_output;
117static const char *output_name;
118
119/* Behaviour control flags set by plugin options. */
120static enum ld_plugin_status onload_ret = LDPS_OK;
121static enum ld_plugin_status claim_file_ret = LDPS_OK;
122static enum ld_plugin_status all_symbols_read_ret = LDPS_OK;
123static enum ld_plugin_status cleanup_ret = LDPS_OK;
124static bfd_boolean register_claimfile_hook = TRUE;
125static bfd_boolean register_allsymbolsread_hook = FALSE;
126static bfd_boolean register_cleanup_hook = FALSE;
127static bfd_boolean dumpresolutions = FALSE;
128
129/* The master list of all claimable/claimed files. */
130static claim_file_t *claimfiles_list = NULL;
131
132/* We keep a tail pointer for easy linking on the end. */
133static claim_file_t **claimfiles_tail_chain_ptr = &claimfiles_list;
134
135/* The last claimed file added to the list, for receiving syms. */
136static claim_file_t *last_claimfile = NULL;
137
138/* The master list of all files to add to the final link. */
139static add_file_t *addfiles_list = NULL;
140
141/* We keep a tail pointer for easy linking on the end. */
142static add_file_t **addfiles_tail_chain_ptr = &addfiles_list;
143
144/* Add a new claimfile on the end of the chain. */
145static enum ld_plugin_status
146record_claim_file (const char *file, off_t filesize)
147{
148 claim_file_t *newfile;
149
150 newfile = malloc (sizeof *newfile);
151 if (!newfile)
152 return LDPS_ERR;
153 memset (newfile, 0, sizeof *newfile);
154 /* Only setup for now is remembering the name to look for. */
155 newfile->file.name = file;
156 newfile->file.filesize = filesize;
157 /* Chain it on the end of the list. */
158 *claimfiles_tail_chain_ptr = newfile;
159 claimfiles_tail_chain_ptr = &newfile->next;
160 /* Record it as active for receiving symbols to register. */
161 last_claimfile = newfile;
162 return LDPS_OK;
163}
164
165/* Add a new addfile on the end of the chain. */
166static enum ld_plugin_status
167record_add_file (const char *file, addfile_enum_t type)
168{
169 add_file_t *newfile;
170
171 newfile = malloc (sizeof *newfile);
172 if (!newfile)
173 return LDPS_ERR;
174 newfile->next = NULL;
175 newfile->name = file;
176 newfile->type = type;
177 /* Chain it on the end of the list. */
178 *addfiles_tail_chain_ptr = newfile;
179 addfiles_tail_chain_ptr = &newfile->next;
180 return LDPS_OK;
181}
182
183/* Parse a command-line argument string into a symbol definition.
184 Symbol-strings follow the colon-separated format:
185 NAME:VERSION:def:vis:size:COMDATKEY
186 where the fields in capitals are strings and those in lower
187 case are integers. We don't allow to specify a resolution as
188 doing so is not meaningful when calling the add symbols hook. */
189static enum ld_plugin_status
190parse_symdefstr (const char *str, struct ld_plugin_symbol *sym)
191{
192 int n;
193 long long size;
194 const char *colon1, *colon2, *colon5;
195
196 /* Locate the colons separating the first two strings. */
197 colon1 = strchr (str, ':');
198 if (!colon1)
199 return LDPS_ERR;
200 colon2 = strchr (colon1+1, ':');
201 if (!colon2)
202 return LDPS_ERR;
203 /* Name must not be empty (version may be). */
204 if (colon1 == str)
205 return LDPS_ERR;
206
207 /* The fifth colon and trailing comdat key string are optional,
208 but the intermediate ones must all be present. */
209 colon5 = strchr (colon2+1, ':'); /* Actually only third so far. */
210 if (!colon5)
211 return LDPS_ERR;
212 colon5 = strchr (colon5+1, ':'); /* Hopefully fourth now. */
213 if (!colon5)
214 return LDPS_ERR;
215 colon5 = strchr (colon5+1, ':'); /* Optional fifth now. */
216
217 /* Finally we'll use sscanf to parse the numeric fields, then
218 we'll split out the strings which we need to allocate separate
219 storage for anyway so that we can add nul termination. */
c02d6661 220 n = sscanf (colon2 + 1, "%hhi:%i:%lli", &sym->def, &sym->visibility, &size);
439b7f41
L
221 if (n != 3)
222 return LDPS_ERR;
223
224 /* Parsed successfully, so allocate strings and fill out fields. */
225 sym->size = size;
c02d6661
AM
226 sym->unused = 0;
227 sym->section_kind = 0;
228 sym->symbol_type = 0;
439b7f41
L
229 sym->resolution = LDPR_UNKNOWN;
230 sym->name = malloc (colon1 - str + 1);
231 if (!sym->name)
232 return LDPS_ERR;
233 memcpy (sym->name, str, colon1 - str);
234 sym->name[colon1 - str] = '\0';
235 if (colon2 > (colon1 + 1))
236 {
237 sym->version = malloc (colon2 - colon1);
238 if (!sym->version)
239 return LDPS_ERR;
240 memcpy (sym->version, colon1 + 1, colon2 - (colon1 + 1));
241 sym->version[colon2 - (colon1 + 1)] = '\0';
242 }
243 else
244 sym->version = NULL;
245 if (colon5 && colon5[1])
246 {
247 sym->comdat_key = malloc (strlen (colon5 + 1) + 1);
248 if (!sym->comdat_key)
249 return LDPS_ERR;
250 strcpy (sym->comdat_key, colon5 + 1);
251 }
252 else
253 sym->comdat_key = 0;
254 return LDPS_OK;
255}
256
257/* Record a symbol to be added for the last-added claimfile. */
258static enum ld_plugin_status
259record_claimed_file_symbol (const char *symdefstr)
260{
261 struct ld_plugin_symbol sym;
262
263 /* Can't add symbols except as belonging to claimed files. */
264 if (!last_claimfile)
265 return LDPS_ERR;
266
267 /* If string doesn't parse correctly, give an error. */
268 if (parse_symdefstr (symdefstr, &sym) != LDPS_OK)
269 return LDPS_ERR;
270
271 /* Check for enough space, resize array if needed, and add it. */
272 if (last_claimfile->n_syms_allocated == last_claimfile->n_syms_used)
273 {
274 int new_n_syms = last_claimfile->n_syms_allocated
275 ? 2 * last_claimfile->n_syms_allocated
276 : 10;
277 last_claimfile->symbols = realloc (last_claimfile->symbols,
278 new_n_syms * sizeof *last_claimfile->symbols);
279 if (!last_claimfile->symbols)
280 return LDPS_ERR;
281 last_claimfile->n_syms_allocated = new_n_syms;
282 }
283 last_claimfile->symbols[last_claimfile->n_syms_used++] = sym;
284
285 return LDPS_OK;
286}
287
288/* Records the status to return from one of the registered hooks. */
289static enum ld_plugin_status
290set_ret_val (const char *whichval, enum ld_plugin_status retval)
291{
292 if (!strcmp ("onload", whichval))
293 onload_ret = retval;
294 else if (!strcmp ("claimfile", whichval))
295 claim_file_ret = retval;
296 else if (!strcmp ("allsymbolsread", whichval))
297 all_symbols_read_ret = retval;
298 else if (!strcmp ("cleanup", whichval))
299 cleanup_ret = retval;
300 else
301 return LDPS_ERR;
302 return LDPS_OK;
303}
304
305/* Records hooks which should be registered. */
306static enum ld_plugin_status
307set_register_hook (const char *whichhook, bfd_boolean yesno)
308{
309 if (!strcmp ("claimfile", whichhook))
310 register_claimfile_hook = yesno;
311 else if (!strcmp ("allsymbolsread", whichhook))
312 register_allsymbolsread_hook = yesno;
313 else if (!strcmp ("cleanup", whichhook))
314 register_cleanup_hook = yesno;
315 else
316 return LDPS_ERR;
317 return LDPS_OK;
318}
319
320/* Determine type of plugin option and pass to individual parsers. */
321static enum ld_plugin_status
322parse_option (const char *opt)
323{
324 if (!strncmp ("fail", opt, 4))
325 return set_ret_val (opt + 4, LDPS_ERR);
326 else if (!strncmp ("pass", opt, 4))
327 return set_ret_val (opt + 4, LDPS_OK);
328 else if (!strncmp ("register", opt, 8))
329 return set_register_hook (opt + 8, TRUE);
330 else if (!strncmp ("noregister", opt, 10))
331 return set_register_hook (opt + 10, FALSE);
332 else if (!strncmp ("claim:", opt, 6))
333 return record_claim_file (opt + 6, 0);
334 else if (!strncmp ("sym:", opt, 4))
335 return record_claimed_file_symbol (opt + 4);
336 else if (!strncmp ("add:", opt, 4))
337 return record_add_file (opt + 4, ADD_FILE);
338 else if (!strncmp ("lib:", opt, 4))
339 return record_add_file (opt + 4, ADD_LIB);
340 else if (!strncmp ("dir:", opt, 4))
341 return record_add_file (opt + 4, ADD_DIR);
342 else if (!strcmp ("dumpresolutions", opt))
343 dumpresolutions = TRUE;
344 else
345 return LDPS_ERR;
346 return LDPS_OK;
347}
348
439b7f41
L
349/* Handle/record information received in a transfer vector entry. */
350static enum ld_plugin_status
351parse_tv_tag (struct ld_plugin_tv *tv)
352{
353#define SETVAR(x) x = tv->tv_u.x
354 switch (tv->tv_tag)
355 {
356 case LDPT_OPTION:
357 return parse_option (tv->tv_u.tv_string);
358 case LDPT_NULL:
359 case LDPT_GOLD_VERSION:
360 case LDPT_GNU_LD_VERSION:
361 case LDPT_API_VERSION:
362 default:
363 break;
364 case LDPT_OUTPUT_NAME:
365 output_name = tv->tv_u.tv_string;
366 break;
367 case LDPT_LINKER_OUTPUT:
368 linker_output = tv->tv_u.tv_val;
369 break;
370 case LDPT_REGISTER_CLAIM_FILE_HOOK:
371 SETVAR(tv_register_claim_file);
372 break;
373 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
374 SETVAR(tv_register_all_symbols_read);
375 break;
376 case LDPT_REGISTER_CLEANUP_HOOK:
377 SETVAR(tv_register_cleanup);
378 break;
379 case LDPT_ADD_SYMBOLS:
380 SETVAR(tv_add_symbols);
381 break;
382 case LDPT_GET_SYMBOLS:
383 SETVAR(tv_get_symbols);
384 break;
385 case LDPT_GET_SYMBOLS_V2:
386 tv_get_symbols_v2 = tv->tv_u.tv_get_symbols;
387 break;
388 case LDPT_ADD_INPUT_FILE:
389 SETVAR(tv_add_input_file);
390 break;
391 case LDPT_MESSAGE:
392 SETVAR(tv_message);
393 break;
394 case LDPT_GET_INPUT_FILE:
395 SETVAR(tv_get_input_file);
396 break;
397 case LDPT_GET_VIEW:
398 SETVAR(tv_get_view);
399 break;
400 case LDPT_RELEASE_INPUT_FILE:
401 SETVAR(tv_release_input_file);
402 break;
403 case LDPT_ADD_INPUT_LIBRARY:
404 SETVAR(tv_add_input_library);
405 break;
406 case LDPT_SET_EXTRA_LIBRARY_PATH:
407 SETVAR(tv_set_extra_library_path);
408 break;
409 }
410#undef SETVAR
411 return LDPS_OK;
412}
413
414/* Standard plugin API entry point. */
415enum ld_plugin_status
416onload (struct ld_plugin_tv *tv)
417{
418 enum ld_plugin_status rv;
419
420 /* This plugin does nothing but dump the tv array. It would
421 be an error if this function was called without one. */
422 if (!tv)
423 return LDPS_ERR;
424
425 /* First entry should always be LDPT_MESSAGE, letting us get
426 hold of it easily so we can send output straight away. */
427 if (tv[0].tv_tag == LDPT_MESSAGE)
428 tv_message = tv[0].tv_u.tv_message;
429
430 do
431 if ((rv = parse_tv_tag (tv)) != LDPS_OK)
432 return rv;
433 while ((tv++)->tv_tag != LDPT_NULL);
434
435 /* Register hooks only if instructed by options. */
436 if (register_claimfile_hook)
437 {
438 if (!tv_register_claim_file)
439 {
440 TV_MESSAGE (LDPL_FATAL, "No register_claim_file hook");
441 fflush (NULL);
442 return LDPS_ERR;
443 }
444 (*tv_register_claim_file) (onclaim_file);
445 }
446 if (register_allsymbolsread_hook)
447 {
448 if (!tv_register_all_symbols_read)
449 {
450 TV_MESSAGE (LDPL_FATAL, "No register_all_symbols_read hook");
451 fflush (NULL);
452 return LDPS_ERR;
453 }
454 (*tv_register_all_symbols_read) (onall_symbols_read);
455 }
456 if (register_cleanup_hook)
457 {
458 if (!tv_register_cleanup)
459 {
460 TV_MESSAGE (LDPL_FATAL, "No register_cleanup hook");
461 fflush (NULL);
462 return LDPS_ERR;
463 }
464 (*tv_register_cleanup) (oncleanup);
465 }
466
467 /* Claim testsuite/ld-plugin/func.c, standalone or in a library. Its
468 size must be SIZE_OF_FUNC_C bytes. */
469#define SIZE_OF_FUNC_C 248
470 if (onload_ret == LDPS_OK
471 && (record_claim_file ("func.c", SIZE_OF_FUNC_C) != LDPS_OK
472 || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
473 || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK
474 || record_claim_file ("libfunc.a", SIZE_OF_FUNC_C) != LDPS_OK
475 || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
476 || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK))
477 onload_ret = LDPS_ERR;
478
479 return onload_ret;
480}
481
482char *
483xstrdup (const char *s)
484{
485 size_t len = strlen (s) + 1;
486 char *ret = malloc (len + 1);
487 return (char *) memcpy (ret, s, len);
488}
489
490/* Standard plugin API registerable hook. */
491static enum ld_plugin_status
492onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
493{
494 /* Let's see if we want to claim this file. */
495 claim_file_t *claimfile = claimfiles_list;
496 size_t len = strlen (file->name);
497 char *name = xstrdup (file->name);
498 char *p = name + len;
499 bfd_boolean islib;
500
501 /* Only match the file name without the directory part. */
502 islib = *p == 'a' && *(p - 1) == '.';
503 for (; p != name; p--)
504 if (IS_DIR_SEPARATOR (*p))
505 {
506 p++;
507 break;
508 }
509
510 while (claimfile)
511 {
512 /* Claim the file only if the file name and size match and don't
513 match the whole library. */
514 if (!strcmp (p, claimfile->file.name)
515 && claimfile->file.filesize == file->filesize
516 && (!islib || file->offset != 0))
517 break;
518 claimfile = claimfile->next;
519 }
520
521 free (name);
522
523 /* If we decided to claim it, record that fact, and add any symbols
524 that were defined for it by plugin options. */
525 *claimed = (claimfile != 0);
526 if (claimfile)
527 {
528 char buffer[30];
529 int fd;
530
531 TV_MESSAGE (LDPL_INFO, "Claimed: %s [@%ld/%ld]", file->name,
532 (long)file->offset, (long)file->filesize);
533
534 claimfile->claimed = TRUE;
535 claimfile->file = *file;
536 if (claimfile->n_syms_used && !tv_add_symbols)
537 claim_file_ret = LDPS_ERR;
538 else if (claimfile->n_syms_used)
539 claim_file_ret = (*tv_add_symbols) (claimfile->file.handle,
540 claimfile->n_syms_used,
541 claimfile->symbols);
542
543 fd = claimfile->file.fd;
544 name = xstrdup (claimfile->file.name);
545 claim_file_ret = tv_release_input_file (claimfile->file.handle);
546 if (claim_file_ret != LDPS_OK)
547 {
548 free (name);
549 return claim_file_ret;
550 }
551 if (read (fd, buffer, sizeof (buffer)) >= 0)
552 {
e3001fd9 553 claim_file_ret = LDPS_ERR;
439b7f41
L
554 TV_MESSAGE (LDPL_FATAL, "Unreleased file descriptor on: %s", name);
555 }
556 free (name);
557 }
558
559 return claim_file_ret;
560}
561
562/* Standard plugin API registerable hook. */
563static enum ld_plugin_status
564onall_symbols_read (void)
565{
566 static const char *resolutions[] =
567 {
568 "LDPR_UNKNOWN",
569 "LDPR_UNDEF",
570 "LDPR_PREVAILING_DEF",
571 "LDPR_PREVAILING_DEF_IRONLY",
572 "LDPR_PREEMPTED_REG",
573 "LDPR_PREEMPTED_IR",
574 "LDPR_RESOLVED_IR",
575 "LDPR_RESOLVED_EXEC",
576 "LDPR_RESOLVED_DYN",
577 "LDPR_PREVAILING_DEF_IRONLY_EXP",
578 };
579 claim_file_t *claimfile = dumpresolutions ? claimfiles_list : NULL;
580 add_file_t *addfile = addfiles_list;
581 TV_MESSAGE (LDPL_INFO, "hook called: all symbols read.");
582 for ( ; claimfile; claimfile = claimfile->next)
583 {
584 enum ld_plugin_status rv;
585 int n;
586 if (claimfile->n_syms_used && !tv_get_symbols_v2)
587 return LDPS_ERR;
588 else if (!claimfile->n_syms_used)
589 continue;
590 else if (!claimfile->file.handle)
591 continue;
592 rv = tv_get_symbols_v2 (claimfile->file.handle, claimfile->n_syms_used,
593 claimfile->symbols);
594 if (rv != LDPS_OK)
595 return rv;
596 for (n = 0; n < claimfile->n_syms_used; n++)
597 TV_MESSAGE (LDPL_INFO, "Sym: '%s%s%s' Resolution: %s",
598 claimfile->symbols[n].name,
599 claimfile->symbols[n].version ? "@" : "",
600 (claimfile->symbols[n].version
601 ? claimfile->symbols[n].version : ""),
602 resolutions[claimfile->symbols[n].resolution]);
603 }
604 for ( ; addfile ; addfile = addfile->next)
605 {
606 enum ld_plugin_status rv;
607 if (addfile->type == ADD_LIB && tv_add_input_library)
608 rv = (*tv_add_input_library) (addfile->name);
609 else if (addfile->type == ADD_FILE && tv_add_input_file)
610 rv = (*tv_add_input_file) (addfile->name);
611 else if (addfile->type == ADD_DIR && tv_set_extra_library_path)
612 rv = (*tv_set_extra_library_path) (addfile->name);
613 else
614 rv = LDPS_ERR;
615 if (rv != LDPS_OK)
616 return rv;
617 }
618 fflush (NULL);
619 return all_symbols_read_ret;
620}
621
622/* Standard plugin API registerable hook. */
623static enum ld_plugin_status
624oncleanup (void)
625{
626 TV_MESSAGE (LDPL_INFO, "hook called: cleanup.");
627 fflush (NULL);
628 return cleanup_ret;
629}
This page took 0.279047 seconds and 4 git commands to generate.