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