37e7b8eaaf3bd2f29337763fed63a4a8a91fee51
[platform/upstream/make.git] / load.c
1 /* Loading dynamic objects for GNU Make.
2 Copyright (C) 2012-2016 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 <http://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", err));
88           else
89             OS (error, flocp, "%s", err);
90           return NULL;
91         }
92
93       /* Assert that the GPL license symbol is defined.  */
94       symp = (load_func_t) dlsym (dlp, "plugin_is_GPL_compatible");
95       if (! symp)
96         OS (fatal, flocp,
97              _("Loaded object %s is not declared to be GPL compatible"),
98              ldname);
99
100       symp = (load_func_t) dlsym (dlp, symname);
101       if (! symp)
102         {
103           const char *err = dlerror ();
104           OSSS (fatal, flocp, _("Failed to load symbol %s from %s: %s"),
105                 symname, ldname, err);
106         }
107
108       /* Add this symbol to a trivial lookup table.  This is not efficient but
109          it's highly unlikely we'll be loading lots of objects, and we only
110          need it to look them up on unload, if we rebuild them.  */
111       new = xmalloc (sizeof (struct load_list));
112       new->name = xstrdup (ldname);
113       new->dlp = dlp;
114       new->next = loaded_syms;
115       loaded_syms = new;
116     }
117
118   return symp;
119 }
120
121 int
122 load_file (const floc *flocp, const char **ldname, int noerror)
123 {
124   int nmlen = strlen (*ldname);
125   char *new = alloca (nmlen + CSTRLEN (SYMBOL_EXTENSION) + 1);
126   char *symname = NULL;
127   char *loaded;
128   const char *fp;
129   int r;
130   load_func_t symp;
131
132   /* Break the input into an object file name and a symbol name.  If no symbol
133      name was provided, compute one from the object file name.  */
134   fp = strchr (*ldname, '(');
135   if (fp)
136     {
137       const char *ep;
138
139       /* There's an open paren, so see if there's a close paren: if so use
140          that as the symbol name.  We can't have whitespace: it would have
141          been chopped up before this function is called.  */
142       ep = strchr (fp+1, ')');
143       if (ep && ep[1] == '\0')
144         {
145           int l = fp - *ldname;;
146
147           ++fp;
148           if (fp == ep)
149             OS (fatal, flocp, _("Empty symbol name for load: %s"), *ldname);
150
151           /* Make a copy of the ldname part.  */
152           memcpy (new, *ldname, l);
153           new[l] = '\0';
154           *ldname = new;
155           nmlen = l;
156
157           /* Make a copy of the symbol name part.  */
158           symname = new + l + 1;
159           memcpy (symname, fp, ep - fp);
160           symname[ep - fp] = '\0';
161         }
162     }
163
164   /* Add this name to the string cache so it can be reused later.  */
165   *ldname = strcache_add (*ldname);
166
167   /* If this object has been loaded, we're done.  */
168   loaded = allocated_variable_expand ("$(.LOADED)");
169   fp = strstr (loaded, *ldname);
170   r = fp && (fp==loaded || fp[-1]==' ') && (fp[nmlen]=='\0' || fp[nmlen]==' ');
171   if (r)
172     goto exit;
173
174   /* If we didn't find a symbol name yet, construct it from the ldname.  */
175   if (! symname)
176     {
177       char *p = new;
178
179       fp = strrchr (*ldname, '/');
180 #ifdef HAVE_DOS_PATHS
181       if (fp)
182         {
183           const char *fp2 = strchr (fp, '\\');
184
185           if (fp2 > fp)
186             fp = fp2;
187         }
188       else
189         fp = strrchr (*ldname, '\\');
190       /* The (improbable) case of d:foo.  */
191       if (fp && *fp && fp[1] == ':')
192         fp++;
193 #endif
194       if (!fp)
195         fp = *ldname;
196       else
197         ++fp;
198       while (isalnum (*fp) || *fp == '_')
199         *(p++) = *(fp++);
200       strcpy (p, SYMBOL_EXTENSION);
201       symname = new;
202     }
203
204   DB (DB_VERBOSE, (_("Loading symbol %s from %s\n"), symname, *ldname));
205
206   /* Load it!  */
207   symp = load_object (flocp, noerror, *ldname, symname);
208   if (! symp)
209     return 0;
210
211   /* Invoke the symbol.  */
212   r = (*symp) (flocp);
213
214   /* If it succeeded, add the load file to the loaded variable.  */
215   if (r > 0)
216     {
217       size_t loadlen = strlen (loaded);
218       char *newval = alloca (loadlen + strlen (*ldname) + 2);
219       /* Don't add a space if it's empty.  */
220       if (loadlen)
221         {
222           memcpy (newval, loaded, loadlen);
223           newval[loadlen++] = ' ';
224         }
225       strcpy (&newval[loadlen], *ldname);
226       do_variable_definition (flocp, ".LOADED", newval, o_default, f_simple, 0);
227     }
228
229  exit:
230   free (loaded);
231   return r;
232 }
233
234 void
235 unload_file (const char *name)
236 {
237   struct load_list *d;
238
239   for (d = loaded_syms; d != NULL; d = d->next)
240     if (streq (d->name, name) && d->dlp)
241       {
242         if (dlclose (d->dlp))
243           perror_with_name ("dlclose", d->name);
244         d->dlp = NULL;
245         break;
246       }
247 }
248
249 #else
250
251 int
252 load_file (const floc *flocp, const char **ldname UNUSED, int noerror)
253 {
254   if (! noerror)
255     O (fatal, flocp,
256        _("The 'load' operation is not supported on this platform."));
257
258   return 0;
259 }
260
261 void
262 unload_file (const char *name UNUSED)
263 {
264   O (fatal, NILF, "INTERNAL: Cannot unload when load is not supported!");
265 }
266
267 #endif  /* MAKE_LOAD */