Skip to content
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

Should boost force symbol visibility if built as a static lib? #243

Closed
epvbergen opened this issue Sep 25, 2018 · 13 comments
Closed

Should boost force symbol visibility if built as a static lib? #243

epvbergen opened this issue Sep 25, 2018 · 13 comments

Comments

@epvbergen
Copy link

I'm consuming boost::asio as a static library in a project that is delivered as a shared library. To avoid conflicts in clients, I try to hide all symbols originating from my dependencies by linking with -Wl,--exclude-libs,ALL.

To my surprise, this does not work for boost::asio. A little research showed that at some point it was deemed a good idea to force default visibility on exception typeinfo and support classes for printing the exceptions from boost, see eg. https://svn.boost.org/trac10/ticket/4594 which proposed a #pragma visibility push default, only conditional on the compiler. So regardless of whether boost is built as a static or a dynamic library, the symbols get marked with default visibility on GCC.

And now I wonder if this is really the correct policy, or if boost (and any library) should rather refrain from explicitly setting visibility attributes when building a static library. Doesn't that make more sense?

Right now I have a nasty symbol leak that causes conflicts in my clients using a different version of asio. Updated: the problem has been reported before, along with a possible solution, see https://svn.boost.org/trac10/ticket/12722.

N.B.: moved here from boostorg/asio#145

@jzmaddock
Copy link
Collaborator

Confirmed, but this is an issue with throw_exception and should be reported here: https://github.com/boostorg/throw_exception. That header should be using BOOST_SYMBOL_VISIBLE rather than the push/pop's. I would guess that their workaround pre-dates the introduction of BOOST_SYMBOL_VISIBLE.

@epvbergen
Copy link
Author

I've reported it there and proposed a small patch to replace the push/pops with BOOST_SYMBOL_VISIBLE.

However, that only seems to be half the solution, since BOOST_SYMBOL_VISIBLE is always set to attribute((visibility("default"))) in config/compiler/gcc.hpp if GNUC >= 4, regardless of whether the boost build system is building a shared library or not.

In the end I need the ability to build my boost-using shared library with -fvisibility=hidden and -fvisibility=hidden-inlines, without individual symbols from Boost forcing themselves out of my interface.

I guess somehow or other, that implies adding an extra condition before setting BOOST_SYMBOL_VISIBLE to whatever the compiler needs.

A possibility could be a BOOST_INTERFACE_VISIBLE macro that either Boost itself can set when Boost is building a shared library, or that the user can set if she uses boost classes as part of the interface of her own shared library. What do you think?

@jzmaddock
Copy link
Collaborator

Ah my bad, we have an issue here, because there are 2 conflicting use cases:

  • Using boost - we want exceptions to be visible types, otherwise we can't catch them.
  • Hiding boost as an internal dependency - where we want all boost symbols to be hidden.

I'm not sure that we've ever really supported the second case - except as a sort of accident by having code that's not marked up.

I'll raise this on the mailing list, it's probably better to try and fix boost-wide (if we can) rather than each library doing it's own thing.

@epvbergen
Copy link
Author

Any feedback on this?

@jzmaddock
Copy link
Collaborator

I have an idea, but I'm working on another bug report right now.... nag me if I'm not back to you shortly...

@jzmaddock
Copy link
Collaborator

My suggestion was going to be a macro - say BOOST_DISABLE_VISIBLE_SYMBOLS - which when set by the user would result in the BOOST_SYMBOL_* macros being set to nothing. That would then allow you to hide everything. I have a suspicion that some things (exception classes and some of the serialization lib) will not then work correctly, but I'm not sure?

@Lastique
Copy link
Member

Lastique commented May 19, 2019

I don't think this is a good idea. This may break exceptions and RTTI support. Boost.Log relies on this, for example. Other libraries, probably, too (Boost.Any comes to mind).

One example where making all symbols hidden may make sense is when a user builds a monolithic executable that does not use any other shared libraries, or uses them without passing any Boost-related types, including exceptions. Those cases can be covered by linker scripts, there's no need to change Boost for that.

The particular issue with Boost.ASIO though should be reported to upstream ASIO. I see no reason why it should mark all of its symbols visible (see here and here). But it may need to mark so exceptions, selectively. This is not a Boost.Config issue and changing Boost.Config will not fix Boost.ASIO and it will break other libraries.

@uecasm
Copy link

uecasm commented May 20, 2019

If you are wanting to hide all internal usage of Boost, and you are:

  1. Building a single shared library, and not a static library.
  2. Using Boost as a static library, not as shared libraries.
  3. Not using Boost types in your own library's interface (at all; not even in "private" parts of public header files). (This may also require you to use explicit visibility for your own library's symbols too.)
  4. Catching all Boost exceptions at your interface boundary and converting them to some other (visible) exception type.

Then it is probably safe (and correct) to disable Boost symbol visibility across the board. Violating any one of those four constraints would break things, however.

@nigels-com
Copy link

I meet all four criteria above. Question is how to force all the symbols private?

@Lastique
Copy link
Member

Use linker scripts.

@weglimir
Copy link

I came accross exactly the same problem, where our dynamic library is one of huge amount of custom libraries in multi plug-in environment. Disabling ASIO symbols with macro switch partially helped, as all symbols in header like boost system still use BOOST_SYMBOL_VISIBLE and this caused to crash in my case by throwing an exception from asio. I guess this case is quite uncommon, as in my example there are 100 dynamic libs which work in independent way, and are not interacting with each other at all. That being said I think it would be very helpfull if there would be any way to simply diable BOOST_SYMBOL_VISIBLE by overriding it to nothing during compilation.

@jcelerier
Copy link
Contributor

Use linker scripts.

that only works on platforms which supports those. What's wrong with just adding a BOOST_HIDE_ALL_SYMBOLS macro which would define BOOST_SYMBOL_EXPORT and BOOST_SYMBOL_VISIBLE to the relevant platform attribute for that ?

in addition, doing this at the linker stage prevents compiler optimizations that could be done based on symbol visibility.

@jcelerier
Copy link
Contributor

See #441 for a patch

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

No branches or pull requests

7 participants