-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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
[libc++] After 3583bf3ad8c5, building Firefox results in hidden symbol errors #79027
Comments
Yes, this should work. However, you are incorrect that the symbols just uses "whatever is currently active". The explicit instantiation has a |
Hmm, is that so? In
in the // append
template <class _CharT, class _Traits, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string<_CharT, _Traits, _Allocator>&
basic_string<_CharT, _Traits, _Allocator>::append(const value_type* __s, size_type __n) {
... I see |
Yes, you are. The explicit instantiations are listed in |
This should work, but I do agree this seems like a Clang bug since we should still be applying the required visibility attribute on everything that we externally instantiate. I think we'll need to reduce your test case. |
I let cvise do some brute-forcing of the preprocessed version of the above testcase, and it came up with the following: // clang++ -fvisibility=hidden -fvisibility-inlines-hidden -fPIC -shared hidden-append.cpp -o hidden-append-works.so
// clang++ -DPUSH_HIDDEN -fPIC -shared hidden-append.cpp -o hidden-append-fails.so
#ifdef PUSH_HIDDEN
#pragma GCC visibility push(hidden)
#endif
namespace std {
inline namespace __1 {
template <class _Tp> using __make_unsigned_t = __make_unsigned(_Tp);
template <class> struct make_unsigned {
using type = __make_unsigned_t<long>;
};
template <class> struct char_traits;
template <class> class allocator;
template <class _CharT, class = char_traits<_CharT>, class = allocator<_CharT>>
struct basic_string {
void append(const char *, make_unsigned<long>::type);
};
} // namespace __1
} // namespace std
void foo(std::basic_string<char> &out, char const *in, unsigned long len) {
out.append(in, len);
}
But I'm not sure if this may be too far reduced? |
The linker failure is due to an undefined hidden symbol, I think Clang's behavior is correct and matches GCC ( In general, (Clang's visibility has some minor issues related to template insantiations, https://reviews.llvm.org/D154774, but this is not that case.) |
Does this mean that we basically can’t rely on the namespace-level attribute being inherited by declarations inside the namespace because a pragma outside our control could override it? That seems inconvenient. |
I do wonder whether that’s something we want to support. At the end of they day the user is setting a pragma and then including the standard library — to some extent we are doing what the user is asking. |
It's a tricky question. I think that what Mozilla is doing is a Very Big Hammer, so maybe they should opt for something more subtle. On the other hand, they seem to have been getting away with this trick for a very long time, and the obvious reply to a bug on their tracker about this would be: "but libstdc++ has no problems with this" :) (And that is because libstdc++ marks the whole std namespace with On the other hand, there is some text in gcc's Wiki doc about visibility (https://gcc.gnu.org/wiki/Visibility) where they mention:
I think that "affects extern declarations as well" is the key here, and clang is behaving the same way: using a hidden visibility pragma before including anything is force-hiding everything that not explicitly says it is non-hidden. The biggest hammer, maybe questionable if it is worth supporting. That said, I am unsure if clang has behaved differently in the past with regards to the |
I'm investigating a build breakage of Firefox with very recent libc++ (as of
llvmorg-18-init-2850-g
3583bf3), on FreeBSD 15-CURRENT. What happens is that most of the build goes well, until it tries to link its main shared library,libxul.so
:After much digging, the reason for these symbols being hidden turns out to be a little trick Mozilla uses to limit the amount of visible symbols in
libxul.so
: the file https://github.com/mozilla/gecko-dev/blob/master/config/gcc_hidden.h which gets included in front of every.cpp
file, using-include /wrkdirs/usr/ports/www/firefox/work/firefox-122.0/config/gcc_hidden.h
.The file contains nothing but a
#pragma GCC visibility push(hidden)
line, so that everything following is hidden, including libc++ headers such as<string>
,<vector>
, etc.Before commit 3583bf3, this made no difference since most of the templates used in libc++ headers are prefixed with
_LIBCPP_TEMPLATE_VIS
, and this expanded to either__attribute__((__type_visibility__("default")))
(for clang) or__attribute__((__visibility__("default")))
(for gcc), which would override the#pragma GCC visibility push(hidden)
state.After commit 3583bf3 however, this no longer happens:
_LIBCPP_TEMPLATE_VIS
is defined empty if clang is used, and whatever visibility is "active" due to such a pragma is used for most templates in the libc++ headers.So this example shared library:
Will not compile and link anymore with libc++ 18:
Now I am aware that Firefox could use
-fvisibility=hidden
and maybe also-fvisibility-inlines-hidden
to achieve the same effect, and that does indeed seem to work, at least for the above simple test case.But before I go to Mozilla and report this as a bug in their tracker, I would like to have an official word from the libc++ maintainers about whether this whole use case is supposed to work at all.
That is: can you do
#pragma GCC visibility push(hidden)
(or its clang equivalent, if it exists), and then expect to include any libc++ headers without problems? If so, commit 3583bf3 would have to be adjusted to make it possible, since it no longer works now.Or: is this kind of messing with visibility around libc++ headers officially unsupported, and would applications attempting to use such methods have to be adjusted?
The text was updated successfully, but these errors were encountered: