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