1/* Test plugin for the GNU linker.  Check non-object IR file as well as
2   get_input_file, get_view, release_input_file and get_symbols interfaces.
3   Copyright (C) 2016-2024 Free Software Foundation, Inc.
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#if BFD_SUPPORTS_PLUGINS
25#include "plugin-api.h"
26#include "filenames.h"
27/* For ARRAY_SIZE macro only - we don't link the library itself.  */
28#include "libiberty.h"
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_CLAIM_FILE_HOOK_V2),
86  ADDENTRY(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK),
87  ADDENTRY(LDPT_REGISTER_CLEANUP_HOOK),
88  ADDENTRY(LDPT_ADD_SYMBOLS),
89  ADDENTRY(LDPT_GET_SYMBOLS),
90  ADDENTRY(LDPT_GET_SYMBOLS_V2),
91  ADDENTRY(LDPT_ADD_INPUT_FILE),
92  ADDENTRY(LDPT_MESSAGE),
93  ADDENTRY(LDPT_GET_INPUT_FILE),
94  ADDENTRY(LDPT_GET_VIEW),
95  ADDENTRY(LDPT_RELEASE_INPUT_FILE),
96  ADDENTRY(LDPT_ADD_INPUT_LIBRARY),
97  ADDENTRY(LDPT_OUTPUT_NAME),
98  ADDENTRY(LDPT_SET_EXTRA_LIBRARY_PATH),
99  ADDENTRY(LDPT_GNU_LD_VERSION)
100};
101
102/* Function pointers to cache hooks passed at onload time.  */
103static ld_plugin_register_claim_file tv_register_claim_file = 0;
104static ld_plugin_register_claim_file_v2 tv_register_claim_file_v2 = 0;
105static ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0;
106static ld_plugin_register_cleanup tv_register_cleanup = 0;
107static ld_plugin_add_symbols tv_add_symbols = 0;
108static ld_plugin_get_symbols tv_get_symbols = 0;
109static ld_plugin_get_symbols tv_get_symbols_v2 = 0;
110static ld_plugin_add_input_file tv_add_input_file = 0;
111static ld_plugin_message tv_message = 0;
112static ld_plugin_get_input_file tv_get_input_file = 0;
113static ld_plugin_get_view tv_get_view = 0;
114static ld_plugin_release_input_file tv_release_input_file = 0;
115static ld_plugin_add_input_library tv_add_input_library = 0;
116static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0;
117
118/* Other cached info from the transfer vector.  */
119static enum ld_plugin_output_file_type linker_output;
120static const char *output_name;
121
122/* Behaviour control flags set by plugin options.  */
123static enum ld_plugin_status onload_ret = LDPS_OK;
124static enum ld_plugin_status claim_file_ret = LDPS_OK;
125static enum ld_plugin_status all_symbols_read_ret = LDPS_OK;
126static enum ld_plugin_status cleanup_ret = LDPS_OK;
127static bool register_claimfile_hook = true;
128static bool register_allsymbolsread_hook = false;
129static bool register_cleanup_hook = false;
130static bool dumpresolutions = false;
131static bool allsymbolsread_silent = false;
132
133/* The master list of all claimable/claimed files.  */
134static claim_file_t *claimfiles_list = NULL;
135
136/* We keep a tail pointer for easy linking on the end.  */
137static claim_file_t **claimfiles_tail_chain_ptr = &claimfiles_list;
138
139/* The last claimed file added to the list, for receiving syms.  */
140static claim_file_t *last_claimfile = NULL;
141
142/* The master list of all files to add to the final link.  */
143static add_file_t *addfiles_list = NULL;
144
145/* We keep a tail pointer for easy linking on the end.  */
146static add_file_t **addfiles_tail_chain_ptr = &addfiles_list;
147
148/* Add a new claimfile on the end of the chain.  */
149static enum ld_plugin_status
150record_claim_file (const char *file, off_t filesize)
151{
152  claim_file_t *newfile;
153
154  newfile = malloc (sizeof *newfile);
155  if (!newfile)
156    return LDPS_ERR;
157  memset (newfile, 0, sizeof *newfile);
158  /* Only setup for now is remembering the name to look for.  */
159  newfile->file.name = file;
160  newfile->file.filesize = filesize;
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/* Add a new addfile on the end of the chain.  */
170static enum ld_plugin_status
171record_add_file (const char *file, addfile_enum_t type)
172{
173  add_file_t *newfile;
174
175  newfile = malloc (sizeof *newfile);
176  if (!newfile)
177    return LDPS_ERR;
178  newfile->next = NULL;
179  newfile->name = file;
180  newfile->type = type;
181  /* Chain it on the end of the list.  */
182  *addfiles_tail_chain_ptr = newfile;
183  addfiles_tail_chain_ptr = &newfile->next;
184  return LDPS_OK;
185}
186
187/* Parse a command-line argument string into a symbol definition.
188   Symbol-strings follow the colon-separated format:
189	NAME:VERSION:def:vis:size:COMDATKEY
190   where the fields in capitals are strings and those in lower
191   case are integers.  We don't allow to specify a resolution as
192   doing so is not meaningful when calling the add symbols hook.  */
193static enum ld_plugin_status
194parse_symdefstr (const char *str, struct ld_plugin_symbol *sym)
195{
196  int n;
197  long long size;
198  const char *colon1, *colon2, *colon5;
199
200  /* Locate the colons separating the first two strings.  */
201  colon1 = strchr (str, ':');
202  if (!colon1)
203    return LDPS_ERR;
204  colon2 = strchr (colon1+1, ':');
205  if (!colon2)
206    return LDPS_ERR;
207  /* Name must not be empty (version may be).  */
208  if (colon1 == str)
209    return LDPS_ERR;
210
211  /* The fifth colon and trailing comdat key string are optional,
212     but the intermediate ones must all be present.  */
213  colon5 = strchr (colon2+1, ':');	/* Actually only third so far.  */
214  if (!colon5)
215    return LDPS_ERR;
216  colon5 = strchr (colon5+1, ':');	/* Hopefully fourth now.  */
217  if (!colon5)
218    return LDPS_ERR;
219  colon5 = strchr (colon5+1, ':');	/* Optional fifth now.  */
220
221  /* Finally we'll use sscanf to parse the numeric fields, then
222     we'll split out the strings which we need to allocate separate
223     storage for anyway so that we can add nul termination.  */
224  n = sscanf (colon2 + 1, "%hhi:%i:%lli", &sym->def, &sym->visibility, &size);
225  if (n != 3)
226    return LDPS_ERR;
227
228  /* Parsed successfully, so allocate strings and fill out fields.  */
229  sym->size = size;
230  sym->unused = 0;
231  sym->section_kind = 0;
232  sym->symbol_type = 0;
233  sym->resolution = LDPR_UNKNOWN;
234  sym->name = malloc (colon1 - str + 1);
235  if (!sym->name)
236    return LDPS_ERR;
237  memcpy (sym->name, str, colon1 - str);
238  sym->name[colon1 - str] = '\0';
239  if (colon2 > (colon1 + 1))
240    {
241      sym->version = malloc (colon2 - colon1);
242      if (!sym->version)
243	return LDPS_ERR;
244      memcpy (sym->version, colon1 + 1, colon2 - (colon1 + 1));
245      sym->version[colon2 - (colon1 + 1)] = '\0';
246    }
247  else
248    sym->version = NULL;
249  if (colon5 && colon5[1])
250    {
251      sym->comdat_key = malloc (strlen (colon5 + 1) + 1);
252      if (!sym->comdat_key)
253	return LDPS_ERR;
254      strcpy (sym->comdat_key, colon5 + 1);
255    }
256  else
257    sym->comdat_key = 0;
258  return LDPS_OK;
259}
260
261/* Record a symbol to be added for the last-added claimfile.  */
262static enum ld_plugin_status
263record_claimed_file_symbol (const char *symdefstr)
264{
265  struct ld_plugin_symbol sym;
266
267  /* Can't add symbols except as belonging to claimed files.  */
268  if (!last_claimfile)
269    return LDPS_ERR;
270
271  /* If string doesn't parse correctly, give an error.  */
272  if (parse_symdefstr (symdefstr, &sym) != LDPS_OK)
273    return LDPS_ERR;
274
275  /* Check for enough space, resize array if needed, and add it.  */
276  if (last_claimfile->n_syms_allocated == last_claimfile->n_syms_used)
277    {
278      int new_n_syms = last_claimfile->n_syms_allocated
279			? 2 * last_claimfile->n_syms_allocated
280			: 10;
281      last_claimfile->symbols = realloc (last_claimfile->symbols,
282			new_n_syms * sizeof *last_claimfile->symbols);
283      if (!last_claimfile->symbols)
284	return LDPS_ERR;
285      last_claimfile->n_syms_allocated = new_n_syms;
286    }
287  last_claimfile->symbols[last_claimfile->n_syms_used++] = sym;
288
289  return LDPS_OK;
290}
291
292/* Records the status to return from one of the registered hooks.  */
293static enum ld_plugin_status
294set_ret_val (const char *whichval, enum ld_plugin_status retval)
295{
296  if (!strcmp ("onload", whichval))
297    onload_ret = retval;
298  else if (!strcmp ("claimfile", whichval))
299    claim_file_ret = retval;
300  else if (!strcmp ("allsymbolsread", whichval))
301    all_symbols_read_ret = retval;
302  else if (!strcmp ("cleanup", whichval))
303    cleanup_ret = retval;
304  else
305    return LDPS_ERR;
306  return LDPS_OK;
307}
308
309/* Records hooks which should be registered.  */
310static enum ld_plugin_status
311set_register_hook (const char *whichhook, bool yesno)
312{
313  if (!strcmp ("claimfile", whichhook))
314    register_claimfile_hook = yesno;
315  else if (!strcmp ("allsymbolsread", whichhook))
316    register_allsymbolsread_hook = yesno;
317  else if (!strcmp ("allsymbolsreadsilent", whichhook))
318    {
319      register_allsymbolsread_hook = yesno;
320      allsymbolsread_silent = true;
321    }
322  else if (!strcmp ("cleanup", whichhook))
323    register_cleanup_hook = yesno;
324  else
325    return LDPS_ERR;
326  return LDPS_OK;
327}
328
329/* Determine type of plugin option and pass to individual parsers.  */
330static enum ld_plugin_status
331parse_option (const char *opt)
332{
333  if (!strncmp ("fatal", opt, 5))
334    {
335      TV_MESSAGE (LDPL_FATAL, "Fatal error");
336      fflush (NULL);
337    }
338  else if (!strncmp ("error", opt, 5))
339    {
340      TV_MESSAGE (LDPL_ERROR, "Error");
341      fflush (NULL);
342    }
343  else if (!strncmp ("warning", opt, 7))
344    {
345      TV_MESSAGE (LDPL_WARNING, "Warning");
346      fflush (NULL);
347    }
348  else if (!strncmp ("fail", opt, 4))
349    return set_ret_val (opt + 4, LDPS_ERR);
350  else if (!strncmp ("pass", opt, 4))
351    return set_ret_val (opt + 4, LDPS_OK);
352  else if (!strncmp ("register", opt, 8))
353    return set_register_hook (opt + 8, true);
354  else if (!strncmp ("noregister", opt, 10))
355    return set_register_hook (opt + 10, false);
356  else if (!strncmp ("claim:", opt, 6))
357    return record_claim_file (opt + 6, 0);
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/* Handle/record information received in a transfer vector entry.  */
374static enum ld_plugin_status
375parse_tv_tag (struct ld_plugin_tv *tv)
376{
377#define SETVAR(x) x = tv->tv_u.x
378  switch (tv->tv_tag)
379    {
380      case LDPT_OPTION:
381	return parse_option (tv->tv_u.tv_string);
382      case LDPT_NULL:
383      case LDPT_GOLD_VERSION:
384      case LDPT_GNU_LD_VERSION:
385      case LDPT_API_VERSION:
386      default:
387	break;
388      case LDPT_OUTPUT_NAME:
389	output_name = tv->tv_u.tv_string;
390	break;
391      case LDPT_LINKER_OUTPUT:
392	linker_output = tv->tv_u.tv_val;
393	break;
394      case LDPT_REGISTER_CLAIM_FILE_HOOK:
395	SETVAR(tv_register_claim_file);
396	break;
397      case LDPT_REGISTER_CLAIM_FILE_HOOK_V2:
398	SETVAR(tv_register_claim_file_v2);
399	break;
400      case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
401	SETVAR(tv_register_all_symbols_read);
402	break;
403      case LDPT_REGISTER_CLEANUP_HOOK:
404	SETVAR(tv_register_cleanup);
405	break;
406      case LDPT_ADD_SYMBOLS:
407	SETVAR(tv_add_symbols);
408	break;
409      case LDPT_GET_SYMBOLS:
410	SETVAR(tv_get_symbols);
411	break;
412      case LDPT_GET_SYMBOLS_V2:
413	tv_get_symbols_v2 = tv->tv_u.tv_get_symbols;
414	break;
415      case LDPT_ADD_INPUT_FILE:
416	SETVAR(tv_add_input_file);
417	break;
418      case LDPT_MESSAGE:
419	SETVAR(tv_message);
420	break;
421      case LDPT_GET_INPUT_FILE:
422	SETVAR(tv_get_input_file);
423	break;
424      case LDPT_GET_VIEW:
425	SETVAR(tv_get_view);
426	break;
427      case LDPT_RELEASE_INPUT_FILE:
428	SETVAR(tv_release_input_file);
429	break;
430      case LDPT_ADD_INPUT_LIBRARY:
431	SETVAR(tv_add_input_library);
432	break;
433      case LDPT_SET_EXTRA_LIBRARY_PATH:
434	SETVAR(tv_set_extra_library_path);
435	break;
436    }
437#undef SETVAR
438  return LDPS_OK;
439}
440
441/* Standard plugin API entry point.  */
442enum ld_plugin_status
443onload (struct ld_plugin_tv *tv)
444{
445  enum ld_plugin_status rv;
446
447  /* This plugin does nothing but dump the tv array.  It would
448     be an error if this function was called without one.  */
449  if (!tv)
450    return LDPS_ERR;
451
452  /* First entry should always be LDPT_MESSAGE, letting us get
453     hold of it easily so we can send output straight away.  */
454  if (tv[0].tv_tag == LDPT_MESSAGE)
455    tv_message = tv[0].tv_u.tv_message;
456
457  do
458    if ((rv = parse_tv_tag (tv)) != LDPS_OK)
459      return rv;
460  while ((tv++)->tv_tag != LDPT_NULL);
461
462  /* Register hooks only if instructed by options.  */
463  if (register_claimfile_hook)
464    {
465      if (!tv_register_claim_file)
466	{
467	  TV_MESSAGE (LDPL_FATAL, "No register_claim_file hook");
468	  fflush (NULL);
469	  return LDPS_ERR;
470	}
471      (*tv_register_claim_file) (onclaim_file);
472    }
473  if (register_allsymbolsread_hook)
474    {
475      if (!tv_register_all_symbols_read)
476	{
477	  TV_MESSAGE (LDPL_FATAL, "No register_all_symbols_read hook");
478	  fflush (NULL);
479	  return LDPS_ERR;
480	}
481      (*tv_register_all_symbols_read) (onall_symbols_read);
482    }
483  if (register_cleanup_hook)
484    {
485      if (!tv_register_cleanup)
486	{
487	  TV_MESSAGE (LDPL_FATAL, "No register_cleanup hook");
488	  fflush (NULL);
489	  return LDPS_ERR;
490	}
491      (*tv_register_cleanup) (oncleanup);
492    }
493
494  /* Claim testsuite/ld-plugin/pr20070b.c, standalone or in a library.
495     Its size must be SIZE_OF_PR20070B_C bytes.  */
496#define SIZE_OF_PR20070B_C	248
497  if (onload_ret == LDPS_OK
498      && (record_claim_file ("pr20070b.c", SIZE_OF_PR20070B_C) != LDPS_OK
499	  || record_claimed_file_symbol ("def::0:0:0") != LDPS_OK
500	  || record_claimed_file_symbol ("weakdef::1:0:0") != LDPS_OK
501	  || record_claimed_file_symbol ("undef::2:0:0") != LDPS_OK
502	  || record_claimed_file_symbol ("weakundef::3:0:0") != LDPS_OK
503	  || record_claimed_file_symbol ("common::4:0:0") != LDPS_OK
504	  || record_claim_file ("libpr20070.a", SIZE_OF_PR20070B_C) != LDPS_OK
505	  || record_claimed_file_symbol ("def::0:0:0") != LDPS_OK
506	  || record_claimed_file_symbol ("weakdef::1:0:0") != LDPS_OK
507	  || record_claimed_file_symbol ("undef::2:0:0") != LDPS_OK
508	  || record_claimed_file_symbol ("weakundef::3:0:0") != LDPS_OK
509	  || record_claimed_file_symbol ("common::4:0:0") != LDPS_OK))
510    onload_ret = LDPS_ERR;
511
512  return onload_ret;
513}
514
515char *
516xstrdup (const char *s)
517{
518  size_t len = strlen (s) + 1;
519  char *ret = malloc (len + 1);
520  return (char *) memcpy (ret, s, len);
521}
522
523/* Standard plugin API registerable hook.  */
524static enum ld_plugin_status
525onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
526{
527  /* Let's see if we want to claim this file.  */
528  claim_file_t *claimfile = claimfiles_list;
529  size_t len = strlen (file->name);
530  char *name = xstrdup (file->name);
531  char *p = name + len;
532  bool islib;
533
534  /* Only match the file name without the directory part.  */
535  islib = *p == 'a' && *(p - 1) == '.';
536  for (; p != name; p--)
537    if (IS_DIR_SEPARATOR (*p))
538      {
539	p++;
540	break;
541      }
542
543  while (claimfile)
544    {
545      /* Claim the file only if the file name and size match and don't
546	 match the whole library.  */
547      if (!strcmp (p, claimfile->file.name)
548	  && claimfile->file.filesize == file->filesize
549	  && (!islib || file->offset != 0))
550	break;
551      claimfile = claimfile->next;
552    }
553
554  free (name);
555
556  /* If we decided to claim it, record that fact, and add any symbols
557     that were defined for it by plugin options.  */
558  *claimed = (claimfile != 0);
559  if (claimfile)
560    {
561      claimfile->claimed = true;
562      claimfile->file = *file;
563      if (claimfile->n_syms_used && !tv_add_symbols)
564	return LDPS_ERR;
565      else if (claimfile->n_syms_used)
566	return (*tv_add_symbols) (claimfile->file.handle,
567				claimfile->n_syms_used, claimfile->symbols);
568    }
569
570  return claim_file_ret;
571}
572
573/* Standard plugin API registerable hook.  */
574static enum ld_plugin_status
575onall_symbols_read (void)
576{
577  static const char *resolutions[] =
578    {
579      "LDPR_UNKNOWN",
580      "LDPR_UNDEF",
581      "LDPR_PREVAILING_DEF",
582      "LDPR_PREVAILING_DEF_IRONLY",
583      "LDPR_PREEMPTED_REG",
584      "LDPR_PREEMPTED_IR",
585      "LDPR_RESOLVED_IR",
586      "LDPR_RESOLVED_EXEC",
587      "LDPR_RESOLVED_DYN",
588      "LDPR_PREVAILING_DEF_IRONLY_EXP",
589    };
590  claim_file_t *claimfile = dumpresolutions ? claimfiles_list : NULL;
591  add_file_t *addfile = addfiles_list;
592  struct ld_plugin_input_file file;
593  const void *view;
594  char buffer[30];
595  int fd;
596  char *filename;
597  if (! allsymbolsread_silent)
598    TV_MESSAGE (LDPL_INFO, "hook called: all symbols read.");
599  for ( ; claimfile; claimfile = claimfile->next)
600    {
601      enum ld_plugin_status rv;
602      int n;
603      if (claimfile->n_syms_used && !tv_get_symbols_v2)
604	return LDPS_ERR;
605      else if (!claimfile->n_syms_used)
606        continue;
607      else if (!claimfile->file.handle)
608        continue;
609      rv = tv_get_input_file (claimfile->file.handle, &file);
610      if (rv != LDPS_OK)
611	return rv;
612      TV_MESSAGE (LDPL_INFO, "Input: %s (%s)", file.name,
613		  claimfile->file.name);
614      rv = tv_get_view (claimfile->file.handle, &view);
615      if (rv != LDPS_OK)
616	return rv;
617#define EXPECTED_VIEW "/* The first line of this file must match the expectation of"
618#define EXPECTED_VIEW_LENGTH (sizeof (EXPECTED_VIEW) - 1)
619      if (file.filesize != SIZE_OF_PR20070B_C
620	  || SIZE_OF_PR20070B_C < EXPECTED_VIEW_LENGTH
621	  || memcmp (view, EXPECTED_VIEW, EXPECTED_VIEW_LENGTH) != 0)
622	{
623	  char result[EXPECTED_VIEW_LENGTH + 1];
624	  memcpy (result, view, sizeof (result));
625	  result[EXPECTED_VIEW_LENGTH] = '\0';
626	  TV_MESSAGE (LDPL_INFO, "Incorrect view:");
627	  TV_MESSAGE (LDPL_INFO, "  Expect: " EXPECTED_VIEW);
628	  TV_MESSAGE (LDPL_INFO, "  Result: %s", result);
629	}
630      rv = tv_get_symbols_v2 (claimfile->file.handle, claimfile->n_syms_used,
631			      claimfile->symbols);
632      if (rv != LDPS_OK)
633	return rv;
634      for (n = 0; n < claimfile->n_syms_used; n++)
635	TV_MESSAGE (LDPL_INFO, "Sym: '%s%s%s' Resolution: %s",
636		    claimfile->symbols[n].name,
637		    claimfile->symbols[n].version ? "@" : "",
638		    (claimfile->symbols[n].version
639		     ? claimfile->symbols[n].version : ""),
640		    resolutions[claimfile->symbols[n].resolution]);
641      fd = claimfile->file.fd;
642      filename = xstrdup (claimfile->file.name);
643      rv = tv_release_input_file (claimfile->file.handle);
644      if (rv != LDPS_OK)
645	{
646	  free (filename);
647	  return rv;
648	}
649      if (read (fd, buffer, sizeof (buffer)) >= 0)
650	{
651	  TV_MESSAGE (LDPL_FATAL, "Unreleased file descriptor on: %s",
652		      claimfile->file.name);
653	  free (filename);
654	  return LDPS_ERR;
655	}
656      free (filename);
657    }
658  for ( ; addfile ; addfile = addfile->next)
659    {
660      enum ld_plugin_status rv;
661      if (addfile->type == ADD_LIB && tv_add_input_library)
662	rv = (*tv_add_input_library) (addfile->name);
663      else if (addfile->type == ADD_FILE && tv_add_input_file)
664	rv = (*tv_add_input_file) (addfile->name);
665      else if (addfile->type == ADD_DIR && tv_set_extra_library_path)
666	rv = (*tv_set_extra_library_path) (addfile->name);
667      else
668	rv = LDPS_ERR;
669      if (rv != LDPS_OK)
670	return rv;
671    }
672  fflush (NULL);
673  return all_symbols_read_ret;
674}
675
676/* Standard plugin API registerable hook.  */
677static enum ld_plugin_status
678oncleanup (void)
679{
680  TV_MESSAGE (LDPL_INFO, "hook called: cleanup.");
681  fflush (NULL);
682  return cleanup_ret;
683}
684#endif /* BFD_SUPPORTS_PLUGINS */
685