-
Notifications
You must be signed in to change notification settings - Fork 1.1k
build: Add SECP256K1_NO_EXPORTS option to avoid default visibility for static builds #1674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sigh yeah.
Alternatively, we could wrap the entire Windows logic in an #ifndef SECP256K1_API
, like the non-Windows logic, giving the user an ultimate override. That's simpler. Is it better? I tend to say yes.
include/secp256k1.h
Outdated
* 1. If using Libtool, it defines DLL_EXPORT automatically. | ||
* 2. In other cases, SECP256K1_DLL_EXPORT must be defined. */ | ||
* 2. If using CMake, it defines SECP256K1_DLL_EXPORT automatically. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what I had in mind here is rather this (and I agree this isn't clear from the comment):
* 1. Libtool has a built-in -DDLL_EXPORT for all builds of DLLs.
* 2. Non-libtool builds should define SECP256K1_DLL_EXPORT. */
(... as we do in CMakeLists.txt)
include/secp256k1.h
Outdated
@@ -153,7 +153,7 @@ typedef int (*secp256k1_nonce_function)( | |||
#endif | |||
#ifndef SECP256K1_API | |||
/* All cases not captured by the Windows-specific logic. */ | |||
# if defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD) | |||
# if defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD) && !defined(SECP256K1_NO_EXPORTS) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think && !defined(SECP256K1_NO_EXPORTS)
should also be added to line 131 for Windows. (If you set this for whatever reason, you probably expect it to work also on Windows.)
nit: SECP256K1_NO_EXPORT
is a slightly better name because it matches (SECP256K1)_DLL_EXPORT
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added that to begin with but removed it because we can only reach this when building a Windows DLL, and it doesn't make sense to do so without exporting.
I can add it back for symmetry if you'd like, but I'm not sure there's any actual use for it?
Okay, actually... Let me take a step back. What do we (as libsecp256k1) want for static builds in general? Or is this a question that just cannot be answered properly because it depends on the user because some users would want to reexport, some won't (e.g., kernel). But even if the latter is true, it seems to me that the defaults should at least be consistent across platforms, no? |
IMO static builds should always be hidden by default and shared should be exported: My initial attempt was something like: diff --git a/include/secp256k1.h b/include/secp256k1.h
index 1d25a02a..8f718636 100644
--- a/include/secp256k1.h
+++ b/include/secp256k1.h
@@ -129,10 +129,10 @@ typedef int (*secp256k1_nonce_function)(
* Attributes" in the GCC manual and the recommendations in
* https://gcc.gnu.org/wiki/Visibility. */
# if defined(SECP256K1_BUILD)
-# if defined(DLL_EXPORT) || defined(SECP256K1_DLL_EXPORT)
+# if defined(DLL_EXPORT) || defined(SECP256K1_EXPORT_SYMBOLS)
/* Building libsecp256k1 as a DLL.
* 1. If using Libtool, it defines DLL_EXPORT automatically.
- * 2. In other cases, SECP256K1_DLL_EXPORT must be defined. */
+ * 2. In other cases, SECP256K1_EXPORT_SYMBOLS must be defined. */
# define SECP256K1_API extern __declspec (dllexport)
# else
/* Building libsecp256k1 as a static library on Windows.
@@ -153,7 +153,7 @@ typedef int (*secp256k1_nonce_function)(
#endif
#ifndef SECP256K1_API
/* All cases not captured by the Windows-specific logic. */
-# if defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD)
+# if defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD) && defined(SECP256K1_EXPORT_SYMBOLS)
/* Building libsecp256k1 using GCC or compatible. */
# define SECP256K1_API extern __attribute__ ((visibility ("default")))
# else
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f31b8c8f..57bb7dae 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -20,11 +20,9 @@ if(SECP256K1_ASM STREQUAL "arm32")
target_link_libraries(secp256k1_asm INTERFACE secp256k1_asm_arm)
endif()
-if(WIN32)
# Define our export symbol only for shared libs.
- set_target_properties(secp256k1 PROPERTIES DEFINE_SYMBOL SECP256K1_DLL_EXPORT)
+ set_target_properties(secp256k1 PROPERTIES DEFINE_SYMBOL SECP256K1_EXPORT_SYMBOLS)
target_compile_definitions(secp256k1 INTERFACE $<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:SECP256K1_STATIC>)
-endif()
# Object libs don't know if they're being built for a shared or static lib.
# Grab the PIC property from secp256k1 which knows. Sadly, that would make the CMake and Autotools behaviors diverge as there's no way to get autotools to define anything for a shared build. But since Non-Windows+CMake is likely to be the most popular build config eventually (if it isn't already), maybe we could accept that divergence and provide an additional opt-out for autotools builders? It's ideal though imo, as it does the right thing by default in all cases, and would allow As mentioned above, I'm not sure there's any utility in allowing the opposite.. building shared and disabling exports. |
Btw, this is only becoming an issue now because we (the kernel working group) were discussing a monolithic libbitcoinkernel.a yesterday, which would include secp: theuni/bitcoin@4f43838 While testing that, the shared version ( |
Is this what you mean by monolithic? What would be the benefit of this? It feels to me that if you consume the
But yes, I agree. I tend to agree that hidden is a sane default for static libs, and that reexporting should be asked for explicitly. I tend to like your
Indeed.
The question for our autotools build will be: Should it define By the way, for anyone wondering what visibility is supposed to do anyway in a static build (because visibility is a concept that applies only to shared libs): The only way visibility influences a static lib is that the visibility information is kept in the static lib, solely for the potential case that this static library will be linked into a shared lib (and the visibility will apply there again). |
cc @hebasto |
define SECP256K1_CMAKE_SHARED_BUILD for all CMake shared lib builds, not just Windows. This allows us to only set default symbol visibility for shared lib builds. For static libs built with CMake, or Autotools builds for Windows, static builds will prefer to hide symbols by default. Autotools does not have the ability to set a define for non-Windows shared builds, so the best we can do there is guess. SECP256K1_NO_EXPORT_SYMBOLS can be defined as an escape-hatch in case a non-Windows CMake builder wants default visibility.
…ption" This reverts commit d147876. Hidden visiblity is still useful for builders including a static libsecp.
See the commit linked above. This came out of a discussion about having a self-contained The only solution that seems to work is to do the same with secp: linking in its objects. I agree this is a violation of the interface as Core shouldn't have to know anything about secp's internals. Ideally there would be a convenience library to handle this, something like
I've pushed a revised version of this. It guesses that non-Windows Autotools builders always want default visibility, and provides an opt-out to override.
Yes, exactly, thanks for the link :) Additionally, d147876 is reverted here as that's needed to actually do the hiding. |
Wait. This is the story on non-Windows:
But this means we also won't need to set For posterity, it is possible to check whether a shared library is built by libtool.This is an earlier attempt of mine:# if defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD)
/* Building libsecp256k1 using GCC or compatible.
*
* We would like to set default visibility if building a shared library and
* hidden visibility if building a static library.
* 1. Libtool has a builtin -DPIC (and -fPIC on targets that support it).
* 2. Our CMake build defines SECP256K1_SHARED_BUILD automatically.
* 3. In all other cases, we guess based on __PIC__, which is (always defined
* and) greater than 0 if -fpic or -fPIC is used. */
# if defined(PIC) || defined(SECP256K1_EXPORT_SYMBOLS) || (defined(__PIC__) && __PIC__ > 0)
# define SECP256K1_API extern __attribute__ ((visibility ("default")))
# elif defined(SECP256K1_NO_EXPORT_SYMBOLS)
# define SECP256K1_API extern __attribute__ ((visibility ("hidden")))
# endif
# endif |
Yeah, why not. If Core needs this somewhere, then it's easier to maintain it here. |
Yep, I agree with everything here. As I mentioned in #1677, this would generally do the right thing, but it would break builds for shared lib builders who compile with |
Unfortunately, PIC isn't reliable for detecting shared libs either :( See Core's autoconf depends builds for example: https://github.com/bitcoin/bitcoin/blob/master/depends/funcs.mk#L187 Even when building static libs, we need to compile PIC as the objects will eventually be used in PIE binaries. |
Yep, yet another visiblity PR :)
Noticed this when building libbitcoinkernel for Linux. Core needs a way to link a static secp into a (shared or static) kernel without exporting the secp symbols.
autotools defines
DLL_EXPORT
for dll builds. CMake mimics this behavior and definesSECP256K1_DLL_EXPORT
.This means that currently Windows builds only export their symbols for shared libraries, and all other platforms export for shared AND static.
Unfortunately, there's no way to make autotools define a special variable when building for other platforms.
CMake could define a variable for shared lib builds on all platforms, which the header could then use to decide whether or not to export symbols, but that would introduce a behavioral difference between the build-systems.
Instead, provide an escape hatch via
SECP256K1_NO_EXPORTS
which can be used when building secp for non-Windows targets when exports are not desired (when building a static lib).