From a66469e003526679b793f2d4623219aab2230b2f Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 3 May 2013 16:09:12 +0300 Subject: [PATCH] Fix interfacing with and remaking dynamic objects on MS-Windows. load.c (load_object, load_file): Accept an additional argument DLP and return in it a pointer that can be used to unload the dynamic object. read.c (eval): Call load_file with an additional argument, and record the pointer returned there in the 'struct file' object of dynamic objects in that object's 'struct file'. commands.c (execute_file_commands): Unload dynamic objects before remaking them, to avoid failure to remake if the OS doesn't allow overwriting objects that are in use. filedef.h (struct file): New member dlopen_ptr. gnumake.h (GMK_EXPORT): Define to dllexport/dllimport decorations for Windows and to nothing on other platforms. (gmk_eval, gmk_expand, gmk_add_function): Add GMK_EXPORT qualifier to prototypes. makeint.h (MAIN): Define before including gnumake.h, to give correct dllexport decorations to exported functions. (load_file): Adjust prototype. loadapi.c: Don't include gnumake.h, since makeint.h already includes it, and takes care of defining MAIN before doing so. build_w32.bat (LinkGCC): Produce an import library for functions exported by Make for loadable dynamic objects. w32/compat/posixfcn.c (dlclose): New function. w32/include/dlfcn.h (dlclose): Add prototype. scripts/features/load: Fix signatures of testload_gmk_setup and explicit_setup, to bring them in line with the documentation. --- ChangeLog | 35 +++++++++++++++++++++++++++++++++++ build_w32.bat | 4 +++- commands.c | 8 ++++++++ filedef.h | 2 ++ gnumake.h | 21 ++++++++++++++++----- load.c | 21 ++++++++++++--------- loadapi.c | 2 -- makeint.h | 9 +++++++-- read.c | 4 +++- tests/ChangeLog | 5 +++++ tests/scripts/features/load | 4 ++-- w32/compat/posixfcn.c | 11 +++++++++++ w32/include/dlfcn.h | 1 + 13 files changed, 105 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index e8033d6f..786ae4db 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,38 @@ +2013-05-03 Eli Zaretskii + + * load.c (load_object, load_file): Accept an additional argument + DLP and return in it a pointer that can be used to unload the + dynamic object. + + * read.c (eval): Call load_file with an additional argument, and + record the pointer returned there in the 'struct file' object of + dynamic objects in that object's 'struct file'. + + * commands.c (execute_file_commands): Unload dynamic objects + before remaking them, to avoid failure to remake if the OS doesn't + allow overwriting objects that are in use. + + * filedef.h (struct file): New member dlopen_ptr. + + * gnumake.h (GMK_EXPORT): Define to dllexport/dllimport + decorations for Windows and to nothing on other platforms. + (gmk_eval, gmk_expand, gmk_add_function): Add GMK_EXPORT qualifier + to prototypes. + + * makeint.h (MAIN): Define before including gnumake.h, to give + correct dllexport decorations to exported functions. + (load_file): Adjust prototype. + + * loadapi.c: Don't include gnumake.h, since makeint.h already + includes it, and takes care of defining MAIN before doing so. + + * build_w32.bat (LinkGCC): Produce an import library for functions + exported by Make for loadable dynamic objects. + + * w32/compat/posixfcn.c (dlclose): New function. + + * w32/include/dlfcn.h (dlclose): Add prototype. + 2013-05-01 Eli Zaretskii * job.c (start_job_command) [WINDOWS32]: Make the same fix for diff --git a/build_w32.bat b/build_w32.bat index 27ac3dba..43ae6e2d 100644 --- a/build_w32.bat +++ b/build_w32.bat @@ -290,8 +290,10 @@ set GUILEOBJ=guile.o echo on gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% %GUILECFLAGS% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c guile.c :LinkGCC +Rem The version NN of libgnumake-NN.dll.a should be bumped whenever +Rem the API changes in binary-incompatible manner. @echo on -gcc -mthreads -gdwarf-2 -g3 -o gnumake.exe variable.o rule.o remote-stub.o commands.o file.o getloadavg.o default.o signame.o expand.o dir.o main.o getopt1.o %GUILEOBJ% job.o read.o version.o getopt.o arscan.o remake.o misc.o hash.o strcache.o ar.o function.o vpath.o implicit.o loadapi.o load.o glob.o fnmatch.o pathstuff.o posixfcn.o w32_misc.o sub_proc.o w32err.o %GUILELIBS% -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32 +gcc -mthreads -gdwarf-2 -g3 -o gnumake.exe variable.o rule.o remote-stub.o commands.o file.o getloadavg.o default.o signame.o expand.o dir.o main.o getopt1.o %GUILEOBJ% job.o read.o version.o getopt.o arscan.o remake.o misc.o hash.o strcache.o ar.o function.o vpath.o implicit.o loadapi.o load.o glob.o fnmatch.o pathstuff.o posixfcn.o w32_misc.o sub_proc.o w32err.o %GUILELIBS% -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32 -Wl,--out-implib=libgnumake-1.dll.a @GoTo BuildEnd :Usage echo Usage: %0 [options] [gcc] diff --git a/commands.c b/commands.c index 698816fa..e8a9f1c2 100644 --- a/commands.c +++ b/commands.c @@ -14,6 +14,8 @@ 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 + #include "makeint.h" #include "dep.h" #include "filedef.h" @@ -468,6 +470,12 @@ execute_file_commands (struct file *file) set_file_variables (file); + /* If this is a loaded dynamic object, unload it before remaking. + Some systems don't allow to overwrite a loaded shared + library. */ + if (file->dlopen_ptr) + dlclose (file->dlopen_ptr); + /* Start the commands running. */ new_job (file); } diff --git a/filedef.h b/filedef.h index b6ba2420..50eec74b 100644 --- a/filedef.h +++ b/filedef.h @@ -61,6 +61,8 @@ struct file int command_flags; /* Flags OR'd in for cmds; see commands.h. */ char update_status; /* Status of the last attempt to update, or -1 if none has been made. */ + void *dlopen_ptr; /* For dynamic loaded objects: pointer to + pass to dlclose to unload the object. */ enum cmd_state /* State of the commands. */ { /* Note: It is important that cs_not_started be zero. */ cs_not_started, /* Not yet started. */ diff --git a/gnumake.h b/gnumake.h index c6f7bd82..a6308fe0 100644 --- a/gnumake.h +++ b/gnumake.h @@ -26,13 +26,23 @@ typedef struct unsigned long lineno; } gmk_floc; +#ifdef _WIN32 +# ifdef MAIN +# define GMK_EXPORT __declspec(dllexport) +# else +# define GMK_EXPORT __declspec(dllimport) +# endif +#else +# define GMK_EXPORT +#endif + /* Run $(eval ...) on the provided string BUFFER. */ -void gmk_eval (const char *buffer, const gmk_floc *floc); +void GMK_EXPORT gmk_eval (const char *buffer, const gmk_floc *floc); /* Run GNU make expansion on the provided string STR. Returns an allocated buffer that the caller must free. */ -char *gmk_expand (const char *str); +char * GMK_EXPORT gmk_expand (const char *str); /* Register a new GNU make function NAME (maximum of 255 chars long). When the function is expanded in the makefile, FUNC will be invoked with @@ -49,8 +59,9 @@ char *gmk_expand (const char *str); If EXPAND_ARGS is 0, the arguments to the function will not be expanded before FUNC is called. If EXPAND_ARGS is non-0, they will be expanded. */ -void gmk_add_function (const char *name, - char *(*func)(const char *nm, int argc, char **argv), - int min_args, int max_args, int expand_args); +void GMK_EXPORT gmk_add_function (const char *name, + char *(*func)(const char *nm, + int argc, char **argv), + int min_args, int max_args, int expand_args); #endif /* _GNUMAKE_H_ */ diff --git a/load.c b/load.c index 9a838299..f20c1c79 100644 --- a/load.c +++ b/load.c @@ -32,11 +32,13 @@ this program. If not, see . */ static load_func_t load_object (const gmk_floc *flocp, int noerror, - const char *ldname, const char *symname) + 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); @@ -46,7 +48,6 @@ load_object (const gmk_floc *flocp, int noerror, 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, '/') @@ -54,14 +55,14 @@ load_object (const gmk_floc *flocp, int noerror, && ! strchr (ldname, '\\') #endif ) - dlp = dlopen (concat (2, "./", ldname), RTLD_LAZY|RTLD_GLOBAL); + *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); + if (! *dlp) + *dlp = dlopen (ldname, RTLD_LAZY|RTLD_GLOBAL); /* Still no? Then fail. */ - if (! dlp) + if (! *dlp) { if (noerror) DB (DB_BASIC, ("%s", dlerror())); @@ -70,7 +71,7 @@ load_object (const gmk_floc *flocp, int noerror, return NULL; } - symp = dlsym (dlp, symname); + symp = dlsym (*dlp, symname); if (! symp) fatal (flocp, _("Failed to load symbol %s from %s: %s"), symname, ldname, dlerror()); @@ -80,7 +81,7 @@ load_object (const gmk_floc *flocp, int noerror, } int -load_file (const gmk_floc *flocp, const char **ldname, int noerror) +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); @@ -90,6 +91,8 @@ load_file (const gmk_floc *flocp, const char **ldname, int noerror) 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, '('); @@ -165,7 +168,7 @@ load_file (const gmk_floc *flocp, const char **ldname, int noerror) DB (DB_VERBOSE, (_("Loading symbol %s from %s\n"), symname, *ldname)); /* Load it! */ - symp = load_object(flocp, noerror, *ldname, symname); + symp = load_object(flocp, noerror, *ldname, symname, dlp); if (! symp) return 0; diff --git a/loadapi.c b/loadapi.c index f2823e1d..d79c41c6 100644 --- a/loadapi.c +++ b/loadapi.c @@ -14,8 +14,6 @@ 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 "gnumake.h" - #include "makeint.h" #include "filedef.h" diff --git a/makeint.h b/makeint.h index 812ead18..535db1d6 100644 --- a/makeint.h +++ b/makeint.h @@ -48,8 +48,12 @@ char *alloca (); #endif /* Include the externally-visible content. - Be sure to use the local one, and not one installed on the system. */ + Be sure to use the local one, and not one installed on the system. + Define MAIN for proper selection of dllexport/dllimport declarations + for MS-Windows. */ +#define MAIN #include "gnumake.h" +#undef MAIN #ifdef CRAY /* This must happen before #include so @@ -476,7 +480,8 @@ int guile_gmake_setup (const gmk_floc *flocp); /* Loadable object support. Sets to the strcached name of the loaded file. */ typedef int (*load_func_t)(const gmk_floc *flocp); -int load_file (const gmk_floc *flocp, const char **filename, int noerror); +int load_file (const gmk_floc *flocp, const char **filename, int noerror, + void **dlp); #ifdef HAVE_VFORK_H # include diff --git a/read.c b/read.c index b74e4a9a..50f8414e 100644 --- a/read.c +++ b/read.c @@ -937,11 +937,12 @@ eval (struct ebuffer *ebuf, int set_default) struct nameseq *next = files->next; const char *name = files->name; struct dep *deps; + void *dlp; free_ns (files); files = next; - if (! load_file (&ebuf->floc, &name, noerror) && ! noerror) + if (! load_file (&ebuf->floc, &name, noerror, &dlp) && ! noerror) fatal (&ebuf->floc, _("%s: failed to load"), name); deps = alloc_dep (); @@ -950,6 +951,7 @@ eval (struct ebuffer *ebuf, int set_default) deps->file = lookup_file (name); if (deps->file == 0) deps->file = enter_file (name); + deps->file->dlopen_ptr = dlp; } continue; diff --git a/tests/ChangeLog b/tests/ChangeLog index 98c59705..0a72ebfd 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,8 @@ +2013-05-03 Eli Zaretskii + + * scripts/features/load: Fix signatures of testload_gmk_setup and + explicit_setup, to bring them in line with the documentation. + 2013-04-28 Paul Smith * scripts/features/output-sync (output_sync_set): Add tests for diff --git a/tests/scripts/features/load b/tests/scripts/features/load index 47267fee..78d5c512 100644 --- a/tests/scripts/features/load +++ b/tests/scripts/features/load @@ -21,14 +21,14 @@ print $F <<'EOF' ; #include "gnumake.h" int -testload_gmk_setup () +testload_gmk_setup (gmk_floc *pos) { gmk_eval ("TESTLOAD = implicit", 0); return 1; } int -explicit_setup () +explicit_setup (gmk_floc *pos) { gmk_eval ("TESTLOAD = explicit", 0); return 1; diff --git a/w32/compat/posixfcn.c b/w32/compat/posixfcn.c index cafc8649..0a08c652 100644 --- a/w32/compat/posixfcn.c +++ b/w32/compat/posixfcn.c @@ -338,6 +338,17 @@ dlsym (void *handle, const char *name) return (void *)addr; } +int +dlclose (void *handle) +{ + if (!handle || handle == INVALID_HANDLE_VALUE) + return -1; + if (!FreeLibrary (handle)) + return -1; + + return 0; +} + #endif /* MAKE_LOAD */ diff --git a/w32/include/dlfcn.h b/w32/include/dlfcn.h index c95fee2b..c8523ad1 100644 --- a/w32/include/dlfcn.h +++ b/w32/include/dlfcn.h @@ -9,5 +9,6 @@ extern void *dlopen (const char *, int); extern void *dlsym (void *, const char *); extern char *dlerror (void); +extern int dlclose (void *); #endif /* DLFCN_H */