11

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.

4
  • 2
    The name "default" is somehow mis-named, at least counter-intuitive. It actualy means "public". The overall compiler options -fvisibility=hidden or -fvisibility-inlines-hidden does not override function attributes. In your code, all function within namespace ns are marked as having public visibility. And the compiler can thus export names of inline function. See gcc.gnu.org/onlinedocs/gcc/… Commented Sep 6, 2018 at 12:34
  • 1
    @Oliv: But this doesn't explain the difference between the inline functions and the instantiations of inline template functions. Commented Sep 6, 2018 at 12:40
  • Indeed, If that guy [the compiler] has any way of making a mistake, he will. -Edward A. Murphy Commented Sep 6, 2018 at 12:53
  • Just to be sure we understand each other, by declaring publicly visible inline function you let the compiler a choice: either it generates the function code and export the function name, or it does not generates the function code and does not export the function name. You are luckier than Murphy, because Murphy's technician would have exported the four function names! Commented Sep 6, 2018 at 13:12

1 Answer 1

2

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).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.