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