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