cygwin, g++, templates, and DLLs

Charles Wilson cygwin@cwilson.fastmail.fm
Wed Apr 19 01:20:00 GMT 2006


I've been struggling with this problem for over a week now, and I've 
read every old (and not so old) thread on cygwin, mingw (and even MS) 
mailing list I could find.

(1) you have a DLL whose source code includes a template class.
(2) that template class has a static member variable
(3) the DLL and the DLL's client both use a specific instantiation of 
that template.

But the DLL has one copy of the static member var, and the app has a 
different copy.

Now, because only Comeau has implemented the 'export' keyword for 
templates, you really can't "export" a template from a DLL. From my 
reading, it appears that instead, you must export a specific complete 
instantiation:

template class __declspec(dllexport|dllimport) MyTemplate< int >;

where the declspec argument is dllexport or dllimport, depending on 
whether you're building the DLL or using the DLL.  That kinda makes 
sense.  Except it doesn't always work...

I've uploaded a test case
ftp://cygutils.fruitbat.org/pub/cygutils/TestCase.tar.bz2

which contains the actual source code [*]

     352  TestCase/TestDLL/force.cpp
     136  TestCase/TestDLL/force.h
     271  TestCase/TestDLL/init.cpp
     341  TestCase/TestDLL/init.h
    1222  TestCase/TestDLL/TestDLL.h
    1271  TestCase/TestDLL/TestT.cpp
     603  TestCase/TestDLL/TestT.h
     335  TestCase/TestExe/main.cpp

and the pre-precessed code with a Makefile that builds the DLL and App.

  820739  TestCase/force.ii
  820622  TestCase/init.ii
  820768  TestCase/main.ii
     372  TestCase/Makefile

[*] TestDLL.h #includes 'Support/config.h' which is an entry point into 
a whole mess of configury junk that I could not publish.  As far as this 
test case is concerned, config.h's job is simply to ensure that the 
macros in TestDLL.h are properly defined (dllexport, dllimport, etc) 
So, since I couldn't include that stuff, the Makefile uses the 
pre-processed .ii files directly.

If you compile and link this test, you'll see the following generated 
output:

$ ./TestExe
creating new list<T>
ADDING: dll-static
The FULL List:
dll-static
~~~~~~~~~~~~~~~~~~~~~~
The DLL is loaded
ADDING: dll-call-into
The FULL List:
dll-static
dll-call-into
~~~~~~~~~~~~~~~~~~~~~~
ADDING: dll-stack
The FULL List:
dll-static
dll-call-into
dll-stack
~~~~~~~~~~~~~~~~~~~~~~
creating new list<T>    <<<<<< [1]
ADDING: APP-stack
The FULL List:
APP-stack               <<<<<< [2]
~~~~~~~~~~~~~~~~~~~~~~
ADDING: APP-explicit
The FULL List:
APP-stack               <<<<<< [3]
APP-explicit            <<<<<< [3]
~~~~~~~~~~~~~~~~~~~~~~
ADDING: dll-call-into
The FULL List:
dll-static
dll-call-into
dll-stack
dll-call-into
~~~~~~~~~~~~~~~~~~~~~~

At [1], a *second* static data member is allocated for this template 
specialization.  That's bad.  At [2], an entry is added (from the app) 
into this empty data member -- when it *should* be added into the data 
member allocated by the DLL, and which already contains three other 
entries, instead.  At [3], same song, second verse.  However, if the app 
calls a function in the DLL, which THEN calls a static method on the 
template specialization, the "correct" static member var -- the one in 
the DLL -- is modified.  So if the app call that template class's static 
method directly the "wrong" member var is modified -- but the app can 
call "indirectly" thru a different function exposed by the DLL.


    Inspecting the .ii files, we see that in init.ii we have
template class __attribute__((dllexport)) TESTDLL::TestT< std::string >;
    and in force.ii we ALSO have
template class __attribute__((dllexport)) TESTDLL::TestT< std::string >;

Looking in the .o's for the static data member ('theList') using 'nm -a 
--demangle force.o | grep theList', we get:

force.o:
00000000 d .data$_ZN7TESTDLL5TestTISsE7theListE
00000000 D TESTDLL::TestT<std::string>::theList
init.o:
00000000 d .data$_ZN7TESTDLL5TestTISsE7theListE
00000000 D TESTDLL::TestT<std::string>::theList

This is okay, because the ld knows how to merge these duplicates when 
linking the DLL, as we can see by using objdump -t on the DLL.  That 
shows only one copy in the symbol table:
[ 16](sec  2)(fl 0x00)(ty   0)(scl   3) (nx 1) 0x00000140 
.data$_ZN7TESTDLL5TestTISsE7theListE
[5239](sec  2)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000140 
TESTDLL::TestT<std::string>::theList

This is all exactly as I'd expect.  So now, let's look at the app:

in the main.ii file, I see
template class __attribute__((dllimport)) TESTDLL::TestT< std::string >;
which is supposedly The Right Thing To Do.

however, using nm on main.o, I get
00000000 d .data$_ZN7TESTDLL5TestTISsE7theListE
00000000 D TESTDLL::TestT<std::string>::theList
which is exactly what appears in the DLL .o's -- and that's bad.


Can anybody tell me what's really going on?  I've two (feared) 
possibilities:

2005-08-10:
https://sourceforge.net/tracker/?func=detail&atid=102435&aid=1255376&group_id=2435
where Danny says "A patch I recently submitted to GCC
fixes the bug on trunk (4.1). The patch is undergoing revison
to make it acceptable." but there is no indication it actually made it 
in to 4.1...

OR the "mystery bug" danny mentioned at that same link ("is due to YA 
dllimport bug (that one was about template instantiations erroneously 
being marked as dllimport)" but which I can find no other mention, nor a 
link to the gcc-patches list.  (Of course, it seems MY problem is that a 
template instantiation, explicitly declared dllimport, is NOT getting 
marked properly).


Even if one of these two issues is the problem -- and has been fixed -- 
it means I must build a 4.1 compiler...is that true, or <wild hope> is 
there are very simple fix that I'm just not seeing? </wild hope>

So, Assorted Smart People Who Know About Cygwin/Mingw-GCC Internals, any 
advice?

--
Chuck


--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/



More information about the Cygwin mailing list