Overriding visibility of template instances

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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<base, aux>::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<typename T, typename A>
struct intermediate : public T {

	intermediate();

	void initialize();

	~intermediate();
};

struct __attribute__((visibility("default"))) child;

struct child : intermediate<base, aux> {

	child();

	~child();
};

###############################################################
##
## lib.cpp

#include "header.H"

template<typename T, typename A>
intermediate<T, A>::intermediate()=default;

template<typename T, typename A>
intermediate<T, A>::~intermediate()=default;

template<typename T, typename A>
void intermediate<T, A>::initialize()
{
}

///////////////////////////////////////////////////////////////////////
//
// UNCOMMENT TO FIX THE LINK ERROR
//
// template void __attribute__((visibility("default")))
//           intermediate<base, aux>::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<base, aux>::initialize in order to compile the vtable for the impl class.

aux has hidden visibility, and gcc 10 instantiates intermediate<base, aux> 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<base, aux>::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<typename T, typename A>
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?


Attachment: pgpnHbJIb3Umw.pgp
Description: PGP signature


[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux