Imported Upstream version 4.4
[platform/upstream/make.git] / 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 */