(root)/
make-4.4/
src/
load.c
       1  /* Loading dynamic objects for GNU Make.
       2  Copyright (C) 2012-2022 Free Software Foundation, Inc.
       3  This file is part of GNU Make.
       4  
       5  GNU Make is free software; you can redistribute it and/or modify it under the
       6  terms of the GNU General Public License as published by the Free Software
       7  Foundation; either version 3 of the License, or (at your option) any later
       8  version.
       9  
      10  GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
      11  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      12  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      13  
      14  You should have received a copy of the GNU General Public License along with
      15  this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  #include "makeint.h"
      18  
      19  #if MAKE_LOAD
      20  
      21  #include <string.h>
      22  #include <ctype.h>
      23  #include <stdlib.h>
      24  #include <dlfcn.h>
      25  #include <errno.h>
      26  
      27  #define SYMBOL_EXTENSION        "_gmk_setup"
      28  
      29  #include "debug.h"
      30  #include "filedef.h"
      31  #include "variable.h"
      32  
      33  /* Tru64 V4.0 does not have this flag */
      34  #ifndef RTLD_GLOBAL
      35  # define RTLD_GLOBAL 0
      36  #endif
      37  
      38  struct load_list
      39    {
      40      struct load_list *next;
      41      const char *name;
      42      void *dlp;
      43    };
      44  
      45  static struct load_list *loaded_syms = NULL;
      46  
      47  static load_func_t
      48  load_object (const floc *flocp, int noerror, const char *ldname,
      49               const char *symname)
      50  {
      51    static void *global_dl = NULL;
      52    load_func_t symp;
      53  
      54    if (! global_dl)
      55      {
      56        global_dl = dlopen (NULL, RTLD_NOW|RTLD_GLOBAL);
      57        if (! global_dl)
      58          {
      59            const char *err = dlerror ();
      60            OS (fatal, flocp, _("Failed to open global symbol table: %s"), err);
      61          }
      62      }
      63  
      64    symp = (load_func_t) dlsym (global_dl, symname);
      65    if (! symp)
      66      {
      67        struct load_list *new;
      68        void *dlp = NULL;
      69  
      70      /* If the path has no "/", try the current directory first.  */
      71        if (! strchr (ldname, '/')
      72  #ifdef HAVE_DOS_PATHS
      73            && ! strchr (ldname, '\\')
      74  #endif
      75           )
      76          dlp = dlopen (concat (2, "./", ldname), RTLD_LAZY|RTLD_GLOBAL);
      77  
      78        /* If we haven't opened it yet, try the default search path.  */
      79        if (! dlp)
      80          dlp = dlopen (ldname, RTLD_LAZY|RTLD_GLOBAL);
      81  
      82        /* Still no?  Then fail.  */
      83        if (! dlp)
      84          {
      85            const char *err = dlerror ();
      86            if (noerror)
      87              DB (DB_BASIC, ("%s\n", err));
      88            else
      89              OS (error, flocp, "%s", err);
      90            return NULL;
      91          }
      92  
      93        DB (DB_VERBOSE, (_("Loaded shared object %s\n"), ldname));
      94  
      95        /* Assert that the GPL license symbol is defined.  */
      96        symp = (load_func_t) dlsym (dlp, "plugin_is_GPL_compatible");
      97        if (! symp)
      98          OS (fatal, flocp,
      99               _("Loaded object %s is not declared to be GPL compatible"),
     100               ldname);
     101  
     102        symp = (load_func_t) dlsym (dlp, symname);
     103        if (! symp)
     104          {
     105            const char *err = dlerror ();
     106            OSSS (fatal, flocp, _("Failed to load symbol %s from %s: %s"),
     107                  symname, ldname, err);
     108          }
     109  
     110        /* Add this symbol to a trivial lookup table.  This is not efficient but
     111           it's highly unlikely we'll be loading lots of objects, and we only
     112           need it to look them up on unload, if we rebuild them.  */
     113        new = xmalloc (sizeof (struct load_list));
     114        new->name = xstrdup (ldname);
     115        new->dlp = dlp;
     116        new->next = loaded_syms;
     117        loaded_syms = new;
     118      }
     119  
     120    return symp;
     121  }
     122  
     123  int
     124  load_file (const floc *flocp, struct file *file, int noerror)
     125  {
     126    const char *ldname = file->name;
     127    size_t nmlen = strlen (ldname);
     128    char *new = alloca (nmlen + CSTRLEN (SYMBOL_EXTENSION) + 1);
     129    char *symname = NULL;
     130    const char *fp;
     131    int r;
     132    load_func_t symp;
     133  
     134    /* Break the input into an object file name and a symbol name.  If no symbol
     135       name was provided, compute one from the object file name.  */
     136    fp = strchr (ldname, '(');
     137    if (fp)
     138      {
     139        const char *ep;
     140  
     141        /* There's an open paren, so see if there's a close paren: if so use
     142           that as the symbol name.  We can't have whitespace: it would have
     143           been chopped up before this function is called.  */
     144        ep = strchr (fp+1, ')');
     145        if (ep && ep[1] == '\0')
     146          {
     147            size_t l = fp - ldname;
     148  
     149            ++fp;
     150            if (fp == ep)
     151              OS (fatal, flocp, _("Empty symbol name for load: %s"), ldname);
     152  
     153            /* Make a copy of the ldname part.  */
     154            memcpy (new, ldname, l);
     155            new[l] = '\0';
     156            ldname = new;
     157            nmlen = l;
     158  
     159            /* Make a copy of the symbol name part.  */
     160            symname = new + l + 1;
     161            memcpy (symname, fp, ep - fp);
     162            symname[ep - fp] = '\0';
     163          }
     164      }
     165  
     166    /* Make sure this name is in the string cache.  */
     167    ldname = file->name = strcache_add (ldname);
     168  
     169    /* If this object has been loaded, we're done: return -1 to ensure make does
     170       not rebuild again.  If a rebuild is allowed it was set up when this
     171       object was initially loaded.  */
     172    file = lookup_file (ldname);
     173    if (file && file->loaded)
     174      return -1;
     175  
     176    /* If we didn't find a symbol name yet, construct it from the ldname.  */
     177    if (! symname)
     178      {
     179        char *p = new;
     180  
     181        fp = strrchr (ldname, '/');
     182  #ifdef HAVE_DOS_PATHS
     183        if (fp)
     184          {
     185            const char *fp2 = strchr (fp, '\\');
     186  
     187            if (fp2 > fp)
     188              fp = fp2;
     189          }
     190        else
     191          fp = strrchr (ldname, '\\');
     192        /* The (improbable) case of d:foo.  */
     193        if (fp && *fp && fp[1] == ':')
     194          fp++;
     195  #endif
     196        if (!fp)
     197          fp = ldname;
     198        else
     199          ++fp;
     200        while (isalnum ((unsigned char) *fp) || *fp == '_')
     201          *(p++) = *(fp++);
     202        strcpy (p, SYMBOL_EXTENSION);
     203        symname = new;
     204      }
     205  
     206    DB (DB_VERBOSE, (_("Loading symbol %s from %s\n"), symname, ldname));
     207  
     208    /* Load it!  */
     209    symp = load_object (flocp, noerror, ldname, symname);
     210    if (! symp)
     211      return 0;
     212  
     213    /* Invoke the symbol.  */
     214    r = (*symp) (flocp);
     215  
     216    /* If the load didn't fail, add the file to the .LOADED variable.  */
     217    if (r)
     218      do_variable_definition(flocp, ".LOADED", ldname, o_file, f_append_value, 0);
     219  
     220    return r;
     221  }
     222  
     223  int
     224  unload_file (const char *name)
     225  {
     226    int rc = 0;
     227    struct load_list *d;
     228  
     229    for (d = loaded_syms; d != NULL; d = d->next)
     230      if (streq (d->name, name) && d->dlp)
     231        {
     232          DB (DB_VERBOSE, (_("Unloading shared object %s\n"), name));
     233          rc = dlclose (d->dlp);
     234          if (rc)
     235            perror_with_name ("dlclose: ", d->name);
     236          else
     237            d->dlp = NULL;
     238          break;
     239        }
     240  
     241    return rc;
     242  }
     243  
     244  #else
     245  
     246  int
     247  load_file (const floc *flocp, struct file *file UNUSED, int noerror)
     248  {
     249    if (! noerror)
     250      O (fatal, flocp,
     251         _("The 'load' operation is not supported on this platform"));
     252  
     253    return 0;
     254  }
     255  
     256  int
     257  unload_file (const char *name UNUSED)
     258  {
     259    O (fatal, NILF, "INTERNAL: Cannot unload when load is not supported");
     260  }
     261  
     262  #endif  /* MAKE_LOAD */