delorie.com/archives/browse.cgi   search  
Mail Archives: cygwin/2007/11/25/06:43:49

X-Recipient: archive-cygwin AT delorie DOT com
X-Spam-Check-By: sourceware.org
Date: Sun, 25 Nov 2007 12:43:21 +0100
From: Peter Novak <peter DOT novak AT tu-clausthal DOT de>
To: cygwin AT cygwin DOT com
Subject: [app porting to cygwin] Invoking DLL exported routine problem
Message-ID: <20071125114321.GS5783@aronde.net>
Mime-Version: 1.0
X-PGP-Key: http://peter.aronde.net/publickey.asc
X-IsSubscribed: yes
Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm
List-Id: <cygwin.cygwin.com>
List-Subscribe: <mailto:cygwin-subscribe AT cygwin DOT com>
List-Archive: <http://sourceware.org/ml/cygwin/>
List-Post: <mailto:cygwin AT cygwin DOT com>
List-Help: <mailto:cygwin-help AT cygwin DOT com>, <http://sourceware.org/ml/#faqs>
Sender: cygwin-owner AT cygwin DOT com
Mail-Followup-To: cygwin AT cygwin DOT com
Delivered-To: mailing list cygwin AT cygwin DOT com

--kak5YK1pHfL2v46D
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

Hello,
I am struggling with porting my application from Linux to Cygwin and
as I am not 100% at home in Cygwin regarding DLLs and friends, I hope to
get some advice, or hints here. I apologize in advance for a question
looking like a newbie cry for help with "everything", but I tried my
best to study the scarce resources on magic of building DLLs and loading
them in Cygwin without a result.

In short, I have a program using plug-ins implemented as DLLs providing
a fixed interface in a form of a C++ class and C exported routines. The
problem is that loading the DLL and invoking the exported C functions
works perfectly well when loaded from a simple testing code, however
fails when the same code is embedded within a more complex application
setting. I suspect a corrupted stack, or some improper pointer
conversion which I do not understand. Obviously a return type of the
routine seems to be important somehow...

As I said, my application loads DLL plug-ins. However, as I need several
independent instances of the same DLL to be loaded at the same time
(plug-ins can be loaded several times and serve completely independently
with different data fed to them), for each I fork a separate process
which loads a single plug-in DLL and communicates with the main process
via shared memory and mutex signaling. This all works well on Linux and
I managed to compile my application successfully under cygwin as well
(everything including the shared memory communication works and is
ported correctly). What does not work is invocation of certain functions
exported from the DLL plug-ins. That means that dl_openext and dl_sym
work properly, however invocation of routing inside the DLL makes the
subprocess crash. The same code, when put to a standalone testing
program without subprocesses works however correctly.

The interface of the DLL includes code similar to the following:

extern "C"=20
{
	unsigned int get_api_version()
	{
		return 123;
	}

	CInterface* register_api()
	{
		CMyInterface pModule =3D new CMyInterface;
		return pModule;
	}
}

where CMyInterface is a subclass of a purely abstract (all methods are
virtual) root class CMyInterface. All the following operation the
plug-in is to perform are invoked as method calls on the instance of
CInterface. The pattern is modeled according to DLL C++ API from
http://aegisknight.org/cppinterface.html.

The code which loads the DLL uses libtool's ltdl library and invokes the
two functions looks similar to the following (error checking omitted):

	// initialize ltdl
        lt_dlinit();

        // open the library (jztemplate is the name of my library above)
        lt_dlhandle m_hLibrary =3D lt_dlopenext("cygjztemplate-0");

        // find the KR module API version routine <-- WORKS WELL
        lt_ptr hVersionFunc =3D lt_dlsym (m_hLibrary, "get_api_version");

	// Invocation of the=20
        std::cout << ((TPtrVerFunc)hVersionFunc)() << std::endl;

	// find the interface loading routine <-- WORKS WELL
        lt_ptr hLoadFunc =3D lt_dlsym (m_hLibrary, "regsiter_api");

	// get the instance of the interface <-- PROBLEM HERE
	CInterface* pModule =3D ((TPtrLoadFunc) hLoadFunc)();
=09
	//...

