Skip to content

build: Add SECP256K1_FORCE_HIDDEN_VISIBILITY #1677

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

real-or-random
Copy link
Contributor

Alternative to #1674.

*
* While visibility is a concept that applies only to shared libraries, setting
* visibility will still make a difference when building a static library: the
* visibility settings will be stored in the static library, solely for the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind sharing more details regarding "the visibility settings will be stored in the static library" please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See https://stackoverflow.com/a/67473340/2725281. I don't think I have more to share, but I assume @theuni can confirm the behavior.

@real-or-random
Copy link
Contributor Author

Like #1674, this defaults to "default" (haha) visibility. While not perfect for static builds -- we'd probably prefer a default of "hidden" there -- this avoids any attempt to detection in the preprocessor whether a static or a shared library is built. Some kind of detection is possible (see my comment in #1674), but it will most likely never be perfect, and some manual override will be necessary anyway. Defaulting to "default" avoids the need to detect and is thus much simpler.

@theuni
Copy link
Contributor

theuni commented Jun 3, 2025

Hmm.

There's a pretty common assumption that when using -DCMAKE_C_VISIBILITY_PRESET=hidden or CXXFLAGS="-fvisibility=hidden", necessary symbols will be override that setting and be set to default. That's the premise behind the de-facto guide, at least.

So I think many packagers would run into problems with this, as that flag (which is usually considered safe for visibility-aware libs) would essentially produce broken shared libs (except on Windows).

@real-or-random
Copy link
Contributor Author

Hm, that assumption sounds misguided to me. If a packager expects a visibility-aware lib, they should let the lib set the flags...

Whatever, I think it's easy to address this concern by setting "default" in an else branch:

#ifndef SECP256K1_API
# if defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD)
#  if defined(SECP256K1_FORCE_HIDDEN_VISIBILITY)
#   define SECP256K1_API extern __attribute__ ((visibility ("hidden")))
#  else
#   define SECP256K1_API extern __attribute__ ((visibility ("default")))
#  endif
# endif
#endif

What do you think?

@theuni
Copy link
Contributor

theuni commented Jun 3, 2025

Hm, that assumption sounds misguided to me. If a packager expects a visibility-aware lib, they should let the lib set the flags...

libsecp's in a pretty unique position here because it has no private non-static functions and only a handful of private vars. Most libs have a huge pile of functions and marking them all as hidden would be infeasible. Hence the compiler flag/default override combo.

Edit:
Arguably the lib is setting the flags if it sets CMAKE_C_VISIBILITY_PRESET to "hidden". For the reason I just mentioned, for most libs, that's more feasible. A packager then having that on by default in their toolchain file is just a noop.

Whatever, I think it's easy to address this concern by setting "default" in an else branch:

#ifndef SECP256K1_API
# if defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD)
#  if defined(SECP256K1_FORCE_HIDDEN_VISIBILITY)
#   define SECP256K1_API extern __attribute__ ((visibility ("hidden")))
#  else
#   define SECP256K1_API extern __attribute__ ((visibility ("default")))
#  endif
# endif
#endif

What do you think?

Sure, it's not conventional, but it gets the job done :)

@real-or-random
Copy link
Contributor Author

Sure, it's not conventional, but it gets the job done :)

Ok, I can update the PR tomorrow. :)


A packager then having that on by default in their toolchain file is just a noop.

Yep. So for visibility-aware libs, it's a noop, and for non-aware libs, it breaks the build. Sounds like an option you don't wanna have in your toolchain file. ;) Okay, just kidding, I guess there may be aware libs which leave setting the compiler detault to the user.

@TheCharlatan
Copy link

Not sure yet what to think of the two approaches, but it does strike me as very unfortunate that we are introducing more bespoke behaviour yet again. Can we at least only allow this being set when SECP256K1_DISABLE_SHARED is ON?

@real-or-random
Copy link
Contributor Author

real-or-random commented Jun 4, 2025

but it does strike me as very unfortunate that we are introducing more bespoke behaviour yet again.

I am not sure how bespoke this is, in the end. I think the ability to configure visibility for static builds is a valid use case in general, not specific to Bitcoin Core's planned use. It's just pretty niche... (That's different from #1678, which is merely a hack that works around a lack of functionality in CMake.)

edit: But if the goal is to avoid the need for any of these, then I believe the proper way is to abandon the idea of building a monolithic kernel. IIUC, then you won't need to the CMake workaround, and you can use -Wl,--exclude-libs,ALL, so you won't need a visibility override. But I assume there are good reasons to have a monolithic kernel?

Can we at least only allow this being set when SECP256K1_DISABLE_SHARED is ON?

In both PRs, the override is currently just an #if not exposed anywhere in the build systems. So if you set this via -D... on the command line, you need to expect that you're on your own. But as I said, I believe this is a valid feature, and so there's no good reason not to expose it in CMake and configure (could be done in a follow-up PR). And these will be the right places to raise an error when the override is used in a shared build.

The name should make it clear that this is intended for ELF platforms
only.
@real-or-random
Copy link
Contributor Author

Sure, it's not conventional, but it gets the job done :)

Ok, I can update the PR tomorrow. :)

Force-pushed this update.

Here's another variant of that approach: real-or-random@2d92ac3 Instead of an override to force "hidden", this has an override to do nothing. Then the user can use -fvisibility to set the visibility. That's a somewhat uncommon use of -fvisibility, but this variant has the advantage (?) that the override won't break the build for shared libs. But I'm not convinced that it's better in the end.

* solely for the potential case that the static library will be linked into
* a shared library. In that case, the stored visibility settings will
* resurface and be honored for the shared library. */
# define SECP256K1_API extern __attribute__ ((visibility("hidden")))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Win32 needs this branch too, no? Otherwise it'll still require -fvisibility=hidden for static libs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, maybe #if defined(SECP256K1_FORCE_HIDDEN_VISIBILITY) could just go at the top, and keep with the #ifndef(SECP256K1_API) pattern below it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Win32 needs this branch too, no? Otherwise it'll still require -fvisibility=hidden for static libs?

I don't think so (but I haven't tested this so far). As far as I understand, gcc defaults to hidden on Windows as soon as it sees an explicit __declspec(dllexport) annotation (and MSVC anyway exports only annotated symbols). At least this is what the libtool manual claims:

It should be noted that the GNU auto-export feature is turned off when an explicit __declspec(dllexport) is seen. The GNU tools do this to not make more symbols visible for projects that have already taken the trouble to decorate symbols.

https://www.gnu.org/software/libtool/manual/html_node/Windows-DLLs.html

Actually, maybe #if defined(SECP256K1_FORCE_HIDDEN_VISIBILITY) could just go at the top, and keep with the #ifndef(SECP256K1_API) pattern below it?

Not sure if I can follow. If you still think we'll need this for Windows, can you suggest/sketch a patch?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries.

Copy link
Contributor

@theuni theuni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 42d3133

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants