summaryrefslogtreecommitdiff
path: root/load.c
diff options
context:
space:
mode:
Diffstat (limited to 'load.c')
-rw-r--r--load.c240
1 files changed, 240 insertions, 0 deletions
diff --git a/load.c b/load.c
new file mode 100644
index 0000000..a2cbc25
--- /dev/null
+++ b/load.c
@@ -0,0 +1,240 @@
+/* Loading dynamic objects for GNU Make.
+Copyright (C) 2012-2013 Free Software Foundation, Inc.
+This file is part of GNU Make.
+
+GNU Make is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 3 of the License, or (at your option) any later
+version.
+
+GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "makeint.h"
+
+#if MAKE_LOAD
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <errno.h>
+
+#define SYMBOL_EXTENSION "_gmk_setup"
+
+#include "debug.h"
+#include "filedef.h"
+#include "variable.h"
+
+struct load_list
+ {
+ struct load_list *next;
+ const char *name;
+ void *dlp;
+ };
+
+static struct load_list *loaded_syms = NULL;
+
+static load_func_t
+load_object (const gmk_floc *flocp, int noerror,
+ const char *ldname, const char *symname)
+{
+ static void *global_dl = NULL;
+ load_func_t symp;
+
+ if (! global_dl)
+ {
+ global_dl = dlopen (NULL, RTLD_NOW|RTLD_GLOBAL);
+ if (! global_dl)
+ fatal (flocp, _("Failed to open global symbol table: %s"), dlerror ());
+ }
+
+ symp = (load_func_t) dlsym (global_dl, symname);
+ if (! symp)
+ {
+ struct load_list *new;
+ void *dlp = NULL;
+
+ /* If the path has no "/", try the current directory first. */
+ if (! strchr (ldname, '/')
+#ifdef HAVE_DOS_PATHS
+ && ! strchr (ldname, '\\')
+#endif
+ )
+ dlp = dlopen (concat (2, "./", ldname), RTLD_LAZY|RTLD_GLOBAL);
+
+ /* If we haven't opened it yet, try the default search path. */
+ if (! dlp)
+ dlp = dlopen (ldname, RTLD_LAZY|RTLD_GLOBAL);
+
+ /* Still no? Then fail. */
+ if (! dlp)
+ {
+ if (noerror)
+ DB (DB_BASIC, ("%s", dlerror ()));
+ else
+ error (flocp, "%s", dlerror ());
+ return NULL;
+ }
+
+ /* Assert that the GPL license symbol is defined. */
+ symp = (load_func_t) dlsym (dlp, "plugin_is_GPL_compatible");
+ if (! symp)
+ fatal (flocp, _("Loaded object %s is not declared to be GPL compatible"),
+ ldname);
+
+ symp = (load_func_t) dlsym (dlp, symname);
+ if (! symp)
+ fatal (flocp, _("Failed to load symbol %s from %s: %s"),
+ symname, ldname, dlerror ());
+
+ /* Add this symbol to a trivial lookup table. This is not efficient but
+ it's highly unlikely we'll be loading lots of objects, and we only
+ need it to look them up on unload, if we rebuild them. */
+ new = xmalloc (sizeof (struct load_list));
+ new->name = xstrdup (ldname);
+ new->dlp = dlp;
+ new->next = loaded_syms;
+ loaded_syms = new;
+ }
+
+ return symp;
+}
+
+int
+load_file (const gmk_floc *flocp, const char **ldname, int noerror)
+{
+ int nmlen = strlen (*ldname);
+ char *new = alloca (nmlen + CSTRLEN (SYMBOL_EXTENSION) + 1);
+ char *symname = NULL;
+ char *loaded;
+ const char *fp;
+ int r;
+ load_func_t symp;
+
+ /* Break the input into an object file name and a symbol name. If no symbol
+ name was provided, compute one from the object file name. */
+ fp = strchr (*ldname, '(');
+ if (fp)
+ {
+ const char *ep;
+
+ /* There's an open paren, so see if there's a close paren: if so use
+ that as the symbol name. We can't have whitespace: it would have
+ been chopped up before this function is called. */
+ ep = strchr (fp+1, ')');
+ if (ep && ep[1] == '\0')
+ {
+ int l = fp - *ldname;;
+
+ ++fp;
+ if (fp == ep)
+ fatal (flocp, _("Empty symbol name for load: %s"), *ldname);
+
+ /* Make a copy of the ldname part. */
+ memcpy (new, *ldname, l);
+ new[l] = '\0';
+ *ldname = new;
+
+ /* Make a copy of the symbol name part. */
+ symname = new + l + 1;
+ memcpy (symname, fp, ep - fp);
+ symname[ep - fp] = '\0';
+ }
+ }
+
+ /* Add this name to the string cache so it can be reused later. */
+ *ldname = strcache_add (*ldname);
+
+ /* If this object has been loaded, we're done. */
+ loaded = allocated_variable_expand ("$(.LOADED)");
+ fp = strstr (loaded, *ldname);
+ r = fp && (fp==loaded || fp[-1]==' ') && (fp[nmlen]=='\0' || fp[nmlen]==' ');
+ free (loaded);
+ if (r)
+ return 1;
+
+ /* If we didn't find a symbol name yet, construct it from the ldname. */
+ if (! symname)
+ {
+ char *p = new;
+
+ fp = strrchr (*ldname, '/');
+#ifdef HAVE_DOS_PATHS
+ if (fp)
+ {
+ const char *fp2 = strchr (fp, '\\');
+
+ if (fp2 > fp)
+ fp = fp2;
+ }
+ else
+ fp = strrchr (*ldname, '\\');
+ /* The (improbable) case of d:foo. */
+ if (fp && *fp && fp[1] == ':')
+ fp++;
+#endif
+ if (!fp)
+ fp = *ldname;
+ else
+ ++fp;
+ while (isalnum (*fp) || *fp == '_')
+ *(p++) = *(fp++);
+ strcpy (p, SYMBOL_EXTENSION);
+ symname = new;
+ }
+
+ DB (DB_VERBOSE, (_("Loading symbol %s from %s\n"), symname, *ldname));
+
+ /* Load it! */
+ symp = load_object (flocp, noerror, *ldname, symname);
+ if (! symp)
+ return 0;
+
+ /* Invoke the symbol. */
+ r = (*symp) (flocp);
+
+ /* If it succeeded, add the load file to the loaded variable. */
+ if (r > 0)
+ do_variable_definition (flocp, ".LOADED", *ldname, o_default, f_append, 0);
+
+ return r;
+}
+
+void
+unload_file (const char *name)
+{
+ struct load_list *d;
+
+ for (d = loaded_syms; d != NULL; d = d->next)
+ if (streq (d->name, name) && d->dlp)
+ {
+ if (dlclose (d->dlp))
+ perror_with_name ("dlclose", d->name);
+ d->dlp = NULL;
+ break;
+ }
+}
+
+#else
+
+int
+load_file (const gmk_floc *flocp, const char **ldname, int noerror)
+{
+ if (! noerror)
+ fatal (flocp, _("The 'load' operation is not supported on this platform."));
+
+ return 0;
+}
+
+void
+unload_file (const char *name)
+{
+ fatal (NILF, "INTERNAL: Cannot unload when load is not supported!");
+}
+
+#endif /* MAKE_LOAD */