… and more Windows fixes, please see the individual commits.
The fixed issues were discovered in #1198.
… and more Windows fixes, please see the individual commits.
The fixed issues were discovered in #1198.
This fixes a build issue with MSVC. While MSVC imports *functions*
from DLLs automatically when building a consumer of the DLL, it does
not import *variables* automatically. In these cases, we need an
explicit __declspec(dllimport).
This commit simply changes our logic to what the libtool manual
suggests, which has a very comprehensive writeup on the topic. Note
that in particular, this solution is carefully designed not to break
static linking. However, as described in the libtool manual,
statically linking the library with MSVC will output warning LNK4217.
This is still the best solution overall, because the warning is
merely a cosmetic issue.
Besides improving the examples, this makes sure that the examples
import a variable (instead of a function), namely the static context,
from the library. This is helpful when testing MSVC builds, because
the MSVC linker tends to be awkward when importing variables.
... and use correct format to pass linker flags
Before: CI times out when a wine task fails.
After: Wine tasks exit properly when they fail.
Note that users will have the same issue that they see linker warning LNK4217 when they try to use MSVC to link statically against libsecp256k1 (but better a warning than a failing build.)
I think we should note that somewhere and recommend ignoring that warning by passing -ignore:4217
(but where should we note that?). We could alternatively offer the possibility suppress the dllimport() stuff when a macro SECP256K1_STATIC_LINKAGE
is defined, but I don’t think that’s better than just documenting the issue. (The macro will also need documentation.)
SECP256K1_STATIC_LINKAGE
would do is result in not emitting a warning at link time that can be more directly ignored too, I don’t think there is much need for it.
114@@ -115,6 +115,12 @@ AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [
115 if test x"$GCC" != x"yes" && test x"$build_windows" = x"yes"; then
116 SECP_TRY_APPEND_CFLAGS([-W2 -wd4146], $1) # Moderate warning level, disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned"
117 SECP_TRY_APPEND_CFLAGS([-external:anglebrackets -external:W0], $1) # Suppress warnings from #include <...> files
118+ # We pass -ignore:4217 to the MSVC linker to suppress warning 4217 when
119+ # importing variables from a statically linked secp256k1.
120+ # (See the libtool manual, section "Windows DLLs" for background.)
121+ # Unfortunately, libtool tries to be too clever and strips "-Xlinker arg"
Sorry for touching this PR after 2 weeks, but I need to clarify a few things, at least for myself.
After @theuni’s #1105, when cross-compiling a library for Windows, the SECP256K1_API
macro is defined to __declspec(dllexport)
because Libtool uses -DDLL_EXPORT
.
After this PR, that is no longer the case.
Are we skipping __declspec(dllexport)
for cross-compiled DLL altogether?
After @theuni’s #1105, when cross-compiling a library for Windows, the
SECP256K1_API
macro is defined to__declspec(dllexport)
because Libtool uses-DDLL_EXPORT
.After this PR, that is no longer the case.
I can’t follow. What’s the exact combination of defines under which this PR has changed something and which bothers you?
I can’t follow. What’s the exact combination of defines under which this PR has changed something and which bothers you?
Nothing special. Just ./configure --host=x86_64-w64-mingw32 --disable-static
. The SECP256K1_API
macro is defined as follows:
0# define SECP256K1_API __declspec(dllexport)
0# define SECP256K1_API __attribute__ ((visibility ("default")))
Ok, I see.
Are we skipping
__declspec(dllexport)
for cross-compiled DLL altogether?
Yes, at least when using MinGW. (I assume you call it cross-compiling when GNU tools are used to build a DLL?) As I understand https://www.gnu.org/software/libtool/manual/libtool.html#Windows-DLLs, this should work. I guess MinGW counts as GNU tool. I simply took the code from there because they seem to have thought that through.
Yes, at least when using MinGW. (I assume you call it cross-compiling when GNU tools are used to build a DLL?)
Correct, I’m using MinGW.
As I understand https://www.gnu.org/software/libtool/manual/libtool.html#Windows-DLLs, this should work.
Apparently, it works :)
Thanks to @theuni it is clear now that this PR did introduce a regression that listed in #1181:
- With
CFLAGS=-fvisibility=default
, there’s a few exported variables :secp256k1_ecmult_gen_prec_table
,secp256k1_pre_g
,secp256k1_pre_g_128
I’ve verified that @theuni’s suggestion fixes this regression.
Sorry for the late question :)
I’m curios about rationale of putting the extern
keyword into the macro?
I’m curios about rationale of putting the
extern
keyword into the macro?
Ha, the simple answer is that I took this from the libtool manual and it just worked.
If you’re asking why we need extern
at all, then this is a bit ugly in C: The rules are different for functions are variables: https://port70.net/~nsz/c/c11/n1570.html#6.2.2p5 In my own words: extern
is always the default for functions, so you don’t have to write it. For variables, extern
is only the default if they appear in “file scope”/top level.
So if some user code had #include <secp256k1.h>
within a function (for whatever reason), our variable declarations wouldn’t work without extern
. (See https://gcc.godbolt.org/z/9h1qqEjT7, also try enabling linking and look at the linker errors).
I’m curios about rationale of putting the
extern
keyword into the macro?Ha, the simple answer is that I took this from the libtool manual and it just worked.
If you’re asking why we need
extern
at all…
My point is if we keep extern
in their place in the source code, then the SECP256K1_API_VAR
macro becomes equal to SECP256K1_API
.
I’m curios about rationale of putting the
extern
keyword into the macro?Ha, the simple answer is that I took this from the libtool manual and it just worked. If you’re asking why we need
extern
at all…My point is if we keep
extern
in their place in the source code, then theSECP256K1_API_VAR
macro becomes equal toSECP256K1_API
.
Oh, I see. In the # elif defined _MSC_VER
case, it’s simply not true that SECP256K1_API_VAR
= extern
+ SECP256K1_API
. See also my response here: #1362 (review)