160777Sobrien/* Test plugin for the GNU linker.
260777Sobrien   Copyright (C) 2010-2024 Free Software Foundation, Inc.
360777Sobrien
489879Sobrien   This file is part of the GNU Binutils.
560777Sobrien
677324Sobrien   This program is free software; you can redistribute it and/or modify
777324Sobrien   it under the terms of the GNU General Public License as published by
877324Sobrien   the Free Software Foundation; either version 3 of the License, or
977324Sobrien   (at your option) any later version.
1077324Sobrien
1177324Sobrien   This program is distributed in the hope that it will be useful,
1260777Sobrien   but WITHOUT ANY WARRANTY; without even the implied warranty of
1360777Sobrien   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1460777Sobrien   GNU General Public License for more details.
1560777Sobrien
1660777Sobrien   You should have received a copy of the GNU General Public License
1760777Sobrien   along with this program; if not, write to the Free Software
1877324Sobrien   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
1977324Sobrien   MA 02110-1301, USA.  */
2077324Sobrien
2160777Sobrien#include "sysdep.h"
2260777Sobrien#include "bfd.h"
2360777Sobrien#if BFD_SUPPORTS_PLUGINS
2460777Sobrien#include "plugin-api.h"
2560777Sobrien/* For ARRAY_SIZE macro only - we don't link the library itself.  */
2660777Sobrien#include "libiberty.h"
2760777Sobrien
2860777Sobrien#include <ctype.h> /* For isdigit.  */
2960777Sobrien
3077324Sobrienextern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
3177324Sobrienstatic enum ld_plugin_status onclaim_file (const struct ld_plugin_input_file *file,
3277324Sobrien				int *claimed);
3360777Sobrienstatic enum ld_plugin_status onall_symbols_read (void);
3460777Sobrienstatic enum ld_plugin_status oncleanup (void);
3560777Sobrien
3689879Sobrien/* Helper for calling plugin api message function.  */
3789879Sobrien#define TV_MESSAGE if (tv_message) (*tv_message)
3889879Sobrien
3960777Sobrien/* Struct for recording files to claim / files claimed.  */
4060777Sobrientypedef struct claim_file
4160777Sobrien{
4260777Sobrien  struct claim_file *next;
4360777Sobrien  struct ld_plugin_input_file file;
4460777Sobrien  bool claimed;
4560777Sobrien  struct ld_plugin_symbol *symbols;
4677324Sobrien  int n_syms_allocated;
4760777Sobrien  int n_syms_used;
4860777Sobrien} claim_file_t;
4960777Sobrien
5060777Sobrien/* Types of things that can be added at all symbols read time.  */
5160777Sobrientypedef enum addfile_enum
5260777Sobrien{
5360777Sobrien  ADD_FILE,
5477324Sobrien  ADD_LIB,
5577324Sobrien  ADD_DIR
5677324Sobrien} addfile_enum_t;
5760777Sobrien
5860777Sobrien/* Struct for recording files to add to final link.  */
5960777Sobrientypedef struct add_file
6060777Sobrien{
6160777Sobrien  struct add_file *next;
6260777Sobrien  const char *name;
6360777Sobrien  addfile_enum_t type;
6460777Sobrien} add_file_t;
6560777Sobrien
6689879Sobrien/* Helper macro for defining array of transfer vector tags and names.  */
6789879Sobrien#define ADDENTRY(tag) { tag, #tag }
6889879Sobrien
6960777Sobrien/* Struct for looking up human-readable versions of tag names.  */
7060777Sobrientypedef struct tag_name
7160777Sobrien{
7260777Sobrien  enum ld_plugin_tag tag;
7360777Sobrien  const char *name;
7460777Sobrien} tag_name_t;
7560777Sobrien
7660777Sobrien/* Array of all known tags and their names.  */
7760777Sobrienstatic const tag_name_t tag_names[] =
7860777Sobrien{
7960777Sobrien  ADDENTRY(LDPT_NULL),
8060777Sobrien  ADDENTRY(LDPT_API_VERSION),
8160777Sobrien  ADDENTRY(LDPT_GOLD_VERSION),
8260777Sobrien  ADDENTRY(LDPT_LINKER_OUTPUT),
8360777Sobrien  ADDENTRY(LDPT_OPTION),
8460777Sobrien  ADDENTRY(LDPT_REGISTER_CLAIM_FILE_HOOK),
8560777Sobrien  ADDENTRY(LDPT_REGISTER_CLAIM_FILE_HOOK_V2),
8660777Sobrien  ADDENTRY(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK),
8760777Sobrien  ADDENTRY(LDPT_REGISTER_CLEANUP_HOOK),
8860777Sobrien  ADDENTRY(LDPT_ADD_SYMBOLS),
8960777Sobrien  ADDENTRY(LDPT_GET_SYMBOLS),
9060777Sobrien  ADDENTRY(LDPT_GET_SYMBOLS_V2),
9160777Sobrien  ADDENTRY(LDPT_ADD_INPUT_FILE),
9260777Sobrien  ADDENTRY(LDPT_MESSAGE),
9360777Sobrien  ADDENTRY(LDPT_GET_INPUT_FILE),
9460777Sobrien  ADDENTRY(LDPT_GET_VIEW),
9560777Sobrien  ADDENTRY(LDPT_RELEASE_INPUT_FILE),
9660777Sobrien  ADDENTRY(LDPT_ADD_INPUT_LIBRARY),
9760777Sobrien  ADDENTRY(LDPT_OUTPUT_NAME),
9860777Sobrien  ADDENTRY(LDPT_SET_EXTRA_LIBRARY_PATH),
9960777Sobrien  ADDENTRY(LDPT_GNU_LD_VERSION)
10060777Sobrien};
10160777Sobrien
10260777Sobrien/* Function pointers to cache hooks passed at onload time.  */
10360777Sobrienstatic ld_plugin_register_claim_file tv_register_claim_file = 0;
10460777Sobrienstatic ld_plugin_register_claim_file_v2 tv_register_claim_file_v2 = 0;
10560777Sobrienstatic ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0;
10660777Sobrienstatic ld_plugin_register_cleanup tv_register_cleanup = 0;
10760777Sobrienstatic ld_plugin_add_symbols tv_add_symbols = 0;
10860777Sobrienstatic ld_plugin_get_symbols tv_get_symbols = 0;
10960777Sobrienstatic ld_plugin_get_symbols tv_get_symbols_v2 = 0;
11060777Sobrienstatic ld_plugin_add_input_file tv_add_input_file = 0;
11160777Sobrienstatic ld_plugin_message tv_message = 0;
11260777Sobrienstatic ld_plugin_get_input_file tv_get_input_file = 0;
11360777Sobrienstatic ld_plugin_get_view tv_get_view = 0;
11460777Sobrienstatic ld_plugin_release_input_file tv_release_input_file = 0;
11560777Sobrienstatic ld_plugin_add_input_library tv_add_input_library = 0;
11660777Sobrienstatic ld_plugin_set_extra_library_path tv_set_extra_library_path = 0;
11760777Sobrien
11860777Sobrien/* Other cached info from the transfer vector.  */
11960777Sobrienstatic enum ld_plugin_output_file_type linker_output;
12060777Sobrienstatic const char *output_name;
12160777Sobrien
12260777Sobrien/* Behaviour control flags set by plugin options.  */
12360777Sobrienstatic enum ld_plugin_status onload_ret = LDPS_OK;
12460777Sobrienstatic enum ld_plugin_status claim_file_ret = LDPS_OK;
12560777Sobrienstatic enum ld_plugin_status all_symbols_read_ret = LDPS_OK;
12660777Sobrienstatic enum ld_plugin_status cleanup_ret = LDPS_OK;
12760777Sobrienstatic bool register_claimfile_hook = false;
12860777Sobrienstatic bool register_allsymbolsread_hook = false;
12960777Sobrienstatic bool register_cleanup_hook = false;
13060777Sobrienstatic bool dumpresolutions = false;
13160777Sobrien
13260777Sobrien/* The master list of all claimable/claimed files.  */
13360777Sobrienstatic claim_file_t *claimfiles_list = NULL;
13460777Sobrien
13560777Sobrien/* We keep a tail pointer for easy linking on the end.  */
13660777Sobrienstatic claim_file_t **claimfiles_tail_chain_ptr = &claimfiles_list;
13760777Sobrien
13860777Sobrien/* The last claimed file added to the list, for receiving syms.  */
13960777Sobrienstatic claim_file_t *last_claimfile = NULL;
14060777Sobrien
14160777Sobrien/* The master list of all files to add to the final link.  */
14260777Sobrienstatic add_file_t *addfiles_list = NULL;
14360777Sobrien
14460777Sobrien/* We keep a tail pointer for easy linking on the end.  */
14560777Sobrienstatic add_file_t **addfiles_tail_chain_ptr = &addfiles_list;
14660777Sobrien
14760777Sobrien/* Number of bytes read in claim file before deciding if the file can be
14860777Sobrien   claimed.  */
14960777Sobrienstatic int bytes_to_read_before_claim = 0;
15060777Sobrien
15160777Sobrien/* Add a new claimfile on the end of the chain.  */
15260777Sobrienstatic enum ld_plugin_status
15360777Sobrienrecord_claim_file (const char *file)
15460777Sobrien{
15560777Sobrien  claim_file_t *newfile;
15660777Sobrien
15760777Sobrien  newfile = malloc (sizeof *newfile);
15860777Sobrien  if (!newfile)
15960777Sobrien    return LDPS_ERR;
16060777Sobrien  memset (newfile, 0, sizeof *newfile);
16160777Sobrien  /* Only setup for now is remembering the name to look for.  */
16260777Sobrien  newfile->file.name = file;
16360777Sobrien  /* Chain it on the end of the list.  */
16460777Sobrien  *claimfiles_tail_chain_ptr = newfile;
16560777Sobrien  claimfiles_tail_chain_ptr = &newfile->next;
16660777Sobrien  /* Record it as active for receiving symbols to register.  */
16760777Sobrien  last_claimfile = newfile;
16860777Sobrien  return LDPS_OK;
16960777Sobrien}
17060777Sobrien
17160777Sobrien/* How many bytes to read before claiming (or not) an input file.  */
17260777Sobrienstatic enum ld_plugin_status
17360777Sobrienrecord_read_length (const char *length)
17460777Sobrien{
17560777Sobrien  const char *tmp;
17660777Sobrien
17760777Sobrien  tmp = length;
17860777Sobrien  while (*tmp != '\0' && isdigit (*tmp))
17960777Sobrien    ++tmp;
18060777Sobrien  if (*tmp != '\0' || *length == '\0')
18160777Sobrien    return LDPS_ERR;
18260777Sobrien
18360777Sobrien  bytes_to_read_before_claim = atoi (length);
18460777Sobrien  return LDPS_OK;
18560777Sobrien}
18660777Sobrien
18760777Sobrien/* Add a new addfile on the end of the chain.  */
18860777Sobrienstatic enum ld_plugin_status
18989879Sobrienrecord_add_file (const char *file, addfile_enum_t type)
19089879Sobrien{
19189879Sobrien  add_file_t *newfile;
19277324Sobrien
19377324Sobrien  newfile = malloc (sizeof *newfile);
19477324Sobrien  if (!newfile)
19577324Sobrien    return LDPS_ERR;
19677324Sobrien  newfile->next = NULL;
19777324Sobrien  newfile->name = file;
19860777Sobrien  newfile->type = type;
19960777Sobrien  /* Chain it on the end of the list.  */
20060777Sobrien  *addfiles_tail_chain_ptr = newfile;
20160777Sobrien  addfiles_tail_chain_ptr = &newfile->next;
20260777Sobrien  return LDPS_OK;
20360777Sobrien}
20460777Sobrien
20560777Sobrien/* Parse a command-line argument string into a symbol definition.
20660777Sobrien   Symbol-strings follow the colon-separated format:
20760777Sobrien	NAME:VERSION:def:vis:size:COMDATKEY
20860777Sobrien   where the fields in capitals are strings and those in lower
20960777Sobrien   case are integers.  We don't allow to specify a resolution as
21077324Sobrien   doing so is not meaningful when calling the add symbols hook.  */
21177324Sobrienstatic enum ld_plugin_status
21277324Sobrienparse_symdefstr (const char *str, struct ld_plugin_symbol *sym)
21360777Sobrien{
21460777Sobrien  int n;
21560777Sobrien  long long size;
21660777Sobrien  const char *colon1, *colon2, *colon5;
21760777Sobrien
21860777Sobrien  /* Locate the colons separating the first two strings.  */
21960777Sobrien  colon1 = strchr (str, ':');
22060777Sobrien  if (!colon1)
22160777Sobrien    return LDPS_ERR;
22260777Sobrien  colon2 = strchr (colon1+1, ':');
22360777Sobrien  if (!colon2)
22460777Sobrien    return LDPS_ERR;
22577324Sobrien  /* Name must not be empty (version may be).  */
22677324Sobrien  if (colon1 == str)
22777324Sobrien    return LDPS_ERR;
22860777Sobrien
22960777Sobrien  /* The fifth colon and trailing comdat key string are optional,
23077324Sobrien     but the intermediate ones must all be present.  */
23177324Sobrien  colon5 = strchr (colon2+1, ':');	/* Actually only third so far.  */
23277324Sobrien  if (!colon5)
23377324Sobrien    return LDPS_ERR;
23489879Sobrien  colon5 = strchr (colon5+1, ':');	/* Hopefully fourth now.  */
23589879Sobrien  if (!colon5)
23689879Sobrien    return LDPS_ERR;
23777324Sobrien  colon5 = strchr (colon5+1, ':');	/* Optional fifth now.  */
23877324Sobrien
23977324Sobrien  /* Finally we'll use sscanf to parse the numeric fields, then
24077324Sobrien     we'll split out the strings which we need to allocate separate
24177324Sobrien     storage for anyway so that we can add nul termination.  */
24277324Sobrien  n = sscanf (colon2 + 1, "%hhi:%i:%lli", &sym->def, &sym->visibility, &size);
24377324Sobrien  if (n != 3)
24477324Sobrien    return LDPS_ERR;
24577324Sobrien
24689879Sobrien  /* Parsed successfully, so allocate strings and fill out fields.  */
24789879Sobrien  sym->size = size;
24889879Sobrien  sym->unused = 0;
24989879Sobrien  sym->section_kind = 0;
25089879Sobrien  sym->symbol_type = 0;
25189879Sobrien  sym->resolution = LDPR_UNKNOWN;
25289879Sobrien  sym->name = malloc (colon1 - str + 1);
25389879Sobrien  if (!sym->name)
25489879Sobrien    return LDPS_ERR;
25589879Sobrien  memcpy (sym->name, str, colon1 - str);
25689879Sobrien  sym->name[colon1 - str] = '\0';
25789879Sobrien  if (colon2 > (colon1 + 1))
258    {
259      sym->version = malloc (colon2 - colon1);
260      if (!sym->version)
261	return LDPS_ERR;
262      memcpy (sym->version, colon1 + 1, colon2 - (colon1 + 1));
263      sym->version[colon2 - (colon1 + 1)] = '\0';
264    }
265  else
266    sym->version = NULL;
267  if (colon5 && colon5[1])
268    {
269      sym->comdat_key = malloc (strlen (colon5 + 1) + 1);
270      if (!sym->comdat_key)
271	return LDPS_ERR;
272      strcpy (sym->comdat_key, colon5 + 1);
273    }
274  else
275    sym->comdat_key = 0;
276  return LDPS_OK;
277}
278
279/* Record a symbol to be added for the last-added claimfile.  */
280static enum ld_plugin_status
281record_claimed_file_symbol (const char *symdefstr)
282{
283  struct ld_plugin_symbol sym;
284
285  /* Can't add symbols except as belonging to claimed files.  */
286  if (!last_claimfile)
287    return LDPS_ERR;
288
289  /* If string doesn't parse correctly, give an error.  */
290  if (parse_symdefstr (symdefstr, &sym) != LDPS_OK)
291    return LDPS_ERR;
292
293  /* Check for enough space, resize array if needed, and add it.  */
294  if (last_claimfile->n_syms_allocated == last_claimfile->n_syms_used)
295    {
296      int new_n_syms = last_claimfile->n_syms_allocated
297			? 2 * last_claimfile->n_syms_allocated
298			: 10;
299      last_claimfile->symbols = realloc (last_claimfile->symbols,
300			new_n_syms * sizeof *last_claimfile->symbols);
301      if (!last_claimfile->symbols)
302	return LDPS_ERR;
303      last_claimfile->n_syms_allocated = new_n_syms;
304    }
305  last_claimfile->symbols[last_claimfile->n_syms_used++] = sym;
306
307  return LDPS_OK;
308}
309
310/* Records the status to return from one of the registered hooks.  */
311static enum ld_plugin_status
312set_ret_val (const char *whichval, enum ld_plugin_status retval)
313{
314  if (!strcmp ("onload", whichval))
315    onload_ret = retval;
316  else if (!strcmp ("claimfile", whichval))
317    claim_file_ret = retval;
318  else if (!strcmp ("allsymbolsread", whichval))
319    all_symbols_read_ret = retval;
320  else if (!strcmp ("cleanup", whichval))
321    cleanup_ret = retval;
322  else
323    return LDPS_ERR;
324  return LDPS_OK;
325}
326
327/* Records hooks which should be registered.  */
328static enum ld_plugin_status
329set_register_hook (const char *whichhook, bool yesno)
330{
331  if (!strcmp ("claimfile", whichhook))
332    register_claimfile_hook = yesno;
333  else if (!strcmp ("allsymbolsread", whichhook))
334    register_allsymbolsread_hook = yesno;
335  else if (!strcmp ("cleanup", whichhook))
336    register_cleanup_hook = yesno;
337  else
338    return LDPS_ERR;
339  return LDPS_OK;
340}
341
342/* Determine type of plugin option and pass to individual parsers.  */
343static enum ld_plugin_status
344parse_option (const char *opt)
345{
346  if (!strncmp ("fail", opt, 4))
347    return set_ret_val (opt + 4, LDPS_ERR);
348  else if (!strncmp ("pass", opt, 4))
349    return set_ret_val (opt + 4, LDPS_OK);
350  else if (!strncmp ("register", opt, 8))
351    return set_register_hook (opt + 8, true);
352  else if (!strncmp ("noregister", opt, 10))
353    return set_register_hook (opt + 10, false);
354  else if (!strncmp ("claim:", opt, 6))
355    return record_claim_file (opt + 6);
356  else if (!strncmp ("read:", opt, 5))
357    return record_read_length (opt + 5);
358  else if (!strncmp ("sym:", opt, 4))
359    return record_claimed_file_symbol (opt + 4);
360  else if (!strncmp ("add:", opt, 4))
361    return record_add_file (opt + 4, ADD_FILE);
362  else if (!strncmp ("lib:", opt, 4))
363    return record_add_file (opt + 4, ADD_LIB);
364  else if (!strncmp ("dir:", opt, 4))
365    return record_add_file (opt + 4, ADD_DIR);
366  else if (!strcmp ("dumpresolutions", opt))
367    dumpresolutions = true;
368  else
369    return LDPS_ERR;
370  return LDPS_OK;
371}
372
373/* Output contents of transfer vector array entry in human-readable form.  */
374static void
375dump_tv_tag (size_t n, struct ld_plugin_tv *tv)
376{
377  size_t tag;
378  char unknownbuf[40];
379  const char *name;
380
381  for (tag = 0; tag < ARRAY_SIZE (tag_names); tag++)
382    if (tag_names[tag].tag == tv->tv_tag)
383      break;
384  sprintf (unknownbuf, "unknown tag #%d", tv->tv_tag);
385  name = (tag < ARRAY_SIZE (tag_names)) ? tag_names[tag].name : unknownbuf;
386  switch (tv->tv_tag)
387    {
388      case LDPT_OPTION:
389      case LDPT_OUTPUT_NAME:
390	TV_MESSAGE (LDPL_INFO, "tv[%d]: %s '%s'", n, name,
391		    tv->tv_u.tv_string);
392        break;
393      case LDPT_REGISTER_CLAIM_FILE_HOOK:
394      case LDPT_REGISTER_CLAIM_FILE_HOOK_V2:
395      case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
396      case LDPT_REGISTER_CLEANUP_HOOK:
397      case LDPT_ADD_SYMBOLS:
398      case LDPT_GET_SYMBOLS:
399      case LDPT_GET_SYMBOLS_V2:
400      case LDPT_ADD_INPUT_FILE:
401      case LDPT_MESSAGE:
402      case LDPT_GET_INPUT_FILE:
403      case LDPT_GET_VIEW:
404      case LDPT_RELEASE_INPUT_FILE:
405      case LDPT_ADD_INPUT_LIBRARY:
406      case LDPT_SET_EXTRA_LIBRARY_PATH:
407	TV_MESSAGE (LDPL_INFO, "tv[%d]: %s func@0x%p", n, name,
408		    (void *)(tv->tv_u.tv_message));
409        break;
410      case LDPT_NULL:
411      case LDPT_API_VERSION:
412      case LDPT_GOLD_VERSION:
413      case LDPT_LINKER_OUTPUT:
414      case LDPT_GNU_LD_VERSION:
415      default:
416	TV_MESSAGE (LDPL_INFO, "tv[%d]: %s value %W (%d)", n, name,
417		    (bfd_vma)tv->tv_u.tv_val, tv->tv_u.tv_val);
418	break;
419    }
420}
421
422/* Handle/record information received in a transfer vector entry.  */
423static enum ld_plugin_status
424parse_tv_tag (struct ld_plugin_tv *tv)
425{
426#define SETVAR(x) x = tv->tv_u.x
427  switch (tv->tv_tag)
428    {
429      case LDPT_OPTION:
430	return parse_option (tv->tv_u.tv_string);
431      case LDPT_NULL:
432      case LDPT_GOLD_VERSION:
433      case LDPT_GNU_LD_VERSION:
434      case LDPT_API_VERSION:
435      default:
436	break;
437      case LDPT_OUTPUT_NAME:
438	output_name = tv->tv_u.tv_string;
439	break;
440      case LDPT_LINKER_OUTPUT:
441	linker_output = tv->tv_u.tv_val;
442	break;
443      case LDPT_REGISTER_CLAIM_FILE_HOOK:
444	SETVAR(tv_register_claim_file);
445	break;
446      case LDPT_REGISTER_CLAIM_FILE_HOOK_V2:
447	SETVAR(tv_register_claim_file_v2);
448	break;
449      case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
450	SETVAR(tv_register_all_symbols_read);
451	break;
452      case LDPT_REGISTER_CLEANUP_HOOK:
453	SETVAR(tv_register_cleanup);
454	break;
455      case LDPT_ADD_SYMBOLS:
456	SETVAR(tv_add_symbols);
457	break;
458      case LDPT_GET_SYMBOLS:
459	SETVAR(tv_get_symbols);
460	break;
461      case LDPT_GET_SYMBOLS_V2:
462	tv_get_symbols_v2 = tv->tv_u.tv_get_symbols;
463	break;
464      case LDPT_ADD_INPUT_FILE:
465	SETVAR(tv_add_input_file);
466	break;
467      case LDPT_MESSAGE:
468	SETVAR(tv_message);
469	break;
470      case LDPT_GET_INPUT_FILE:
471	SETVAR(tv_get_input_file);
472	break;
473      case LDPT_GET_VIEW:
474	SETVAR(tv_get_view);
475	break;
476      case LDPT_RELEASE_INPUT_FILE:
477	SETVAR(tv_release_input_file);
478	break;
479      case LDPT_ADD_INPUT_LIBRARY:
480	SETVAR(tv_add_input_library);
481	break;
482      case LDPT_SET_EXTRA_LIBRARY_PATH:
483	SETVAR(tv_set_extra_library_path);
484	break;
485    }
486#undef SETVAR
487  return LDPS_OK;
488}
489
490/* Record any useful information in transfer vector entry and display
491   it in human-readable form using the plugin API message() callback.  */
492enum ld_plugin_status
493parse_and_dump_tv_tag (size_t n, struct ld_plugin_tv *tv)
494{
495  enum ld_plugin_status rv = parse_tv_tag (tv);
496  dump_tv_tag (n, tv);
497  return rv;
498}
499
500/* Standard plugin API entry point.  */
501enum ld_plugin_status
502onload (struct ld_plugin_tv *tv)
503{
504  size_t n = 0;
505  enum ld_plugin_status rv;
506
507  /* This plugin does nothing but dump the tv array.  It would
508     be an error if this function was called without one.  */
509  if (!tv)
510    return LDPS_ERR;
511
512  /* First entry should always be LDPT_MESSAGE, letting us get
513     hold of it easily so we can send output straight away.  */
514  if (tv[0].tv_tag == LDPT_MESSAGE)
515    tv_message = tv[0].tv_u.tv_message;
516
517  fflush (NULL);
518  TV_MESSAGE (LDPL_INFO, "Hello from testplugin.");
519
520  do
521    if ((rv = parse_and_dump_tv_tag (n++, tv)) != LDPS_OK)
522      return rv;
523  while ((tv++)->tv_tag != LDPT_NULL);
524
525  /* Register hooks only if instructed by options.  */
526  if (register_claimfile_hook)
527    {
528      if (!tv_register_claim_file)
529	{
530	  TV_MESSAGE (LDPL_FATAL, "No register_claim_file hook");
531	  fflush (NULL);
532	  return LDPS_ERR;
533	}
534      (*tv_register_claim_file) (onclaim_file);
535    }
536  if (register_allsymbolsread_hook)
537    {
538      if (!tv_register_all_symbols_read)
539	{
540	  TV_MESSAGE (LDPL_FATAL, "No register_all_symbols_read hook");
541	  fflush (NULL);
542	  return LDPS_ERR;
543	}
544      (*tv_register_all_symbols_read) (onall_symbols_read);
545    }
546  if (register_cleanup_hook)
547    {
548      if (!tv_register_cleanup)
549	{
550	  TV_MESSAGE (LDPL_FATAL, "No register_cleanup hook");
551	  fflush (NULL);
552	  return LDPS_ERR;
553	}
554      (*tv_register_cleanup) (oncleanup);
555    }
556  fflush (NULL);
557  return onload_ret;
558}
559
560/* Standard plugin API registerable hook.  */
561static enum ld_plugin_status
562onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
563{
564  /* Possible read of some bytes out of the input file into a buffer.  This
565     simulates a plugin that reads some file content in order to decide if
566     the file should be claimed or not.  */
567  if (bytes_to_read_before_claim > 0)
568    {
569      char *buffer = malloc (bytes_to_read_before_claim);
570
571      if (buffer == NULL)
572        return LDPS_ERR;
573      if (read (file->fd, buffer, bytes_to_read_before_claim) < 0)
574        return LDPS_ERR;
575      free (buffer);
576    }
577
578  /* Let's see if we want to claim this file.  */
579  claim_file_t *claimfile = claimfiles_list;
580  while (claimfile)
581    {
582      if (!strcmp (file->name, claimfile->file.name))
583	break;
584      claimfile = claimfile->next;
585    }
586
587  /* Inform the user/testsuite.  */
588  TV_MESSAGE (LDPL_INFO, "hook called: claim_file %s [@%ld/%ld] %s",
589	      file->name, (long)file->offset, (long)file->filesize,
590	      claimfile ? "CLAIMED" : "not claimed");
591  fflush (NULL);
592
593  /* If we decided to claim it, record that fact, and add any symbols
594     that were defined for it by plugin options.  */
595  *claimed = (claimfile != 0);
596  if (claimfile)
597    {
598      claimfile->claimed = true;
599      claimfile->file = *file;
600      if (claimfile->n_syms_used && !tv_add_symbols)
601	return LDPS_ERR;
602      else if (claimfile->n_syms_used)
603	return (*tv_add_symbols) (claimfile->file.handle,
604				claimfile->n_syms_used, claimfile->symbols);
605    }
606
607  return claim_file_ret;
608}
609
610/* Standard plugin API registerable hook.  */
611static enum ld_plugin_status
612onall_symbols_read (void)
613{
614  static const char *resolutions[] =
615    {
616      "LDPR_UNKNOWN",
617      "LDPR_UNDEF",
618      "LDPR_PREVAILING_DEF",
619      "LDPR_PREVAILING_DEF_IRONLY",
620      "LDPR_PREEMPTED_REG",
621      "LDPR_PREEMPTED_IR",
622      "LDPR_RESOLVED_IR",
623      "LDPR_RESOLVED_EXEC",
624      "LDPR_RESOLVED_DYN",
625      "LDPR_PREVAILING_DEF_IRONLY_EXP",
626    };
627  claim_file_t *claimfile = dumpresolutions ? claimfiles_list : NULL;
628  add_file_t *addfile = addfiles_list;
629  TV_MESSAGE (LDPL_INFO, "hook called: all symbols read.");
630  for ( ; claimfile; claimfile = claimfile->next)
631    {
632      enum ld_plugin_status rv;
633      int n;
634      if (claimfile->n_syms_used && !tv_get_symbols_v2)
635	return LDPS_ERR;
636      else if (!claimfile->n_syms_used)
637        continue;
638      rv = tv_get_symbols_v2 (claimfile->file.handle, claimfile->n_syms_used,
639			      claimfile->symbols);
640      if (rv != LDPS_OK)
641	return rv;
642      for (n = 0; n < claimfile->n_syms_used; n++)
643	TV_MESSAGE (LDPL_INFO, "Sym: '%s%s%s' Resolution: %s",
644		    claimfile->symbols[n].name,
645		    claimfile->symbols[n].version ? "@" : "",
646		    (claimfile->symbols[n].version
647		     ? claimfile->symbols[n].version : ""),
648		    resolutions[claimfile->symbols[n].resolution]);
649    }
650  for ( ; addfile ; addfile = addfile->next)
651    {
652      enum ld_plugin_status rv;
653      if (addfile->type == ADD_LIB && tv_add_input_library)
654	rv = (*tv_add_input_library) (addfile->name);
655      else if (addfile->type == ADD_FILE && tv_add_input_file)
656	rv = (*tv_add_input_file) (addfile->name);
657      else if (addfile->type == ADD_DIR && tv_set_extra_library_path)
658	rv = (*tv_set_extra_library_path) (addfile->name);
659      else
660	rv = LDPS_ERR;
661      if (rv != LDPS_OK)
662	return rv;
663    }
664  fflush (NULL);
665  return all_symbols_read_ret;
666}
667
668/* Standard plugin API registerable hook.  */
669static enum ld_plugin_status
670oncleanup (void)
671{
672  TV_MESSAGE (LDPL_INFO, "hook called: cleanup.");
673  fflush (NULL);
674  return cleanup_ret;
675}
676#endif /* BFD_SUPPORTS_PLUGINS */
677