X-Spam-Check-By: sourceware.org Message-ID: <44458EAE.8000601@cwilson.fastmail.fm> Date: Tue, 18 Apr 2006 21:13:18 -0400 From: Charles Wilson User-Agent: Thunderbird 1.5 (Windows/20051201) MIME-Version: 1.0 To: cygwin AT cygwin DOT com Subject: cygwin, g++, templates, and DLLs Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com 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 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 <<<<<< [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::theList init.o: 00000000 d .data$_ZN7TESTDLL5TestTISsE7theListE 00000000 D TESTDLL::TestT::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::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::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 is there are very simple fix that I'm just not seeing? 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/