-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Use explicit symbol visibility. #314
Conversation
@theuni care to RE: LGTM over here? |
If I remove the SECP256K1_EXPORT from an API definition, everything keeps working. I would expect that since the visibility is default and not overridden anymore, external calls (from benchmarks) would fail? |
Ah, I guess the benchmarks linking the library is not considered to be across the library boundary. objdump -x on the .so file shows that doing so makes the API call go from 'g' to 'l' (global to local?). This raises the question: how can we test this? Nothing will catch a missing SECP256K1_EXPORT line. Other than that, Tested ACK. |
Needs rebase. |
@gmaxwell LGTM after rebase. @sipa Off the top of my head, we could test by:
|
@sipa Merge this and my next PR will be changing all the benchmarks to not be -static which then tests this. As I mentioned, I can't see any reason to -static the bench, as it works with static only builds even without it (thanks to libtool). |
@gmaxwell Will merge after rebase :) |
OKAY, rebased. |
Added the win32 dllimport. It took me a while to determine what this actually did, most of the MSFT documentation is pretty uninformative. A useful description is here: https://msdn.microsoft.com/en-us/library/aa271769%28v=vs.60%29.aspx |
Ultra nit: SECP256K1_EXPORT may be interpreted as "try to export", while its behaviour depends on whether we're in the library or not. Suggest SECP256K1_API? |
@sipa see #223 (comment) for reasoning behind change from _API to _EXPORT |
I'm for renaming No support for static build (other than predefining empty |
@sipa Sorry, I got confused between the names used in the two pull requests. My preference is
Also, I agree with @chfast that there should be a # if defined _MSC_VER || defined __CYGWIN__
# define SECP256K1_HELPER_LOCAL
# define SECP256K1_HELPER_DLL_IMPORT __declspec(dllimport)
# define SECP256K1_HELPER_DLL_EXPORT __declspec(dllexport)
# else
# if __GNUC__ >= 4
# define SECP256K1_HELPER_LOCAL __attribute__ ((visibility ("hidden")))
# define SECP256K1_HELPER_DLL_IMPORT __attribute__ ((visibility ("default")))
# define SECP256K1_HELPER_DLL_EXPORT __attribute__ ((visibility ("default")))
# else
# define SECP256K1_HELPER_LOCAL
# define SECP256K1_HELPER_DLL_IMPORT
# define SECP256K1_HELPER_DLL_EXPORT
# endif
# endif
# if defined SECP256K1_STATIC
# define SECP256K1_API SECP256K1_HELPER_LOCAL
# elif defined SECP256K1_EXPORTS
# define SECP256K1_API SECP256K1_HELPER_DLL_EXPORT
# else
# define SECP256K1_API SECP256K1_HELPER_DLL_IMPORT
# endif |
Static builds are supported just fine (unless dllimport breaks them on windows, in which case I think we should drop dllimport). In fact they are currently the built by default and used by the benchmarks by default in the current configuration. I currently disagree with the separate static define. It is messy, increases the already nearly untestably large ifdef combinitoric complexity, and unnecessary as far as I can tell. |
Also fix the nullness requirements for schnorr nonce-pair generation.
Changed the name to _API. |
Sounds like dllimport may break static linking (or at least make it less efficient). |
There are three sceanrios (actually four, but two can be collapsed), which requires more than one flag.
The |
evoskuil: When building the library the visibility annotations are set on the prototypes. This is mediated by the singular SECP256K1_BUILD macro which we also need for other reasons. Then using the library they are not. On *nix with libtool the library isn't even built twice for static vs dynamic: both outputs are created from the same intermediate result. You haven't explained why you are treating static specially. It is not special on unix (beyond the visibility policy being a no-op), is it actually special on win32 in some other way? |
@gmaxwell I don't see where Based on that assumption and this construction... # if defined(_WIN32)
# ifdef SECP256K1_BUILD
# define SECP256K1_API __declspec(dllexport)
# else
# define SECP256K1_API __declspec(dllimport)
# endif
...
# endif ... how does |
SECP256K1_BUILD is being set by the build system.
Having a special case for static building is incompatible with the fact
that the shared and static library are being built from the same .o.file.
|
Nevertheless, it is not possible to build or link statically for |
@evoskuil Does specifying dllexport break static linking?
|
Yes, and I'm pretty sure |
Can you explain how (e.g. what error results?) The original macro set I used (which doesn't set dllimport) is the same as I've used in other projects running on hundreds of millions of windows devices (and most built with the microsoft toolchain of some vintage or another). I'm unclear on the mechanism that would cause it to fail, as the microsoft documentation is vague. It works in mingw32 for me at least. |
@sipa SECP256K1_BUILD is set by secp256k1.c. FWIW. |
From what I read, not setting dllimport is always safe, just slightly less If dllimport is not causing any problems... sure, but if setting it is a |
The problem is with
for all functions so defined. More info here: https://msdn.microsoft.com/en-us/library/35bhkfb6.aspx Given your objectives I'd recommend just not setting |
So is the avoidance of a jump when calling the library worth:
|
@sipa It is sort of ugly that the caller has to make this distinction, but it's also extremely common in Windows builds. I try to minimize it to having to only define a symbol when linking statically, and doing nothing when linking dynamically. Most of the work I do is in C++, where this distinction is more significant. I don't think the first two issues you list are significant, but achieving your objective of a single compile seems to outweigh the small runtime hit. |
I don't really care about the compile time. More about the fact that the
build system needs to be aware of this. @cfields how hard would that be,
you think?
|
Oops, @theuni ^
|
The use of static makes this somewhat redundant currently, though if we later have multiple compilation units it will be needed. This also sets the dllexport needed for shared libraries on win32.
dropped the import. It's not just our own build system, or even alternative build-systems for our library that are my concern, but it's the fact that any downstream users' build-system would be impacted by the static/dynamic difference. I could see this being especially irritating for other software which is primarily developed on *nix, then mysteriously failing on windows. |
@@ -119,6 +119,20 @@ typedef int (*secp256k1_nonce_function)( | |||
# define SECP256K1_INLINE inline | |||
# endif | |||
|
|||
#ifndef SECP256K1_API | |||
# if defined(_WIN32) | |||
# ifdef SECP256K1_BUILD |
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.
Can you rename SECP256K1_BUILD
to SECP256K1_EXPORTS
? It is weird that SECP256K1_BUILD
is defined for dynamic builds but not for static builds.
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.
No. SECP256K1_BUILD does not differ for static vs dynamic build. There is no preprocessor differences between static and dynamic builds. This define is set during the build of the library itself, only during the build of library itself, and generally should not be touched by users.
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.
@gmaxwell I get that it's an objective is to avoid the conditional compilation of a static and dynamic build, and to avoid exposing that distinction to the library consumer. But what I don't understand is how this is actually accomplished. When intending to load a DLL a project is initially linked against a static (stub) lib. Since the stub forwards calls to the DLL it is necessarily a different build than the (full) static lib. In VC the stub and full static lib have the same name and define the same symbols, but I don't see how the full static lib can possibly be used as a stub and therefore how distinct builds can be avoided. It makes sense that one can link statically against the stub lib, built with |
@evoskuil The static and dynamic library are not the same. But they are Given that @gmaxwell's header preamble works in Opus on Windows, here is my
|
@sipa Are you saying that the build system produces a |
@evoskuil I can't test on Windows, but that's what I assume, yes. In Linux it works that way approximately. |
@sipa In order for it to work properly I believe it must be this way. But this of course means you cannot avoid exposing the conditionality to the consumer, even if you don't define |
@evoskuil Well obviously something in the build system needs to know what it is linking to. But the compiler doesn't need to know. Not on the library side, not on user side. Even better, the one building the library can choose whether to build the static version, the dynamic version, or both. And the user doesn't even need to care what was produced - only find the library. |
Anyway, back on track: is there are reason to think that the current pull request will not work? |
@sipa As long as you are exposing the three build outputs I described above, there's no problem. Sharing |
@evoskuil I believe this will work, but feel free to report Windows-related problems. |
Heh, @evoskuil Is bringing up my original concern here (see #312 (comment)). @sipa Fyi, libtool does build 2 separate objects with different sets of defines as necessary. DLL_EXPORT controls whether we're building for a shared lib or not, so that symbols can be exported as necessary. |
The use of static makes this somewhat redundant currently, though if
we later have multiple compilation units it will be needed.
This also sets the dllexport needed for shared libraries on win32.