From 8c7dbea9f193ae21d193453d7a9eb6d2089618d6 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sun, 26 Nov 2017 13:00:48 +0000 Subject: [PATCH] Plugin support on Windows/MinGW config/ChangeLog: 2017-11-14 Boris Kolpackov * gcc-plugin.m4: Add support for MinGW. gcc/ChangeLog: 2017-11-14 Boris Kolpackov * plugin.c (add_new_plugin): Use platform-specific library extensions. (try_init_one_plugin): Alternative implementation for MinGW. * Makefile.in (plugin_implib): New. (gengtype-lex.c): Fix broken AIX workaround. * configure: Regenerate. * doc/plugins.texi: Document support for MinGW. gcc/c/ChangeLog: 2017-11-14 Boris Kolpackov * Make-lang.in (c.install-plugin): Install backend import library. gcc/cp/ChangeLog: 2017-11-14 Boris Kolpackov * Make-lang.in (c++.install-plugin): Install backend import library. libcc1/ChangeLog: 2017-11-14 Boris Kolpackov * configure: Regenerate. From-SVN: r255154 --- config/ChangeLog | 4 ++ config/gcc-plugin.m4 | 15 +++++- gcc/ChangeLog | 9 ++++ gcc/Makefile.in | 14 ++++- gcc/c/ChangeLog | 4 ++ gcc/c/Make-lang.in | 9 +++- gcc/configure | 15 +++++- gcc/cp/ChangeLog | 4 ++ gcc/cp/Make-lang.in | 5 ++ gcc/doc/plugins.texi | 55 ++++++++++++++++++-- gcc/plugin.c | 120 +++++++++++++++++++++++++++++++++++++++---- libcc1/ChangeLog | 4 ++ libcc1/configure | 15 +++++- 13 files changed, 253 insertions(+), 20 deletions(-) diff --git a/config/ChangeLog b/config/ChangeLog index 2bb5244caa4..a44722ed528 100644 --- a/config/ChangeLog +++ b/config/ChangeLog @@ -1,3 +1,7 @@ +2017-11-14 Boris Kolpackov + + * gcc-plugin.m4: Add support for MinGW. + 2017-11-17 Igor Tsimbalist * cet.m4: New file. diff --git a/config/gcc-plugin.m4 b/config/gcc-plugin.m4 index dd06a58cd13..38b2ae6e12e 100644 --- a/config/gcc-plugin.m4 +++ b/config/gcc-plugin.m4 @@ -19,8 +19,21 @@ AC_DEFUN([GCC_ENABLE_PLUGINS], enable_plugin=yes; default_plugin=yes) pluginlibs= + plugin_check=yes case "${host}" in + *-*-mingw*) + # Since plugin support under MinGW is not as straightforward as on + # other platforms (e.g., we have to link import library, etc), we + # only enable it if explicitly requested. + if test x"$default_plugin" = x"yes"; then + enable_plugin=no + elif test x"$enable_plugin" = x"yes"; then + # Use make's target variable to derive import library name. + pluginlibs='-Wl,--export-all-symbols -Wl,--out-implib=[$]@.a' + plugin_check=no + fi + ;; *-*-darwin*) if test x$build = x$host; then export_sym_check="nm${exeext} -g" @@ -41,7 +54,7 @@ AC_DEFUN([GCC_ENABLE_PLUGINS], ;; esac - if test x"$enable_plugin" = x"yes"; then + if test x"$enable_plugin" = x"yes" -a x"$plugin_check" = x"yes"; then AC_MSG_CHECKING([for exported symbols]) if test "x$export_sym_check" != x; then diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 3445e3e08ee..e52effababa 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +2017-11-14 Boris Kolpackov + + * plugin.c (add_new_plugin): Use platform-specific library extensions. + (try_init_one_plugin): Alternative implementation for MinGW. + * Makefile.in (plugin_implib): New. + (gengtype-lex.c): Fix broken AIX workaround. + * configure: Regenerate. + * doc/plugins.texi: Document support for MinGW. + 2017-11-25 Jakub Jelinek PR rtl-optimization/81553 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index f2a897c5962..c428eaab023 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -57,6 +57,7 @@ MAKEOVERRIDES = build=@build@ host=@host@ host_noncanonical=@host_noncanonical@ +host_os=@host_os@ target=@target@ target_noncanonical:=@target_noncanonical@ @@ -393,6 +394,11 @@ PLUGINLIBS = @pluginlibs@ enable_plugin = @enable_plugin@ +# On MinGW plugin installation involves installing import libraries. +ifeq ($(enable_plugin),yes) + plugin_implib := $(if $(strip $(filter mingw%,$(host_os))),yes,no) +endif + enable_host_shared = @enable_host_shared@ enable_as_accelerator = @enable_as_accelerator@ @@ -2828,11 +2834,15 @@ $(genprog:%=build/gen%$(build_exeext)): build/gen%$(build_exeext): build/gen%.o $(filter-out $(BUILD_LIBDEPS), $^) $(BUILD_LIBS) # Generated source files for gengtype. Prepend inclusion of -# bconfig.h because AIX requires _LARGE_FILES to be defined before +# config.h/bconfig.h because AIX requires _LARGE_FILES to be defined before # any system header is included. gengtype-lex.c : gengtype-lex.l -$(FLEX) $(FLEXFLAGS) -o$@ $< && { \ - echo '#include "bconfig.h"' > $@.tmp; \ + echo '#ifdef HOST_GENERATOR_FILE' > $@.tmp; \ + echo '#include "config.h"' >> $@.tmp; \ + echo '#else' >> $@.tmp; \ + echo '#include "bconfig.h"' >> $@.tmp; \ + echo '#endif' >> $@.tmp; \ cat $@ >> $@.tmp; \ mv $@.tmp $@; \ } diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 1d698508131..087a1e7cfaf 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,7 @@ +2017-11-14 Boris Kolpackov + + * Make-lang.in (c.install-plugin): Install backend import library. + 2017-11-23 Jakub Jelinek * c-parser.c (c_parser_omp_declare_simd): Reject declare simd in diff --git a/gcc/c/Make-lang.in b/gcc/c/Make-lang.in index cfd8cd2b169..b194f227646 100644 --- a/gcc/c/Make-lang.in +++ b/gcc/c/Make-lang.in @@ -125,7 +125,14 @@ check-c : check-gcc c.install-common: c.install-man: -c.install-plugin: + +c.install-plugin: installdirs +# Install import library. +ifeq ($(plugin_implib),yes) + $(mkinstalldirs) $(DESTDIR)$(plugin_resourcesdir) + $(INSTALL_DATA) cc1$(exeext).a $(DESTDIR)/$(plugin_resourcesdir)/cc1$(exeext).a +endif + c.uninstall: # diff --git a/gcc/configure b/gcc/configure index d4461e2fdd2..39eb3c82930 100755 --- a/gcc/configure +++ b/gcc/configure @@ -29619,8 +29619,21 @@ fi pluginlibs= + plugin_check=yes case "${host}" in + *-*-mingw*) + # Since plugin support under MinGW is not as straightforward as on + # other platforms (e.g., we have to link import library, etc), we + # only enable it if explicitly requested. + if test x"$default_plugin" = x"yes"; then + enable_plugin=no + elif test x"$enable_plugin" = x"yes"; then + # Use make's target variable to derive import library name. + pluginlibs='-Wl,--export-all-symbols -Wl,--out-implib=$@.a' + plugin_check=no + fi + ;; *-*-darwin*) if test x$build = x$host; then export_sym_check="nm${exeext} -g" @@ -29641,7 +29654,7 @@ fi ;; esac - if test x"$enable_plugin" = x"yes"; then + if test x"$enable_plugin" = x"yes" -a x"$plugin_check" = x"yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for exported symbols" >&5 $as_echo_n "checking for exported symbols... " >&6; } diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index e93964178eb..854df5afe91 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,7 @@ +2017-11-14 Boris Kolpackov + + * Make-lang.in (c++.install-plugin): Install backend import library. + 2017-11-23 Jakub Jelinek * parser.c (cp_parser_omp_declare): Change return type to bool from diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in index c852f6a38b4..7af4a4cb0b8 100644 --- a/gcc/cp/Make-lang.in +++ b/gcc/cp/Make-lang.in @@ -238,6 +238,11 @@ c++.install-plugin: installdirs $(mkinstalldirs) $(DESTDIR)$$dir; \ $(INSTALL_DATA) $$path $(DESTDIR)$$dest; \ done +# Install import library. +ifeq ($(plugin_implib),yes) + $(mkinstalldirs) $(DESTDIR)$(plugin_resourcesdir) + $(INSTALL_DATA) cc1plus$(exeext).a $(DESTDIR)/$(plugin_resourcesdir)/cc1plus$(exeext).a +endif c++.uninstall: -rm -rf $(DESTDIR)$(bindir)/$(CXX_INSTALL_NAME)$(exeext) diff --git a/gcc/doc/plugins.texi b/gcc/doc/plugins.texi index 6fc7b9ae9a2..dc3fa212881 100644 --- a/gcc/doc/plugins.texi +++ b/gcc/doc/plugins.texi @@ -34,14 +34,17 @@ can be quite useful. @section Loading Plugins Plugins are supported on platforms that support @option{-ldl --rdynamic}. They are loaded by the compiler using @code{dlopen} -and invoked at pre-determined locations in the compilation -process. +-rdynamic} as well as Windows/MinGW. They are loaded by the compiler +using @code{dlopen} or equivalent and invoked at pre-determined +locations in the compilation process. Plugins are loaded with -@option{-fplugin=/path/to/@var{name}.so} @option{-fplugin-arg-@var{name}-@var{key1}[=@var{value1}]} +@option{-fplugin=/path/to/@var{name}.@var{ext}} @option{-fplugin-arg-@var{name}-@var{key1}[=@var{value1}]} +Where @var{name} is the plugin name and @var{ext} is the platform-specific +dynamic library extension. It should be @code{dll} on Windows/MinGW, +@code{dylib} on Darwin/Mac OS X, and @code{so} on all other platforms. The plugin arguments are parsed by GCC and passed to respective plugins as key-value pairs. Multiple plugins can be invoked by specifying multiple @option{-fplugin} arguments. @@ -49,7 +52,7 @@ specifying multiple @option{-fplugin} arguments. A plugin can be simply given by its short name (no dots or slashes). When simply passing @option{-fplugin=@var{name}}, the plugin is loaded from the @file{plugin} directory, so @option{-fplugin=@var{name}} is -the same as @option{-fplugin=`gcc -print-file-name=plugin`/@var{name}.so}, +the same as @option{-fplugin=`gcc -print-file-name=plugin`/@var{name}.@var{ext}}, using backquote shell syntax to query the @file{plugin} directory. @node Plugin API @@ -508,6 +511,48 @@ A single source file plugin may be built with @code{g++ -I`gcc plugin.so}, using backquote shell syntax to query the @file{plugin} directory. +Plugin support on Windows/MinGW has a number of limitations and +additional requirements. When building a plugin on Windows we have to +link an import library for the corresponding backend executable, for +example, @file{cc1.exe}, @file{cc1plus.exe}, etc., in order to gain +access to the symbols provided by GCC. This means that on Windows a +plugin is language-specific, for example, for C, C++, etc. If you wish +to use your plugin with multiple languages, then you will need to +build multiple plugin libraries and either instruct your users on how +to load the correct version or provide a compiler wrapper that does +this automatically. + +Additionally, on Windows the plugin library has to export the +@code{plugin_is_GPL_compatible} and @code{plugin_init} symbols. If you +do not wish to modify the source code of your plugin, then you can use +the @option{-Wl,--export-all-symbols} option or provide a suitable DEF +file. Alternatively, you can export just these two symbols by decorating +them with @code{__declspec(dllexport)}, for example: + +@smallexample +#ifdef _WIN32 +__declspec(dllexport) +#endif +int plugin_is_GPL_compatible; + +#ifdef _WIN32 +__declspec(dllexport) +#endif +int plugin_init (plugin_name_args *, plugin_gcc_version *) +@end smallexample + +The import libraries are installed into the @code{plugin} directory +and their names are derived by appending the @code{.a} extension to +the backend executable names, for example, @file{cc1.exe.a}, +@file{cc1plus.exe.a}, etc. The following command line shows how to +build the single source file plugin on Windows to be used with the C++ +compiler: + +@smallexample +g++ -I`gcc -print-file-name=plugin`/include -shared -Wl,--export-all-symbols \ +-o plugin.dll plugin.c `gcc -print-file-name=plugin`/cc1plus.exe.a +@end smallexample + When a plugin needs to use @command{gengtype}, be sure that both @file{gengtype} and @file{gtype.state} have the same version as the GCC for which the plugin is built. diff --git a/gcc/plugin.c b/gcc/plugin.c index 9892748cd15..db18e642680 100644 --- a/gcc/plugin.c +++ b/gcc/plugin.c @@ -34,6 +34,16 @@ along with GCC; see the file COPYING3. If not see #include "plugin-version.h" #endif +#ifdef __MINGW32__ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#endif + #define GCC_PLUGIN_STRINGIFY0(X) #X #define GCC_PLUGIN_STRINGIFY1(X) GCC_PLUGIN_STRINGIFY0 (X) @@ -144,7 +154,7 @@ get_plugin_base_name (const char *full_name) /* First get the base name part of the full-path name, i.e. NAME.so. */ char *base_name = xstrdup (lbasename (full_name)); - /* Then get rid of '.so' part of the name. */ + /* Then get rid of the extension in the name, e.g., .so. */ strip_off_ending (base_name, strlen (base_name)); return base_name; @@ -175,12 +185,27 @@ add_new_plugin (const char* plugin_name) if (name_is_short) { base_name = CONST_CAST (char*, plugin_name); - /* FIXME: the ".so" suffix is currently builtin, since plugins - only work on ELF host systems like e.g. Linux or Solaris. - When plugins shall be available on non ELF systems such as - Windows or MacOS, this code has to be greatly improved. */ + +#if defined(__MINGW32__) + static const char plugin_ext[] = ".dll"; +#elif defined(__APPLE__) + /* Mac OS has two types of libraries: dynamic libraries (.dylib) and + plugins (.bundle). Both can be used with dlopen()/dlsym() but the + former cannot be linked at build time (i.e., with the -lfoo linker + option). A GCC plugin is therefore probably a Mac OS plugin but their + use seems to be quite rare and the .bundle extension is more of a + recommendation rather than the rule. This raises the questions of how + well they are supported by tools (e.g., libtool). So to avoid + complications let's use the .dylib extension for now. In the future, + if this proves to be an issue, we can always check for both + extensions. */ + static const char plugin_ext[] = ".dylib"; +#else + static const char plugin_ext[] = ".so"; +#endif + plugin_name = concat (default_plugin_dir_name (), "/", - plugin_name, ".so", NULL); + plugin_name, plugin_ext, NULL); if (access (plugin_name, R_OK)) fatal_error (input_location, @@ -573,6 +598,85 @@ invoke_plugin_callbacks_full (int event, void *gcc_data) } #ifdef ENABLE_PLUGIN + +/* Try to initialize PLUGIN. Return true if successful. */ + +#ifdef __MINGW32__ + +// Return a message string for last error or NULL if unknown. Must be freed +// with LocalFree(). +static inline char * +win32_error_msg () +{ + char *msg; + return FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + 0, + GetLastError (), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (char*)&msg, + 0, + 0) + ? msg + : NULL; +} + +static bool +try_init_one_plugin (struct plugin_name_args *plugin) +{ + HMODULE dl_handle; + plugin_init_func plugin_init; + + dl_handle = LoadLibrary (plugin->full_name); + if (!dl_handle) + { + char *err = win32_error_msg (); + error ("cannot load plugin %s\n%s", plugin->full_name, err); + LocalFree (err); + return false; + } + + /* Check the plugin license. Unlike the name suggests, GetProcAddress() + can be used for both functions and variables. */ + if (GetProcAddress (dl_handle, str_license) == NULL) + { + char *err = win32_error_msg (); + fatal_error (input_location, + "plugin %s is not licensed under a GPL-compatible license\n" + "%s", plugin->full_name, err); + } + + /* Unlike dlsym(), GetProcAddress() returns a pointer to a function so we + can cast directly without union tricks. */ + plugin_init = (plugin_init_func) + GetProcAddress (dl_handle, str_plugin_init_func_name); + + if (plugin_init == NULL) + { + char *err = win32_error_msg (); + FreeLibrary (dl_handle); + error ("cannot find %s in plugin %s\n%s", str_plugin_init_func_name, + plugin->full_name, err); + LocalFree (err); + return false; + } + + /* Call the plugin-provided initialization routine with the arguments. */ + if ((*plugin_init) (plugin, &gcc_version)) + { + FreeLibrary (dl_handle); + error ("fail to initialize plugin %s", plugin->full_name); + return false; + } + /* Leak dl_handle on purpose to ensure the plugin is loaded for the + entire run of the compiler. */ + return true; +} + +#else // POSIX-like with dlopen()/dlsym(). + /* We need a union to cast dlsym return value to a function pointer as ISO C forbids assignment between function pointer and 'void *'. Use explicit union instead of __extension__() for @@ -581,8 +685,6 @@ invoke_plugin_callbacks_full (int event, void *gcc_data) #define PTR_UNION_AS_VOID_PTR(NAME) (NAME._q) #define PTR_UNION_AS_CAST_PTR(NAME) (NAME._nq) -/* Try to initialize PLUGIN. Return true if successful. */ - static bool try_init_one_plugin (struct plugin_name_args *plugin) { @@ -634,7 +736,7 @@ try_init_one_plugin (struct plugin_name_args *plugin) entire run of the compiler. */ return true; } - +#endif /* Routine to dlopen and initialize one plugin. This function is passed to (and called by) the hash table traverse routine. Return 1 for the diff --git a/libcc1/ChangeLog b/libcc1/ChangeLog index ba77d02902d..c248bf8dd20 100644 --- a/libcc1/ChangeLog +++ b/libcc1/ChangeLog @@ -1,3 +1,7 @@ +2017-11-14 Boris Kolpackov + + * configure: Regenerate. + 2017-11-16 Sergio Durigan Junior Pedro Alves diff --git a/libcc1/configure b/libcc1/configure index d6f480fe930..23d1a7645ff 100755 --- a/libcc1/configure +++ b/libcc1/configure @@ -14552,8 +14552,21 @@ fi pluginlibs= + plugin_check=yes case "${host}" in + *-*-mingw*) + # Since plugin support under MinGW is not as straightforward as on + # other platforms (e.g., we have to link import library, etc), we + # only enable it if explicitly requested. + if test x"$default_plugin" = x"yes"; then + enable_plugin=no + elif test x"$enable_plugin" = x"yes"; then + # Use make's target variable to derive import library name. + pluginlibs='-Wl,--export-all-symbols -Wl,--out-implib=$@.a' + plugin_check=no + fi + ;; *-*-darwin*) if test x$build = x$host; then export_sym_check="nm${exeext} -g" @@ -14574,7 +14587,7 @@ fi ;; esac - if test x"$enable_plugin" = x"yes"; then + if test x"$enable_plugin" = x"yes" -a x"$plugin_check" = x"yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for exported symbols" >&5 $as_echo_n "checking for exported symbols... " >&6; } -- 2.30.2