they logically should be hidden by default
C++ requires all functions (including inline
functions) to have the same address in all translation units. Local statics should also be shared among all translation units. Making inline functions hidden violates those requirements if program is built as multiple shared objects (.so files).
Inline functions should have public visibility so that dynamic linker could choose one definition from all existing ones at runtime.
GCC wiki mentions this:
-fvisibility-inlines-hidden
can be used with no source alterations, unless you need to override it for inlines where address identity is important either for the function itself or any function local static data.
Consider following example. Executable source:
// main.cpp
#include <cstdio>
struct A { };
inline A &foo()
{
static A a;
return a;
}
int main()
{
void *p = &foo();
std::printf("main() - %p\n", p);
}
Shared object source:
#include <cstdio>
struct A { };
inline A &foo()
{
static A a;
return a;
}
static __attribute__((constructor)) void init()
{
void *p = &foo();
std::printf("main() - %p\n", p);
}
If you build both and link executable against this shared object, you can see that foo
always returns the same address in both translation units.
Now if you add __attribute__((visibility("hidden")))
to those inline functions, then you will see that address is different in different translation units.
This is not what some C++ program might expect.
Most people today think that inline
has nothing to do with actual function inlining. This is not exactly true. ELF targets try to make dynamic linking transparent e.g. program should behave identically if it is build as a single executable or as multiple shared objects.
To make it possible ELF requires all functions with public visibility to be called though GOT or through PLT as if it is "imported" function. This is required so that every function could be overriden by another library (or executable itself). This also forbids inlining for all public non-inline functions (see section 3.5.5 here which shows that public function call in PIC should be made through PLT).
Code inlining for public inline functions is possible exactly because inline
allows program behavior to be undefined when multiple definitions of inline function are not equivalent.
Interesting note: Clang violates this ELF requirement and is able to inline public functions on ELF targets anyway. GCC can do the same with -fno-semantic-interposition
flag.