/* Loading dynamic objects for GNU Make. Copyright (C) 2012 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 . */ #include "makeint.h" #if MAKE_LOAD #include #include #include #include #include #define SYMBOL_EXTENSION "_gmake_setup" static void *global_dl = NULL; #include "debug.h" #include "filedef.h" #include "variable.h" static int init_symbol (const gmk_floc *flocp, const char *ldname, load_func_t symp) { int r; const char *p; int nmlen = strlen (ldname); char *loaded = allocated_variable_expand("$(.LOADED)"); /* If it's already been loaded don't do it again. */ p = strstr (loaded, ldname); r = p && (p==loaded || p[-1]==' ') && (p[nmlen]=='\0' || p[nmlen]==' '); free (loaded); if (r) return 1; /* Now invoke the symbol. */ r = (*symp) (flocp); /* If it succeeded, add the symbol to the loaded variable. */ if (r > 0) do_variable_definition (flocp, ".LOADED", ldname, o_default, f_append, 0); return r; } int load_file (const gmk_floc *flocp, const char **ldname, int noerror) { load_func_t symp; const char *fp; char *symname = NULL; char *new = alloca (strlen (*ldname) + CSTRLEN (SYMBOL_EXTENSION) + 1); if (! global_dl) { global_dl = dlopen (NULL, RTLD_NOW|RTLD_GLOBAL); if (! global_dl) fatal (flocp, _("Failed to open global symbol table: %s"), dlerror()); } /* If a symbol name was provided, use it. */ 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 we didn't find a symbol name yet, construct it from the ldname. */ if (! symname) { char *p = new; fp = strrchr (*ldname, '/'); 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)); /* See if it's already defined. */ symp = (load_func_t) dlsym (global_dl, symname); if (! symp) { void *dlp = NULL; /* If the path has no "/", try the current directory first. */ if (! strchr (*ldname, '/')) 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 0; } symp = dlsym (dlp, symname); if (! symp) fatal (flocp, _("Failed to load symbol %s from %s: %s"), symname, *ldname, dlerror()); } /* Invoke the symbol to initialize the loaded object. */ return init_symbol(flocp, *ldname, symp); } #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; } #endif /* MAKE_LOAD */