I want to statically link libc++ and libc++abi into my ELF DSO (shared
library). (Statically linking avoids incompatibilities with other
libc++/libstdc++/etc. loaded at runtime by other DSO-s.) (My DSO exports
no C++ APIs, imports no C++ APIs, does not throw or catch exceptions
across DSO boundaries, does not require callers to delete returned
pointers, etc.)
To achieve this, I am using -fvisibility=hidden,
-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS, and
-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS (and some other tweaks) when
compiling libc++, libc++abi, and my own code. I build libc++ and libc++abi
as static libraries and link them into my DSO.
This seems to work well. However, Clang forces some libc++abi symbols to
default visibility. This puts these symbols in my DSO's dynamic export
table. Thus:
* Linking against my DSO may inadvertently use my DSO's symbols instead of
the symbols you expect (e.g. the system libstdc++'s symbols).
* It also means my DSO's references to its libc++abi symbols may be
replaced at runtime with another DSO's symbols (e.g. the system
libstdc++'s symbols).
From what I have found so far, default visibility for libc++abi symbols is
forced in two places in Clang:
Is there a reason these symbols *must* have default visibility? I
understand that, in general, typeinfo should have default visibility for
exception handling to function properly. Does an opt-in option to change
the visibility of these symbols (for use cases like mine) sound reasonable?
Typically the way people do this is by passing a --version-script to the linker, which lets you include/exclude symbols as you wish regardless of the visibility attributes.
Even more so when building against libstdc++ headers, which forces nearly everything to have default visibility, no matter what.
This creates typeinfo definitions for the builtin types, such as typeinfo(int). They are automatically emitted by Clang
when it is building libc++abi (https://godbolt.org/g/OtdstM). Typeinfo equality is fundamentally
based on the uniqueness of symbols. If there are two definitions of a type’s typeinfo in a program things like
dynamic_cast and exception catch blocks may stop working.
At least that’s my understanding of why those symbols are forced to have default visibility.
The global new/delete are *universally* replaceable. If you have a
hidden definition it can't be replaced.
If the user specifies -fvisibility=hidden, aren't they saying they
explicitly don't want a replaceable version?
Typeinfo equality is fundamentally based on the uniqueness of symbols.
If there are two definitions of a type's typeinfo in a program things
like dynamic_cast and exception catch blocks may stop working.
I suspected that, but I recall seeing a fallback path in case the
uniqueness guarantee isn't met. Maybe I misread.
But if I specify __attribute__((visibility("hidden"))), another DSO
shouldn't reference the type (with dynamic_cast or catch()), so shouldn't
hidden visibility be allowed?