(root)/
binutils-2.41/
ld/
libdep_plugin.c
       1  /* libdeps plugin for the GNU linker.
       2     Copyright (C) 2020-2023 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  #if BFD_SUPPORTS_PLUGINS
      24  #include "plugin-api.h"
      25  
      26  #include <ctype.h> /* For isspace.  */
      27  
      28  extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
      29  
      30  /* Helper for calling plugin api message function.  */
      31  #define TV_MESSAGE if (tv_message) (*tv_message)
      32  
      33  /* Function pointers to cache hooks passed at onload time.  */
      34  static ld_plugin_register_claim_file tv_register_claim_file = 0;
      35  static ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0;
      36  static ld_plugin_register_cleanup tv_register_cleanup = 0;
      37  static ld_plugin_message tv_message = 0;
      38  static ld_plugin_add_input_library tv_add_input_library = 0;
      39  static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0;
      40  
      41  /* Handle/record information received in a transfer vector entry.  */
      42  static enum ld_plugin_status
      43  parse_tv_tag (struct ld_plugin_tv *tv)
      44  {
      45  #define SETVAR(x) x = tv->tv_u.x
      46    switch (tv->tv_tag)
      47      {
      48        case LDPT_REGISTER_CLAIM_FILE_HOOK:
      49  	SETVAR(tv_register_claim_file);
      50  	break;
      51        case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
      52  	SETVAR(tv_register_all_symbols_read);
      53  	break;
      54        case LDPT_REGISTER_CLEANUP_HOOK:
      55  	SETVAR(tv_register_cleanup);
      56  	break;
      57        case LDPT_MESSAGE:
      58  	SETVAR(tv_message);
      59  	break;
      60        case LDPT_ADD_INPUT_LIBRARY:
      61  	SETVAR(tv_add_input_library);
      62  	break;
      63        case LDPT_SET_EXTRA_LIBRARY_PATH:
      64  	SETVAR(tv_set_extra_library_path);
      65  	break;
      66        default:
      67  	break;
      68      }
      69  #undef SETVAR
      70    return LDPS_OK;
      71  }
      72  
      73  /* Defs for archive parsing.  */
      74  #define ARMAGSIZE	8
      75  typedef struct arhdr
      76  {
      77    char ar_name[16];
      78    char ar_date[12];
      79    char ar_uid[6];
      80    char ar_gid[6];
      81    char ar_mode[8];
      82    char ar_size[10];
      83    char ar_fmag[2];
      84  } arhdr;
      85  
      86  typedef struct linerec
      87  {
      88    struct linerec *next;
      89    char line[];
      90  } linerec;
      91  
      92  #define LIBDEPS "__.LIBDEP/ "
      93  
      94  static linerec *line_head, **line_tail = &line_head;
      95  
      96  static enum ld_plugin_status
      97  get_libdeps (int fd)
      98  {
      99    arhdr ah;
     100    int len;
     101    unsigned long mlen;
     102    size_t amt;
     103    linerec *lr;
     104    enum ld_plugin_status rc = LDPS_NO_SYMS;
     105  
     106    lseek (fd, ARMAGSIZE, SEEK_SET);
     107    for (;;)
     108      {
     109        len = read (fd, (void *) &ah, sizeof (ah));
     110        if (len != sizeof (ah))
     111  	break;
     112        mlen = strtoul (ah.ar_size, NULL, 10);
     113        if (!mlen || strncmp (ah.ar_name, LIBDEPS, sizeof (LIBDEPS)-1))
     114  	{
     115  	  lseek (fd, mlen, SEEK_CUR);
     116  	  continue;
     117  	}
     118        amt = mlen + sizeof (linerec);
     119        if (amt <= mlen)
     120  	return LDPS_ERR;
     121        lr = malloc (amt);
     122        if (!lr)
     123  	return LDPS_ERR;
     124        lr->next = NULL;
     125        len = read (fd, lr->line, mlen);
     126        lr->line[mlen-1] = '\0';
     127        *line_tail = lr;
     128        line_tail = &lr->next;
     129        rc = LDPS_OK;
     130        break;
     131      }
     132    return rc;
     133  }
     134  
     135  /* Turn a string into an argvec.  */
     136  static char **
     137  str2vec (char *in)
     138  {
     139    char **res;
     140    char *s, *first, *end;
     141    char *sq, *dq;
     142    int i;
     143  
     144    end = in + strlen (in);
     145    s = in;
     146    while (isspace ((unsigned char) *s)) s++;
     147    first = s;
     148  
     149    i = 1;
     150    while ((s = strchr (s, ' ')))
     151      {
     152        s++;
     153        i++;
     154      }
     155    res = (char **)malloc ((i+1) * sizeof (char *));
     156    if (!res)
     157      return res;
     158  
     159    i = 0;
     160    sq = NULL;
     161    dq = NULL;
     162    res[0] = first;
     163    for (s = first; *s; s++)
     164      {
     165        if (*s == '\\')
     166  	{
     167  	  memmove (s, s+1, end-s-1);
     168  	  end--;
     169  	}
     170        if (isspace ((unsigned char) *s))
     171  	{
     172  	  if (sq || dq)
     173  	    continue;
     174  	  *s++ = '\0';
     175  	  while (isspace ((unsigned char) *s)) s++;
     176  	  if (*s)
     177  	    res[++i] = s;
     178  	}
     179        if (*s == '\'' && !dq)
     180  	{
     181  	  if (sq)
     182  	    {
     183  	      memmove (sq, sq+1, s-sq-1);
     184  	      memmove (s-2, s+1, end-s-1);
     185  	      end -= 2;
     186  	      s--;
     187  	      sq = NULL;
     188  	    }
     189  	  else
     190  	    {
     191  	      sq = s;
     192  	    }
     193  	}
     194        if (*s == '"' && !sq)
     195  	{
     196  	  if (dq)
     197  	    {
     198  	      memmove (dq, dq+1, s-dq-1);
     199  	      memmove (s-2, s+1, end-s-1);
     200  	      end -= 2;
     201  	      s--;
     202  	      dq = NULL;
     203  	    }
     204  	  else
     205  	    {
     206  	      dq = s;
     207  	    }
     208  	}
     209      }
     210    res[++i] = NULL;
     211    return res;
     212  }
     213  
     214  static char *prevfile;
     215  
     216  /* Standard plugin API registerable hook.  */
     217  static enum ld_plugin_status
     218  onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
     219  {
     220    enum ld_plugin_status rv;
     221  
     222    *claimed = 0;
     223  
     224    /* If we've already seen this file, ignore it.  */
     225    if (prevfile && !strcmp (file->name, prevfile))
     226      return LDPS_OK;
     227  
     228    /* If it's not an archive member, ignore it.  */
     229    if (!file->offset)
     230      return LDPS_OK;
     231  
     232    if (prevfile)
     233      free (prevfile);
     234  
     235    prevfile = strdup (file->name);
     236    if (!prevfile)
     237      return LDPS_ERR;
     238  
     239    /* This hook only gets called on actual object files.
     240     * We have to examine the archive ourselves, to find
     241     * our LIBDEPS member.  */
     242    rv = get_libdeps (file->fd);
     243    if (rv == LDPS_ERR)
     244      return rv;
     245  
     246    if (rv == LDPS_OK)
     247      {
     248        linerec *lr = (linerec *)line_tail;
     249        /* Inform the user/testsuite.  */
     250        TV_MESSAGE (LDPL_INFO, "got deps for library %s: %s",
     251  		  file->name, lr->line);
     252        fflush (NULL);
     253      }
     254  
     255    return LDPS_OK;
     256  }
     257  
     258  /* Standard plugin API registerable hook.  */
     259  static enum ld_plugin_status
     260  onall_symbols_read (void)
     261  {
     262    linerec *lr;
     263    char **vec;
     264    enum ld_plugin_status rv = LDPS_OK;
     265  
     266    while ((lr = line_head))
     267      {
     268        line_head = lr->next;
     269        vec = str2vec (lr->line);
     270        if (vec)
     271  	{
     272  	  int i;
     273  	  for (i = 0; vec[i]; i++)
     274  	    {
     275  	      if (vec[i][0] != '-')
     276  		{
     277  		  TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s",
     278  			      vec[i]);
     279  		  fflush (NULL);
     280  		  continue;
     281  		}
     282  	      if (vec[i][1] == 'l')
     283  		rv = tv_add_input_library (vec[i]+2);
     284  	      else if (vec[i][1] == 'L')
     285  		rv = tv_set_extra_library_path (vec[i]+2);
     286  	      else
     287  		{
     288  		  TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s",
     289  			      vec[i]);
     290  		  fflush (NULL);
     291  		}
     292  	      if (rv != LDPS_OK)
     293  		break;
     294  	    }
     295  	  free (vec);
     296  	}
     297        free (lr);
     298      }
     299    line_tail = NULL;
     300    return rv;
     301  }
     302  
     303  /* Standard plugin API registerable hook.  */
     304  static enum ld_plugin_status
     305  oncleanup (void)
     306  {
     307    if (prevfile)
     308      {
     309        free (prevfile);
     310        prevfile = NULL;
     311      }
     312    if (line_head)
     313      {
     314        linerec *lr;
     315        while ((lr = line_head))
     316  	{
     317  	  line_head = lr->next;
     318  	  free (lr);
     319  	}
     320        line_tail = NULL;
     321      }
     322    return LDPS_OK;
     323  }
     324  
     325  /* Standard plugin API entry point.  */
     326  enum ld_plugin_status
     327  onload (struct ld_plugin_tv *tv)
     328  {
     329    enum ld_plugin_status rv;
     330  
     331    /* This plugin requires a valid tv array.  */
     332    if (!tv)
     333      return LDPS_ERR;
     334  
     335    /* First entry should always be LDPT_MESSAGE, letting us get
     336       hold of it easily so we can send output straight away.  */
     337    if (tv[0].tv_tag == LDPT_MESSAGE)
     338      tv_message = tv[0].tv_u.tv_message;
     339  
     340    do
     341      if ((rv = parse_tv_tag (tv)) != LDPS_OK)
     342        return rv;
     343    while ((tv++)->tv_tag != LDPT_NULL);
     344  
     345    /* Register hooks.  */
     346    if (tv_register_claim_file
     347        && tv_register_all_symbols_read
     348        && tv_register_cleanup)
     349      {
     350        (*tv_register_claim_file) (onclaim_file);
     351        (*tv_register_all_symbols_read) (onall_symbols_read);
     352        (*tv_register_cleanup) (oncleanup);
     353      }
     354    fflush (NULL);
     355    return LDPS_OK;
     356  }
     357  #endif /* BFD_SUPPORTS_PLUGINS */