/* 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 "_gmk_setup" #include "debug.h" #include "filedef.h" #include "variable.h" static load_func_t load_object (const gmk_floc *flocp, int noerror, const char *ldname, const char *symname, void **dlp) { static void *global_dl = NULL; load_func_t symp; *dlp = NULL; 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) { /* 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; } symp = dlsym (*dlp, symname); if (! symp) fatal (flocp, _("Failed to load symbol %s from %s: %s"), symname, ldname, dlerror()); } return symp; } int load_file (const gmk_floc *flocp, const char **ldname, int noerror, void **dlp) { 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; *dlp = NULL; /* 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, dlp); 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; } #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 */