Signatures of the DLL exported functions are defined as:

	typedef unsigned int (*TPtrVerFunc)();
	typedef CInterface* (*TPtrLoadFunc)();


PROBLEM:
When the code above is invoked from a simple testing program (that code
is wrapped into a main() function) it works without a problem. If
however, it is invoked from a subprocess of my complex application, it
crashes on invocation of the hLoadFunc. However hVersionFunc is invoked
correctly as many times as I call it.

Finally, let me also list the linking options for the main program,
testing program and the library itself.

*** Main program
g++ -DDEBUG -DLOGGING_ON -Wall -DPACKAGELIBDIR=3D\"/usr/local/lib/jazzyk\"
-g -O2 -L/usr/lib -o app.exe app.o (other *.o files omitted)
-lboost_program_options-gcc-mt -lltdl


*** Testing program:
g++ Test.cpp -o test.exe -I/usr/include/boost-1_33_1/ -lltdl


*** DLL library (as produced by automake/libtool):
/bin/sh ./libtool --tag=3DCXX   --mode=3Dcompile g++ -DHAVE_CONFIG_H -I.  -g
-DJZMODULEDLL   -g -O2 -MT libjztemplate_la-module.lo -MD -MP -MF
.deps/libjztemplate_la-module.Tpo -c -o libjztemplate_la-module.lo `test
-f 'module.cpp' || echo './'`module.cpp

mkdir .libs

g++ -DHAVE_CONFIG_H -I. -g -DJZMODULEDLL -g -O2 -MT
libjztemplate_la-module.lo -MD -MP -MF .deps/libjztemplate_la-module.Tpo
-c module.cpp  -DDLL_EXPORT -DPIC -o .libs/libjztemplate_la-module.o

g++ -DHAVE_CONFIG_H -I. -g -DJZMODULEDLL -g -O2 -MT
libjztemplate_la-module.lo -MD -MP -MF .deps/libjztemplate_la-module.Tpo
-c module.cpp -o libjztemplate_la-module.o >/dev/null 2>&1

mv -f .deps/libjztemplate_la-module.Tpo
.deps/libjztemplate_la-module.Plo

/bin/sh ./libtool --tag=3DCXX   --mode=3Dlink g++  -g -O2 -no-undefined
-version-info 0:0:0  -o libjztemplate.la -rpath /usr/local/lib
libjztemplate_la-module.lo=20=20

g++ -shared -nostdlib   .libs/libjztemplate_la-module.o
-L/usr/lib/gcc/i686-pc-cygwin/3.4.4
-L/usr/lib/gcc/i686-pc-cygwin/3.4.4/../../.. -lstdc++ -lgcc -lcygwin
-luser32 -lkernel32 -ladvapi32 -lshell32 -lgcc   -o
.libs/cygjztemplate-0.dll -Wl,--enable-auto-image-base -Xlinker
--out-implib -Xlinker .libs/libjztemplate.dll.a


For completeness here is the output of nm on the DLL:
65881000 T _get_api_version
65881030 T _register_api

65881380 t __GLOBAL__D_get_api_version
65881370 t __GLOBAL__I_get_api_version

Notice, that for get_api_version, there are also two entries with prefix
__GLOBAL, while for the register_api there's nothing like that. Does
that mean anything? I tried to add other functions to the interface and
invoking such which had only a one entry was also OK. It seems to be
rather the return type which causes problems.

Also note that pointers returned by dl_sym correspond exactly to those
in the first two entries of type "T".

Is there perhaps some issue hidden in the interplay of forked
subprocesses which load DLLs I should know about? Obvioulsy fork on
cygwin is a non-trivial issue itself. And why does libtool generate DLL
which contains for some routines a single entry, but for some several?
Is it significant?

I would be extremely thankful if somebody could direct me to something
suspicious in what I described above, or possibly point out some facts
regarding DLLs in Cygwin which might help me resolve the problem.

Thanks in advance,

Peter Novak.


--kak5YK1pHfL2v46D
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQFHSV/ZwqpFHLvXwlwRAqeKAJ9ZhHU3cb2d8fR3X3wgk/wT8hQjXwCgkK4U
Qp9MOg1OveTRpiIZ/cXX5ik=
=mIcr
-----END PGP SIGNATURE-----

--kak5YK1pHfL2v46D--

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019