xref: /netbsd-src/external/gpl3/binutils/dist/ld/testplug2.c (revision f8cf1a9151c7af1cb0bd8b09c13c66bca599c027)
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-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 
30 extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
31 static enum ld_plugin_status onclaim_file (const struct ld_plugin_input_file *file,
32 				int *claimed);
33 static enum ld_plugin_status onall_symbols_read (void);
34 static 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.  */
40 typedef 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.  */
51 typedef 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.  */
59 typedef 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.  */
70 typedef 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.  */
77 static 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.  */
103 static ld_plugin_register_claim_file tv_register_claim_file = 0;
104 static ld_plugin_register_claim_file_v2 tv_register_claim_file_v2 = 0;
105 static ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0;
106 static ld_plugin_register_cleanup tv_register_cleanup = 0;
107 static ld_plugin_add_symbols tv_add_symbols = 0;
108 static ld_plugin_get_symbols tv_get_symbols = 0;
109 static ld_plugin_get_symbols tv_get_symbols_v2 = 0;
110 static ld_plugin_add_input_file tv_add_input_file = 0;
111 static ld_plugin_message tv_message = 0;
112 static ld_plugin_get_input_file tv_get_input_file = 0;
113 static ld_plugin_get_view tv_get_view = 0;
114 static ld_plugin_release_input_file tv_release_input_file = 0;
115 static ld_plugin_add_input_library tv_add_input_library = 0;
116 static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0;
117 
118 /* Other cached info from the transfer vector.  */
119 static enum ld_plugin_output_file_type linker_output;
120 static const char *output_name;
121 
122 /* Behaviour control flags set by plugin options.  */
123 static enum ld_plugin_status onload_ret = LDPS_OK;
124 static enum ld_plugin_status claim_file_ret = LDPS_OK;
125 static enum ld_plugin_status all_symbols_read_ret = LDPS_OK;
126 static enum ld_plugin_status cleanup_ret = LDPS_OK;
127 static bool register_claimfile_hook = true;
128 static bool register_allsymbolsread_hook = false;
129 static bool register_cleanup_hook = false;
130 static bool dumpresolutions = false;
131 static bool allsymbolsread_silent = false;
132 
133 /* The master list of all claimable/claimed files.  */
134 static claim_file_t *claimfiles_list = NULL;
135 
136 /* We keep a tail pointer for easy linking on the end.  */
137 static claim_file_t **claimfiles_tail_chain_ptr = &claimfiles_list;
138 
139 /* The last claimed file added to the list, for receiving syms.  */
140 static claim_file_t *last_claimfile = NULL;
141 
142 /* The master list of all files to add to the final link.  */
143 static add_file_t *addfiles_list = NULL;
144 
145 /* We keep a tail pointer for easy linking on the end.  */
146 static add_file_t **addfiles_tail_chain_ptr = &addfiles_list;
147 
148 /* Add a new claimfile on the end of the chain.  */
149 static enum ld_plugin_status
150 record_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.  */
170 static enum ld_plugin_status
171 record_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.  */
193 static enum ld_plugin_status
194 parse_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.  */
262 static enum ld_plugin_status
263 record_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.  */
293 static enum ld_plugin_status
294 set_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.  */
310 static enum ld_plugin_status
311 set_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.  */
330 static enum ld_plugin_status
331 parse_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.  */
374 static enum ld_plugin_status
375 parse_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.  */
442 enum ld_plugin_status
443 onload (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/func.c, standalone or in a library.  Its
495      size must be SIZE_OF_FUNC_C bytes.  */
496 #define SIZE_OF_FUNC_C	248
497   if (onload_ret == LDPS_OK
498       && (record_claim_file ("func.c", SIZE_OF_FUNC_C) != LDPS_OK
499 	  || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
500 	  || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK
501 	  || record_claim_file ("libfunc.a", SIZE_OF_FUNC_C) != LDPS_OK
502 	  || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
503 	  || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK))
504     onload_ret = LDPS_ERR;
505 
506   return onload_ret;
507 }
508 
509 char *
510 xstrdup (const char *s)
511 {
512   size_t len = strlen (s) + 1;
513   char *ret = malloc (len + 1);
514   return (char *) memcpy (ret, s, len);
515 }
516 
517 /* Standard plugin API registerable hook.  */
518 static enum ld_plugin_status
519 onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
520 {
521   /* Let's see if we want to claim this file.  */
522   claim_file_t *claimfile = claimfiles_list;
523   size_t len = strlen (file->name);
524   char *name = xstrdup (file->name);
525   char *p = name + len;
526   bool islib;
527 
528   /* Only match the file name without the directory part.  */
529   islib = *p == 'a' && *(p - 1) == '.';
530   for (; p != name; p--)
531     if (IS_DIR_SEPARATOR (*p))
532       {
533 	p++;
534 	break;
535       }
536 
537   while (claimfile)
538     {
539       /* Claim the file only if the file name and size match and don't
540 	 match the whole library.  */
541       if (!strcmp (p, claimfile->file.name)
542 	  && claimfile->file.filesize == file->filesize
543 	  && (!islib || file->offset != 0))
544 	break;
545       claimfile = claimfile->next;
546     }
547 
548   free (name);
549 
550   /* If we decided to claim it, record that fact, and add any symbols
551      that were defined for it by plugin options.  */
552   *claimed = (claimfile != 0);
553   if (claimfile)
554     {
555       claimfile->claimed = true;
556       claimfile->file = *file;
557       if (claimfile->n_syms_used && !tv_add_symbols)
558 	return LDPS_ERR;
559       else if (claimfile->n_syms_used)
560 	return (*tv_add_symbols) (claimfile->file.handle,
561 				claimfile->n_syms_used, claimfile->symbols);
562     }
563 
564   return claim_file_ret;
565 }
566 
567 /* Standard plugin API registerable hook.  */
568 static enum ld_plugin_status
569 onall_symbols_read (void)
570 {
571   static const char *resolutions[] =
572     {
573       "LDPR_UNKNOWN",
574       "LDPR_UNDEF",
575       "LDPR_PREVAILING_DEF",
576       "LDPR_PREVAILING_DEF_IRONLY",
577       "LDPR_PREEMPTED_REG",
578       "LDPR_PREEMPTED_IR",
579       "LDPR_RESOLVED_IR",
580       "LDPR_RESOLVED_EXEC",
581       "LDPR_RESOLVED_DYN",
582       "LDPR_PREVAILING_DEF_IRONLY_EXP",
583     };
584   claim_file_t *claimfile = dumpresolutions ? claimfiles_list : NULL;
585   add_file_t *addfile = addfiles_list;
586   struct ld_plugin_input_file file;
587   const void *view;
588   char buffer[30];
589   int fd;
590   char *filename;
591   if (! allsymbolsread_silent)
592     TV_MESSAGE (LDPL_INFO, "hook called: all symbols read.");
593   for ( ; claimfile; claimfile = claimfile->next)
594     {
595       enum ld_plugin_status rv;
596       int n;
597       if (claimfile->n_syms_used && !tv_get_symbols_v2)
598 	return LDPS_ERR;
599       else if (!claimfile->n_syms_used)
600         continue;
601       else if (!claimfile->file.handle)
602         continue;
603       rv = tv_get_input_file (claimfile->file.handle, &file);
604       if (rv != LDPS_OK)
605 	return rv;
606       TV_MESSAGE (LDPL_INFO, "Input: %s (%s)", file.name,
607 		  claimfile->file.name);
608       rv = tv_get_view (claimfile->file.handle, &view);
609       if (rv != LDPS_OK)
610 	return rv;
611 #define EXPECTED_VIEW "/* The first line of this file must match the expectation of"
612 #define EXPECTED_VIEW_LENGTH (sizeof (EXPECTED_VIEW) - 1)
613       if (file.filesize != SIZE_OF_FUNC_C
614 	  || SIZE_OF_FUNC_C < EXPECTED_VIEW_LENGTH
615 	  || memcmp (view, EXPECTED_VIEW, EXPECTED_VIEW_LENGTH) != 0)
616 	{
617 	  char result[EXPECTED_VIEW_LENGTH + 1];
618 	  memcpy (result, view, sizeof (result));
619 	  result[EXPECTED_VIEW_LENGTH] = '\0';
620 	  TV_MESSAGE (LDPL_INFO, "Incorrect view:");
621 	  TV_MESSAGE (LDPL_INFO, "  Expect: " EXPECTED_VIEW);
622 	  TV_MESSAGE (LDPL_INFO, "  Result: %s", result);
623 	}
624       rv = tv_get_symbols_v2 (claimfile->file.handle, claimfile->n_syms_used,
625 			      claimfile->symbols);
626       if (rv != LDPS_OK)
627 	return rv;
628       for (n = 0; n < claimfile->n_syms_used; n++)
629 	TV_MESSAGE (LDPL_INFO, "Sym: '%s%s%s' Resolution: %s",
630 		    claimfile->symbols[n].name,
631 		    claimfile->symbols[n].version ? "@" : "",
632 		    (claimfile->symbols[n].version
633 		     ? claimfile->symbols[n].version : ""),
634 		    resolutions[claimfile->symbols[n].resolution]);
635       fd = claimfile->file.fd;
636       filename = xstrdup (claimfile->file.name);
637       rv = tv_release_input_file (claimfile->file.handle);
638       if (rv != LDPS_OK)
639 	{
640 	  free (filename);
641 	  return rv;
642 	}
643       if (read (fd, buffer, sizeof (buffer)) >= 0)
644 	{
645 	  TV_MESSAGE (LDPL_FATAL, "Unreleased file descriptor on: %s",
646 		      claimfile->file.name);
647 	  free (filename);
648 	  return LDPS_ERR;
649 	}
650       free (filename);
651     }
652   for ( ; addfile ; addfile = addfile->next)
653     {
654       enum ld_plugin_status rv;
655       if (addfile->type == ADD_LIB && tv_add_input_library)
656 	rv = (*tv_add_input_library) (addfile->name);
657       else if (addfile->type == ADD_FILE && tv_add_input_file)
658 	rv = (*tv_add_input_file) (addfile->name);
659       else if (addfile->type == ADD_DIR && tv_set_extra_library_path)
660 	rv = (*tv_set_extra_library_path) (addfile->name);
661       else
662 	rv = LDPS_ERR;
663       if (rv != LDPS_OK)
664 	return rv;
665     }
666   fflush (NULL);
667   return all_symbols_read_ret;
668 }
669 
670 /* Standard plugin API registerable hook.  */
671 static enum ld_plugin_status
672 oncleanup (void)
673 {
674   TV_MESSAGE (LDPL_INFO, "hook called: cleanup.");
675   fflush (NULL);
676   return cleanup_ret;
677 }
678 #endif /* BFD_SUPPORTS_PLUGINS */
679