parallel build
[platform/upstream/make.git] / load.c
1 /* Loading dynamic objects for GNU Make.
2 Copyright (C) 2012-2013 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 struct load_list
34   {
35     struct load_list *next;
36     const char *name;
37     void *dlp;
38   };
39
40 static struct load_list *loaded_syms = NULL;
41
42 static load_func_t
43 load_object (const gmk_floc *flocp, int noerror,
44              const char *ldname, const char *symname)
45 {
46   static void *global_dl = NULL;
47   load_func_t symp;
48
49   if (! global_dl)
50     {
51       global_dl = dlopen (NULL, RTLD_NOW|RTLD_GLOBAL);
52       if (! global_dl)
53         fatal (flocp, _("Failed to open global symbol table: %s"), dlerror ());
54     }
55
56   symp = (load_func_t) dlsym (global_dl, symname);
57   if (! symp)
58     {
59       struct load_list *new;
60       void *dlp = NULL;
61
62     /* If the path has no "/", try the current directory first.  */
63       if (! strchr (ldname, '/')
64 #ifdef HAVE_DOS_PATHS
65           && ! strchr (ldname, '\\')
66 #endif
67          )
68         dlp = dlopen (concat (2, "./", ldname), RTLD_LAZY|RTLD_GLOBAL);
69
70       /* If we haven't opened it yet, try the default search path.  */
71       if (! dlp)
72         dlp = dlopen (ldname, RTLD_LAZY|RTLD_GLOBAL);
73
74       /* Still no?  Then fail.  */
75       if (! dlp)
76         {
77           if (noerror)
78             DB (DB_BASIC, ("%s", dlerror ()));
79           else
80             error (flocp, "%s", dlerror ());
81           return NULL;
82         }
83
84       /* Assert that the GPL license symbol is defined.  */
85       symp = (load_func_t) dlsym (dlp, "plugin_is_GPL_compatible");
86       if (! symp)
87         fatal (flocp, _("Loaded object %s is not declared to be GPL compatible"),
88                ldname);
89
90       symp = (load_func_t) dlsym (dlp, symname);
91       if (! symp)
92         fatal (flocp, _("Failed to load symbol %s from %s: %s"),
93                symname, ldname, dlerror ());
94
95       /* Add this symbol to a trivial lookup table.  This is not efficient but
96          it's highly unlikely we'll be loading lots of objects, and we only
97          need it to look them up on unload, if we rebuild them.  */
98       new = xmalloc (sizeof (struct load_list));
99       new->name = xstrdup (ldname);
100       new->dlp = dlp;
101       new->next = loaded_syms;
102       loaded_syms = new;
103     }
104
105   return symp;
106 }
107
108 int
109 load_file (const gmk_floc *flocp, const char **ldname, int noerror)
110 {
111   int nmlen = strlen (*ldname);
112   char *new = alloca (nmlen + CSTRLEN (SYMBOL_EXTENSION) + 1);
113   char *symname = NULL;
114   char *loaded;
115   const char *fp;
116   int r;
117   load_func_t symp;
118
119   /* Break the input into an object file name and a symbol name.  If no symbol
120      name was provided, compute one from the object file name.  */
121   fp = strchr (*ldname, '(');
122   if (fp)
123     {
124       const char *ep;
125
126       /* There's an open paren, so see if there's a close paren: if so use
127          that as the symbol name.  We can't have whitespace: it would have
128          been chopped up before this function is called.  */
129       ep = strchr (fp+1, ')');
130       if (ep && ep[1] == '\0')
131         {
132           int l = fp - *ldname;;
133
134           ++fp;
135           if (fp == ep)
136             fatal (flocp, _("Empty symbol name for load: %s"), *ldname);
137
138           /* Make a copy of the ldname part.  */
139           memcpy (new, *ldname, l);
140           new[l] = '\0';
141           *ldname = new;
142
143           /* Make a copy of the symbol name part.  */
144           symname = new + l + 1;
145           memcpy (symname, fp, ep - fp);
146           symname[ep - fp] = '\0';
147         }
148     }
149
150   /* Add this name to the string cache so it can be reused later.  */
151   *ldname = strcache_add (*ldname);
152
153   /* If this object has been loaded, we're done.  */
154   loaded = allocated_variable_expand ("$(.LOADED)");
155   fp = strstr (loaded, *ldname);
156   r = fp && (fp==loaded || fp[-1]==' ') && (fp[nmlen]=='\0' || fp[nmlen]==' ');
157   free (loaded);
158   if (r)
159     return 1;
160
161   /* If we didn't find a symbol name yet, construct it from the ldname.  */
162   if (! symname)
163     {
164       char *p = new;
165
166       fp = strrchr (*ldname, '/');
167 #ifdef HAVE_DOS_PATHS
168       if (fp)
169         {
170           const char *fp2 = strchr (fp, '\\');
171
172           if (fp2 > fp)
173             fp = fp2;
174         }
175       else
176         fp = strrchr (*ldname, '\\');
177       /* The (improbable) case of d:foo.  */
178       if (fp && *fp && fp[1] == ':')
179         fp++;
180 #endif
181       if (!fp)
182         fp = *ldname;
183       else
184         ++fp;
185       while (isalnum (*fp) || *fp == '_')
186         *(p++) = *(fp++);
187       strcpy (p, SYMBOL_EXTENSION);
188       symname = new;
189     }
190
191   DB (DB_VERBOSE, (_("Loading symbol %s from %s\n"), symname, *ldname));
192
193   /* Load it!  */
194   symp = load_object (flocp, noerror, *ldname, symname);
195   if (! symp)
196     return 0;
197
198   /* Invoke the symbol.  */
199   r = (*symp) (flocp);
200
201   /* If it succeeded, add the load file to the loaded variable.  */
202   if (r > 0)
203     do_variable_definition (flocp, ".LOADED", *ldname, o_default, f_append, 0);
204
205   return r;
206 }
207
208 void
209 unload_file (const char *name)
210 {
211   struct load_list *d;
212
213   for (d = loaded_syms; d != NULL; d = d->next)
214     if (streq (d->name, name) && d->dlp)
215       {
216         if (dlclose (d->dlp))
217           perror_with_name ("dlclose", d->name);
218         d->dlp = NULL;
219         break;
220       }
221 }
222
223 #else
224
225 int
226 load_file (const gmk_floc *flocp, const char **ldname, int noerror)
227 {
228   if (! noerror)
229     fatal (flocp, _("The 'load' operation is not supported on this platform."));
230
231   return 0;
232 }
233
234 void
235 unload_file (const char *name)
236 {
237   fatal (NILF, "INTERNAL: Cannot unload when load is not supported!");
238 }
239
240 #endif  /* MAKE_LOAD */