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