On mingw and MSVC 14, in 32-bit mode, I'm seeing these errors in a testdir build with C++ tests:
depbase=`echo test-netdb-c++.o | sed 's|[^/]*$|.deps/&|;s|\.o$||'`;\ i686-w64-mingw32-g++ -DHAVE_CONFIG_H -DEXEEXT=\".exe\" -I. -I../../gltests -I.. -DGNULIB_STRICT_CHECKING=1 -DIN_GNULIB_TESTS=1 -I. -I../../gltests -I.. -I../../gltests/.. -I../gllib -I../../gltests/../gllib -I/usr/local/mingw32/include -Wall -MT test-netdb-c++.o -MD -MP -MF $depbase.Tpo -c -o test-netdb-c++.o ../../gltests/test-netdb-c++.cc &&\ mv -f $depbase.Tpo $depbase.Po In file included from ../gllib/netdb.h:42:0, from ../../gltests/test-netdb-c++.cc:22: ../gllib/netdb.h: In member function ‘gnulib::_gl_getaddrinfo_wrapper::operator gnulib::_gl_getaddrinfo_wrapper::type() const’: ../gllib/netdb.h:647:1: error: invalid conversion from ‘int (__attribute__((__stdcall__)) *)(const char*, const char*, const addrinfo*, addrinfo**)’ to ‘gnulib::_gl_getaddrinfo_wrapper::type {aka int (*)(const char*, const char*, const addrinfo*, addrinfo**)}’ [-fpermissive] _GL_CXXALIAS_SYS (getaddrinfo, int, ^ ../gllib/netdb.h: In member function ‘gnulib::_gl_freeaddrinfo_wrapper::operator gnulib::_gl_freeaddrinfo_wrapper::type() const’: ../gllib/netdb.h:661:1: error: invalid conversion from ‘void (__attribute__((__stdcall__)) *)(LPADDRINFO) {aka void (__attribute__((__stdcall__)) *)(addrinfo*)}’ to ‘gnulib::_gl_freeaddrinfo_wrapper::type {aka void (*)(addrinfo*)}’ [-fpermissive] _GL_CXXALIAS_SYS (freeaddrinfo, void, (struct addrinfo *ai)); ^ make[4]: *** [Makefile:9813: test-netdb-c++.o] Error 1 source='../../gltests/test-netdb-c++.cc' object='test-netdb-c++.obj' libtool=no \ DEPDIR=.deps depmode=msvc7 /bin/sh ../../build-aux/depcomp \ /home/bruno/msvc/compile cl -nologo -DHAVE_CONFIG_H -DEXEEXT=\".exe\" -I. -I../../gltests -I.. -DGNULIB_STRICT_CHECKING=1 -DIN_GNULIB_TESTS=1 -I. -I../../gltests -I.. -I../../gltests/.. -I../gllib -I../../gltests/../gllib -D_WIN32_WINNT=_WIN32_WINNT_WINXP -I/usr/local/msvc32/include -MD -c -o test-netdb-c++.obj `cygpath -w '../../gltests/test-netdb-c++.cc'` test-netdb-c++.cc ../gllib\netdb.h(651): error C2440: 'return': cannot convert from 'INT (__stdcall *)(PCSTR,PCSTR,const ADDRINFOA *,PADDRINFOA *)' to 'gnulib::_gl_getaddrinfo_wrapper::type' ../gllib\netdb.h(651): note: This conversion requires a reinterpret_cast, a C-style cast or function-style cast ../gllib\netdb.h(661): error C2440: 'return': cannot convert from 'void (__stdcall *)(PADDRINFOA)' to 'gnulib::_gl_freeaddrinfo_wrapper::type' ../gllib\netdb.h(661): note: This conversion requires a reinterpret_cast, a C-style cast or function-style cast make[4]: *** [Makefile:9824: test-netdb-c++.obj] Error 2 It's complaining about the fact that we expect a function declaration with the default calling convention (cdecl), but the winsock2 functions are declared with '__stdcall', which is incompatible with cdecl in 32-bit mode (see https://docs.microsoft.com/en-us/cpp/cpp/stdcall?view=vs-2019 ). This patch fixes it. 2019-12-11 Bruno Haible <br...@clisp.org> getaddrinfo: Fix calling convention in 32-bit mode on native Windows. * m4/getaddrinfo.m4 (gl_GETADDRINFO): Test whether getaddrinfo has a non-POSIX signature. If so, set REPLACE_GETADDRINFO. Define HAVE_GETADDRINFO as a C macro. * lib/netdb.in.h (getaddrinfo, freeaddrinfo): If REPLACE_GETADDRINFO, declare as replacement functions. * lib/getaddrinfo.c (getaddrinfo, freeaddrinfo): If HAVE_GETADDRINFO, define as no-op overrides. * m4/netdb_h.m4 (gl_NETDB_H_DEFAULTS): Initialize REPLACE_GETADDRINFO. * modules/netdb (Makefile.am): Substitute REPLACE_GETADDRINFO. * modules/getaddrinfo (Depends-on, configure.ac): Test REPLACE_GETADDRINFO. * doc/posix-functions/getaddrinfo.texi: Mention calling convention problem. * doc/posix-functions/freeaddrinfo.texi: Mention header file and calling convention problems. diff --git a/doc/posix-functions/freeaddrinfo.texi b/doc/posix-functions/freeaddrinfo.texi index 4aa9af2..8116571 100644 --- a/doc/posix-functions/freeaddrinfo.texi +++ b/doc/posix-functions/freeaddrinfo.texi @@ -11,6 +11,12 @@ Portability problems fixed by Gnulib: @item This function is missing on some platforms: HP-UX 11.11, IRIX 6.5, Cygwin 1.5.x, mingw, MSVC 14. +@item +On Windows, this function is declared in @code{<ws2tcpip.h>} rather than in +@code{<netdb.h>}. +@item +On Windows, in 32-bit mode, this function is defined with a calling convention +that is different from @code{cdecl}. @end itemize Portability problems not fixed by Gnulib: diff --git a/doc/posix-functions/getaddrinfo.texi b/doc/posix-functions/getaddrinfo.texi index 1e4b244..eae5b8b 100644 --- a/doc/posix-functions/getaddrinfo.texi +++ b/doc/posix-functions/getaddrinfo.texi @@ -14,6 +14,9 @@ HP-UX 11.11, IRIX 6.5, Cygwin 1.5.x, mingw, MSVC 14. @item On Windows, this function is declared in @code{<ws2tcpip.h>} rather than in @code{<netdb.h>}. +@item +On Windows, in 32-bit mode, this function is defined with a calling convention +that is different from @code{cdecl}. @end itemize Portability problems not fixed by Gnulib: diff --git a/lib/getaddrinfo.c b/lib/getaddrinfo.c index 123a2b2..56cbe2b 100644 --- a/lib/getaddrinfo.c +++ b/lib/getaddrinfo.c @@ -54,18 +54,41 @@ # define PF_UNSPEC 0 #endif -#if defined _WIN32 && !defined __CYGWIN__ -# define WINDOWS_NATIVE -#endif +#if HAVE_GETADDRINFO + +/* Override with cdecl calling convention. */ + +int +getaddrinfo (const char *restrict nodename, + const char *restrict servname, + const struct addrinfo *restrict hints, + struct addrinfo **restrict res) +# undef getaddrinfo +{ + return getaddrinfo (nodename, servname, hints, res); +} + +void +freeaddrinfo (struct addrinfo *ai) +# undef freeaddrinfo +{ + freeaddrinfo (ai); +} + +#else + +# if defined _WIN32 && !defined __CYGWIN__ +# define WINDOWS_NATIVE +# endif /* gl_sockets_startup */ -#include "sockets.h" +# include "sockets.h" -#ifdef WINDOWS_NATIVE +# ifdef WINDOWS_NATIVE /* Avoid warnings from gcc -Wcast-function-type. */ -# define GetProcAddress \ - (void *) GetProcAddress +# define GetProcAddress \ + (void *) GetProcAddress typedef int (WSAAPI *getaddrinfo_func) (const char*, const char*, const struct addrinfo*, @@ -112,20 +135,20 @@ use_win32_p (void) return 1; } -#endif +# endif static bool validate_family (int family) { /* FIXME: Support more families. */ -#if HAVE_IPV4 +# if HAVE_IPV4 if (family == PF_INET) return true; -#endif -#if HAVE_IPV6 +# endif +# if HAVE_IPV6 if (family == PF_INET6) return true; -#endif +# endif if (family == PF_UNSPEC) return true; return false; @@ -144,23 +167,23 @@ getaddrinfo (const char *restrict nodename, struct hostent *he; void *storage; size_t size; -#if HAVE_IPV6 +# if HAVE_IPV6 struct v6_pair { struct addrinfo addrinfo; struct sockaddr_in6 sockaddr_in6; }; -#endif -#if HAVE_IPV4 +# endif +# if HAVE_IPV4 struct v4_pair { struct addrinfo addrinfo; struct sockaddr_in sockaddr_in; }; -#endif +# endif -#ifdef WINDOWS_NATIVE +# ifdef WINDOWS_NATIVE if (use_win32_p ()) return getaddrinfo_ptr (nodename, servname, hints, res); -#endif +# endif if (hints && (hints->ai_flags & ~(AI_CANONNAME|AI_PASSIVE))) /* FIXME: Support more flags. */ @@ -179,11 +202,11 @@ getaddrinfo (const char *restrict nodename, if (!(hints->ai_flags & AI_PASSIVE)) return EAI_NONAME; -#ifdef HAVE_IPV6 +# ifdef HAVE_IPV6 nodename = (hints->ai_family == AF_INET6) ? "::" : "0.0.0.0"; -#else +# else nodename = "0.0.0.0"; -#endif +# endif } if (servname) @@ -217,17 +240,17 @@ getaddrinfo (const char *restrict nodename, switch (he->h_addrtype) { -#if HAVE_IPV6 +# if HAVE_IPV6 case PF_INET6: size = sizeof (struct v6_pair); break; -#endif +# endif -#if HAVE_IPV4 +# if HAVE_IPV4 case PF_INET: size = sizeof (struct v4_pair); break; -#endif +# endif default: return EAI_NODATA; @@ -239,7 +262,7 @@ getaddrinfo (const char *restrict nodename, switch (he->h_addrtype) { -#if HAVE_IPV6 +# if HAVE_IPV6 case PF_INET6: { struct v6_pair *p = storage; @@ -261,9 +284,9 @@ getaddrinfo (const char *restrict nodename, tmp->ai_addrlen = sizeof *sinp; } break; -#endif +# endif -#if HAVE_IPV4 +# if HAVE_IPV4 case PF_INET: { struct v4_pair *p = storage; @@ -285,7 +308,7 @@ getaddrinfo (const char *restrict nodename, tmp->ai_addrlen = sizeof *sinp; } break; -#endif +# endif default: free (storage); @@ -313,21 +336,21 @@ getaddrinfo (const char *restrict nodename, tmp->ai_addr->sa_family = he->h_addrtype; tmp->ai_family = he->h_addrtype; -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN +# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN switch (he->h_addrtype) { -#if HAVE_IPV4 +# if HAVE_IPV4 case AF_INET: tmp->ai_addr->sa_len = sizeof (struct sockaddr_in); break; -#endif -#if HAVE_IPV6 +# endif +# if HAVE_IPV6 case AF_INET6: tmp->ai_addr->sa_len = sizeof (struct sockaddr_in6); break; -#endif +# endif } -#endif +# endif /* FIXME: If more than one address, create linked list of addrinfo's. */ @@ -340,13 +363,13 @@ getaddrinfo (const char *restrict nodename, void freeaddrinfo (struct addrinfo *ai) { -#ifdef WINDOWS_NATIVE +# ifdef WINDOWS_NATIVE if (use_win32_p ()) { freeaddrinfo_ptr (ai); return; } -#endif +# endif while (ai) { @@ -366,11 +389,11 @@ getnameinfo (const struct sockaddr *restrict sa, socklen_t salen, char *restrict service, socklen_t servicelen, int flags) { -#ifdef WINDOWS_NATIVE +# ifdef WINDOWS_NATIVE if (use_win32_p ()) return getnameinfo_ptr (sa, salen, node, nodelen, service, servicelen, flags); -#endif +# endif /* FIXME: Support other flags. */ if ((node && nodelen > 0 && !(flags & NI_NUMERICHOST)) || @@ -383,18 +406,18 @@ getnameinfo (const struct sockaddr *restrict sa, socklen_t salen, switch (sa->sa_family) { -#if HAVE_IPV4 +# if HAVE_IPV4 case AF_INET: if (salen < sizeof (struct sockaddr_in)) return EAI_FAMILY; break; -#endif -#if HAVE_IPV6 +# endif +# if HAVE_IPV6 case AF_INET6: if (salen < sizeof (struct sockaddr_in6)) return EAI_FAMILY; break; -#endif +# endif default: return EAI_FAMILY; } @@ -403,23 +426,23 @@ getnameinfo (const struct sockaddr *restrict sa, socklen_t salen, { switch (sa->sa_family) { -#if HAVE_IPV4 +# if HAVE_IPV4 case AF_INET: if (!inet_ntop (AF_INET, &(((const struct sockaddr_in *) sa)->sin_addr), node, nodelen)) return EAI_SYSTEM; break; -#endif +# endif -#if HAVE_IPV6 +# if HAVE_IPV6 case AF_INET6: if (!inet_ntop (AF_INET6, &(((const struct sockaddr_in6 *) sa)->sin6_addr), node, nodelen)) return EAI_SYSTEM; break; -#endif +# endif default: return EAI_FAMILY; @@ -429,12 +452,12 @@ getnameinfo (const struct sockaddr *restrict sa, socklen_t salen, if (service && servicelen > 0 && flags & NI_NUMERICSERV) switch (sa->sa_family) { -#if HAVE_IPV4 +# if HAVE_IPV4 case AF_INET: -#endif -#if HAVE_IPV6 +# endif +# if HAVE_IPV6 case AF_INET6: -#endif +# endif { unsigned short int port = ntohs (((const struct sockaddr_in *) sa)->sin_port); @@ -446,3 +469,5 @@ getnameinfo (const struct sockaddr *restrict sa, socklen_t salen, return 0; } + +#endif diff --git a/lib/netdb.in.h b/lib/netdb.in.h index 8013465..d5078de 100644 --- a/lib/netdb.in.h +++ b/lib/netdb.in.h @@ -158,33 +158,61 @@ struct addrinfo # endif # endif -# if !@HAVE_DECL_GETADDRINFO@ /* Translate name of a service location and/or a service name to set of socket addresses. For more details, see the POSIX:2008 specification <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html>. */ +# if @REPLACE_GETADDRINFO@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef getaddrinfo +# define getaddrinfo rpl_getaddrinfo +# endif +_GL_FUNCDECL_RPL (getaddrinfo, int, + (const char *restrict nodename, + const char *restrict servname, + const struct addrinfo *restrict hints, + struct addrinfo **restrict res) + _GL_ARG_NONNULL ((4))); +_GL_CXXALIAS_RPL (getaddrinfo, int, + (const char *restrict nodename, + const char *restrict servname, + const struct addrinfo *restrict hints, + struct addrinfo **restrict res)); +# else +# if !@HAVE_DECL_GETADDRINFO@ _GL_FUNCDECL_SYS (getaddrinfo, int, (const char *restrict nodename, const char *restrict servname, const struct addrinfo *restrict hints, struct addrinfo **restrict res) _GL_ARG_NONNULL ((4))); -# endif +# endif _GL_CXXALIAS_SYS (getaddrinfo, int, (const char *restrict nodename, const char *restrict servname, const struct addrinfo *restrict hints, struct addrinfo **restrict res)); +# endif _GL_CXXALIASWARN (getaddrinfo); -# if !@HAVE_DECL_FREEADDRINFO@ /* Free 'addrinfo' structure AI including associated storage. For more details, see the POSIX:2008 specification <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html>. */ +# if @REPLACE_GETADDRINFO@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef freeaddrinfo +# define freeaddrinfo rpl_freeaddrinfo +# endif +_GL_FUNCDECL_RPL (freeaddrinfo, void, (struct addrinfo *ai) + _GL_ARG_NONNULL ((1))); +_GL_CXXALIAS_RPL (freeaddrinfo, void, (struct addrinfo *ai)); +# else +# if !@HAVE_DECL_FREEADDRINFO@ _GL_FUNCDECL_SYS (freeaddrinfo, void, (struct addrinfo *ai) _GL_ARG_NONNULL ((1))); -# endif +# endif _GL_CXXALIAS_SYS (freeaddrinfo, void, (struct addrinfo *ai)); +# endif _GL_CXXALIASWARN (freeaddrinfo); # if @REPLACE_GAI_STRERROR@ diff --git a/m4/getaddrinfo.m4 b/m4/getaddrinfo.m4 index 59b8571..d1d01bf 100644 --- a/m4/getaddrinfo.m4 +++ b/m4/getaddrinfo.m4 @@ -1,4 +1,4 @@ -# getaddrinfo.m4 serial 31 +# getaddrinfo.m4 serial 32 dnl Copyright (C) 2004-2019 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -55,10 +55,40 @@ AC_DEFUN([gl_GETADDRINFO], if test "$gl_cv_w32_getaddrinfo" = "yes"; then GETADDRINFO_LIB="-lws2_32" LIBS="$gai_saved_LIBS $GETADDRINFO_LIB" + dnl Check for correct signature, in particular for a cdecl-compatible + dnl calling convention. + AC_CACHE_CHECK([for getaddrinfo with POSIX signature], + [gl_cv_func_getaddrinfo_posix_signature], + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +#include <sys/types.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_WS2TCPIP_H +#include <ws2tcpip.h> +#endif +#include <stddef.h> +extern +#ifdef __cplusplus +"C" +#endif +int getaddrinfo (const char *, const char *, const struct addrinfo *, struct addrinfo **); +]])], + [gl_cv_func_getaddrinfo_posix_signature=yes], + [gl_cv_func_getaddrinfo_posix_signature=no]) + ]) + if test $gl_cv_func_getaddrinfo_posix_signature = no; then + REPLACE_GETADDRINFO=1 + fi else HAVE_GETADDRINFO=0 fi fi + AC_DEFINE_UNQUOTED([HAVE_GETADDRINFO], [$HAVE_GETADDRINFO], + [Define to 1 if getaddrinfo exists, or to 0 otherwise.]) # We can't use AC_REPLACE_FUNCS here because gai_strerror may be an # inline function declared in ws2tcpip.h, so we need to get that diff --git a/m4/netdb_h.m4 b/m4/netdb_h.m4 index 498c566..cb762c6 100644 --- a/m4/netdb_h.m4 +++ b/m4/netdb_h.m4 @@ -1,4 +1,4 @@ -# netdb_h.m4 serial 11 +# netdb_h.m4 serial 12 dnl Copyright (C) 2008-2019 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -40,4 +40,5 @@ AC_DEFUN([gl_NETDB_H_DEFAULTS], HAVE_DECL_GETADDRINFO=1; AC_SUBST([HAVE_DECL_GETADDRINFO]) HAVE_DECL_GETNAMEINFO=1; AC_SUBST([HAVE_DECL_GETNAMEINFO]) REPLACE_GAI_STRERROR=0; AC_SUBST([REPLACE_GAI_STRERROR]) + REPLACE_GETADDRINFO=0; AC_SUBST([REPLACE_GETADDRINFO]) ]) diff --git a/modules/getaddrinfo b/modules/getaddrinfo index 195a24a..490838a 100644 --- a/modules/getaddrinfo +++ b/modules/getaddrinfo @@ -10,18 +10,18 @@ Depends-on: netdb sys_socket extensions -gettext-h [test $HAVE_GETADDRINFO = 0 || test $HAVE_DECL_GAI_STRERROR = 0 || test $REPLACE_GAI_STRERROR = 1] -inet_ntop [test $HAVE_GETADDRINFO = 0] -snprintf [test $HAVE_GETADDRINFO = 0] -stdbool [test $HAVE_GETADDRINFO = 0] -strdup [test $HAVE_GETADDRINFO = 0] -servent [test $HAVE_GETADDRINFO = 0] -hostent [test $HAVE_GETADDRINFO = 0] -sockets [test $HAVE_GETADDRINFO = 0] +gettext-h [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1 || test $HAVE_DECL_GAI_STRERROR = 0 || test $REPLACE_GAI_STRERROR = 1] +inet_ntop [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1] +snprintf [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1] +stdbool [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1] +strdup [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1] +servent [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1] +hostent [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1] +sockets [test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1] configure.ac: gl_GETADDRINFO -if test $HAVE_GETADDRINFO = 0; then +if test $HAVE_GETADDRINFO = 0 || test $REPLACE_GETADDRINFO = 1; then AC_LIBOBJ([getaddrinfo]) fi if test $HAVE_DECL_GAI_STRERROR = 0 || test $REPLACE_GAI_STRERROR = 1; then diff --git a/modules/netdb b/modules/netdb index b324866..89408fa 100644 --- a/modules/netdb +++ b/modules/netdb @@ -36,6 +36,7 @@ netdb.h: netdb.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) -e 's|@''HAVE_DECL_GETADDRINFO''@|$(HAVE_DECL_GETADDRINFO)|g' \ -e 's|@''HAVE_DECL_GETNAMEINFO''@|$(HAVE_DECL_GETNAMEINFO)|g' \ -e 's|@''REPLACE_GAI_STRERROR''@|$(REPLACE_GAI_STRERROR)|g' \ + -e 's|@''REPLACE_GETADDRINFO''@|$(REPLACE_GETADDRINFO)|g' \ -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \