Reputation: 2949
What is the rational behind the fact that compiling
namespace ns __attribute__((visibility("default"))) {
template<typename T>
inline int func1(const T& x) {
return x;
}
inline int func2(int x) {
return x;
}
struct test {
template<typename T>
int func3(const T &x) { return x; }
int func4(int x) { return x; }
};
}
int __attribute__((visibility("default"))) client(int x) {
ns::test t;
const int y1 = ns::func1(x);
const int y2 = ns::func2(x);
const int y3 = t.func3(x);
const int y4 = t.func4(x);
return y1 + y2 + y3 + y4;
}
with
g++ -Wall -fPIC \
-fvisibility=hidden -fvisibility-inlines-hidden \
-shared -o libtest.so test.cpp
yields a library exporting ns::test::func1<int>()
and ns::test::func3<int>()
but not ns::func2()
nor ns::test::func4()
? Both template functions are defined inline
and -fvisibility-inlines-hidden
tells the compiler to hide them — or at least their instantiations which hopefully are inline too.
Hiding the function templates func1
and func3
explicitly, i.e. with
template<typename T>
int __attribute__((visibility("hidden"))) func(const T &x) { return x; }
leads to the expected behavior. Omitting the default visibility in the namespace definition hides the two instantiations.
Background: We try to minimize the amount of visible symbols in our library. Thus we're using the mentioned compiler flags and attributes. Of course this is also necessary for all static third party libraries. But unfortunately those inline template functions inside included header files are completely out of our control. For example, every instantiation of
namespace std {
template<typename _CharT, typename _Traits, typename _Alloc>
inline bool
operator==(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
const _CharT* __rhs)
{ return __lhs.compare(__rhs) == 0; }
}
from #include <string>
will happily generate a publicly visible symbol inside my library. And the most annoying part, it will be put inside my library without any version information, so no GLIBCXX_3.x.z
to differentiate.
Bonus Question: What's the overall impact using a linker script like
{
global:
_Z6clienti;
local:
*;
};
As far as I understand, this is only really feasible, if I don't use any exception handling nor dynamic type casting across the boundaries of my library. But what happens with those inline functions? It feels like this whole hidden visibility thing violates the one definition rule anyway, thus it's not a big deal.
Upvotes: 11
Views: 1614
Reputation: 2409
GCC did not respect -fvisibility-inlines-hidden
for template functions; it was a bug that was fixed starting from gcc-10.
With GCC 10, all four functions have hidden visibility (command line flag takes precedence over visibility attribute specified on the enclosing namespace).
Upvotes: 2