I ran into an issue, a link failure, while building with gcc 10 my shared
library and a program that links with the shared library. I believe that
older versions of gcc/binutils did not result in this link failure, this is
somethink new.
For a general explanation: I am trying to minimize which symbols my shared
library exports. To that end I make judicious use of hidden symbol
visibility, marking entire classes as hidden, and not making template
implementations publicly visible as well; instantiating entire templates
with hidden visibility; as well as not making the template implementations,
only declarations.
I should also mention that I understand the reason for the following link
failure, and I am trying to figure out the best way to fix it, but I'm
running into a brick wall, that I'll explain after the minimal code dump.
The following results in this link failure:
/usr/bin/ld: prog.o:(.data.rel.ro._ZTV4impl[_ZTV4impl]+0x20): undefined
reference to `intermediate::initialize()'
###############################################################
##
## Makefile
LTOFLAGS =
all: prog
prog: prog.o lib.so
g++ -o prog $(LTOFLAGS) prog.o lib.so
prog.o: prog.cpp header.H Makefile
g++ $(LTOFLAGS) -c -fpic -DPIC -o prog.o prog.cpp
lib.so: lib.o
g++ -shared $(LTOFLAGS) -o lib.so lib.o
lib.o: lib.cpp header.H Makefile
g++ $(LTOFLAGS) -c -fpic -DPIC -o lib.o lib.cpp
###############################################################
##
## header.H
struct base {
base();
virtual ~base();
///////////////////////////////////////////////////
//
// REMOVE VIRTUAL TO FIX THE LINK ERROR:
virtual void initialize();
};
///////////////////////////////////////////////////////////////
//
// REMOVE THE HIDDEN ATTRIBUTE TO FIX THE LINK ERROR:
struct __attribute__((visibility("hidden"))) aux {};
template
struct intermediate : public T {
intermediate();
void initialize();
~intermediate();
};
struct __attribute__((visibility("default"))) child;
struct child : intermediate {
child();
~child();
};
###############################################################
##
## lib.cpp
#include "header.H"
template
intermediate::intermediate()=default;
template
intermediate::~intermediate()=default;
template
void intermediate::initialize()
{
}
///////////////////////////////////////////////////////////////////////
//
// UNCOMMENT TO FIX THE LINK ERROR
//
// template void __attribute__((visibility("default")))
// intermediate::initialize();
//
base::base()=default;
base::~base()=default;
void base::initialize()
{
}
child::child()=default;
child::~child()=default;
###############################################################
##
## prog.cpp
#include "header.H"
struct impl : public child {
};
int main()
{
impl i;
return 0;
}
###############################################################
##
## End of example
The problem is that prog.cpp needs a reference to intermediate::initialize in order to compile the vtable for the impl class.
aux has hidden visibility, and gcc 10 instantiates intermediate
with all of its symbols having hidden visibility.
The link failure goes away if I do not define base::initialize as virtual,
so that class method is not needed for the vtable, or I do not declare the
"aux" class as having hidden visibility. The aux class is not needed, and
only triggers the hidden visibility of any instance of the intermediate
template.
The link failure also goes away if I explicitly instantiate
intermediate::initialize with default visibility in lib.cpp.
prog.cpp does not need the actual definition of this method, it doesn't
invoke it, and only needs its symbol.
I am trying to figure out if there's any way to fix this linking failure
while:
1) having "aux" keep its hidden visibility
2) and without having to explicitly instantiate every instance of
intermediate::initialize() with default visibility
Adding __attribute__((visibility("default"))) to the declaration of the
template class method still results in the link failure:
void initialize() __attribute((visibility("default")));
Explicitly declaring default visibility for the template still results in a
link failure:
template
struct __attribute((visibility("default"))) intermediate : public T {
Sprinkling push/pop visibility pragmas also makes no difference. That's all
I could think of. Any other options I overlooked?