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 */